diff options
91 files changed, 23917 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..d9112e73 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +naba.kumar@nokia.com +alberto.mardegan@nokia.com +zeeshan.ali@nokia.com diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..c4792dd2 --- /dev/null +++ b/COPYING @@ -0,0 +1,515 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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 +^L + 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. + + + <one line to give the library's name and a brief idea of what it +does.> + Copyright (C) <year> <name of author> + + 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..150a52fa --- /dev/null +++ b/ChangeLog @@ -0,0 +1,2923 @@ +2007-03-09 Naba Kumar <naba.kumar@nokia.com> + + * configure.ac: Version 4.17. + + * configure.ac, server/Makefile.am: Fixed server conditional build + and simplified enable args. + + * configure.ac: Removed date from version number. + * test/Makefile.am, configure.ac, server/Makefile.am, + Makefile.am: Fixed conditional build of server and test. + +2007-03-09 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac, server/Makefile.am, + server/com.nokia.chavo.mission_control.service.in, + server/org.freedesktop.Telepathy.MissionControl.service.in, + xml/mcd-dbus-services.xml, libmissioncontrol/mission-control.h, + src/mcd-service.c: + Change DBus paths to org.freedesktop.Telepathy.MissionControl. + + * Makefile.am, com.nokia.chavo.mission_control.service.in, + configure.ac, server/Makefile.am, test/Makefile.am, test/mc-server.c: + Moving mc-server into a separate directory, and add a configure switch + to install it as the mission-control server. Move the DBus service + file to the same directory. + +2007-03-05 Naba Kumar <naba.kumar@nokia.com> + + * configure.ac, debian/changlog: Bumped version to 4.16. + + * (removed) LICENSE: Removed because it is redundant with COPYING file. + + * test/mc-server.c, test/mc-client.c, debian/copyright, + src/mcd-master.h, src/mcd-presence-frame.h, src/mcd-mission.c, + src/mcd-controller.c, src/mcd-chan-handler.c, src/mcd-mission.h, + src/mcd-controller.h, src/mcd-chan-handler.h, src/mcd-connection.c, + src/mcd-operation.c, src/mcd-service.c, src/mcd-connection.h, + src/mcd-operation.h, src/mcd-service.h, src/mcd-channel.c, + src/mcd-manager.c, src/mcd-channel.h, src/mcd-dispatcher-context.h, + src/mcd-manager.h, src/mcd-proxy.c, src/mcd-main.c, + src/mcd-dispatcher.c, src/mcd-debug.c, src/mcd-proxy.h, + src/mcd-dispatcher.h, src/mcd-debug.h, src/mcd-master.c, + src/mcd-presence-frame.c, COPYING, libmissioncontrol/mc-protocol.c, + libmissioncontrol/mc-account.h, libmissioncontrol/mc-account-cli.c, + libmissioncontrol/mc.c, libmissioncontrol/mission-control.c, + libmissioncontrol/mc-account-monitor-priv.h, + libmissioncontrol/mc-profile.h, libmissioncontrol/mc-protocol.h + libmissioncontrol/mc.h, libmissioncontrol/mission-control.h, + libmissioncontrol/mc-manager.c, libmissioncontrol/mc-manager.h, + libmissioncontrol/mc-account-priv.h, + libmissioncontrol/mc-account-monitor.c, libmissioncontrol/test.c, + libmissioncontrol/mc-protocol-priv.h, + libmissioncontrol/mc-account-monitor.h, libmissioncontrol/mc-account.c, + libmissioncontrol/mc-manager-priv.h, libmissioncontrol/mc-profile.c: + Set lisence to LGPL. + +2007-03-02 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + Version 4.15. + + * libmissioncontrol/mc-account-monitor.c: + Don't emit notifications when the avatar token changes; "avatar_id" is + the key to be monitored. + + * libmissioncontrol/mc-account-priv.h: + * libmissioncontrol/mc-account.[hc]: + * src/mcd-connection.c: + Introduce a new GConf key for the accounts, "avatar_id", which is + meant to change whenever the avatar image changes. + +2007-02-28 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.14. + + * libmissioncontrol/mission-control.c: + * xml/mcd-dbus-services.xml: + Make the D-Bus methods store the presence in an unsigned integer. + + * libmissioncontrol/mc-account.c: + * src/mcd-connection.c: + Implement clearing of avatars. + + * configure.ac: + * debian/control: + * libmissioncontrol/mission-control.[hc]: + * src/mcd-channel.[hc]: + * src/mcd-connection.c: + Support for libtelepathy version 0.0.50. + +2007-02-23 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mc-account-monitor.c: + After emitting the "account-created" signal, check if the account is + already enabled and, in case, emit "account-enabled". + +2007-02-16 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mc-account.c: + Make sure all the data has been flushed into the GConf DB before + enabling an account. + + * configure.ac: + * debian/changelog: + Version 4.13. + + * libmissioncontrol/mc-account.c: + Create account data directory if it not exists. + + * src/mcd-connection.[hc], src/mcd-master.c: + Monitor account changes and call mcd_connection_account_changed() to + update the avatar. + + * src/mcd-connection.c: + Connect the "destroy" signal only after making sure the proxy is + valid. + +2007-02-14 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.12. + + * configure.ac: + * src/mcd-chan-handler.c: + Make the channel handler location path configurable. + + * src/Makefile.am: + * src/mcd-service.c: + Set a prefix when calling dbus-binding-tool. + + * configure.ac: + * debian/changelog: + Version 4.11. + +2007-02-13 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mc-account.c: + Fix typo in accessing the GConf key. + +2007-02-07 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.10. + +2007-02-06 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mc-account-monitor.h: + Add include guards. + + * src/mcd-channel.[hc]: + Add mcd_channel_get_name() for getting the Telepathy name of the + channel. + +2007-02-02 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mc-account.c: + * src/mcd-connection.c: + Allow setting NULL as avatar filename. + +2007-01-31 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mc-account.[hc]: + * libmissioncontrol/mc-profile.[hc]: + Remove deprecated *_supports_invisible() APIs. + + * libmissioncontrol/test.c: + * src/mcd-master.c: + Use new APIs for testing if invisible presence is supported. + +2007-01-30 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.9. + + * libmissioncontrol/mc-account.c: + Avoid duplicating code too much. + + * libmissioncontrol/mc-account.[hc]: + Add mc_account_set_avatar_mime_type() (only for MC). + + * libmissioncontrol/mission-control.c: + Implement D-Bus call to MC's RemoteAvatarChanged method. + + * src/mcd-connection.[hc], src/mcd-dispatcher.h, src/mcd-master.[hc], + src/mcd-service.c, xml/mcd-dbus-services.xml: + Implement remote_avatar_changed method, for retrieving our own avatar. + + * libmissioncontrol/mc-account.c: + Fix avatar copy function, unset token when setting the avatar. + +2007-01-29 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.8. + + * src/mcd-connection.c: + Set the avatar when connecting. + + * libmissioncontrol/mc-account.[hc]: + Add mc_account_set_avatar_token(), to be used by MC when updating the + avatar. + + * libmissioncontrol/mc-profile.[hc]: + Add "supports-avatars" capability to profiles. + + * configure.ac: + Allow setting the account data base directory. + + * libmissioncontrol/mc-account-priv.h: + * libmissioncontrol/mc-account.c: + Implement mc_account_{get,set}_avatar() APIs. On account + creation/deletion, create/delete the account data directory. + +2007-01-26 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mission-control.[hc]: + Add a TpConn parameter to mission_control_remote_avatar_changed API. + + * doc/reference/mission-control-sections.txt: + * doc/reference/tmpl/mc-account.sgml: + * doc/reference/tmpl/mission-control.sgml: + * libmissioncontrol/mc-account.[hc]: + * libmissioncontrol/mission-control.[hc]: + Stubs for new APIs: mc_account_{set,get}_avatar() for setting/getting + the account avatar in GConf, and + mission_control_remote_avatar_changed() method for signalling that the + own avatar has been changed in the server. + +2007-01-23 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.7. + + * src/mcd-connection.c: (on_presence_requested): + Disconnect the connection before closing it. + +2007-01-22 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.6. + + * src/mcd-connection.[hc]: + * src/mcd-manager.c: + When disabling accounts, emit the AccountStatusChanged signal with the + reason "requested". Fixes NB#50067. + + * libmissioncontrol/mission-control.c: + Don't make a D-Bus call whenever we want to know if MC is running. + + * libmissioncontrol/mission-control.c: + Don't register a D-Bus filter for every instance; instead, install + only a global filter and maintain a list of the objects which must be + notified of the "ServiceEnded" signal. + +2007-01-19 Alberto Mardegan <alberto.mardegan@nokia.com> + + * doc/reference/mission-control-sections.txt: + * doc/reference/tmpl/mission-control-unused.sgml: + * doc/reference/tmpl/mission-control.sgml: + * libmissioncontrol/mission-control.h: + Documentation updates. + + * libmissioncontrol/mission-control.c: + Added mission_control_free_account_statuses() API to free the array of + the account states returned in the callback function of + mission_control_get_current_status(). + +2007-01-18 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.5. + + * libmissioncontrol/mc-account-monitor.h: + Make apps which use only McAccountMonitor compile. + +2007-01-18 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.4. + + * libmissioncontrol/mc-account.c: (_add_one_setting): + Add support for 32-bit integer parameters. + + * libmissioncontrol/mission-control.[hc]: + * src/mcd-service.c: + * xml/mcd-dbus-services.xml: + Added a new method, mission_control_get_current_status(), for querying + the global status of MC: presence, connection status, and all + accounts' states too. + + * src/mcd-connection.c: (_mcd_connection_setup): + Fix possible memleak. + + * src/mcd-manager.c: (_find_connection_by_path): + Fix possible segfault. + +2007-01-17 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mc-account.h: + * libmissioncontrol/mc-profile.[hc]: + Mark some functions as deprecated, and remove some unused code. + + * libmissioncontrol/mc-account-monitor.[hc]: + Add new API mc_account_monitor_get_supported_presences() for listing + all presences supported in any account. + + * libmissioncontrol/Makefile.am: + * libmissioncontrol/mc-account.[hc]: + * libmissioncontrol/mc-profile.[hc]: + * libmissioncontrol/mission-control.h: + Add new APIs in McAccount and McProfile for retrieving the supported + presences, and to test if one presence state is supported. + +2007-01-11 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.3. + + * libmissioncontrol/mission-control.[hc]: + Let the ..._request_channel() method return an operation id to be used + for cancelling the channel request. + +2007-01-11 Naba Kumar <naba.kumar@nokia.com> + + * src/*.[ch], libmissioncontrol/*.[ch], test/*.[ch], debian/copying, + COPYING, LICENSE: Updated license headers. + +2007-01-10 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 4.2. + +2007-01-09 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mc-profile.c: + * src/mcd-channel.c: + * src/mcd-connection.c: + * src/mcd-debug.c: + * src/mcd-dispatcher.c: + * src/mcd-master.c: + * src/mcd-service.c: + Memleak hunting! + + * libmissioncontrol/mc-account-cli.c: + * libmissioncontrol/mc-account.[hc]: + * libmissioncontrol/mission-control.c: + * libmissioncontrol/test.c: + Rewritten McAccount's API: now the mc_account_get_{unique,display, + normalized}_name() functions return a const string, not to be freed. + +2007-01-05 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/rules: + * libmissioncontrol.pc.in: + * libmissioncontrol/mc-profile.c: + Turn the profiles' directory into a configure switch, and make it + available in pkg-config too. + + * src/mcd-master.c: + Implement "default-presence" property. + +2007-01-04 Naba Kumar <naba.kumar@nokia.com> + + * src/*.[ch], libmissioncontrol/*.[ch], test/*.[ch], debian/copying, + COPYING: Added license headers. + +2007-01-04 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-master.[hc]: + Add a method for setting the default presence, instead of retrieving + it from a hardcoded GConf key. + + * src/mcd-dispatcher.c: + * src/mcd-master.c: + * src/mcd-proxy.c: + Correct object disposal so all reference counts end up at zero. + +2007-01-03 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-chan-handler.c: + Remove unnecessary parameters, use g_error() for reporting errors and + aborting. + +2007-01-02 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mission-control.[hc]: + Make the operation_id counter a static variable, since it must be + unique in the context of a process. Remove its usage in functions + which are not supposed to be cancelled. + + * Makefile.am: + * configure.ac: + * test/Makefile.am: + * test/mc-server.c: + Create a client and a server basic test applications. + + * libmissioncontrol/mission-control.c: + Don't use a negative value for indicating an error in the connection + status. + + * configure.ac: + * debian/changelog: + Version 4.1. + + * libmissioncontrol/mission-control.c: + Don't free the GError after the callback invocation; let the callback + handler take care of it. + + * src/mcd-service.c: + Free error after calling dbus_g_method_return_error(). + + * libmissioncontrol/mission-control.[hc]: + Make mission_control_cancel_channel_request return a gboolean. + +2006-12-29 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: + Remove unused properties. + + * src/mcd-channel.c: + * src/mcd-connection.c: + Since libtelepathy doesn't synthesize the StatusChanger and Closed + signals, take care of proxy destruction by monitoring the "destroy" + signal. + +2006-12-28 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-channel.h, src/mcd-connection.[hc], src/mcd-manager.[hc], + src/mcd-master.[hc], src/mcd-service.c: + Unify request_channel and request_channel_with_string_handle + implementations, removing some duplicate code. + + * doc/reference/tmpl/mission-control.sgml: + * libmissioncontrol/mc-account-monitor.c: + * libmissioncontrol/mission-control.c: + Don't emit the "Error" signal if MC is not running (the error is being + reported by the API anyway). Some documentation updates. + + * src/mcd-service.c: + * xml/mcd-dbus-services.xml: + Remove the CancelLastRequest call completely. + +2006-12-27 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mission-control.[hc]: + Modify all synchronous functions by adding a GError parameter and + removing the "Error" signal. Modify asynchronous functions, and add a + callback function for error reporting. + +2006-12-19 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: + Reset the abort reason when reconnecting. Don't use the "network + error" reason when we don't know the real reason for an early + disconnection. Fixes NB#50067. + + * doc/mc-dbus-iface.html: + D-Bus docs updated. + + * libmissioncontrol/mission-control.[hc]: + Removed mission_control_cancel_last_request() API, and all related + code. + +2006-12-18 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_status_changed_cb): + Initiate auto-reconnection even in the case where we get disconnected + with no reason specified. Fixes NB#42727. + + * src/mcd-connection.c: (_mcd_connection_setup): + If opening a new Telepathy connection fails, set the account status to + disconnected. This will help the clients to notify the user that we + couldn't connect, and also eliminates some critical warnings. + +2006-12-15 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Releasing version 4.0. + + * libmissioncontrol/mission-control.c: + Emit Error signal whenever needed. + + * src/mcd-service.c: + Correctly set up so that errors will be shown as g_warnings. + + * configure.ac, debian/changelog, + libmissioncontrol/mission-control.c, + src/mcd-connection.c, src/mcd-manager.c: + Merge in all changes till version 3.17. + + * libmissioncontrol/mission-control-signals-marshal.list, + libmissioncontrol/mission-control.[hc], src/mcd-channel.[hc], + src/mcd-connection.[hc], src/mcd-manager.[hc], src/mcd-master.[hc], + src/mcd-service.c, src/mcd-signals-marshal.list, + xml/mcd-dbus-services.xml: + Rewrite the D-Bus interface: remove the client-pid and serial + parameters from most calls, and instead consider the sender of the + D-Bus calls. + Turn the request_channel* functions into async calls. + +2006-12-12 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.15. + +2006-12-11 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_set_presence): + Add the weak pointer only after we are sure it's not NULL. + +2006-12-08 Alberto Mardegan <alberto.mardegan@nokia.com> + + * doc/reference/Makefile.am: + * doc/reference/mission-control.types: + Support for signals documentation. + + * libmissioncontrol/mc-manager.c, libmissioncontrol/mc-profile.c, + libmissioncontrol/mc-protocol.c, libmissioncontrol/mc-protocol.h, + doc/reference/tmpl/*: + Add some API documentation. + +2006-12-07 Alberto Mardegan <alberto.mardegan@nokia.com> + + * debian/rules, libmissioncontrol/mc-account-monitor.c, + libmissioncontrol/mc-account.c, libmissioncontrol/mc.c, + libmissioncontrol/mission-control.c: + Documenting MC API. + + * configure.ac: + * debian/changelog: + Version 3.14. + + * src/mcd-presence-frame.c: (mcd_presence_frame_disconnect), + (mcd_presence_frame_class_init): + When we get disconnected, clear the requested presence. + Fixes NB#49144. + +2006-12-05 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_release_tp_connection): + Set the account status to disconnected, regardless whether the + Telepathy connection is there or not. + + * src/mcd-service.c: + Reintroduce the handler for the status-actual signal from + McdPresenceFrame, but now exit only if there are no connections; + connections in disconnected state will prevent MC from exiting, since + they are supposed to be trying tor reconnect. + + * src/mcd-connection.c: (_mcd_connection_status_changed_cb), + (_mcd_connection_setup): + Set the account status to "connecting" as soon as the connection gets + disconnected because of network error. + + * doc/mc-dbus-iface.html, libmissioncontrol/mission-control.c, + src/mcd-service.c, src/mcd-signals-marshal.list, + xml/mcd-dbus-services.xml: + Add the last requested presence as a parameter for the StatusActual + DBus signal. + +2006-12-04 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: + Instead of a fixed interval timer for reconnection, double the time at + every attempt (but make it no longer than half a hour anyway). + + * src/mcd-connection.c: + If the connection is disconnected, don't abort it; instead, set up a + timer and try to reconnect it later. Fixes NB#49028. + + * src/mcd-connection.c: + Don't leave the interface proxies around: store them as weak pointers. + + * src/mcd-service.c: + Don't shutdown when the presence goes offline, since the cause could + be a network error. + Don't shutdown when status-actual signal is emitted with DISCONNECTED + state (again, could be a network error); remove the _on_status_actual + signal handler. + Instead, shutdown when McdService gets disconnected. + + * src/mcd-manager.c: + Remove the on_account_status_changed() handler from Mcdmanager, since + it was useless (it was called when the connection had already been + removed). + +2006-12-01 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mission-control.c: + * libmissioncontrol/mission-control.h: + Move gtk-doc comments from .h to .c and clean them up. + +2006-11-30 Alberto Mardegan <alberto.mardegan@nokia.com> + + * Makefile.am: + * configure.ac: + * doc/*: + Add initial support for gtk-doc. + + * xml/mcd-dbus-services.xml: + Naming all input parameters. + + * doc/mc-dbus-iface.html: + Including short HTML documentation of the DBus MC interface. + +2006-11-29 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.13. + + * libmissioncontrol/mission-control.[ch], src/mcd-service.c, + xml/mcd-dbus-services.xml: + Implement the StatusActual signal. Fixes NB#42727. + +2006-11-28 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.12. + +2006-11-24 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-master.c: (_mcd_master_init_account_monitoring), + (_mcd_master_dispose_account_monitoring), (_mcd_master_dispose): + Remove unused variables and release the account monitor on McdMaster + disposal. + +2006-11-22 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-master.c, src/mcd-master.h, src/mcd-service.c, + xml/mcd-dbus-services.xml: + Remove channel usage count test for determining whether to go + automatically offline. Instead, rewrite the + connect-all_with_default_presence() method to make it asyncronous and + store the DBus sender strings to determine if the processes which + requested the default presence are alive; if they are all dead, switch + back to offline presence (unless the user manually switched it to + online). + +2006-11-20 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mc-profile.c: (_mc_profile_load): + Retrieve localized display name for the profile. Fixes NB#47596. + +2006-11-20 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mission-control.c: + (mission_control_cancel_last_request): + Since cancel_last_request is broken, but someone might still be using + it, make it call cancel_last_channel_request. + + * src/mcd-connection.c: (_mcd_connection_get_normalized_name), + (_mcd_connection_status_changed_cb): + When the connection is established, inspect the self handle and set + the normalized name into the account. + +2006-11-17 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.11. + + * libmissioncontrol/mc-account-priv.h: + * libmissioncontrol/mc-account.c: (mc_account_get_normalized_name), + (mc_account_set_normalized_name): + * libmissioncontrol/mc-account.h: + Add mc_account_{get,set}_normalized_name() to the API. + + * libmissioncontrol/mission-control.c: (mission_control_new): + Don't listen to every dbus message, but only to NameOwnerChange. + + * libmissioncontrol/mission-control.c, + libmissioncontrol/mission-control.h, src/mcd-connection.c, + src/mcd-connection.h, src/mcd-manager.c, src/mcd-manager.h, + src/mcd-master.c, src/mcd-master.h, src/mcd-service.c, + xml/mcd-dbus-services.xml: + Introduce new API: mission_control_cancel_channel_request() for + cancelling a channel request identified by the operation_id now + returned by mission_control_request_channel_with_string_handle(). + +2006-11-15 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: + Completely rewritten presence implementation. Correctly handles all + telepathy well-known statuses and uses much less memory. + +2006-11-14 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-manager.c: (_mcd_manager_nuke_connections): + Reset the error to NULL, or there'll be problem on the next iteration + of the loop. + +2006-11-13 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.10. + + * src/mcd-manager.c: (on_presence_requested), + (mcd_manager_request_channel_with_string_handle): + * src/mcd-master.c: + (mcd_master_request_channel_with_string_handle): + * src/mcd-service.c: + (mcd_service_request_channel_with_string_handle): + Make sure we go online before attempting to request a channel. + Fixes NB#47136. + +2006-11-10 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mission-control.c: (dbus_filter_func), + (mission_control_dispose), (mission_control_class_init), + (mission_control_new): + Implemented "ServiceEnded" signal: this will be emitted whenever a + mission-control process terminates. + +2006-11-09 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/mc-account-cli.c: Fixed account addition and + removal. Changed how parameters are specified. + +2006-11-09 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.9. + + * libmissioncontrol/mc-account-cli.c: (show_help), (main): + Correct program name in mc-account help screen. + + * src/mcd-dispatcher.c: (_mcd_dispatcher_send): + Was still using "suppress-handler" property of McdChannel; change it + to "outgoing". Fixes NB#46811. + +2006-11-03 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-operation.c: (_mcd_operation_take_mission): + When reparenting, inherit all possible statuses from the parent + mission. + +2006-11-03 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.8. + + * src/mcd-operation.c: (_mcd_operation_take_mission): + Inherit the "connected" status from the parent mission. + +2006-11-03 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-connection.c: Remove timer and idle sources + when connection object is destroyed. Fixes bug #46307 + +2006-11-03 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * libmissioncontrol/mc-account.c: (_account_name_from_key): + * libmissioncontrol/test.c: (test_mc_account_list), + (test_account_monitor), (main): + Fix compilation warnings. + +2006-11-02 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/mc-account-priv.h, + libmissioncontrol/mc-account-monitor.c, + libmissioncontrol/mc-account.c: Emit account-created signal + only when the account is 'complete' and do not enable + the account by default (let the accounts creation wizard + enable it when it is finished creating the account). Only + mark deleted accounts by a 'deleted' flag so that the account + is still usabled/accesible after deletion and set up a timer + to expunge deleted accounts after 2 secs. mc_accounts_list() + will now only return 'complete' accounts and not anything that + is found in gconf database. Accounts that have been marked + deleted are considered incomplete. Fixes NB#46297 + + * configure.ac, debian/control: Bumped version to 3.7 + +2006-11-01 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-channel.c: (_mcd_channel_dispose): + * src/mcd-operation.c: (_mcd_operation_dispose), + (_mcd_operation_remove_mission): + Clean some more debug messages. + + * src/mcd-debug.c: (mcd_debug_get_level): + * src/mcd-debug.h: + Add mcd_debug_get_level() function. + + * configure.ac: + * debian/changelog: + Version 3.6. + + * debian/rules: + Compile with G_DISABLE_CAST_CHECKS. + + * configure.ac: + Add independent configure flags for checks/cast checks/asserts. + + * src/mcd-debug.c: (mcd_debug_ref), (mcd_debug_unref), + (mcd_debug_print_tree), (mcd_debug_init): + * src/mcd-debug.h: + Introduce a MC_DEBUG environment variable to turn on/off some + debugging informations. + +2006-10-31 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mc-profile.c: (mc_profile_get_unique_name), + (mc_profile_get_configuration_ui), (mc_profile_get_display_name), + (mc_profile_get_icon_name), (mc_profile_get_branding_icon_name), + (mc_profile_get_supports_invisible), (mc_profile_get_protocol), + (mc_profile_get_protocol_name), (mc_profile_get_vcard_field), + (mc_profile_get_default_account_domain), + (mc_profile_is_default_for_vcard_field), + (mc_profile_get_capabilities), (mc_profile_get_default_setting): + Take out the calls to _mc_profile_load() from g_return* checks, since + we want it to be executed anyway. + + * configure.ac: + Allow disabling of GLIB checks (G_DISABLE_ASSERT, G_DISABLE_CHECKS). + +2006-10-27 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-dispatcher-context.h: + * src/mcd-master.c: (mcd_master_set_offline_on_idle): + Minor cleanups, removed some unnecessary code. + +2006-10-26 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_release_tp_connection): + Removed some unused code about contacts' precences. + +2006-10-25 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.5. + +2006-10-24 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-manager.c: (abort_requested_channel), + (abort_requested_channels), (on_presence_requested): + If mission-control is going online because of some channel request + and the user cancels the connectivity setup, we must notify the UI + about these requested channels being aborted (fixes NB#43666). + + * libmissioncontrol/mc-account.c: (mc_accounts_list): + Removed duplicate call to _mc_account_monitor_list() (fixes NB#39642). + +2006-10-23 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.4. + + * src/mcd-master.c: (mcd_master_set_default_presence): + Avoid setting the default presence if we are disconnected but a + presence has already been requested (fixes #44124). + + * src/mcd-connection.c: (presence_str_to_enum), + (_mcd_connection_set_fallback_presences), + (_mcd_connection_presence_enable), (_mcd_connection_set_presence): + Remodel the presence fallback structure, and return the actually + selected presence in the AccountStatusChanged signal (fixes #42138). + * src/mcd-presence-frame.c: + (_mcd_presence_frame_update_actual_presences), + (_mcd_presence_frame_update_actual_presence): + Correct the way the actual presence is computed. + +2006-10-20 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_new_channel_cb), + (on_capabilities_changed), (on_channel_capabilities_timeout), + (on_capabilities_timeout), (_mcd_connection_setup_capabilities), + (_mcd_connection_status_changed_cb), + (mcd_async_request_chan_callback), + (mcd_async_request_handle_callback): + Wait for capabilities: if a call fails within 5 seconds from the + connection establishment, wait for the contact's CapabilitiesChanged + signal and try again in case we receive any (fixes #36260). + For outgoing channels, ignore the NewChannel handler; instead, perform + the channel creation when the RequestChannel method returns. + + * src/mcd-connection.c: (on_channel_capabilities_timeout), + (map_tp_error_to_mc_error), (mcd_async_request_chan_callback): + Report MC_CONTACT_DOES_NOT_SUPPORT_VOICE_ERROR only for StreamedMedia + channels (better fix for #43777). + +2006-10-20 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.3. + + * libmissioncontrol/makefile.am: + Fix libmissioncontrol-config library version. + +2006-10-19 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.2. + + * src/mcd-channel.c: (_mcd_channel_set_property), + (_mcd_channel_get_property), (mcd_channel_class_init), + (mcd_channel_new): + * src/mcd-channel.h: + * src/mcd-connection.c: (_mcd_connection_new_channel_cb), + (mcd_connection_request_channel), + (mcd_connection_request_channel_with_string_handle): + * src/mcd-dispatcher.c: (_mcd_dispatcher_enter_state_machine): + Substitute the "suppress-handler" property from McdChannel with + "outgoing" and handle it accordingly. + Ignore unexpected NewChannel signals which have the suppress_handler + flag set (fixes #43446). + + * src/mcd-dispatcher.c: + Fix some crashes that happened randomly when the channel was closed + shortly after creation. + + * src/mcd-connection.c: (map_tp_error_to_mc_error): + Fix error code returned to the UI in case of missing capabilities + (fixes #43777). + +2006-10-18 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-channel.c: (mcd_channel_new): + * src/mcd-channel.h: + Use TelepathyConnectionHandleType instead of guint. + + * src/mcd-master.c: (_on_dispatcher_channel_removed), + (mcd_master_set_offline_on_idle): + Don't go offline if channels other than text/streamed are closed. + +2006-10-18 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 3.1. + +2006-10-18 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_new_channel_cb), + (mcd_async_request_handle_callback): + Rewritten the check for existing channels: compare the channel type, + and not the channel handle type (fixes #43422). + + * libmissioncontrol/mc-account-priv.h: + Revert the accounts path back to "/apps/telepathy/mc/accounts". + +2006-10-18 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-master.c: (mcd_master_get_online_connection_names): + Add a NULL pointer at the end of the connection names array (fixes + #43192). + +2006-10-17 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/mc-account-priv.h: Fixed account gconf path. + + * libmissioncontrol/mc-*.[ch]: Moved accounts/profiles/managers code + in libmissioncontrol. + * Makefile.am, src/Makefile.am, libmissioncontrol/Makefile.am, + xml/Makefile.am: Fixed Makefile.am files for proper installation and + packaging. + * configure.ac: Removed dependency on account library. + * libmissioncontrol.pc.in: Updated library flags. + * libmissioncontrol/mission-control.[ch], src/*.[ch]: Updated to use + new accounts api. + +2006-10-13 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-channel.c: (on_channel_members_changed), + (mcd_channel_new): + * src/mcd-connection.c: (_mcd_connection_presence_enable), + (_mcd_connection_new_channel_cb), (_mcd_connection_finalize), + (_mcd_connection_release_tp_connection): + * src/mcd-dispatcher.c: (mcd_dispatcher_get_channel_type_usage): + * src/mcd-manager.c: (on_presence_requested), + (_mcd_manager_finalize): + * src/mcd-mission.c: (_mcd_mission_set_mode), (_mcd_set_property): + * src/mcd-operation.c: (_mcd_operation_remove_mission): + * src/mcd-service.c: (_on_dispatcher_channel_removed), + (_on_dispatcher_channel_dispatched): + Remove some g_debug()s. + +2006-10-11 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 2.6. + +2006-10-10 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-channel.c: (_mcd_channel_release_tp_channel): + Don't close Telepathy Contact List channels. + + * src/mcd-master.c: + (mcd_master_request_channel_with_string_handle): + Make sure the connectivity is up (or has been requested) before + attempting to request a channel (fixes #42822). + +2006-10-09 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version 2.5. + +2006-10-09 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-channel.c: (mcd_channel_class_init): + * src/mcd-connection.c: (mcd_async_request_handle_callback): + * src/mcd-dispatcher.c: (_mcd_dispatcher_handle_channel_async_cb), + (channel_on_state_machine), (_mcd_dispatcher_send): + If the requested channel is already open, return it (works for both + incoming and outgoing channels). + +2006-10-06 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-manager.c: (_mcd_manager_set_presence_frame), + (_mcd_manager_dispose), (_mcd_manager_set_property): + Move the setting of the "presence-frame" property to a separate + function, since beacuse of the G_PARAM_CONSTRUCT_ONLY flag we were + not allowed to set it via a g_object_set(). + +2006-10-06 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-proxy.c: (mcd_proxy_class_init): + Set McdProxy as a subclass of McdOperation. + Install "proxy-object" property. + + * src/mcd-master.c: (_mcd_master_dispose), (mcd_master_init): + Create a proxy for forwarding McdMission signals to the McdDispatcher + and McdPresenceFrame. + +2006-10-05 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-dispatcher.c: (_mcd_dispatcher_handle_channel_async_cb), + (mcd_dispatcher_init): + Moved away all mode-setting stuff into the filters. + +2006-10-05 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Releasing MC 2.4. + +2006-10-05 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-master.c: (mcd_master_set_default_presence): + Conditions rewritten for better consistency. + +2006-10-05 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-master.c: (mcd_master_set_default_presence): + Don't allow setting the default presence if a presence + has already been requested. + +2006-10-04 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Releasing version 2.3 + +2006-10-04 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-chan-handler.c: (_mcd_channel_handler_packer), + (_mcd_channel_handlers_read_conf_files): + * src/mcd-chan-handler.h: + * src/mcd-connection.c: (_mcd_connection_advertise_capabilities), + (_mcd_connection_status_changed_cb): + * src/mcd-dispatcher.c: (_mcd_dispatcher_finalize), + (_build_channel_capabilities), (mcd_dispatcher_init), + (mcd_dispatcher_get_channel_capabilities): + * src/mcd-dispatcher.h: + Added support for channel handlers' capabilities. + +2006-10-04 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-dispatcher.c: (_mcd_dispatcher_remove_channel_watch), + (mcd_dispatcher_init): + Removed some old "exit_after_last_channel" stuff, which is now handled + in mcd-master under the name "offline_on_idle". + +2006-10-04 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + Version number++. + + * src/mcd-master.c: (mcd_master_set_default_presence): + Add some more checks before setting "offline_on_idle". + +2006-10-03 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mission-control.c: + (mission_control_set_presence): + Don't ignore requests for offline presence if MC is not running: fixes + bug #42150. + +2006-10-03 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-service.c: (mcd_dispose), (mcd_service_init): + Disconnecting handlers. Also keep a reference to presence_frame and + dispatcher as long signals are connected to them. + +2006-10-03 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-master.c: (_on_dispatcher_channel_removed), + (_mcd_master_dispose), (mcd_master_init), + (mcd_master_set_offline_on_idle), (mcd_master_request_presence), + (mcd_master_set_default_presence): + If online presence was not requested directly by the user, when the + last conversation channel is closed set the presence to offline again. + Fixes bug #42013. + +2006-10-02 Alberto Mardegan <alberto.mardegan@nokia.com> + + * libmissioncontrol/mission-control.h: + Added declaration for mission_control_get_used_channels_count() + (closes bug #42020). + +2006-10-02 Alberto Mardegan <alberto.mardegan@nokia.com> + + * configure.ac: + * debian/changelog: + * debian/control: + New maintainer, new version. + +2006-10-02 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-manager.c: (requested_channel_process), + (on_presence_stable), (requested_channel_free), + (request_channel_delayed), (_mcd_manager_finalize), + (_mcd_manager_set_property), (mcd_manager_request_channel), + (mcd_manager_request_channel_with_string_handle): + If channel creation fails because the connection has not been set up + yet, store the channel request in a structure, and process it once the + "presence-stable" signal has been catched. + + * src/mcd-presence-frame.c: (mcd_presence_frame_class_init), + (mcd_presence_frame_init), (_mcd_presence_frame_request_presence), + (_mcd_presence_frame_check_stable), + (_mcd_presence_frame_update_stable), + (mcd_presence_frame_set_account_status), + (mcd_presence_frame_is_stable): + * src/mcd-presence-frame.h: + * src/mcd-signals-marshal.list: + Add mcd_presence_frame_is_stable() function and "presence-stable" + signal. + +2006-10-02 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (mcd_async_request_handle_callback): + Check for errors must be done before using the function parameters, + since in case of error they may be unset. + +2006-09-29 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-manager.c: (_mcd_manager_dispose): + Use the "presence-frame" property to clean up in dispose. + +2006-09-29 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-service.c: Fix bug on error handling: g_propagate_error() + itself takes care of freeing the error. + +2006-09-27 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-connection.c: Restore channel object in cases where + handle == 0 in NewChannel events. + + * src/mcd-service.c: Do not emit channel failed errors when + there is no requestor. Start shutdown timer if offline presence + is requested. + + * src/mcd-connection.c, src/mcd-channel.c: Added type checks, + debug prints and ref accounting. + + * src/mcd-dispatcher.c: Fixed memory corruption that would + lead to randon crashes when a channel is destroyed. + + * src/mcd-channel.c: Close channel on abort. + * src/dispatcher.c: Fixed flag to set. + +2006-09-26 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_status_changed_cb): + _mcd_connection_set_presence() must be called in all cases. + +2006-09-26 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-master.c: + Fix the gconf path name. + * src/mcd-mission.c: (_mcd_mission_set_mode): + Only set the mode when not already done. + +2006-09-26 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-dispatcher.c: (mcd_dispatcher_get_channel_type_usage), + (_mcd_dispatcher_enter_state_machine), + (mcd_dispatcher_context_process): + Rename next_func_id to next_func_index. + +2006-09-26 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-presence-frame.c: Emit status changed and presence + changed in the right order. + * src/mcd-service.c: Do not emit CONNECTING state to the interface, + cause the ui doesn't handle it correctly. + * src/mcd-connection.c: Pass connection status reason correctly. + +2006-09-26 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-channel.c: (on_tp_channel_closed), + (_mcd_channel_set_property), (mcd_channel_get_members): + Correct an assertion and put one more. + +2006-09-25 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-channel.c: (mcd_channel_get_members): + Removed an unneeded if. + +2006-09-25 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-channel.c: (on_channel_members_changed), + (mcd_channel_new): Fix typo: signal "member-accepted" should be + "members-accepted". + +2006-09-25 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-manager.c: Create connection objects only if they + are not already created. + + * src/mcd-presence-frame.h, src/mcd-controller.c, + src/mcd-connection.c, src/mcd-service.c, + src/mcd-signals-marshal.list, src/mcd-presence-frame.c: + Defined a new 'status-actual' signal that acts as the accumulated + connection signal for all accounts. Shutdown MC if accumulated + connection status gets to DISCONNECTED (i.e. all accounts has + got to disconnected state). Also when created a new connection, + it might fail immidiately and we may not have a chance to capture + the status changed signal. Take care of that if the status is + disconnected just after creating a connection. + +2006-09-25 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-channel.c: (_mcd_channel_set_property), + (mcd_channel_new): Only make assertions if the telepathy channel being + set is not NULL. + + * src/mcd-connection.c: (_mcd_connection_status_changed_cb): + Add connection status to debug output. + +2006-09-25 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_status_changed_cb): + Rewrote the function using a switch() on the connection status. + Calls to dbus_g_proxy_disconnect_signal() have been removed, + since the dispose or abort functions should take care of them. + +2006-09-25 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_status_changed_cb): + Move the call to mcd_presence_frame_set_account_status() before the + call to _mcd_connection_set_presence(), because otherwise the + requested presence will not be set successfully. + +2006-09-23 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-mission.c, src/mcd-mission.h, src/mcd-connection.c, + src/mcd-service.c, src/mcd-operation.c, src/mcd-manager.c, + src/mcd-proxy.c, src/mcd-dispatcher.c, src/mcd-master.c, + src/mcd-presence-frame.c: Removed bunch of virtual functions for + system states, instead used a generic flags that could be + set or unset. All system states now use this flags api. + + * src/mcd-connection.c: Abort connection correctly by holding a + temporary reference to it (because it is not not know in advance + if the object would die during updating of account connection status). + + * src/mcd-operation.c: Disconnect listening to abort signal in + dispose. + + * src/mcd-manager.c: Used gtk idle to call on_presence_request_idle() + in _mcd_manager_connect() (just like it's been done in + on_presence_requested()). The call in _connect() was just a delayed + invokation. + +2006-09-22 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-channel.c: (_mcd_channel_release_tp_channel): + Put some more debug. + * src/mcd-connection.c: (_mcd_connection_status_changed_cb), + (_mcd_connection_release_tp_connection), (_mcd_connection_dispose): + Disconnect tp_conn seperately. + * src/mcd-manager.c: (mcd_manager_remove_account): + Call mcd_mission_abort instead of emiting the signal. + * src/mcd-master.c: (_mcd_master_on_account_enabled): + Add account to manager first and then presence_frame. + * src/mcd-presence-frame.c: (mcd_presence_frame_add_account), + (mcd_presence_frame_remove_account): Do not set presence/status of the + account yourself on add/remove of account. + +2006-09-22 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_set_presence), + (_mcd_connection_status_changed_cb): + Avoid calling mcd_presence_frame_set_account_status() if + the presence-frame is gone. + + * src/mcd-manager.c: (_mcd_manager_disconnect), + (mcd_manager_class_init): + Abort all connections when getting disconnected. + + * src/mcd-master.c: (_mcd_master_disconnect), + (mcd_master_get_account_status), + (mcd_master_get_account_connection_details): + unref() the accounts returned by rtcom_account_lookup(). + + * src/mcd-mission.c: (mcd_mission_class_init): + Added "dimmed" and "lit" signals. + +2006-09-22 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-dispatcher.c: (_mcd_dispatcher_handle_channel_async_cb): + no need to unref DBusConnection. + +2006-09-22 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-channel.c: (_mcd_channel_dispose), + (mcd_channel_get_members): + * src/mcd-connection.c: (_mcd_connection_dispose): + * src/mcd-dispatcher-context.h: + * src/mcd-dispatcher.c: (on_master_abort), + (_mcd_dispatcher_handle_channel_async_cb), + (_mcd_dispatcher_start_channel_handler), + (_mcd_dispatcher_set_property), + (mcd_dispatcher_context_get_channel_object), + (mcd_dispatcher_context_get_connection), + (mcd_dispatcher_context_get_connection_object): + * src/mcd-manager.c: (_mcd_manager_create_connection), + (_mcd_manager_create_connections), (_find_connection), + (_find_connection_by_path), (on_account_status_changed), + (_mcd_manager_set_property), (_mcd_manager_get_property), + (mcd_manager_can_handle_account), (mcd_manager_add_account), + (mcd_manager_remove_account): + * src/mcd-master.c: (_manager_has_account), + (_mcd_master_find_manager), (_is_manager_responsible), + (_mcd_master_find_potential_manager), + (_mcd_master_on_account_enabled), + (_mcd_master_on_account_disabled), + (_mcd_master_init_account_monitoring), (_get_default_presence), + (mcd_master_init), (mcd_master_get_account_for_connection): + * src/mcd-mission.c: (on_parent_abort), (_mcd_mission_set_parent), + (_mcd_set_property): + * src/mcd-presence-frame.c: + (_mcd_presence_frame_update_actual_presence), + (mcd_presence_frame_set_account_presence), + (mcd_presence_frame_set_account_status), + (mcd_presence_frame_add_account), + (mcd_presence_frame_remove_account): + * src/mcd-presence-frame.h: + * src/mcd-service.c: (mcd_service_init): + - Attempt to add support for setting the account presence on the fly + as soon as it get's enabled/disabled. + - Fixed reference leaks. + +2006-09-22 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-master.h, src/mcd-presence-frame.h, + src/mcd-connection.c, src/mcd-service.c, + src/mcd-manager.c, src/mcd-dispatcher.c, + src/Makefile.am, src/mcd-master.c, + src/mcd-presence-frame.c, + libmissioncontrol/mission-control.c, + libmissioncontrol/mission-control.h, + libmissioncontrol/Makefile.am: Removed deprecated codes. + + * configure.ac: Removed dead code. + +2006-09-22 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-debug.h, src/mcd-debug.c, + src/mcd-service.c, src/mcd-mission.c, src/mcd-controller.c, + src/mcd-mission.h, src/Makefile.am: Added a hierarchy printing + debug function. Moved all debug codes to mcd-debug.[ch]. + +2006-09-22 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-operation.c: Abort children when abort requested on + operation. + +2006-09-22 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-mission.c: (mcd_mission_ref), (mcd_mission_unref): + Add McdMission's reference count to g_debug information. + +2006-09-21 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-manager.c: (on_presence_requested), + (_mcd_manager_connect), (mcd_manager_class_init): + Call on_presence_request_idle() only after the connection has + been set up. + +2006-09-21 Naba Kumar <naba.kumar@nokia.com> + + * debian/control, debian/mission-control-dev.install, + debian/mission-control.install, debian/libmissioncontrol-dev.install, + debian/libmissioncontrol.install: Removed no longer used dependencies. + Device implementation of mission-control has been moved outside + this package. + + * mission-control.pc.in: Added libmissioncontrol-server in LIBS. + + * src/mcd-device-controller.c, src/mcd-main.c, configure.ac, + src/mcd-device-controller.h: No longer any device specific code. + + * src/Makefile.am: Disable build for the daemon for now. + + * src/mcd-service.c, src/mcd-service.h: Fixed include file. + + * (removed) mcd-filtering.c, (removed) mcd-filtering.h, + mcd-connection.c: Removed dead files and code. + + * (removed) src/mcd-object.c, (added) src/mcd-service.c, + (removed) src/mcd-object.h, (added) src/mcd-service.h, + src/mcd-device-controller.h, src/Makefile.am: Renamed files + to mcd-service.[c,h]. + + * src/mcd-object.[ch], mcd-device-controller.[ch], + src/mcd-main.c: Renamed class McdObject to more appropriate + McdService. + + * src/mcd-device-controller.c: Fixed compile errors. + + * src/Makefile.am, xml/mcd-dbus-services.xml: Generate service + header files as mcd-service-gen.h instead of mcd-dbus-services-gen.h. + + * src/mcd-object.c, src/mcd-object.h: Use mcd-service-gen.h + for dbus service methods. Register dbus interface to the class in + class init and register dbus object in instance init. Use dbus + connection object from master instead of creating one by itself. + + src/mcd-connection.c: Use only dbus connection object from master. + src/mcd-master.c: Flush dbus connection object before exit. + + * src/mcd-device-controller.c, src/mcd-device-controller.h: Fixed + class structure and cleaned up. + +2006-09-21 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_status_changed_cb): + When a connection is created, set its presence status according to + what is currently requested in the presence frame. + +2006-09-20 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-dbus-services.c, mcd-object.c: Moved dbus services to + McdObject class definition. + + * src/mcd-object.c, src/mcd-object.h, src/mcd-controller.c, + src/mcd-controller.h, + (new) src/mcd-device-controller.c, src/mcd-main.c + (new) src/mcd-device-controller.h, src/Makefile.am, + src/mcd-master.c: Separated libmissioncontrol-server library + from the main application. Separated device specific logic + away from the server library and into application (daemon). + + * src/mcd-init.c, src/mcd-telepathy-handler.c, + src/mcd-dsm.c, src/mcd-dsm.h, src/mcd.h, + mcd-dus-services.c: Removed dead files. + +2006-09-19 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-channel.c: (_mcd_channel_dispose): + Set the is_disposed member to TRUE in the dispose() method. + * src/mcd-presence-frame.c: (_mcd_presence_frame_dispose), + (_mcd_presence_frame_finalize), (mcd_presence_frame_class_init): + Move the call to g_hash_table_destroy() from the finalize() + method to the dispose() one, since this table holds references to + other objects. + +2006-09-19 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-chan-handler.c: (_mcd_channel_handler_free), + (_mcd_channel_handler_packer): + * src/mcd-chan-handler.h: + * src/mcd-dispatcher-context.h: + * src/mcd-dispatcher.c: (_mcd_dispatcher_handle_channel_async_cb), + (_mcd_dispatcher_start_channel_handler), + (mcd_dispatcher_context_get_chan_handler): + - Rename ChannelHandler to McdChannelHandler. + - Implement a getter for the McdChannelHandler object. + +2006-09-19 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_new_channel_cb): + Fix updating of pending channels' hash table when a new channel is + added. + +2006-09-12 Zeeshan Ali <zeeshan.ali@nokia.com> + + * libmissioncontrol/mission-control.c: + (_missioncontrol_register_signal_marshallers), + (mission_control_init), (mission_control_dispose), + (mission_control_class_init), (mission_control_get_type), + (mission_control_new), (mission_control_set_presence), + (mission_control_get_presence), + (mission_control_get_presence_actual), + (mission_control_request_channel), + (mission_control_request_channel_with_string_handle), + (mission_control_connect_all_with_default_presence), + (mission_control_get_connection_status), + (mission_control_get_online_connections), + (mission_control_get_connection), + (mission_control_cancel_last_request), + (mission_control_get_account_for_connection), + (mission_control_get_used_channels_count), (_handle_mcd_errors), + (set_presence_async_cb), (request_channel_async_cb), + (request_channel_with_string_handle_async_cb), + (connect_all_with_default_presence_async_cb), (check_for_accounts), + (check_mc_running), (cancel_last_request_async_cb): + Merge changes from old mc. + * src/mcd-dispatcher.c: (mcd_dispatcher_register_filter_chain), + (_mcd_dispatcher_enter_state_machine): + Add some more info to the debug output. + +2006-09-08 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-channel.c: Removed construct only flags for the + properties. + + * src/mcd-main.c: Shutdown 3rd party services correctly. + + * src/mcd-mission.c, src/mcd-operation.c: Release reference to + parent mission on its abort signal. + +2006-09-08 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-dispatcher.c: (on_object_abort), + (_mcd_dispatcher_set_property): + Watch for abort signal and unref the object on abort. + * src/mcd-master.c: (_mcd_master_dispose), (mcd_master_class_init): + Master should also over-ride the dispose method to unref all the + member objects. + +2006-09-08 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-manager.c: Nuke stale telepathy connections before + mission control takes over the job. + + * src/mcd-connection.cm, src/mcd-channel.c, src/mcd-channel.h: + Get rid of hacky pending request sturct and instead use Channel + object themselfs as pending channels. Implemented get/set + channel status. + + * xml/mcd-dbus-services.xml: Introduced GetUsedChannelsCount + method again. + + * src/mcd-dbus-services.c: Reordered dbus method implementations + and defined them as static functions. + +2006-09-08 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-main.c: (_shutdown_3rd_party_services), + (_raise_3rd_party_services), (main): + Raise 3rd-party services on start-up and shut them down on abort. + * src/mcd-master.c: (_mcd_master_get_property), + (mcd_master_class_init): + Add 'dbus-connection' property to McdMaster. + +2006-09-08 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-mission.c: (mcd_mission_class_init): + Register McdMode as a GType rather than a GEnum. + +2006-09-08 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-dispatcher-context.h: + * src/mcd-dispatcher.c: (_mcd_dispatcher_enter_state_machine), + (mcd_dispatcher_context_process): + Rename mcd_process_sm to mcd_dispatcher_context_process. Remove + mcd_cancel_sm + +2006-09-08 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.c: Listen and respond to channel dispached and + friends signals. + + * src/mcd-connection.c: Removed dead code and notify channel + abort if telepathy connection is disconnected. + + * src/mcd-channel.c, src/mcd-channel.h: Defined channel status enum + and a notification signal for it. + + * src/mcd-signals-marshal.list, src/mcd-dispatcher.c, + src/mcd-dispatcher.h: Implemented + channel-added, channel-removed, dispached, dispatch-failed signals. + and emit dispatch failed error messages. + + * configure.ac, src/Makefile.am, src/mcd-mission.c: Generate enum + types dynamically. + + * src/mcd-master.c: Make dispatcher a property of master. + +2006-09-07 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-dispatcher-context.h: + * src/mcd-dispatcher.c: (_mcd_dispatcher_handle_channel_async_cb), + (_mcd_dispatcher_start_channel_handler), + (_mcd_dispatcher_drop_channel_handler), + (_mcd_dispatcher_leave_state_machine), (on_channel_abort_context), + (_mcd_dispatcher_enter_state_machine), (mcd_process_sm), + (mcd_dispatcher_context_free), + (mcd_dispatcher_context_get_channel_object), + (mcd_dispatcher_context_get_dispatcher), + (mcd_dispatcher_context_get_connection_object), + (mcd_dispatcher_context_get_channel), + (mcd_dispatcher_context_get_data), + (mcd_dispatcher_context_get_connection), + (mcd_dispatcher_context_get_members), + (mcd_dispatcher_context_set_abort_fn), + (mcd_dispatcher_context_set_data): + Rename all sm_context_t to McdDispatcherContext and also the functions + related to this sturcture. + +2006-09-07 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.c: Cancel shutdown if a new presence was + requested which is not offline. + + * src/mcd-mission.c, src/mcd-mission.h: Use better ref/unref + wrapper. + +2006-09-07 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-dispatcher-context.h: + * src/mcd-dispatcher.c: + Remove wrapper functions for lock/unlock. + +2006-09-07 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-dispatcher-context.h: + * src/mcd-dispatcher.c: (_mcd_dispatcher_handle_channel_async_cb), + (_mcd_dispatcher_start_channel_handler), (mcdf_ctx_get_connection): + Removed some more wrapper functions. + +2006-09-07 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.c, src/mcd-mission.c, src/mcd-connection.c, + src/mcd-channel.c, src/mcd-main.c, src/mcd-dispatcher.c: Fixed 'abort' + signals and object life cycles. Exit mission-control on shutdown + timeout. Fixed state machine context handling. + +2006-09-07 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-dispatcher-context.h: + * src/mcd-dispatcher.c: + Removed the device state related wrapper functions. + +2006-09-07 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-dispatcher-context.h: + * src/mcd-dispatcher.c: (_mcd_dispatcher_handle_channel_async_cb), + (_mcd_dispatcher_start_channel_handler), (mcdf_ctx_get_channel): + Removed the channel-related wrapper functions. + +2006-09-07 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-dispatcher-context.h: + * src/mcd-dispatcher.c: (_mcd_dispatcher_enter_state_machine), + (mcdf_ctx_get_display_state): Update device_state API to use booleans + instead of enums. + * src/mcd-mission.c: (mcd_mission_class_init): register mode by its + GType rather than as enum. + +2006-09-07 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-controller.c, src/mcd-mission.h, + src/mcd-controller.h, src/mcd-operation.c + src/mcd-main.c, src/mcd-master.c: Fixed mission-control shutdown + and object abort signals. + + * src/mcd-dispatcher.c: Moved context creation in enter state + machine. + + * src/mcd-object.c, xml/mcd-dbus-services.xml, + src/mcd-signals-marshal.list, src/mcd-dbus-services.c: + Fixed method parameters. + + * src/mcd-connection.c, src/mcd-channel.c, src/mcd-manager.c, + src/mcd-dispatcher.c, src/mcd-master.c, + src/mcd-operation.c, src/mcd-operation.h: Removed get_children() + method because there is similar get_missions() method and fixed + several memory corruptions. Fixed filters chains initialization. + Disabled filters for now (until the filters are fixed). + +2006-09-07 Alberto Mardegan <alberto.mardegan@nokia.com> + + * src/mcd-mission.c: (mcd_mode_get_type), (_mcd_mission_set_mode), + (mcd_mission_class_init): Register the McdMode enum. + +2006-09-06 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-connection.c, src/mcd-dispatcher.c: Pass handle and + handle_type correctly. Fixed channel handlers launching. + + * src/mcd-connection.c, src/mcd-channel.c, + src/mcd-dispatcher.c, src/mcd-master.c: Fixed dispatcher + propagation and channel properties. + + * src/mcd-dispatcher-context.h, src/mcd-dispatcher.c: + Implement the lock/unlock function for the filters. + Port some more functions from the old mission-control. + + * src/Makefile.am: + Also distribute mcd-dispatcher-context.h + +2006-09-06 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-dispatcher.[c,h], src/mcd-dispatcher-context.h: + separated plugin context API from main dispatcher API. + + * src/mcd-connection.c, src/mcd-connection.h, + src/mcd-manager.c, src/mcd-manager.h, + src/mcd-dispatcher.c, src/mcd-master.c, + src/mcd-dispatcher.h: Passed down dispatcher and dispatch + new channels. + +2006-09-05 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-master.h, src/mcd-object.c, src/mcd-manager.c, + src/mcd-dbus-services.c, src/mcd-manager.h, + src/mcd-master.c: Implemented new method + mcd_get_account_for_connection() properly. + + * src/mcd-object.c: Implemented mission control error quark. + + * src/mcd-dbus-services.c: Implemented new method + mcd_get_account_for_connection() + + * libmissioncontrol/mission-control.c, + libmissioncontrol/mission-control.h, + libmissioncontrol/mission-control-signals-marshal.list: Restored + libmissioncontrol from old MC. + + * src/mcd-dispatcher.c, src/mcd-dispatcher.h: Added get/set + dispatcher methods in context. + + * src/mcd-dispatcher.h: Fixed build error. + + * src/mcd-chan-handler.c: Added missing file. + + * src/mcd-master.h, src/mcd-chan-handler.h, + src/mcd-dbus-services.c, src/mcd-dispatcher.c, + src/Makefile.am, src/mcd-master.c, src/mcd-dispatcher.h: + Implemented missing pieces in dispatcher. + + * src/mcd-dbus-services.c: Fixed build error due to + change in function name. + +2006-09-04 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-channel.c: Implemented mcd_channel_get_members(). + + * src/mcd-master.h, src/mcd-connection.c, + src/mcd-connection.h, src/mcd-channel.c, + src/mcd-manager.c, src/mcd-channel.h, + src/mcd-dbus-services.c, src/mcd-manager.h, + src/mcd-master.c: Implemented channel request stack. + +2006-09-03 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-controller.c, rc/mcd-channel.c, + mrc/mcd-channel.h, src/mcd-dispatcher.c, src/mcd-dispatcher.h: + Ported filtering codes. + +2006-08-30 Naba Kumar <naba.kumar@nokia.com> + + * src/Makefile.am, mission-control.pc.in: Install header files and + fixed include path. + + * src/mcd-connection.c, src/mcd-channel.c, src/mcd-channel.h: Added + extra channel parameters. + +2006-08-30 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_free_presence_info), + (_find_handle), (_mcd_connection_map_status_to_presences), + (_mcd_connection_presence_update_cb), + (_mcd_connection_store_tp_self_handle), + (_mcd_connection_get_tp_status_cb), + (_mcd_connection_set_fallback_presences), + (_mcd_connection_presence_enable), (on_presence_requested), + (_foreach_channel_remove), (_mcd_connection_status_changed_cb), + (_mcd_connection_setup), (_mcd_connection_dispose), + (_mcd_connection_set_property), + (mcd_connection_get_telepathy_details): + * src/mcd-connection.h: + * src/mcd-controller.c: (_mcd_controller_device_mode_cb), + (_mcd_controller_device_state_filter_func), + (_mcd_mode_to_mce_mode), (_mcd_controller_set_mode): + * src/mcd-dispatcher.c: (_mcd_dispatcher_load_filters), + (_mcd_dispatcher_unload_filters), (mcd_dispatcher_init), + (mcd_dispatcher_new), (channel_usage_counter), + (mcdf_get_channel_type_usage), (on_channel_members_changed), + (mcd_get_filter_chain), (mcd_register_filter_chain), + (mcd_unregister_filter_chain), + (mcd_dispatcher_remove_channel_watch), (get_local_pending_cb), + (mcd_dispatcher_add_channel_watch), (on_channel_closed), + (dispose_channel_request), (dispose_state_machine_data), + (mcd_cancel_sm), (enter_state_machine), + (mcdf_ctx_get_dbus_connection), (mcdf_ctx_get_account), + (mcdf_ctx_get_profile), (mcdf_ctx_set_data): + * src/mcd-dispatcher.h: + * src/mcd-filtering.c: (mcdf_ctx_get_channel_object), + (mcdf_ctx_get_request_chan_type_quark), + (mcdf_ctx_get_request_chan_type), + (mcdf_ctx_get_request_conninfo_proxy), + (mcdf_ctx_get_request_conninfo_account_name), + (mcdf_ctx_get_request_conninfo_self_handle), + (mcdf_ctx_get_request_obj_path), + (mcdf_ctx_get_request_handle_type), (mcdf_ctx_get_request_handle), + (mcdf_ctx_get_book), (mcdf_ctx_get_dbus_connection), + (mcdf_ctx_get_display_state), (mcdf_ctx_get_battery_state), + (mcdf_ctx_get_keylock_state), (mcdf_ctx_get_lowmem_state), + (mcdf_ctx_get_data), (mcdf_ctx_get_flags), + (mcdf_ctx_get_account), (mcdf_ctx_get_profile), + (_contact_handles_to_strings), (mcdf_ctx_get_members), + (mcdf_ctx_set_abort_fn), (mcdf_ctx_set_data), + (dispose_channel_request), (dispose_state_machine_data), + (start_channel_handler), (drop_channel_handler), (mcd_process_sm), + (mcd_cancel_sm), (enter_state_machine), + (mcd_async_request_chan_callback), (mcd_get_filter_chain), + (mcd_register_filter_chain), (mcd_unregister_filter_chain), + (on_channel_closed), (on_channel_memberschanged), + (get_local_pending_cb), (channel_usage_counter), + (mcdf_get_channel_type_usage), (mcdf_set_tklock_unlocked), + (remove_channel_watch), (add_channel_watch), (set_voip_mode), + (set_tklock), (channelhandler_destroy_cb), + (handle_channel_async_cb): + * src/mcd-filtering.h: + * src/mcd-manager.c: (_mcd_manager_create_connections), + (_find_connection), (on_account_status_changed), + (on_presence_requested_idle), (_mcd_manager_set_property), + (mcd_manager_new), (mcd_manager_get_account_connection): + * src/mcd-master.c: (_mcd_master_init_managers), + (_get_default_presence), (_is_manager_responsible), + (_mcd_master_find_manager), (_mcd_master_sleep), + (mcd_master_get_account_status), + (mcd_master_get_online_connection_names), + (mcd_master_get_account_connection_details): + * src/mcd-master.h: + * src/mcd-mission.c: (_mcd_mission_dispose), + (_mcd_mission_finalize), (mcd_mission_class_init): + * src/mcd-mission.h: + * src/mcd-object.c: (_on_account_status_changed), + (_on_account_presence_changed), (_on_presence_requested), + (_on_presence_actual), (mcd_object_get): + * src/mcd-operation.c: (_mcd_operation_dispose), + (_mcd_operation_remove_mission), (mcd_operation_get_children): + * src/mcd-operation.h: + * src/mcd-presence-frame.c: (_presence_to_status), + (_mcd_presence_frame_request_presence), + (mcd_presence_frame_request_presence), + (mcd_presence_frame_cancel_last_request), + (_mcd_presence_frame_update_actual_presences), + (_mcd_presence_frame_update_actual_presence), + (mcd_presence_frame_set_account_presence), + (mcd_presence_frame_set_account_status): + * src/mcd-presence-frame.h: + * src/mcd-proxy.c: (_mcd_proxy_abort), + (_mcd_proxy_connect_signals), (_mcd_proxy_disconnect_signals), + (_mcd_proxy_dispose), (mcd_proxy_new): + * src/mcd-proxy.h: Fix indentation. + +2006-08-30 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-filtering.[c,h]: Moved codes from old MC. + +2006-08-30 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/Makefile.am: Treat warnings as errors. + * src/mcd-controller.c: (_mcd_controller_set_mode): remove unused + varriable. + * src/mcd-dispatcher.c: (on_channel_members_changed), + (get_local_pending_cb), (mcd_dispatcher_add_channel_watch), + (mcdf_ctx_set_abort_fn): + * src/mcd-dispatcher.h: + * src/mcd-manager.c: (mcd_manager_init): + * src/mcd-mission.c: (mcd_mission_get_mode): Small fixes to make + mc built with the latest changes. + +2006-08-30 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-mission.c: Fixed compiler warnings. + +2006-08-29 Zeeshan Ali <zeeshan.ali@nokia.com> + + * libmissioncontrol/mission-control.c: + (mission_control_get_presence), + (mission_control_get_presence_actual): + * src/Makefile.am: + * src/mcd-chan-handler.h: + * src/mcd-connection.c: (_mcd_connection_status_changed_cb): + * src/mcd-controller.c: (_mcd_controller_device_mode_cb), + (_mcd_controller_device_state_filter_func), + (_mcd_mode_to_mce_mode), (_mcd_controller_set_mode), + (mcd_controller_class_init): + * src/mcd-dispatcher.c: (_mcd_dispatcher_send), + (_mcd_dispatcher_load_filters), (_mcd_dispatcher_unload_filters), + (_mcd_dispatcher_set_property), (_mcd_dispatcher_get_property), + (_mcd_dispatcher_dispose), (mcd_dispatcher_class_init), + (mcd_dispatcher_init), (mcd_dispatcher_new), + (channel_usage_counter), (mcdf_get_channel_type_usage), + (on_channel_memberschanged), (mcd_get_filter_chain), + (mcd_register_filter_chain), (mcd_unregister_filter_chain), + (mcd_dispatcher_remove_channel_watch), + (mcd_dispatcher_add_channel_watch), (on_channel_closed), + (start_channel_handler), (drop_channel_handler), (mcd_process_sm), + (dispose_channel_request), (dispose_state_machine_data), + (mcd_cancel_sm), (enter_state_machine), + (mcdf_ctx_get_channel_object), + (mcdf_ctx_get_request_chan_type_quark), + (mcdf_ctx_get_request_chan_type), (mcdf_ctx_get_request_obj_path), + (mcdf_ctx_get_request_handle_type), (mcdf_ctx_get_request_handle), + (mcdf_ctx_get_book), (mcdf_ctx_get_dbus_connection), + (mcdf_ctx_get_display_state), (mcdf_ctx_get_battery_state), + (mcdf_ctx_get_keylock_state), (mcdf_ctx_get_data), + (mcdf_ctx_get_account), (mcdf_ctx_get_profile), + (mcdf_ctx_get_members), (mcdf_ctx_set_abort_fn), + (mcdf_ctx_set_data): + * src/mcd-dispatcher.h: + * src/mcd-filtering.c: + (mcdf_ctx_get_data), (mcdf_ctx_get_account), + (mcdf_ctx_get_profile), (mcd_get_filter_chain), + (mcd_register_filter_chain), (mcd_unregister_filter_chain): + * src/mcd-filtering.h: + * src/mcd-manager.c: (on_account_status_changed), + (mcd_manager_class_init), (mcd_manager_init), (mcd_manager_new), + (mcd_manager_get_account_connection): + * src/mcd-manager.h: + * src/mcd-master.c: (_mcd_master_init_managers), + (_mcd_master_find_manager), (mcd_master_init), + (mcd_master_get_account_connection_details): + * src/mcd-mission.c: (_mcd_mission_set_mode), + (_mcd_mission_get_mode), (_mcd_set_property), (_mcd_get_property), + (mcd_mission_class_init), (mcd_mission_set_mode), + (mcd_mission_get_mode): + * src/mcd-mission.h: + * src/mcd-object.c: + * src/mcd-operation.c: + * src/mcd-operation.h: + Trying to port dispatcher with working code from the old mc. + +2006-08-26 Naba Kumar <naba.kumar@nokia.com> + + * (new) src/mcd-proxy.[c,h], src/Makefile.am: Implemented + proxy class. + +2006-08-21 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-presence-frame.c: (mcd_presence_free): No need to check for + NULL before calling g_free() on a pointer. + +2006-08-18 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-manager.c: Removed an unneeded function: + mcd_manager_get_account_status. + +2006-08-18 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-dbus-services.c: (mcd_cancel_last_request): + * src/mcd-master.c: (mcd_master_cancel_last_request): + * src/mcd-master.h: + * src/mcd-presence-frame.c: (mcd_presence_new), + (mcd_presence_free), (mcd_presence_copy), + (_mcd_presence_frame_finalize), (mcd_presence_frame_init), + (_mcd_presence_frame_request_presence), + (mcd_presence_frame_request_presence), + (mcd_presence_frame_cancel_last_request): + * src/mcd-presence-frame.h: + Implement CancelLastRequest. + +2006-08-18 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-connection.c: (mcd_connection_get_telepathy_details): + * src/mcd-connection.h: + * src/mcd-dbus-services.c: (mcd_get_connection): + * src/mcd-manager.c: (_find_connection), + (mcd_manager_get_account_connection): + * src/mcd-manager.h: + * src/mcd-master.c: (_is_manager_responsible), + (_mcd_master_find_manager), + (mcd_master_get_online_connection_names), + (mcd_master_get_account_connection_details): + * src/mcd-master.h: + Implement GetConnection. + +2006-08-17 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-dbus-services.c: (mcd_get_online_connections): + * src/mcd-master.c: (mcd_master_get_online_connection_names): + * src/mcd-master.h: Implement GetOnlineConnections. + +2006-08-17 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-dbus-services.c: (mcd_get_connection_status): + * src/mcd-manager.c: (on_presence_requested_idle), + (mcd_manager_get_account_status): + * src/mcd-master.c: (mcd_master_get_account_status): + * src/mcd-master.h: Implement GetConnectionStatus. + * src/mcd-presence-frame.c: (_presence_to_status), + (_mcd_presence_frame_update_actual_presence): Refactor: Put the + presence to status translation into a static inline function. + +2006-08-17 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-dbus-services.c: (mcd_connect_all_with_default_presence), + (mcd_request_channel), (mcd_request_channel_with_string_handle): + * src/mcd-master.c: (_get_default_presence), + (mcd_master_set_default_presence): + * src/mcd-master.h: + Implement ConnectAllWithDefaultPresence. + +2006-08-16 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-dbus-services.c: (mcd_get_presence), + (mcd_get_presence_actual): + * src/mcd-master.c: (mcd_master_get_actual_presence), + (mcd_master_get_requested_presence): + * src/mcd-master.h: Implement GetPresence and GetPresenceActual. + +2006-08-16 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-mission.c, src/mcd-operation.c, src/mcd-controller, + src/mcd-master.c, src/mcd-object: Added some class documents. + +2006-08-16 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-manager.c: (on_presence_requested_idle): Corrected the logic + for deciding wether to create the connection manager and connections. + +2006-08-16 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_set_property): One more line + break makes code a bit clearer. + * src/mcd-manager.c: (on_presence_requested_idle): Only create TP + connection manager and connections if they are not already created. + * src/mcd-object.c: (_on_account_presence_changed), + (mcd_object_get): Handle presence-changed and emit the appropriate + signal on the dbus. + * src/mcd-presence-frame.c: (mcd_presence_frame_class_init), + (mcd_presence_frame_set_account_presence): rename presence-set signal to + pressence-changed. + +2006-08-15 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-master.c: (_mcd_master_sleep): Only set presence to away + automatically if the current presence is AVAILABLE. + +2006-08-15 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-controller.c: (_mcd_controller_on_sleep_timeout), + (_mcd_controller_inactivity_cb): Introduce a timeout function for + auto-away + * src/mcd-master.c: (_mcd_master_sleep), (_mcd_master_wakeup), + (mcd_master_class_init): Override sleep/wakeup to implement auto-away. + * src/mcd-presence-frame.c: + (mcd_presence_frame_get_actual_presence), + (mcd_presence_frame_get_actual_presence_message): + * src/mcd-presence-frame.h: Add API to support querying actual presence. + +2006-08-15 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_presence_enable): + BUGFIX: assuming wrong type of user_data in a callback, which + made mc segfault when setting the presence to away. + +2006-08-15 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-connection.c: (on_presence_requested): A small indentation + fix. + * src/mcd-presence-frame.c: (_mcd_presence_frame_finalize), + (mcd_presence_frame_set_account_status): save the previous status of + accounts before updating it and call + _presence_frame_set_account_presence() only when needed. + +2006-08-14 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-presence-frame.c: (_mcd_presence_frame_finalize), + (mcd_presence_frame_init), + (mcd_presence_frame_get_requested_presence), + (_mcd_presence_frame_update_actual_presence), + (mcd_presence_frame_get_account_presence), + (mcd_presence_frame_get_account_status), + (mcd_presence_frame_get_account_status_reason), + (mcd_presence_frame_set_accounts): Keep track of actual presence + seperately then the requested presence. assume UNSET to be the + lowest presence level rather than OFFLINE. + +2006-08-14 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_set_property): Use + connect_after instead of connect so that the signal handler is called + after all other signal handlers. + * src/mcd-manager.c: (on_presence_requested_idle), + (on_presence_requested): set the presence in an idle handler. + +2006-08-11 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-connection.c: (mcd_connection_class_init): Fixed a small typo in + naba's last commit. + +2006-08-11 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-connection.c: (_mcd_connection_setup): Emit a dbus signal + on connecting the accounts + * src/mcd-object.c: (_on_presence_actual), (mcd_object_get): Rename + presence-settled to presence-actual. + * src/mcd-presence-frame.c: (mcd_presence_frame_class_init), + (_mcd_presence_frame_update_actual_presences), + (_mcd_presence_frame_update_actual_presence), + (mcd_presence_frame_set_account_presence), + (mcd_presence_frame_set_account_status): Rename + presence-settled to presence-actual and try to emit presence-actual + signal when it's time to do so. + * src/mcd-presence-frame.h: Rename + presence-settled to presence-actual. + +2006-08-10 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-connection.c: Fixed property param spec. + + * src/mcd-connection.c: Implemented presence status changes + from telepathy connection. + + * src/mcd-connection.c: Backported new changes from old mission + control. + +2006-08-09 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/mcd-object.c: (_on_presence_settled), (mcd_object_get): + Emit PresenceStatusActual signal when/if received from the + presence-frame + +2006-08-09 Zeeshan Ali <zeeshan.ali@nokia.com> + + * ChangeLog: Starting to maintain a ChangeLog from now on. + + * src/mcd-master.h, src/mcd-presence-frame.h, src/mcd-object.c + src/mcd-connection.c, src/mcd.h, src/mcd-manager.c, src/mcd-master.c + src/mcd-presence-frame.c: Converting from + TelepathyConnectionPresenceType to McPresence + + * libmissioncontrol/mission-control.[ch]: Moved code from old + missioncontrol. + +2006-08-09 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: emit the AccountStatusChanged? signal on the dbus so + that presence + +2006-08-09 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: applet can update itself. + +2006-08-08 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: You can go now go online, then offline and then + again online and so on without causing a segfault. :) + +2006-08-03 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: Use the dbus bus that started us rather than the + session bus. + +2006-08-03 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: + Figure-out and save the new state on presence change. + +2006-07-27 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: + Renamed mcd_master_set_presence() to mcd_master_request_presence() + +2006-07-27 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: + Renamed the object varriable according to the new API (athough the code + is commented out atm) + +2006-07-27 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: + Renamed the MCObject structures to McdObject? + +2006-07-26 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: + Setting the indentation according to the gnu coding style + +2006-07-26 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: + g_object_new expects a NULL in the end (bugfix) + +2006-07-26 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: + Connecting the chain of SetPresence? + +2006-07-26 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: + DBUS_API_SUBJECT_TO_CHANGE declaration is enough in one place + (Makefile.am) + +2006-07-25 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: Corrected the verion in debian/changelog + +2006-07-25 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: Imported signals to mcd-object from the old version + +2006-07-25 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: Moved some more code from the old mission-control + +2006-07-25 Zeeshan Ali <zeeshan.ali@nokia.com> + + * src/*.h, src/*.c: Updated the version string in configure.ac and debian files + + +2006-06-12 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-presence-frame.h, src/mcd-connection.c + src/mcd-connection.h, src/mcd-channel.c + src/mcd-manager.c, src/mcd-channel.h + src/mcd-signals-marshal.list, src/mcd-dispatcher.c + src/Makefile.am, src/mcd-dispatcher.h + src/mcd-presence-frame.c: New implementation of channel class. Futher + implementation of connection and presence-frame classes. Initial + implementation of dispatcher class. + +2006-06-02 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-connection.c, src/mcd-operation.c, src/mcd-master.h, + src/mcd-presence-frame.h, src/mcd-connection.h, + src/mcd-operation.h, src/mcd-manager.c, + src/mcd-signals-marshal.list, src/mcd-manager.h, + src/mcd-mission.c, src/mcd-controller.c, + src/mcd-mission.h, src/Makefile.am, + src/mcd-controller.h, src/mcd-master.c, + src/mcd-presence-frame.c: Fixed all classess to build. Partial + implementation of all classes. + +2006-06-01 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-connection.c, src/mcd-master.h, src/mcd-presence-frame.h, + src/mcd-connection.h, src/mcd-manager.c, src/mcd-manager.h, + src/mcd-controller.c, src/Makefile.am, src/mcd-master.c, + src/mcd-presence-frame.c: Created master, manager + and presence frame classes and initial structure. + +2006-06-01 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-mission.[c,h], src/mcd-operation.[c,h], + src/mcd-controller.[c,h], src/mcd-connection.[c,h], src/Makefile.am: + Added initial mission control structure. + +2006-05-26 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-init.c, src/mcd-object.c, src/mcd-telepathy-handler.c, + src/mcd-object.h, src/mcd-dbus-services.c, src/mcd-filtering.c: + Cleanups. + + * src/mcd-dbus-services.c: Refactored common code into + _mcd_get_default_presence(). + + * src/mcd-dbus-services.c: Refactored common code into + _mcd_request_channel(). + + * src/mcd-object.c, src/mcd-telepathy-handler.c, src/mcd-object.h: + Made mcd_free_presence_info() easier to use. + + * src/mcd-dbus-services.c: Made mcd_cancel_last_request() use + _mcd_set_presence(). + + * src/mcd-object.c, src/mcd-telepathy-handler.c: + _mcd_status_changed_handler() duplicated a lot of code from + mcd_set_account_presence(). + +2006-05-24 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/mission-control.h, src/mcd.h, + src/mcd-filtering.c(mcd_async_request_chan_callback): Added + MC_CONTACT_DOES_NOT_SUPPORT_VOICE_ERROR. + + * src/mcd-filtering.c, src/mcd-object.c, + src/mcd-telepathy-handler.c: Minor cleanups. + + * src/mcd-object.c: Don't try to connect the same account more + than once. g_hash_table_insert() will call the key_destroy_func + on the key if it is already present. This corrupted the object + path that was being used as the key. + + * src/mcd-object.c, src/mcd-telepathy-handler.c, + src/mcd-object.h, src/mcd-dbus-services.c, src/mcd-filtering.c, + xml/mcd-dbus-services.xml, libmissioncontrol/mission-control.c, + libmissioncontrol/mission-control.h: Added presence-status-actual + signal which reflects the combined status of out accounts. + presence-status-requested was fixed to be emitted when a presence + is actually requested. + +2006-05-23 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.c: Don't clear exit_after_last_channel flag when + mcd_connect_all_with_default_presence() is called. + Partial fix for bug #27122. + + * src/mcd-init.c: Re-connect all accounts even when connectivity + is already connected. Fixes bug #30344. + +2006-05-19 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.c: Set new connection status to CONNECTING. + Fixes bug #29265. + +2006-05-19 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-dbus-services.c, src/mcd-dsm.c, src/mcd-filtering.c, + src/mcd-init.c, src/mcd-object.c, src/mcd-telepathy-handler.c, + src/mcd-object.h: Andrei's changes to .manager file reading and + minor refactoring + debugging output additions. + +2006-05-17 Naba Kumar <naba.kumar@nokia.com> + * src/mcd-object.c: Don't hog the CPU and prevent gabble from + doing real work. + +2006-05-16 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-dbus-services.c, xml/mcd-dbus-services.xml, + libmissioncontrol/mission-control.[c|h]: Added + GetUsedChannelsCount DBUS method with libmissioncontrol + bindings to query for ongoing VoIP calls etc. This fixes bug + #29651. + +2006-05-12 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-filtering.c (start_channel_handler): A few mcd-error + signal emits were missing when we had trouble handling the + channel. The emits should fix bug #26504. + +2006-05-12 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.[c|h], src/mcd-telepathy-handler.c: + Reverted _mcd_connection_destroyed_cb removal from yesterday + since apparently both this and the recent libtelepathy addition + are needed by someone. + +2006-05-12 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.c: Connection status is now also updated when + starting to connect an account. This fixes bug #29265. + + * src/mcd-object.c, src/mcd-signals-marshal.list, + xml/mcd-dbus-services.xml, libmissioncontrol/mission-control.c, + libmissioncontrol/mission-control-signals-marshal.list: + Added PresenceStatusRequested signal which is emitted whenever a + presence change is requested. Emitting can happen due to a request + from application, auto-away etc. + +2006-05-11 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.[c|h], src/mcd-telepathy-handler.c: + Due to recent changes in libtelepathy, mission control is no + longer responsible for detecting destroyed connections. Thus, + _mcd_connection_destroyed_cb and the associated helper struct + destroy_signal_data have been removed as obsolete. Part of + fix for bug #28543. + +2006-05-09 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.c, src/mcd-telepathy-handler.c, + src/mcd-signals-marshal.list, xml/mcd-dbus-services.xml, + libmissioncontrol/mission-control.c, + libmissioncontrol/mission-control-signals-marshal.list: Backed off + the AccountStatusChanged signal modifications done by Tuomas, as + we've been requested to do this differently. + + * src/mcd-dsm.c (_close_channel): Mission Control attempted to + close chanels with wrong type of pointer when shutdown_ind signal + has been received, effectively causing a crash. Now channels are + closed properly. + + * src/mcd-filtering.[c|h] (add_channel_watch), + (remove_channel_watch): Mission Control will have to monitor + MembersChanged signals for VOIP calls in order to know whether the + T&K lock needs to be be locked when the call ends. The locking + functionality has also been added to MC. MC also provides a method + available for filters to indicate whether filters opened the T&K + lock. + + * src/mcd-object.[c|h]: A flag has been added to Mission Control + for keeping track of T&K unlock operations done by the + filters. It's needed to know whether we need to lock T&K again + when call ends. + + * src/mcd-init.c (_mcd_setup_device_state_monitoring): Mission + Control will request information about the T&K lock status on + startup, otherwise we might not know the lock status during the + first VOIP call. + +2006-05-08 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.[c|h], src/mcd-dbus-services.c, + src/mcd-filtering.c: Presence and connectivity are set offline + after the last channel closes in case the channel was initiated + from those conditions. This fixes bug #27122. + + * src/mcd-object.c, src/mcd-telepathy-handler.c, + src/mcd-signals-marshal.list, xml/mcd-dbus-services.xml, + libmissioncontrol/mission-control.c, + libmissioncontrol/mission-control-signals-marshal.list: + AccountStatusChanged signal API changed. Added last requested + presence as the signal's second parameter. + + * src/mcd-dbus-services.c: Connectivity is now handled also in + ConnectAllWithDefaultPresence method. This fixes bug #26797. + +2006-05-05 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-dbus-services.c: Cancellation for the pending MC + shutdown has been added to situations where it makes sense. + + * src/mcd-init.c (_mcd_iap_cb): When we've been disconnected in an + uncontrollable manner, set presence to offline to match reality. + + * src/mcd-dsm.c (_mcd_state_filter_func): The fix for the T&K lock + tracking was still not quite right. Corrected a flawed comparison. + + * src/mcd-init.c: Emit an error signal to indicate when we've lost + network connectivity in an uncontrolled manner. + +2006-05-04 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-init.c (_mcd_inactivity_cb): Fix the autoaway state + book-keeping to complete the bug #23758 fix. + + * src/mcd-object.c (mcd_set_presence_autoaway): Do not touch the + away message string when going to autoaway. + +2006-05-03 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-dbus-services.c: Added implementation of the presence + revert operation when a request is cancelled. + + * src/mcd-object.[c|h]: Added support for storing the previous + presence data when doing a request, so that we can 'cancel' the + request. + + * src/mcd.h: MC_NETWORK_ERROR has been added to supported error + signals. + + * libmissioncontrol/mission-control.c (_handle_mcd_errors): + Support emission of 'generic' errors that are not directly caused + by the request performed by the current application. They will + have PID parameter value 0. Added method + mission_control_cancel_last_request() that can be used to cancel + the last method call (currently only really supports the + mission_control_request_channel_with_string_handle() cancellation). + + * src/mcd-object.c (_mcd_connect_account_from_hash): Emit + MC_NETWORK_ERROR if creation of a connection failed. + + * libmissioncontrol/mission-control.[c|h]: Added the + mission_control_connect_all_with_default_presence() method to the + API to connect all accounts with default presence when we're not + yet offline. MC_NETWORK_ERROR has been added to error signals. + + * src/mcd-dbus-services.c (mcd_connect_all_with_default_presence): + Added a handler for the ConnectAllWithDefaultPresence D-BUS + method. It will connect all accounts with the default presence + value from GConf if we're not online yet. + + * xml/mcd-dbus-services.xml: Added the definition of the new + method ConnectAllWithDefaultPresence. Added CancelLastRequest + method definition. + +2006-05-02 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-init.c, src/Makefile.am, configure.ac: The plugin directory + location is now provided by the autotools files instead of being + hardcoded in the Mission Control. + + * mission-control.pc.in (pluginlibdir): We now provide the plugin + library location in the package configuration file for other + interested components (i.e. the filters) + +2006-04-28 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.[c|h], src/mcd-telepathy-handler.c: Main presence + is now updated when going autoaway thus fixing bug #23758. + Added just-in-time fetching of available presence states: + In case the presence interface doesn't exist yet when status + changes to connected but becomes available later, the presence + options are fetched when setting the account's presence. This fixes + the related race condition. + +2006-04-27 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/mission-control.[c|h]: Libmissioncontrol methods + will now check whether there are any enabled accounts before doing + anything that will invoke Mission Control. Initial experimental + code for the request cancellation support has been added, but it's + not yet tested. + +2006-04-25 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/mission-control.[c|h] + (mission_control_get_presence): We now check whether the Mission + Control is present on the D-BUS in order to avoid unnecessary + starting it up when we're offline and want to know our presence. + + * src/mcd-dsm.c (mcd_shutdown_ind_cb): Mission Control will now + explicitly call close for all channels in order to fix the bug + #25155, in case there will be some connection manager that does + not do it while disconnecting a connection. + +2006-04-24 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-filtering.h: The channel type quark getter prototype + name mismatched the implementation. Fixed. + + * src/mcd.h: Moved some definitions to mcd-filtering.h, because + plugins need access to the same D-BUS service. + +2006-04-23 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-dbus-services.c: Fixed a build breakage. + +2006-04-21 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-init.c (_mcd_inactivity_cb): Mission Control should now + no longer enter autoaway when there is an ongoing VOIP call. + +2006-04-21 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/mission-control.h, src/mcd.h, + src/mcd-dbus-services.c: Added MC_NO_ACCOUNTS_ERROR. + The error is sent in response to all the DBUS methods in case + there aren't any enabled accounts. MC also exits in that case. + + * src/mcd-dbus-services.c: On channel requests in disconnected + state, presence setting is now read from GConf. + + * src/mcd-init.c: Minor change on e_book_open() function call's + parameters to fix bug #26998. + +2006-04-20 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-filtering.c: Merged some upstream fixes from Andrei to + the VOIP mode handling. + +2006-04-19 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd.h: Added definitions for the MCE service calls. + + * src/mcd-filtering.c (remove_channel_watch): When we have no + longer VOIP calls going, exit from VOIP mode to the normal mode by + sending a request to the MCE. + (add_channel_watch): When we have a VOIP call going, send a + request to the MCE to enter the VOIP mode. + +2006-04-12 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/mission-control.[c|h], src/mcd-object.c, + src/mcd-signals-marshal.list, src/mcd-filtering.c, + src/src/chan-handler.h, xml/mcd-dbus-services.xml, + libmissioncontrol/mission-control-signals-marshal.list: Provided + the requested further improvements in the libmissioncontrol error + reporting, so that individual clients get only error signals that + are relevant to them. + + * src/mcd-filtering.[c|h]: The filtering API has been updated to + provide access to the RtcomAccount and RtcomProfile for the + current channel request. Also, the API now includes a helper for + getting the list of addresses for the participants of current + channel. + +2006-04-11 Naba Kumar <naba.kumar@nokia.com> + + * configure.ac: Version number upgraded. + + * xml/mcd-dbus-services.xml: The method definitions have been + changed to include the serial number of the operation. Error + signal has been added. + + * debian/control (Depends): Added dependency for the galago-daemon + version that no longer appears to have issues when being started + by Mission Control. + + * libmissioncontrol/mission-control.c: Support for receiving the + Error signal from the missioncontrol. Do not emit the signal based + on g_error anymore. + + * src/mcd.h, libmissioncontrol/missioncontrol.h: The error domain + quark definition has been removed, as it's no longer used. + + * src/mcd-init.c: The environmental variable check to determine + whether we should launch galago and friends has been disabled and + the startup is done by default. + + * src/mcd-telepathy-handler.c: Use of some obsolete context + members has been removed, other cleanups. + + * src/mcd-chan-handler.h (struct chan_handler_req_t): The channel + handler request structure has been cleaned of some now unnecessary + members. The channel type is now represented as a quark. + + * src/mcd-filtering.[c|h]: The filtering context and the access + API for it had some cleanups. We also keep up a count of the + channel instances per channel type. Part of the fix for bug #25260 + is also here. + + * src/mcd-object.[c|h]: Support for the counting of channels per + channel type has been added. Some obsolete code has been + removed. We now emit error signal to provide applications calling + MCD through libmissioncontrol information about error situations + (bug #25260). + + * src/mcd-dbus-services.c: We now emit error signal to provide + applications calling MCD through libmissioncontrol information + about error situations (bug #25260). Some obsolete code has been + eliminated. + +2006-04-09 Naba Kumar <naba.kumar@nokia.com> + * src/mcd-object.c, src/mcd-telepathy-handler.c: Rearranged + "destroy" signal disconnection and connection object unreferencing + so that MC behaves nicely when connecting accounts with invalid + passwords. Now MC also exits after last account disconnects. These + fix bug 25951. + + * src/mcd-dbus-services.c: Reconnecting the accounts which were + lost due to a Connection Manager crash is now possible. + + * src/mcd-object.c: Minor change to call tp_key_value_list_free + for freeing the keyval list + +2006-04-06 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/mission-control-signals-marshal.list: The + "error" signal signature has been added. + + * src/mcd-dbus-services.c, + libmissioncontrol/missioncontrol.c, + libmissioncontrol/missioncontrol.h: Partial fix for the bug 25260; + instead of pringing warnings, MissionControl sets GError + approriately, so libmissioncontrol can emit error signals + instead. Libmissioncontrol will now also free the allocated + account names to avoid memory leaks (fixing bug 25272). + + * src/mcd.h: Some definitions needed by bug 25260 work were added. + +2006-04-06 Naba Kumar <naba.kumar@nokia.com> + * src/mcd-init.c, src/mcd-object.[c|h]: Mission Control will now + listen for accidentally destroyed connections (for example when + a Connection Manager crashes). + + * src/mcd-init.c, src/mcd-object.c, mcd-dbus-services.c: Fixed + emission of account-status-changed signal in case Connectivity + is lost during it's establishment. + + * src/mcd-init.c, src/mcd-object.[c|h]: Added reading of + MC_RAISE_SERVICES environment variable to decide if external + daemons are started/stopped + +2006-04-05 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.c (mcd_dispose, mcd_connect_all_accounts): The + shutdown/startup of Galago is currently disabled + + * src/mcd-init.c: When Connectivity is lost, Mission Control will + wait for a moment and only then exit if Connectivity does not + return. + + * src/mcd-filtering.[c|h]: Added getter for the self handle + property of the connection. + +2006-04-04 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-telepathy-handler.c: _mcd_free_presence_info was moved + into mcd-object.c and is now public. + + * src/mcd-object.[c|h]: Mission Control will now start Galago when + accounts are connected and shut it when Mission Control exists. + + * src/mcd-init.c (_mcd_read_account_settings): Minor memory leak + fixes + +2006-04-03 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.c: Minor changes to internal filter functionality + calls to be in sync with the naming conventions. + + * src/mcd-filtering.[c|h]: Major updates to the context getters and + context structure. + + * src/mcd-filtering-int.h: The filter processing internal function + definitions have been separated here. + +2006-03-30 Naba Kumar <naba.kumar@nokia.com> + + * debian/control, debian/mission-control-dev.install + debian/changelog, debian/libmissioncontrol-dev.install + src/Makefile.am, configure.ac, mission-control.pc.in + Makefile.am: Split mission-control and filters and have + separate mission-control development package. + + * filter-plugins/mcd-filter-common.h, + filter-plugins/mcd-text-filter-plugin.c, + filter-plugins/mcd-filter-plugins.h, + filter-plugins/mcd-filter-common.c, + filter-plugins/mcd-voip-filter-plugin.c: Moved the filters + to mission-control-filters/src in repository. + + * filters-plugins/*: Removed dead directory. + +2006-03-30 Naba Kumar <naba.kumar@nokia.com> + + * filter-plugins/Makefile.am (libmcd_text_filter_la_SOURCES) + (libmcd_voip_filter_la_SOURCES): While compiling plugins, also use + the new common plugin code file. + + * filter-plugins/mcd-text-filter-plugin.[c|h]: As the plugins + shared most of their code, the overlapping part was moved into + separate source and header files. Minor error handling fixes also + included. + +2006-03-30 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-filtering.c: Naming of functions was unified a + bit. Error messages now include the function where things went + wrong. Memory allocation done with g_new0. + +2006-03-29 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-object.[c|h], src/mcd-telepathy-handler.c, + src/mcd-dbus-services.c, src/mcd-filtering.c, + src/mcd-chan-handler.h, filter-plugins/mcd-text-filter-plugin.c, + filter-plugins/mcd-voip-filter-plugin.c: The + (mission_control_request_channel_with_string_handle) now works + properly. + +2006-03-28 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/mission-control.[c|h]: API changes: + (mission_control_request_channel) no longer uses ChaVoAddress, but + takes the handle and handle type directly instead. Added + (mission_control_request_channel_with_string_handle) function as + requested. + + * libmissioncontrol/chavo-utils.[c|h]: Chavo-utils have been made + obsolete and removed. + + * src/mcd-main.c (main): While initializing the MC, use the new + getter function for the actual MC object. + + * src/mcd-filtering.c: Changed the filtering implementation to + pluginize it. Added the support functions for the pluginized + filters (registration/unregistration, context manipulation etc). + + * src/mcd-dbus-services.c (mcd_request_channel): Changed the + function API as requested to make the ChaVoAddress structure + unnecessary. + (mcd_request_channel_with_string_handle): Added initial version of + the channel request function variant that supports the handle as a + string. + (mcd_get_connection): Ensure that we do not attempt to access an + invalid TpConn object, which would cause a crash. + + * src/mcd-telepathy-handler.c (_mcd_new_channel_handler): Collect + new channel requests with directionality info so that they can be + handled by the filter plugins. + + * src/mcd-object.[c|h]: Clean the filter plugins and channel request + hash while disposing mission control. Mission Control has been + singletonized. Corresponding changes to the header. + + * src/mcd-init.c: Load the filter plugins. Read only the enabled + accounts. + + * filter-plugins/*, debian/mission-control.install, Makefile.am: + The filters have been separated to dynamically loadable libraries. + +2006-03-22 Naba Kumar <naba.kumar@nokia.com> + + * libmissioncontrol/Makefile.am: Added util library to build + flags/libs. + + * libmissioncontrol/chavo-utils.[c|h]: Removed the now obsolete + ChavoAccount definition, as well as its helper function. + + * xml/mcd-dbus-services.xml: Updated RequestChannel method signature. + + * src/mcd-dbus-services.c (mcd_request_channel): The channel type + is now a string, instead of an enum. Removed now unnecessary + mapping from an enum to a string. + + * src/filter-type-voip.c (filter3_invoke_voipengine): Fixed a + couple of warnings by adding the missing casts. + + * src/mcd-init.c (_mcd_read_account_settings): We progressed to + next item in a GList with g_slist_next. While it worked, it's not + really the right thing to do. + + * libmissioncontrol/mission-control.[c|h]: Libmissioncontrol now + uses the string identifiers for the channels instead of the old + enum system. Libmissioncontrol now also uses RtcomAccounts instead + of the old ChavoAccount. + + * configure.ac: Version number updated. + +2006-03-21 Naba Kumar <naba.kumar@nokia.com> + + * configure.ac: Bumped up the version to 0.7. util library is no + longer a dependency (control file also updated + accordingly). util library version 0.0.3 or newer is required. + + * src/mcd-init.c (_mcd_read_account_settings): Account settings + are now read with the util library instead of the deprecated + util library. + +2006-03-17 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-filtering.c (mcd_async_request_chan_callback): No longer + call the filter chain for outgoing requests (fixes bug #24105) + +2006-03-15 Naba Kumar <naba.kumar@nokia.com> + + * configure.ac: Version number update. + + * src/mcd-telepathy-handler.c (_mcd_free_presence_info): Presence + information memory handling fixes. + + * src/mcd-object.[c|h] (_mcd_disconnect_account_from_hash): Emit the + AccountStatusChanged signal only if we're doing + auto-disconnect. PresenceUpdate signal handling/crash fix. + + * src/filter-type-text.c: A missing licence header was added. + + * src/mcd-init.c: Connectivity is now used only on + ARM. Connectivity handling fixes. When returning from autoaway, + preceding presence value is restored.Added listener for system + state events. + + * src/mcd-filtering.c: A missing licence header was added. VOIP + filter support was added. + + * src/Makefile.am (mission_control_SOURCES), + src/filter-type-voip.c: VOIP filter functions added to build. + +2006-03-08 Naba Kumar <naba.kumar@nokia.com> + + * src/mcd-telepathy-handler.c (_mcd_status_changed_handler): + Support added for handling of pending channel requests. + * src/mcd-object.h: Added device state info structure needed for + the DSM filters. + * src/mcd-object.c, src/mcd-dbus-services: Support added for use + of Connectivity on ARM. + * src/mcd-init.c (mcd_init): Added setup for DSM event + monitoring. Also, support added for use of Connectivity on ARM. + * src/mcd-filtering.c: mcd_async_request_chan_callback moved here. + * src/Makefile.am (mission_control_SOURCES): mcd-dsm.c added to + build (Contains the callbacks for device state book-keeping). + * src/filter-type-text.c: Added filter functions for battery low, + display and keylock. Contacts plugin is again used. + * configure.ac: Version updated. + + +2006-03-02 Naba Kumar <naba.kumar@nokia.com> + + * configure.ac: Version updated to 0.2 + * src/mission-control.[c|h]: + * src/mcd-object.[c|h]: + - Dispose handling problems should now be fixed. + * src/mcd-dbus-services.c: (_mcd_async_request_chan_callback) Some + missing error handling was added + + * configure.ac: + * debian/control: + * src/mcd-object.[c|h]: + * src/mcd-filtering.[c|h]: + * src/mcd-init.c: + * src/filter-type-text.c + * src/mcd-telepathy-handler.c + - Added support for contact filtering, merged with other changes. + + * src/mcd-dbus-services.c: + * src/mcd-object.[c|h]: + - AccountStatusChange signal support was completed and some + related presence handling bugs (at least #23194) fixed. + diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/INSTALL diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..3620667f --- /dev/null +++ b/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = libmissioncontrol src xml doc server test + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libmissioncontrol.pc mission-control.pc + diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..9e730364 --- /dev/null +++ b/configure.ac @@ -0,0 +1,134 @@ +AC_INIT(Makefile.am) +AM_INIT_AUTOMAKE(mission-control, 4.17) +AM_CONFIG_HEADER(config.h) + +AC_CANONICAL_HOST + +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_INSTALL +AC_PROG_LIBTOOL + +AC_HEADER_STDC + +AM_CFLAGS="$CFLAGS -Wall -fno-strict-aliasing -ansi -DDMALLOC" + +AC_ARG_ENABLE(cast-checks, [ --disable-cast-checks compile with GLIB cast checks disabled],[cchecks=${enableval}],cchecks=yes) +if test "x$cchecks" = "xno"; then + CFLAGS="$CFLAGS -DG_DISABLE_CAST_CHECKS" +fi + +AC_ARG_ENABLE(asserts, [ --disable-asserts compile with GLIB assertions disabled],[asserts=${enableval}],asserts=yes) +if test "x$asserts" = "xno"; then + CFLAGS="$CFLAGS -DG_DISABLE_ASSERTS" +fi + +AC_ARG_ENABLE(checks, [ --disable-checks compile with GLIB checks disabled],[checks=${enableval}],checks=yes) +if test "x$checks" = "xno"; then + CFLAGS="$CFLAGS -DG_DISABLE_CHECKS" +fi + +AC_ARG_WITH(profiles_dir, AS_HELP_STRING([--with-profiles-dir=<path>],[Directory for storing profiles])) +if test -z "$with_profiles_dir" ; then + PROFILES_DIR="/usr/share/mission-control/profiles" +else + PROFILES_DIR=$with_profiles_dir +fi +AC_SUBST(PROFILES_DIR) +AC_DEFINE_UNQUOTED(PROFILES_DIR,"$PROFILES_DIR", [Directory for storing profiles]) + + +AC_ARG_WITH(accounts_dir, AS_HELP_STRING([--with-accounts-dir=<path>],[Directory for storing accounts])) +if test -z "$with_accounts_dir" ; then + ACCOUNTS_DIR="~/.mission-control/accounts" +else + ACCOUNTS_DIR=$with_accounts_dir +fi +AC_SUBST(ACCOUNTS_DIR) +AC_DEFINE_UNQUOTED(ACCOUNTS_DIR,"$ACCOUNTS_DIR", [Directory for storing accounts]) + + +AC_ARG_WITH(chandlers_dir, AS_HELP_STRING([--with-chandlers-dir=<path>],[Directory for channel handlers])) +if test -z "$with_chandlers_dir" ; then + eval datadir_expanded="$datadir" + CHANDLERS_DIR="${datadir_expanded}/telepathy/managers" +else + CHANDLERS_DIR=$with_chandlers_dir +fi +AC_SUBST(CHANDLERS_DIR) +AC_DEFINE_UNQUOTED(CHANDLERS_DIR,"$CHANDLERS_DIR", [Directory for channel handlers]) + +test_enabled="no" +AC_MSG_CHECKING(whether to build tests) +AC_ARG_ENABLE(tests, + [ --enable-tests build tests. default=no], + [ + AC_MSG_RESULT(${enableval}) + test_enabled="${enableval}" + ], + [ + AC_MSG_RESULT(no) + test_enabled="no" + ] +) +AM_CONDITIONAL(HAVE_TESTS, [test x$test_enabled = xyes]) + +server_enabled="yes" +AC_MSG_CHECKING(whether to build the sample server) +AC_ARG_ENABLE(server, + [ --enable-server build server. default=yes], + [ + AC_MSG_RESULT(${enableval}) + server_enabled="${enableval}" + ], + [ + AC_MSG_RESULT(yes) + server_enabled="yes" + ] +) +AM_CONDITIONAL(HAVE_SERVER, [test x$server_enabled = xyes]) + +dnl *************************************************************************** +dnl Check for marshal and enum generators +dnl *************************************************************************** +GLIB_GENMARSHAL="`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`" +AC_SUBST(GLIB_GENMARSHAL) +GLIB_MKENUMS="`$PKG_CONFIG --variable=glib_mkenums glib-2.0`" +AC_SUBST(GLIB_MKENUMS) + +PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.51, dbus-glib-1 >= 0.51], have_dbus=yes, have_dbus=no) +AC_SUBST(DBUS_CFLAGS) +AC_SUBST(DBUS_LIBS) + +PKG_CHECK_MODULES(TELEPATHY, libtelepathy >= 0.0.50) +AC_SUBST(TELEPATHY_LIBS) +AC_SUBST(TELEPATHY_CFLAGS) + +GCONF_REQUIRED_VERSION=2.0.0 +PKG_CHECK_MODULES(GCONF, gconf-2.0 >= $GCONF_REQUIRED_VERSION) +AC_SUBST(GCONF_CFLAGS) +AC_SUBST(GCONF_LIBS) + +PKG_CHECK_MODULES(GLIB, glib-2.0) +AC_SUBST(GLIB_LIBS) +AC_SUBST(GLIB_CFLAGS) + +GTK_DOC_CHECK([1.3]) + +pluginlibdir=$libdir/mission-control +AC_SUBST(pluginlibdir) + +AC_OUTPUT([ +Makefile \ +mission-control.pc \ +libmissioncontrol.pc \ +src/Makefile \ +xml/Makefile \ +doc/Makefile \ +test/Makefile \ +doc/reference/Makefile \ +libmissioncontrol/Makefile \ +server/Makefile \ +server/org.freedesktop.Telepathy.MissionControl.service +]) diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..dc5adb0f --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = reference + diff --git a/doc/mc-dbus-iface.html b/doc/mc-dbus-iface.html new file mode 100644 index 00000000..45f84e6e --- /dev/null +++ b/doc/mc-dbus-iface.html @@ -0,0 +1,296 @@ +<html> +<head> +<title>MissionControl</title> +<style type="text/css"> +.interface:before { + content: "Interface "; +} +.interface { + margin-top: 1em; + margin-bottom: 1em; + padding-left: 1em; + font-size: 1.6em; + background: #ccf; +} +.methods:before { + content: "Methods"; + font-size: 1.5em; + margin-top: 1em; + margin-bottom: 1em; + padding-left: 1em; + background: #ccf; + display: block; +} +.signals:before { + content: "Signals"; + font-size: 1.5em; + margin-top: 1em; + margin-bottom: 1em; + padding-left: 1em; + background: #ccf; + display: block; +} +.method H3, .signal H3 { + margin-top: 1em; + margin-bottom: 1em; + padding-left: 1em; + font-size: 1.3em; + background: #ccf; +} +.parameters:before { + content: "Parameters:"; + display: block; + padding-bottom: 0.5em; +} +.parameters, .return { + padding: 1em; +} +.return:before { + content: "Return value(s):"; + display: block; + padding-bottom: 0.5em; +} +.enum { + display: block; + font-family: monospace; + padding-left: 1cm; +} +.enum P { + padding: 0; + margin: 0; + padding-left: 1em; + font-family: serif; +} +</style> +</head> +<body> +<h1>Mission Control specifications</h1> +<div class="interface">com.nokia.chavo.mission_control</div> +<p>This is the DBus interface used by mission-control. + +<div class="methods"> + +<div class="method"> +<h3>SetPresence ( i: presence, s: message ) -> None </h3> +<p>Request a presence +<div class="parameters"> +presence - any of the following values: +<div class="enum"> +0 - MC_PRESENCE_UNSET<br> +1 - MC_PRESENCE_OFFLINE<br> +2 - MC_PRESENCE_AVAILABLE<br> +3 - MC_PRESENCE_AWAY<br> +4 - MC_PRESENCE_EXTENDED_AWAY<br> +5 - MC_PRESENCE_HIDDEN<br> +6 - MC_PRESENCE_DO_NOT_DISTURB<br> +</div> +message - NULL or a message string to specify along with the presence (typically with away presence). +</div> +</div> + +<div class="method"> +<h3>GetPresence ( ) -> i </h3> +<p>Get the last requested presence. +</div> + +<div class="method"> +<h3>GetPresenceActual ( ) -> i </h3> +<p>Get the current presence (which could be different from the last requested presence). +</div> + +<div class="method"> +<h3>RequestChannel ( s: account_name, s: type, u: handle, i: handle_type ) -> None </h3> +<p>Request a channel for a given numeric handle.</p> +<div class="parameters"> +account_name - the name of the account requesting the channel<br> +type - a D-Bus interface name representing base channel type<br> +handle - an integer handle representing a contact, room or list<br> +handle_type - an integer representing the handle type<br> +</div> +</div> + +<div class="method"> +<h3>RequestChannelWithStringHandle ( s: account_name, s: type, s: handle, i: handle_type ) -> None </h3> +<p>Request a channel for a given string handle.</p> +<div class="parameters"> +account_name - the name of the account requesting the channel<br> +type - a D-Bus interface name representing base channel type<br> +handle - a string representing a contact, room or list<br> +handle_type - an integer representing the handle type<br> +</div> +</div> + +<div class="method"> +<h3>CancelChannelRequest ( u: operation_id ) -> None </h3> +<p>Cancel a channel request; if the channel has already been created, it will be destroyed.</p> +<div class="parameters"> +operation_id - the unique id of the channel request operation. (<em>this is the <code>serial</code> parameter of the RequestChannelWithStringHandle call — specs are going to change soon, though</em>)<br> +</div> +</div> + +<div class="method"> +<h3>ConnectAllWithDefaultPresence ( ) -> None </h3> +<p>Connect all accounts with the default online presence. +</div> + +<div class="method"> +<h3>GetConnectionStatus ( s: account_name ) -> u </h3> +<p>Get the connection status for the specified account.</p> +<div class="parameters"> +account_name - the account to retrieve the status from<br> +</div> +<div class="return"> +A unsigned integer as defined in Telepathy: +<div class="enum"> +0 - CONNECTION_STATUS_CONNECTED<br> +1 - CONNECTION_STATUS_CONNECTING<br> +2 - CONNECTION_STATUS_DISCONNECTED<br> +</div> +</div> +</div> + +<div class="method"> +<h3>GetOnlineConnections ( ) -> as </h3> +<p>Get the online connections.</p> +<div class="return"> +An array of the names of the online accounts. +</div> +</div> + +<div class="method"> +<h3>GetConnection ( s: account_name ) -> s, o </h3> +<p>Get the connection status for the specified account.</p> +<div class="parameters"> +account_name - the account to retrieve the connection from<br> +</div> +<div class="return"> +s: the connection bus name<br> +o: the connection object path +</div> +</div> + +<div class="method"> +<h3>GetAccountForConnection ( s: object_path ) -> s </h3> +<p>Get the connection's account.</p> +<div class="parameters"> +object_path - the connection's object path<br> +</div> +<div class="return"> +s: the account name<br> +</div> +</div> + +<div class="method"> +<h3>GetUsedChannelsCount ( s: type ) -> u </h3> +<p>Get the number of channels of the specified type.</p> +<div class="parameters"> +type - the channel type<br> +</div> +<div class="return"> +u: the number of existing channels of this type<br> +</div> +</div> + +</div> + +<div class="signals"> + +<div class="signal"> +<h3>AccountStatusChanged ( u: status, u: presence, u: reason, s: account_id )</h3> +<p>Emitted when the status of some account changes. +<div class="parameters"> +status - the connection status of the account<br> +presence - the presence status of the account</br> +reason - the reason why the status change happened. Can be any of the following: +<div class="enum"> +0 - TP_CONN_STATUS_REASON_NONE_SPECIFIED<br> +1 - TP_CONN_STATUS_REASON_REQUESTED<br> +2 - TP_CONN_STATUS_REASON_NETWORK_ERROR<br> +3 - TP_CONN_STATUS_REASON_AUTHENTICATION_FAILED<br> +4 - TP_CONN_STATUS_REASON_ENCRYPTION_ERROR<br> +5 - TP_CONN_STATUS_REASON_NAME_IN_USE<br> +6 - TP_CONN_STATUS_REASON_CERT_NOT_PROVIDED<br> +7 - TP_CONN_STATUS_REASON_CERT_UNTRUSTED<br> +8 - TP_CONN_STATUS_REASON_CERT_EXPIRED<br> +9 - TP_CONN_STATUS_REASON_CERT_NOT_ACTIVATED<br> +10 - TP_CONN_STATUS_REASON_CERT_HOSTNAME_MISMATCH<br> +11 - TP_CONN_STATUS_REASON_CERT_FINGERPRINT_MISMATCH<br> +12 - TP_CONN_STATUS_REASON_CERT_SELF_SIGNED<br> +13 - TP_CONN_STATUS_REASON_CERT_OTHER_ERROR +</div> +account_id - the account whose status has changed +</div> +</div> + +<div class="signal"> +<h3>McdError ( u: serial, s: client_id, u: error_id )</h3> +<p>Emitted on some error. +<div class="parameters"> +serial - <em>operation ID, maybe it will get removed from the specs soon</em><br> +client_id - the d-bus unique name of the client whose this error is meant to</br> +error_id - the error code: +<div class="enum"> +0 - MC_DISCONNECTED_ERROR<br> +1 - MC_INVALID_HANDLE_ERROR<br> +2 - MC_NO_MATCHING_CONNECTION_ERROR<br> +3 - MC_INVALID_ACCOUNT_ERROR<br> +4 - MC_PRESENCE_FAILURE_ERROR<br> +5 - MC_NO_ACCOUNTS_ERROR<br> +6 - MC_NETWORK_ERROR<br> +7 - MC_CONTACT_DOES_NOT_SUPPORT_VOICE_ERROR<br> +8 - MC_LOWMEM_ERROR<br> +9 - MC_CHANNEL_REQUEST_GENERIC_ERROR<br> +10 - MC_CHANNEL_BANNED_ERROR<br> +11 - MC_CHANNEL_FULL_ERROR<br> +12 - MC_CHANNEL_INVITE_ONLY_ERROR<br> +13 - MC_LAST_ERROR +</div> +</div> +</div> + +<div class="signal"> +<h3>PresenceStatusRequested ( u: presence )</h3> +<p>Emitted when a presence has been requested. +<div class="parameters"> +presence - the requested presence. +</div> +</div> + +<div class="signal"> +<h3>PresenceStatusActual ( u: presence )</h3> +<p>Emitted when the actual presence changes. +<div class="parameters"> +presence - the actual presence. +</div> +</div> + +<div class="signal"> +<h3>UsedChannelsCountChanged ( s: type, u: count )</h3> +<p>Emitted when the actual presence changes. +<div class="parameters"> +type - the type of the channels<br> +count - the new number of the channels of this type +</div> +</div> + +<div class="signal"> +<h3>StatusActual ( u: status, u: presence )</h3> +<p>Emitted when the accounts' status changes. +<div class="parameters"> +status - Can be one of these values:<br> +<div class="enum"> +0 - MC_STATUS_DISCONNECTED +<p>When all accounts are disconnected.</p> +1 - MC_STATUS_CONNECTING +<p>When at least one account is connecting.</p> +2 - MC_STATUS_CONNECTED +<p>When at least one account is connected <em>and</em> none is connecting.</p> +</div> +presence - the last requested presence. +</div> +</div> + +</div> + +</body> +</html> diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am new file mode 100644 index 00000000..43fa44b7 --- /dev/null +++ b/doc/reference/Makefile.am @@ -0,0 +1,74 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.6 at least. +AUTOMAKE_OPTIONS = 1.6 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=mission-control + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=../../libmissioncontrol + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS= + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=$(top_srcdir)/libmissioncontrol/*.h +CFILE_GLOB=$(top_srcdir)/libmissioncontrol/*.c + +# Header files to ignore when scanning. +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES= mc-account-monitor-priv.h \ + mc-account-priv.h \ + mc-manager-priv.h \ + mc-protocol-priv.h \ + mc-client-lib-gen.h \ + mission-control-signals-marshal.h + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +INCLUDES=-I../.. $(DBUS_CFLAGS) $(TELEPATHY_CFLAGS) +GTKDOC_LIBS=$(top_builddir)/libmissioncontrol/libmissioncontrol.la $(GCONF_LIBS) $(GLIB_LIBS) $(DBUS_LIBS) $(TELEPATHY_LIBS) + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST += diff --git a/doc/reference/mission-control-docs.sgml b/doc/reference/mission-control-docs.sgml new file mode 100644 index 00000000..12c153a8 --- /dev/null +++ b/doc/reference/mission-control-docs.sgml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"> +<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude"> + <bookinfo> + <title>Mission Control Reference Manual</title> + </bookinfo> + + <chapter> + <title>Mission Control API reference</title> + <xi:include href="xml/mc-account.xml"/> + <xi:include href="xml/mc-profile.xml"/> + <xi:include href="xml/mc-manager.xml"/> + <xi:include href="xml/mc-protocol.xml"/> + <xi:include href="xml/mc-account-monitor.xml"/> + <xi:include href="xml/mc.xml"/> + <xi:include href="xml/mission-control.xml"/> + </chapter> +</book> diff --git a/doc/reference/mission-control-overrides.txt b/doc/reference/mission-control-overrides.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/doc/reference/mission-control-overrides.txt diff --git a/doc/reference/mission-control-sections.txt b/doc/reference/mission-control-sections.txt new file mode 100644 index 00000000..78ba1631 --- /dev/null +++ b/doc/reference/mission-control-sections.txt @@ -0,0 +1,182 @@ +<SECTION> +<FILE>mc-account</FILE> +<TITLE>McAccount</TITLE> +McAccount +mc_account_new +McAccountSettingState +mc_account_lookup +mc_account_lookup_with_profile +mc_account_lookup_with_vcard_field +mc_account_free +mc_account_clear_cache +mc_account_create +mc_account_delete +mc_accounts_list +mc_accounts_list_by_enabled +mc_accounts_list_by_profile +mc_accounts_list_by_vcard_field +mc_accounts_list_free +McAccountFilter +mc_accounts_filter +mc_account_get_unique_name +mc_account_get_profile +mc_account_get_display_name +mc_account_set_display_name +mc_account_get_normalized_name +mc_account_set_normalized_name +mc_account_is_enabled +mc_account_set_enabled +mc_account_get_param_boolean +mc_account_get_param_int +mc_account_get_param_string +mc_account_get_params +mc_account_set_param_boolean +mc_account_set_param_int +mc_account_set_param_string +mc_account_unset_param +mc_account_is_complete +mc_account_exists_supporting_invisible +mc_account_set_avatar +mc_account_get_avatar +<SUBSECTION Standard> +MC_ACCOUNT +MC_IS_ACCOUNT +MC_TYPE_ACCOUNT +mc_account_get_type +MC_ACCOUNT_CLASS +MC_IS_ACCOUNT_CLASS +MC_ACCOUNT_GET_CLASS +</SECTION> + +<SECTION> +<FILE>mc-profile</FILE> +McProfileCapabilityFlags +<TITLE>McProfile</TITLE> +McProfile +mc_profile_new +mc_profile_lookup +mc_profile_lookup_default_for_vcard_field +mc_profile_free +mc_profile_clear_cache +mc_profiles_list +mc_profiles_list_by_vcard_field +mc_profiles_free_list +mc_profile_get_unique_name +mc_profile_get_configuration_ui +mc_profile_get_display_name +mc_profile_get_icon_name +mc_profile_get_branding_icon_name +mc_profile_get_vcard_field +mc_profile_get_default_account_domain +mc_profile_get_supports_invisible +mc_profile_get_protocol +mc_profile_get_protocol_name +mc_profile_is_default_for_vcard_field +mc_profile_get_capabilities +mc_profile_get_default_setting +<SUBSECTION Standard> +MC_PROFILE +MC_IS_PROFILE +MC_TYPE_PROFILE +mc_profile_get_type +MC_PROFILE_CLASS +MC_IS_PROFILE_CLASS +MC_PROFILE_GET_CLASS +</SECTION> + +<SECTION> +<FILE>mc-manager</FILE> +<TITLE>McManager</TITLE> +McManager +mc_manager_lookup +mc_manager_free +mc_manager_clear_cache +mc_managers_list +mc_managers_free_list +mc_manager_get_unique_name +mc_manager_get_bus_name +mc_manager_get_object_path +<SUBSECTION Standard> +MC_MANAGER +MC_IS_MANAGER +MC_TYPE_MANAGER +mc_manager_get_type +MC_MANAGER_CLASS +MC_IS_MANAGER_CLASS +MC_MANAGER_GET_CLASS +</SECTION> + +<SECTION> +<FILE>mc-protocol</FILE> +<TITLE>McProtocol</TITLE> +McProtocol +mc_protocol_lookup +mc_protocol_free +mc_protocols_list +mc_protocols_list_by_manager +mc_protocols_free_list +mc_protocol_get_manager +mc_protocol_get_name +mc_protocol_get_params +mc_protocol_free_params_list +mc_protocol_print +<SUBSECTION Standard> +MC_PROTOCOL +MC_IS_PROTOCOL +MC_TYPE_PROTOCOL +mc_protocol_get_type +MC_PROTOCOL_CLASS +MC_IS_PROTOCOL_CLASS +MC_PROTOCOL_GET_CLASS +</SECTION> + +<SECTION> +<FILE>mc-account-monitor</FILE> +<TITLE>McAccountMonitor</TITLE> +McAccountMonitor +mc_account_monitor_new +<SUBSECTION Standard> +MC_ACCOUNT_MONITOR +MC_IS_ACCOUNT_MONITOR +MC_TYPE_ACCOUNT_MONITOR +mc_account_monitor_get_type +MC_ACCOUNT_MONITOR_CLASS +MC_IS_ACCOUNT_MONITOR_CLASS +MC_ACCOUNT_MONITOR_GET_CLASS +</SECTION> + +<SECTION> +<FILE>mc</FILE> +mc_make_resident +</SECTION> + +<SECTION> +<FILE>mission-control</FILE> +MissionControl +MissionControlClass +MCError +McPresence +McStatus +McAccountStatus +McCallback +McGetCurrentStatusCallback +missioncontrol +missioncontrolclass +mission_control_new +mission_control_set_presence +mission_control_get_presence +mission_control_get_presence_actual +mission_control_request_channel +mission_control_request_channel_with_string_handle +mission_control_cancel_channel_request +mission_control_connect_all_with_default_presence +mission_control_get_connection_status +mission_control_get_online_connections +mission_control_get_connection +mission_control_get_account_for_connection +mission_control_get_used_channels_count +mission_control_get_current_status +mission_control_free_account_statuses +mission_control_remote_avatar_changed +</SECTION> + diff --git a/doc/reference/mission-control.types b/doc/reference/mission-control.types new file mode 100644 index 00000000..8f90607b --- /dev/null +++ b/doc/reference/mission-control.types @@ -0,0 +1,9 @@ +#include <libmissioncontrol/mission-control.h> +#include <libmissioncontrol/mc-account-monitor.h> + +mission_control_get_type +mc_account_monitor_get_type +mc_account_get_type +mc_profile_get_type +mc_protocol_get_type +mc_manager_get_type diff --git a/doc/reference/tmpl/mc-account-monitor-priv.sgml b/doc/reference/tmpl/mc-account-monitor-priv.sgml new file mode 100644 index 00000000..6ea0d92f --- /dev/null +++ b/doc/reference/tmpl/mc-account-monitor-priv.sgml @@ -0,0 +1,16 @@ +<!-- ##### SECTION Title ##### --> +mc-account-monitor-priv + +<!-- ##### SECTION Short_Description ##### --> + + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + diff --git a/doc/reference/tmpl/mc-account-monitor.sgml b/doc/reference/tmpl/mc-account-monitor.sgml new file mode 100644 index 00000000..0edbed2d --- /dev/null +++ b/doc/reference/tmpl/mc-account-monitor.sgml @@ -0,0 +1,70 @@ +<!-- ##### SECTION Title ##### --> +McAccountMonitor + +<!-- ##### SECTION Short_Description ##### --> +Monitor the accounts' status. + +<!-- ##### SECTION Long_Description ##### --> +<para> +The #McAccountMonitor is an object that raises signals when some changes have been made to one account. There are signals for account creation/deletion/change, and enabling/disabling. +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### STRUCT McAccountMonitor ##### --> +<para> + +</para> + + +<!-- ##### SIGNAL McAccountMonitor::account-changed ##### --> +<para> + +</para> + +@mcaccountmonitor: the object which received the signal. +@arg1: + +<!-- ##### SIGNAL McAccountMonitor::account-created ##### --> +<para> + +</para> + +@mcaccountmonitor: the object which received the signal. +@arg1: + +<!-- ##### SIGNAL McAccountMonitor::account-deleted ##### --> +<para> + +</para> + +@mcaccountmonitor: the object which received the signal. +@arg1: + +<!-- ##### SIGNAL McAccountMonitor::account-disabled ##### --> +<para> + +</para> + +@mcaccountmonitor: the object which received the signal. +@arg1: + +<!-- ##### SIGNAL McAccountMonitor::account-enabled ##### --> +<para> + +</para> + +@mcaccountmonitor: the object which received the signal. +@arg1: + +<!-- ##### FUNCTION mc_account_monitor_new ##### --> +<para> + +</para> + +@Returns: + + diff --git a/doc/reference/tmpl/mc-account-priv.sgml b/doc/reference/tmpl/mc-account-priv.sgml new file mode 100644 index 00000000..bc135c67 --- /dev/null +++ b/doc/reference/tmpl/mc-account-priv.sgml @@ -0,0 +1,16 @@ +<!-- ##### SECTION Title ##### --> +mc-account-priv + +<!-- ##### SECTION Short_Description ##### --> + + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + diff --git a/doc/reference/tmpl/mc-account.sgml b/doc/reference/tmpl/mc-account.sgml new file mode 100644 index 00000000..c34ea61f --- /dev/null +++ b/doc/reference/tmpl/mc-account.sgml @@ -0,0 +1,361 @@ +<!-- ##### SECTION Title ##### --> +McAccount + +<!-- ##### SECTION Short_Description ##### --> +MC's representation of an account. + +<!-- ##### SECTION Long_Description ##### --> +<para> +A #McAccount stores all the informations required to use an account (login, password, #McProfile, etc.). +</para> +<para> +The #McAccount API allows clients to manage accounts. +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### STRUCT McAccount ##### --> +<para> + +</para> + + +<!-- ##### FUNCTION mc_account_new ##### --> +<para> + +</para> + +@Returns: + + +<!-- ##### ENUM McAccountSettingState ##### --> +<para> + +</para> + +@MC_ACCOUNT_SETTING_ABSENT: +@MC_ACCOUNT_SETTING_FROM_ACCOUNT: +@MC_ACCOUNT_SETTING_FROM_PROFILE: +@MC_ACCOUNT_SETTING_FROM_PROXY: + +<!-- ##### FUNCTION mc_account_lookup ##### --> +<para> + +</para> + +@unique_name: +@Returns: + + +<!-- ##### FUNCTION mc_account_lookup_with_profile ##### --> +<para> + +</para> + +@profile: +@account: +@Returns: + + +<!-- ##### FUNCTION mc_account_lookup_with_vcard_field ##### --> +<para> + +</para> + +@vcard_field: +@account: +@Returns: + + +<!-- ##### FUNCTION mc_account_free ##### --> +<para> + +</para> + +@account: + + +<!-- ##### FUNCTION mc_account_clear_cache ##### --> +<para> + +</para> + + + +<!-- ##### FUNCTION mc_account_create ##### --> +<para> + +</para> + +@profile: +@Returns: + + +<!-- ##### FUNCTION mc_account_delete ##### --> +<para> + +</para> + +@account: +@Returns: + + +<!-- ##### FUNCTION mc_accounts_list ##### --> +<para> + +</para> + +@Returns: + + +<!-- ##### FUNCTION mc_accounts_list_by_enabled ##### --> +<para> + +</para> + +@enabled: +@Returns: + + +<!-- ##### FUNCTION mc_accounts_list_by_profile ##### --> +<para> + +</para> + +@profile: +@Returns: + + +<!-- ##### FUNCTION mc_accounts_list_by_vcard_field ##### --> +<para> + +</para> + +@vcard_field: +@Returns: + + +<!-- ##### FUNCTION mc_accounts_list_free ##### --> +<para> + +</para> + +@list: + + +<!-- ##### USER_FUNCTION McAccountFilter ##### --> +<para> +This function is called for every #McAccount in the list, and determines which elements have to be removed from the list. +</para> + +@account: The #McAccount. +@data: Pointer to user data. +@Returns: %TRUE if the account must stay in the list, %FALSE if is must be removed. + + +<!-- ##### FUNCTION mc_accounts_filter ##### --> +<para> + +</para> + +@accounts: +@filter: +@data: +@Returns: + + +<!-- ##### FUNCTION mc_account_get_unique_name ##### --> +<para> + +</para> + +@account: +@Returns: + + +<!-- ##### FUNCTION mc_account_get_profile ##### --> +<para> + +</para> + +@account: +@Returns: + + +<!-- ##### FUNCTION mc_account_get_display_name ##### --> +<para> + +</para> + +@account: +@Returns: + + +<!-- ##### FUNCTION mc_account_set_display_name ##### --> +<para> + +</para> + +@account: +@name: +@Returns: + + +<!-- ##### FUNCTION mc_account_get_normalized_name ##### --> +<para> + +</para> + +@account: +@Returns: + + +<!-- ##### FUNCTION mc_account_set_normalized_name ##### --> +<para> + +</para> + +@account: +@name: +@Returns: + + +<!-- ##### FUNCTION mc_account_is_enabled ##### --> +<para> + +</para> + +@account: +@Returns: + + +<!-- ##### FUNCTION mc_account_set_enabled ##### --> +<para> + +</para> + +@account: +@enabled: +@Returns: + + +<!-- ##### FUNCTION mc_account_get_param_boolean ##### --> +<para> + +</para> + +@account: +@name: +@value: +@Returns: + + +<!-- ##### FUNCTION mc_account_get_param_int ##### --> +<para> + +</para> + +@account: +@name: +@value: +@Returns: + + +<!-- ##### FUNCTION mc_account_get_param_string ##### --> +<para> + +</para> + +@account: +@name: +@value: +@Returns: + + +<!-- ##### FUNCTION mc_account_get_params ##### --> +<para> + +</para> + +@account: +@Returns: + + +<!-- ##### FUNCTION mc_account_set_param_boolean ##### --> +<para> + +</para> + +@account: +@name: +@value: +@Returns: + + +<!-- ##### FUNCTION mc_account_set_param_int ##### --> +<para> + +</para> + +@account: +@name: +@value: +@Returns: + + +<!-- ##### FUNCTION mc_account_set_param_string ##### --> +<para> + +</para> + +@account: +@name: +@value: +@Returns: + + +<!-- ##### FUNCTION mc_account_unset_param ##### --> +<para> + +</para> + +@account: +@name: +@Returns: + + +<!-- ##### FUNCTION mc_account_is_complete ##### --> +<para> + +</para> + +@account: +@Returns: + + +<!-- ##### FUNCTION mc_account_set_avatar ##### --> +<para> + +</para> + +@account: +@filename: +@mime_type: +@Returns: + + +<!-- ##### FUNCTION mc_account_get_avatar ##### --> +<para> + +</para> + +@account: +@filename: +@mime_type: +@token: +@Returns: + + diff --git a/doc/reference/tmpl/mc-client-lib-gen.sgml b/doc/reference/tmpl/mc-client-lib-gen.sgml new file mode 100644 index 00000000..7b87ad80 --- /dev/null +++ b/doc/reference/tmpl/mc-client-lib-gen.sgml @@ -0,0 +1,16 @@ +<!-- ##### SECTION Title ##### --> +mc-client-lib-gen + +<!-- ##### SECTION Short_Description ##### --> + + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + diff --git a/doc/reference/tmpl/mc-manager-priv.sgml b/doc/reference/tmpl/mc-manager-priv.sgml new file mode 100644 index 00000000..94387094 --- /dev/null +++ b/doc/reference/tmpl/mc-manager-priv.sgml @@ -0,0 +1,16 @@ +<!-- ##### SECTION Title ##### --> +mc-manager-priv + +<!-- ##### SECTION Short_Description ##### --> + + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + diff --git a/doc/reference/tmpl/mc-manager.sgml b/doc/reference/tmpl/mc-manager.sgml new file mode 100644 index 00000000..4b785ee4 --- /dev/null +++ b/doc/reference/tmpl/mc-manager.sgml @@ -0,0 +1,92 @@ +<!-- ##### SECTION Title ##### --> +McManager + +<!-- ##### SECTION Short_Description ##### --> +A representation of a connection manager. + +<!-- ##### SECTION Long_Description ##### --> +<para> +A #McManager object represents a connection manager, that is the backend which can be used for handling one or more protocols. +</para> +<para> +The #McManager API allows clients to query the available connection managers, and obtain their D-Bus coordinates. +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### STRUCT McManager ##### --> +<para> + +</para> + + +<!-- ##### FUNCTION mc_manager_lookup ##### --> +<para> + +</para> + +@unique_name: +@Returns: + + +<!-- ##### FUNCTION mc_manager_free ##### --> +<para> + +</para> + +@id: + + +<!-- ##### FUNCTION mc_manager_clear_cache ##### --> +<para> + +</para> + + + +<!-- ##### FUNCTION mc_managers_list ##### --> +<para> + +</para> + +@Returns: + + +<!-- ##### FUNCTION mc_managers_free_list ##### --> +<para> + +</para> + +@list: + + +<!-- ##### FUNCTION mc_manager_get_unique_name ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_manager_get_bus_name ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_manager_get_object_path ##### --> +<para> + +</para> + +@id: +@Returns: + + diff --git a/doc/reference/tmpl/mc-profile.sgml b/doc/reference/tmpl/mc-profile.sgml new file mode 100644 index 00000000..c06fb7fd --- /dev/null +++ b/doc/reference/tmpl/mc-profile.sgml @@ -0,0 +1,219 @@ +<!-- ##### SECTION Title ##### --> +McProfile + +<!-- ##### SECTION Short_Description ##### --> +Account profiles. + +<!-- ##### SECTION Long_Description ##### --> +<para> +A #McProfile represent a network service and stores some relevant about it, +such as the underlying protocol, the supported channel types, the default +server, etc. as well as some client-side information, such as the filename of +the icon to be shown in association with it. +</para> +<para> +The #McProfile API provides a way for managing profiles. +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### ENUM McProfileCapabilityFlags ##### --> +<para> + +</para> + +@MC_PROFILE_CAPABILITY_NONE: +@MC_PROFILE_CAPABILITY_CHAT_P2P: +@MC_PROFILE_CAPABILITY_CHAT_ROOM: +@MC_PROFILE_CAPABILITY_CHAT_ROOM_LIST: +@MC_PROFILE_CAPABILITY_VOICE_P2P: +@MC_PROFILE_CAPABILITY_CONTACT_SEARCH: +@MC_PROFILE_CAPABILITY_SPLIT_ACCOUNT: +@MC_PROFILE_CAPABILITY_REGISTRATION_UI: +@MC_PROFILE_CAPABILITY_SUPPORTS_AVATARS: + +<!-- ##### STRUCT McProfile ##### --> +<para> + +</para> + + +<!-- ##### FUNCTION mc_profile_new ##### --> +<para> + +</para> + +@unique_name: +@Returns: + + +<!-- ##### FUNCTION mc_profile_lookup ##### --> +<para> + +</para> + +@unique_name: +@Returns: + + +<!-- ##### FUNCTION mc_profile_lookup_default_for_vcard_field ##### --> +<para> + +</para> + +@vcard_field: +@Returns: + + +<!-- ##### FUNCTION mc_profile_free ##### --> +<para> + +</para> + +@id: + + +<!-- ##### FUNCTION mc_profile_clear_cache ##### --> +<para> + +</para> + + + +<!-- ##### FUNCTION mc_profiles_list ##### --> +<para> + +</para> + +@Returns: + + +<!-- ##### FUNCTION mc_profiles_list_by_vcard_field ##### --> +<para> + +</para> + +@vcard_field: +@Returns: + + +<!-- ##### FUNCTION mc_profiles_free_list ##### --> +<para> + +</para> + +@list: + + +<!-- ##### FUNCTION mc_profile_get_unique_name ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_get_configuration_ui ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_get_display_name ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_get_icon_name ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_get_branding_icon_name ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_get_vcard_field ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_get_default_account_domain ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_get_protocol ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_get_protocol_name ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_is_default_for_vcard_field ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_get_capabilities ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_profile_get_default_setting ##### --> +<para> + +</para> + +@id: +@setting: +@Returns: + + diff --git a/doc/reference/tmpl/mc-protocol-priv.sgml b/doc/reference/tmpl/mc-protocol-priv.sgml new file mode 100644 index 00000000..0116a40a --- /dev/null +++ b/doc/reference/tmpl/mc-protocol-priv.sgml @@ -0,0 +1,16 @@ +<!-- ##### SECTION Title ##### --> +mc-protocol-priv + +<!-- ##### SECTION Short_Description ##### --> + + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + diff --git a/doc/reference/tmpl/mc-protocol.sgml b/doc/reference/tmpl/mc-protocol.sgml new file mode 100644 index 00000000..4cf7d89c --- /dev/null +++ b/doc/reference/tmpl/mc-protocol.sgml @@ -0,0 +1,112 @@ +<!-- ##### SECTION Title ##### --> +McProtocol + +<!-- ##### SECTION Short_Description ##### --> +representation of a communication protocol. + +<!-- ##### SECTION Long_Description ##### --> +<para> +An #McProtocol represents a protocol (such as Jabber, SIP, IRC...) and stores +some parameters about it. It also specifies what is the backend (#McManager) +which can handle this protocol. +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### STRUCT McProtocol ##### --> +<para> + +</para> + + +<!-- ##### FUNCTION mc_protocol_lookup ##### --> +<para> + +</para> + +@id: +@protocol: +@Returns: + + +<!-- ##### FUNCTION mc_protocol_free ##### --> +<para> + +</para> + +@id: + + +<!-- ##### FUNCTION mc_protocols_list ##### --> +<para> + +</para> + +@Returns: + + +<!-- ##### FUNCTION mc_protocols_list_by_manager ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_protocols_free_list ##### --> +<para> + +</para> + +@list: + + +<!-- ##### FUNCTION mc_protocol_get_manager ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_protocol_get_name ##### --> +<para> + +</para> + +@id: +@Returns: + + +<!-- ##### FUNCTION mc_protocol_get_params ##### --> +<para> + +</para> + +@protocol: +@Returns: +<!-- # Unused Parameters # --> +@param: + + +<!-- ##### FUNCTION mc_protocol_free_params_list ##### --> +<para> + +</para> + +@list: + + +<!-- ##### FUNCTION mc_protocol_print ##### --> +<para> + +</para> + +@protocol: + + diff --git a/doc/reference/tmpl/mc.sgml b/doc/reference/tmpl/mc.sgml new file mode 100644 index 00000000..67e067b0 --- /dev/null +++ b/doc/reference/tmpl/mc.sgml @@ -0,0 +1,23 @@ +<!-- ##### SECTION Title ##### --> +mc + +<!-- ##### SECTION Short_Description ##### --> + + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### FUNCTION mc_make_resident ##### --> +<para> + +</para> + + + diff --git a/doc/reference/tmpl/mission-control-signals-marshal.sgml b/doc/reference/tmpl/mission-control-signals-marshal.sgml new file mode 100644 index 00000000..844a6afa --- /dev/null +++ b/doc/reference/tmpl/mission-control-signals-marshal.sgml @@ -0,0 +1,16 @@ +<!-- ##### SECTION Title ##### --> +mission-control-signals-marshal + +<!-- ##### SECTION Short_Description ##### --> + + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + diff --git a/doc/reference/tmpl/mission-control-unused.sgml b/doc/reference/tmpl/mission-control-unused.sgml new file mode 100644 index 00000000..e2354e9f --- /dev/null +++ b/doc/reference/tmpl/mission-control-unused.sgml @@ -0,0 +1,256 @@ +<!-- ##### SECTION ./tmpl/mc-account-monitor-priv.sgml:Long_Description ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mc-account-monitor-priv.sgml:See_Also ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mc-account-monitor-priv.sgml:Short_Description ##### --> + + + +<!-- ##### SECTION ./tmpl/mc-account-monitor-priv.sgml:Title ##### --> +mc-account-monitor-priv + + +<!-- ##### SECTION ./tmpl/mc-account-priv.sgml:Long_Description ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mc-account-priv.sgml:See_Also ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mc-account-priv.sgml:Short_Description ##### --> + + + +<!-- ##### SECTION ./tmpl/mc-account-priv.sgml:Title ##### --> +mc-account-priv + + +<!-- ##### SECTION ./tmpl/mc-client-lib-gen.sgml:Long_Description ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mc-client-lib-gen.sgml:See_Also ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mc-client-lib-gen.sgml:Short_Description ##### --> + + + +<!-- ##### SECTION ./tmpl/mc-client-lib-gen.sgml:Title ##### --> +mc-client-lib-gen + + +<!-- ##### SECTION ./tmpl/mc-manager-priv.sgml:Long_Description ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mc-manager-priv.sgml:See_Also ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mc-manager-priv.sgml:Short_Description ##### --> + + + +<!-- ##### SECTION ./tmpl/mc-manager-priv.sgml:Title ##### --> +mc-manager-priv + + +<!-- ##### SECTION ./tmpl/mc-protocol-priv.sgml:Long_Description ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mc-protocol-priv.sgml:See_Also ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mc-protocol-priv.sgml:Short_Description ##### --> + + + +<!-- ##### SECTION ./tmpl/mc-protocol-priv.sgml:Title ##### --> +mc-protocol-priv + + +<!-- ##### SECTION ./tmpl/mission-control-signals-marshal.sgml:Long_Description ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mission-control-signals-marshal.sgml:See_Also ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/mission-control-signals-marshal.sgml:Short_Description ##### --> + + + +<!-- ##### SECTION ./tmpl/mission-control-signals-marshal.sgml:Title ##### --> +mission-control-signals-marshal + + +<!-- ##### MACRO DBUS_API_SUBJECT_TO_CHANGE ##### --> +<para> + +</para> + + +<!-- ##### MACRO IS_MISSIONCONTROL ##### --> +<para> + +</para> + +@obj: + +<!-- ##### MACRO IS_MISSIONCONTROL_CLASS ##### --> +<para> + +</para> + +@klass: + +<!-- ##### MACRO MC_ERROR ##### --> +<para> + +</para> + + +<!-- ##### MACRO MISSIONCONTROL ##### --> +<para> + +</para> + +@obj: + +<!-- ##### MACRO MISSIONCONTROL_CLASS ##### --> +<para> + +</para> + +@klass: + +<!-- ##### MACRO MISSIONCONTROL_GET_CLASS ##### --> +<para> + +</para> + +@obj: + +<!-- ##### MACRO MISSIONCONTROL_TYPE ##### --> +<para> + +</para> + + +<!-- ##### MACRO MISSION_CONTROL_IFACE ##### --> +<para> + +</para> + + +<!-- ##### MACRO MISSION_CONTROL_PATH ##### --> +<para> + +</para> + + +<!-- ##### MACRO MISSION_CONTROL_SERVICE ##### --> +<para> + +</para> + + +<!-- ##### STRUCT McAccountMonitorClass ##### --> +<para> + +</para> + +@parent_class: + +<!-- ##### ENUM OssoRtcomPresence ##### --> +<para> + +</para> + +@OSSO_RTCOM_PRESENCE_UNSET: +@OSSO_RTCOM_PRESENCE_OFFLINE: +@OSSO_RTCOM_PRESENCE_AVAILABLE: +@OSSO_RTCOM_PRESENCE_AWAY: +@OSSO_RTCOM_PRESENCE_EXTENDED_AWAY: +@OSSO_RTCOM_PRESENCE_HIDDEN: +@OSSO_RTCOM_PRESENCE_DO_NOT_DISTURB: +@LAST_OSSO_RTCOM_PRESENCE: +@MC_PRESENCE_UNSET: +@MC_PRESENCE_OFFLINE: +@MC_PRESENCE_AVAILABLE: +@MC_PRESENCE_AWAY: +@MC_PRESENCE_EXTENDED_AWAY: +@MC_PRESENCE_HIDDEN: +@MC_PRESENCE_DO_NOT_DISTURB: +@LAST_MC_PRESENCE: + +<!-- ##### FUNCTION mc_account_exists_supporting_invisible ##### --> +<para> + +</para> + +@Returns: + +<!-- ##### FUNCTION mc_profile_get_supports_invisible ##### --> +<para> + +</para> + +@id: +@Returns: + +<!-- ##### FUNCTION mission_control_cancel_last_request ##### --> +<para> + +</para> + +@self: + +<!-- ##### FUNCTION mission_control_error_quark ##### --> +<para> + +</para> + +@Returns: + +<!-- ##### FUNCTION mission_control_get_type ##### --> +<para> + +</para> + +@Returns: + diff --git a/doc/reference/tmpl/mission-control.sgml b/doc/reference/tmpl/mission-control.sgml new file mode 100644 index 00000000..034ae433 --- /dev/null +++ b/doc/reference/tmpl/mission-control.sgml @@ -0,0 +1,316 @@ +<!-- ##### SECTION Title ##### --> +mission-control + +<!-- ##### SECTION Short_Description ##### --> +mission-control service API + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### TYPEDEF MissionControl ##### --> +<para> + +</para> + + +<!-- ##### SIGNAL MissionControl::Error ##### --> +<para> + +</para> + +@missioncontrol: the object which received the signal. +@arg1: +@arg2: + +<!-- ##### SIGNAL MissionControl::ServiceEnded ##### --> +<para> + +</para> + +@missioncontrol: the object which received the signal. + +<!-- ##### TYPEDEF MissionControlClass ##### --> +<para> + +</para> + + +<!-- ##### ENUM MCError ##### --> +<para> + +</para> + +@MC_DISCONNECTED_ERROR: +@MC_INVALID_HANDLE_ERROR: +@MC_NO_MATCHING_CONNECTION_ERROR: +@MC_INVALID_ACCOUNT_ERROR: +@MC_PRESENCE_FAILURE_ERROR: +@MC_NO_ACCOUNTS_ERROR: +@MC_NETWORK_ERROR: +@MC_CONTACT_DOES_NOT_SUPPORT_VOICE_ERROR: +@MC_LOWMEM_ERROR: +@MC_CHANNEL_REQUEST_GENERIC_ERROR: +@MC_CHANNEL_BANNED_ERROR: +@MC_CHANNEL_FULL_ERROR: +@MC_CHANNEL_INVITE_ONLY_ERROR: +@MC_LAST_ERROR: + +<!-- ##### ENUM McPresence ##### --> +<para> + +</para> + +@MC_PRESENCE_UNSET: +@MC_PRESENCE_OFFLINE: +@MC_PRESENCE_AVAILABLE: +@MC_PRESENCE_AWAY: +@MC_PRESENCE_EXTENDED_AWAY: +@MC_PRESENCE_HIDDEN: +@MC_PRESENCE_DO_NOT_DISTURB: +@LAST_MC_PRESENCE: + +<!-- ##### ENUM McStatus ##### --> +<para> + +</para> + +@MC_STATUS_DISCONNECTED: +@MC_STATUS_CONNECTING: +@MC_STATUS_CONNECTED: + +<!-- ##### STRUCT McAccountStatus ##### --> +<para> + +</para> + +@unique_name: +@status: +@presence: +@reason: + +<!-- ##### USER_FUNCTION McCallback ##### --> +<para> +Callback used for reporting errors from various #MissionControl calls. +</para> + +@mc: the #MissionControl object. +@error: if set, some error occurred. Must be freed. +@user_data: user data specified when making the call request. + + +<!-- ##### USER_FUNCTION McGetCurrentStatusCallback ##### --> +<para> +This callback is called in response to the #mission_control_get_current_status call, and carries in its parameters the information on #MissionControl status. +</para> + +@mc: the #MissionControl object. +@status: global connection status. +@presence: global presence. +@requested_presence: global requested presence. +@accounts: array of #McAccountStatus structs which hold information about the status (connection and presence) of all enabled accounts. The callback is responsible to free this array by calling #mission_control_free_account_statuses. +@n_accounts: number of accounts in the @accounts array. +@error: if this is not %NULL, then some error occurred and all the status information is invalid. In that case, the callback has to free @error. +@user_data: the user data specified when calling #mission_control_get_current_status. + + +<!-- ##### STRUCT missioncontrol ##### --> +<para> + +</para> + +@parent: +@first_run: + +<!-- ##### STRUCT missioncontrolclass ##### --> +<para> + +</para> + +@parent_class: + +<!-- ##### FUNCTION mission_control_new ##### --> +<para> + +</para> + +@connection: +@Returns: + + +<!-- ##### FUNCTION mission_control_set_presence ##### --> +<para> + +</para> + +@self: +@presence: +@message: +@callback: +@user_data: + + +<!-- ##### FUNCTION mission_control_get_presence ##### --> +<para> + +</para> + +@self: +@error: +@Returns: + + +<!-- ##### FUNCTION mission_control_get_presence_actual ##### --> +<para> + +</para> + +@self: +@error: +@Returns: + + +<!-- ##### FUNCTION mission_control_request_channel ##### --> +<para> + +</para> + +@self: +@account: +@type: +@handle: +@handle_type: +@callback: +@user_data: +@Returns: + + +<!-- ##### FUNCTION mission_control_request_channel_with_string_handle ##### --> +<para> + +</para> + +@self: +@account: +@type: +@handle: +@handle_type: +@callback: +@user_data: +@Returns: +<!-- # Unused Parameters # --> +@Handle_type: + + +<!-- ##### FUNCTION mission_control_cancel_channel_request ##### --> +<para> + +</para> + +@self: +@operation_id: +@error: +@Returns: + + +<!-- ##### FUNCTION mission_control_connect_all_with_default_presence ##### --> +<para> + +</para> + +@self: +@callback: +@user_data: + + +<!-- ##### FUNCTION mission_control_get_connection_status ##### --> +<para> + +</para> + +@self: +@account: +@error: +@Returns: + + +<!-- ##### FUNCTION mission_control_get_online_connections ##### --> +<para> + +</para> + +@self: +@error: +@Returns: + + +<!-- ##### FUNCTION mission_control_get_connection ##### --> +<para> + +</para> + +@self: +@account: +@error: +@Returns: + + +<!-- ##### FUNCTION mission_control_get_account_for_connection ##### --> +<para> + +</para> + +@self: +@connection: +@error: +@Returns: + + +<!-- ##### FUNCTION mission_control_get_used_channels_count ##### --> +<para> + +</para> + +@self: +@type: +@error: +@Returns: + + +<!-- ##### FUNCTION mission_control_get_current_status ##### --> +<para> + +</para> + +@self: +@callback: +@user_data: + + +<!-- ##### FUNCTION mission_control_free_account_statuses ##### --> +<para> + +</para> + +@accounts: + + +<!-- ##### FUNCTION mission_control_remote_avatar_changed ##### --> +<para> + +</para> + +@self: +@connection: +@contact_id: +@token: +@error: +@Returns: + + diff --git a/libmissioncontrol.pc.in b/libmissioncontrol.pc.in new file mode 100644 index 00000000..a407f681 --- /dev/null +++ b/libmissioncontrol.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +profiles_dir=@PROFILES_DIR@ + +Name: libmissioncontrol +Description: Mission Control Client Library +Requires: dbus-1 >= 0.50, libtelepathy >= 0.0.1 +Version: @VERSION@ +Libs: -L${libdir} -lmissioncontrol -lmissioncontrol-config @DBUS_LIBS@ +Cflags: -I${includedir} @DBUS_CFLAGS@ diff --git a/libmissioncontrol/Makefile.am b/libmissioncontrol/Makefile.am new file mode 100644 index 00000000..70d7c5d1 --- /dev/null +++ b/libmissioncontrol/Makefile.am @@ -0,0 +1,101 @@ +INCLUDES = $(DBUS_CFLAGS) $(TELEPATHY_CFLAGS) -I$(top_srcdir) \ + -DMC_DISABLE_DEPRECATED \ + -DLIBDIR="@libdir@" -DLIBVERSION="0" + +BUILT_SOURCES = \ + mission-control-signals-marshal.c \ + mission-control-signals-marshal.h \ + mc-enum-types.c \ + mc-enum-types.h \ + mc-client-lib-gen.h + +CLEANFILES = $(BUILT_SOURCES) + +lib_LTLIBRARIES = libmissioncontrol-config.la libmissioncontrol.la + +libmissioncontrol_config_la_CFLAGS = $(GLIB_CFLAGS) $(GCONF_CFLAGS) +libmissioncontrol_config_la_SOURCES = \ + mc.c\ + mc-manager.c \ + mc-manager-priv.h \ + mc-protocol.c \ + mc-protocol-priv.h \ + mc-profile.c \ + mc-account.c \ + mc-account-priv.h \ + mc-account-monitor.c \ + mc-account-monitor-priv.h \ + mc-enum-types.c + +libmissioncontrol_config_includedir = $(includedir)/libmissioncontrol +libmissioncontrol_config_include_DATA = \ + mc.h \ + mc-account.h \ + mc-account-monitor.h \ + mc-profile.h \ + mc-protocol.h \ + mc-manager.h + +libmissioncontrol_config_la_LIBADD = $(GCONF_LIBS) $(GLIB_LIBS) $(DBUS_LIBS) + +libmissioncontrol_config_la_LDFLAGS = $(common_ldflags) -export-symbols-regex "^mc_" + +libmissioncontrol_la_LIBADD = $(DBUS_LIBS) $(TELEPATHY_LIBS) \ + libmissioncontrol-config.la + +libmissioncontrol_la_SOURCES = \ + mission-control-signals-marshal.c \ + mission-control.c + +mission_control_include = mission-control.h + +mc-client-lib-gen.h: ../xml/mcd-dbus-services.xml + dbus-binding-tool --prefix=mc-client-lib --mode=glib-client $< > $@ + +%-marshal.h: %-marshal.list Makefile.am + glib-genmarshal --header --prefix=$(subst -,_,$*)_marshal $< > $*-marshal.h + +%-marshal.c: %-marshal.list Makefile.am + glib-genmarshal --body --prefix=$(subst -,_,$*)_marshal $< > $*-marshal.c + +mc-enum-types.h: stamp-mc-enum-types.h + @true +stamp-mc-enum-types.h: Makefile $(mission_control_include) mc-enum-types.c + ( cd $(srcdir) && glib-mkenums \ + --fhead "#ifndef __MC_ENUM_TYPES_H__\n#define __MC_ENUM_TYPES_H__\n\n#include \"mission-control.h\"\n\nG_BEGIN_DECLS\n" \ + --fprod "/* enumerations from \"@filename@\" */\n" \ + --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define MC_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ + --ftail "G_END_DECLS\n\n#endif /* __MC_ENUM_TYPES_H__ */" \ + $(mission_control_include) ) >> xgen-geth \ + && (cmp -s xgen-geth mc-enum-types.h || cp xgen-geth mc-enum-types.h ) \ + && rm -f xgen-geth \ + && echo timestamp > $(@F) + +mc-enum-types.c: Makefile $(mission_control_include) + ( cd $(srcdir) && glib-mkenums \ + --fhead "#include \"mc-enum-types.h\"\n#define g_intern_static_string(s) (s)\n" \ + --fprod "\n/* enumerations from \"@filename@\" */" \ + --ftail "\n#define __MC_ENUM_TYPES_C__\n" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (g_intern_static_string (\"@EnumName@\"), values);\n }\n return etype;\n}\n" \ + $(mission_control_include) ) > xgen-getc \ + && cp xgen-getc mc-enum-types.c \ + && rm -f xgen-getc + +bin_PROGRAMS = mc-account +mc_account_SOURCES = mc-account-cli.c +mc_account_LDADD = libmissioncontrol-config.la +noinst_PROGRAMS = test +test_SOURCES = test.c +test_LDADD = libmissioncontrol-config.la + +missioncontrolincludeinstdir=$(includedir)/libmissioncontrol +missioncontrolincludeinst_DATA = \ + mission-control-signals-marshal.h \ + mc-client-lib-gen.h \ + mission-control.h + +EXTRA_DIST = \ + $(missioncontrolincludeinst_DATA) \ + $(libmissioncontrol_config_include_DATA) diff --git a/libmissioncontrol/mc-account-cli.c b/libmissioncontrol/mc-account-cli.c new file mode 100644 index 00000000..64df0ee0 --- /dev/null +++ b/libmissioncontrol/mc-account-cli.c @@ -0,0 +1,359 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <libgen.h> + +#include <glib-object.h> +#include "mc-account.h" + +static gchar *app_name; + +static void +show_help (gchar * err) +{ + if (err) + printf ("Error: %s\n", err); + + printf ("Usage:\n" + " %1$s list\n" + " %1$s add <profile> <display name> string:account=<user_id> string:password=<password> [(int|bool|string):<key>=<value> ...]\n" + " %1$s set <account name> (int|bool|string):<key>=<value> [...]\n" + " %1$s display <account name> <display name>\n" + " %1$s show <account name>\n" + " %1$s enable <account name>\n" + " %1$s disable <account name>\n" + " %1$s delete <account name>\n", + app_name); + + if (err) + exit (-1); + else + exit (0); +} + +static void +on_params_foreach (const gchar * key, const GValue * value) +{ + switch (G_VALUE_TYPE (value)) + { + case G_TYPE_INT: + printf (" (int) %s = %d\n", key, g_value_get_int (value)); + break; + case G_TYPE_UINT: + printf (" (int) %s = %d\n", key, g_value_get_uint (value)); + break; + case G_TYPE_BOOLEAN: + printf (" (bool) %s = %s\n", key, + g_value_get_boolean (value) ? "true" : "false"); + break; + case G_TYPE_STRING: + printf (" (string) %s = %s\n", key, g_value_get_string (value)); + break; + default: + g_warning ("Unknown account setting type."); + } +} + +static gboolean +set_account_param (McAccount *account, gchar *param_value) +{ + gchar **strv_param_value = NULL; + gchar **strv_type_key = NULL; + const gchar *param, *type, *key, *value; + gboolean ret = FALSE; + + if (!param_value) + return FALSE; + + strv_param_value = g_strsplit (param_value, "=", -1); + if (strv_param_value[0] == NULL || + strv_param_value[1] == NULL || + strv_param_value[2] != NULL) + goto CLEANUP; + param = strv_param_value[0]; + value = strv_param_value[1]; + + strv_type_key = g_strsplit (param, ":", -1); + if (strv_type_key[0] == NULL || + strv_type_key[1] == NULL || + strv_type_key[2] != NULL) + goto CLEANUP; + type = strv_type_key[0]; + key = strv_type_key[1]; + + /* Set the key */ + if (strcmp (type, "int") == 0) + { + mc_account_set_param_int (account, key, atoi (value)); + ret = TRUE; + } + else if (strcmp (type, "bool") == 0) + { + mc_account_set_param_boolean (account, key, atoi (value)); + ret = TRUE; + } + else if (strcmp (type, "string") == 0) + { + mc_account_set_param_string (account, key, value); + ret = TRUE; + } +CLEANUP: + if (strv_param_value) + g_strfreev (strv_param_value); + if (strv_type_key) + g_strfreev (strv_type_key); + return ret; +} + +int +main (int argc, char **argv) +{ + app_name = basename (argv[0]); + + if (argc < 2) + show_help ("No command specified"); + + g_type_init (); + /* Command processing */ + + if (strcmp (argv[1], "add") == 0) + { + /* Add */ + McProfile *profile; + McAccount *account; + + if (argc < 6) + show_help ("Invalid add command."); + + profile = mc_profile_lookup (argv[2]); + if (profile == NULL) + { + printf ("Error: No such profile: %s\n", argv[2]); + } + else + { + account = mc_account_create (profile); + if (account == NULL) + { + printf ("Error: Error creating account.\n"); + } + else + { + const gchar *name; + gint i; + gboolean status = TRUE; + + mc_account_set_display_name (account, argv[3]); + for (i = 4; i < argc; i++) + { + status = set_account_param (account, argv[i]); + if (!status) + break; + } + name = mc_account_get_unique_name (account); + if (!status) + { + mc_account_delete (account); + printf ("Account not added successfully: %s\n", name); + show_help ("Invalid account paramenters"); + } + if (!mc_account_is_complete (account)) + { + mc_account_delete (account); + printf ("Account not added successfully: %s\n", name); + show_help ("Given account paramenters does not define a complete account"); + } + printf ("Account added successfully: %s\n", name); + g_object_unref (account); + } + g_object_unref (profile); + } + } + else if (strcmp (argv[1], "delete") == 0) + { + /* Delete account */ + McAccount *account; + + if (argc != 3) + show_help ("Invalid delete command."); + + account = mc_account_lookup (argv[2]); + if (account == NULL) + { + printf ("Error: No such account: %s\n", argv[2]); + } + else + { + if (mc_account_delete (account)) + { + printf ("Account %s deleted sucessfully.\n", argv[2]); + } + else + { + printf ("Error: Error deleting account: %s\n", argv[2]); + } + mc_account_free (account); + } + } + else if (strcmp (argv[1], "list") == 0) + { + /* List accounts */ + GList *accounts, *tmp; + + if (argc != 2) + show_help ("Invalid list command."); + + accounts = mc_accounts_list (); + for (tmp = accounts; tmp != NULL; tmp = tmp->next) + { + McAccount *account; + + account = (McAccount *) tmp->data; + printf ("%s (%s)\n", + mc_account_get_unique_name (account), + mc_account_get_display_name (account)); + + } + mc_accounts_list_free (accounts); + } + else if (strcmp (argv[1], "show") == 0) + { + /* Show account details */ + McAccount *account; + GHashTable *params; + + if (argc != 3) + show_help ("Invalid show command."); + + account = mc_account_lookup (argv[2]); + if (account == NULL) + { + printf ("Error: No such account: %s\n", argv[2]); + exit (1); + } + + params = mc_account_get_params (account); + if (params == NULL) + { + printf ("Error: Failed to retreive params: %s\n", argv[2]); + } + else + { + gboolean enabled; + + enabled = mc_account_is_enabled (account); + + printf (" Account: %s\n", argv[2]); + printf ("Display Name: %s\n", mc_account_get_display_name (account)); + printf (" Enabled: %s\n\n", enabled ? "enabled" : "disabled"); + g_hash_table_foreach (params, (GHFunc) on_params_foreach, NULL); + + g_hash_table_destroy (params); + } + mc_account_free (account); + } + else if (strcmp (argv[1], "enable") == 0) + { + /* Enable account */ + McAccount *account; + + if (argc != 3) + show_help ("Invalid enable command."); + + account = mc_account_lookup (argv[2]); + if (account == NULL) + { + printf ("Error: No such account: %s\n", argv[2]); + exit (-1); + } + mc_account_set_enabled (account, TRUE); + mc_account_free (account); + } + else if (strcmp (argv[1], "disable") == 0) + { + /* Disable account */ + McAccount *account; + + if (argc != 3) + show_help ("Invalid disable command."); + + account = mc_account_lookup (argv[2]); + if (account == NULL) + { + printf ("Error: No such account: %s\n", argv[2]); + exit (-1); + } + mc_account_set_enabled (account, FALSE); + mc_account_free (account); + } + else if (strcmp (argv[1], "display") == 0) + { + /* Set display name */ + McAccount *account; + + if (argc != 4) + show_help ("Invalid display command."); + + account = mc_account_lookup (argv[2]); + if (account == NULL) + { + printf ("Error: No such account: %s\n", argv[2]); + exit (-1); + } + mc_account_set_display_name (account, argv[3]); + mc_account_free (account); + } + else if (strcmp (argv[1], "set") == 0) + { + /* Set account parameter */ + McAccount *account; + + if (argc != 4) + show_help ("Invalid set command."); + + account = mc_account_lookup (argv[2]); + if (!account) + { + printf ("Error: No such account: %s\n", argv[2]); + exit (-1); + } + if (!set_account_param (account, argv[3])) + { + show_help ("Invalid set command."); + } + g_object_unref (account); + } + else if (strcmp (argv[1], "help") == 0 || + strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) + { + show_help (NULL); + } + else + { + show_help ("Unknown command."); + } + return 0; +} diff --git a/libmissioncontrol/mc-account-monitor-priv.h b/libmissioncontrol/mc-account-monitor-priv.h new file mode 100644 index 00000000..c55c2c3f --- /dev/null +++ b/libmissioncontrol/mc-account-monitor-priv.h @@ -0,0 +1,31 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MC_ACCOUNT_MONITOR_PRIV_H__ +#define __MC_ACCOUNT_MONITOR_PRIV_H__ + +McAccount * _mc_account_monitor_lookup (McAccountMonitor *monitor, const gchar *unique_name); +GList * _mc_account_monitor_list (McAccountMonitor *monitor); + +#endif /* __MC_ACCOUNT_MONITOR_PRIV_H__ */ + diff --git a/libmissioncontrol/mc-account-monitor.c b/libmissioncontrol/mc-account-monitor.c new file mode 100644 index 00000000..13ec2a8b --- /dev/null +++ b/libmissioncontrol/mc-account-monitor.c @@ -0,0 +1,440 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <string.h> +#include <strings.h> + +#include <gconf/gconf-client.h> + +#include "mc-account.h" +#include "mc-account-priv.h" +#include "mc-account-monitor.h" + +G_DEFINE_TYPE (McAccountMonitor, mc_account_monitor, G_TYPE_OBJECT); + +#define MC_ACCOUNT_MONITOR_PRIV(monitor) \ + ((McAccountMonitorPrivate *)monitor->priv) + +enum +{ + SIGNAL_CREATED, + SIGNAL_DELETED, + SIGNAL_ENABLED, + SIGNAL_DISABLED, + SIGNAL_CHANGED, + NUM_SIGNALS +}; + +static guint signals[NUM_SIGNALS]; + +typedef struct +{ + GConfClient *gconf_client; + guint gconf_connection; + GHashTable *accounts; + GHashTable *enabledness; +} McAccountMonitorPrivate; + +static void +mc_account_monitor_finalize (GObject *object) +{ + McAccountMonitor *self = MC_ACCOUNT_MONITOR (object); + McAccountMonitorPrivate *priv = MC_ACCOUNT_MONITOR_PRIV (self); + + gconf_client_notify_remove (priv->gconf_client, priv->gconf_connection); + gconf_client_remove_dir ( + priv->gconf_client, MC_ACCOUNTS_GCONF_BASE, NULL); + + g_hash_table_destroy (priv->accounts); + g_hash_table_destroy (priv->enabledness); +} + +static void +mc_account_monitor_class_init (McAccountMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (object_class, sizeof (McAccountMonitorPrivate)); + object_class->finalize = mc_account_monitor_finalize; + + /** + * McAccountMonitor::account-created: + * @self: The #McAccountMonitor. + * @name: The name of the account being created. + * + * Emitted when a new account is created. + */ + signals[SIGNAL_CREATED] = g_signal_new ( + "account-created", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + /** + * McAccountMonitor::account-deleted: + * @self: The #McAccountMonitor. + * @name: The name of the account being deleted. + * + * Emitted when an account is deleted. + */ + signals[SIGNAL_DELETED] = g_signal_new ( + "account-deleted", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + /** + * McAccountMonitor::account-enabled: + * @self: The #McAccountMonitor. + * @name: The name of the account being enabled. + * + * Emitted when an account is enabled. + */ + signals[SIGNAL_ENABLED] = g_signal_new ( + "account-enabled", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + /** + * McAccountMonitor::account-disabled: + * @self: The #McAccountMonitor. + * @name: The name of the account being disabled. + * + * Emitted when an account is disabled. + */ + signals[SIGNAL_DISABLED] = g_signal_new ( + "account-disabled", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + /** + * McAccountMonitor::account-changed: + * @self: The #McAccountMonitor. + * @name: The name of the account being changed. + * + * Emitted when an account is changed. + */ + signals[SIGNAL_CHANGED] = g_signal_new ( + "account-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); +} + +static gchar * +_account_name_from_key (const gchar *key) +{ + guint base_len = strlen (MC_ACCOUNTS_GCONF_BASE); + const gchar *base, *slash; + + g_assert (key == strstr (key, MC_ACCOUNTS_GCONF_BASE)); + g_assert (strlen (key) > base_len + 1); + + base = key + base_len + 1; + slash = index (base, '/'); + + if (slash == NULL) + return g_strdup (base); + else + return g_strndup (base, slash - base); +} + +static void +_gconf_notify_cb (GConfClient *client, guint conn_id, GConfEntry *entry, + gpointer user_data) +{ + McAccountMonitor *monitor = MC_ACCOUNT_MONITOR (user_data); + McAccountMonitorPrivate *priv = MC_ACCOUNT_MONITOR_PRIV (monitor); + gchar *name = NULL; + McAccount *account; + gboolean key_is_enabledness; + + key_is_enabledness = g_str_has_suffix (entry->key, + "/" MC_ACCOUNTS_GCONF_KEY_ENABLED); + name = _account_name_from_key (entry->key); + account = g_hash_table_lookup (priv->accounts, name); + + /* Was account complete before? */ + if (account == NULL) + { + account = _mc_account_new (name); + + /* Account was not complete before and it just become complete */ + if (mc_account_is_complete (account)) + { + g_hash_table_insert (priv->accounts, g_strdup (name), account); + g_signal_emit (monitor, signals[SIGNAL_CREATED], 0, name); + + /* check if the account is enabled and, in case, emit the respective + * signal */ + if (mc_account_is_enabled (account)) + { + g_hash_table_insert (priv->enabledness, g_strdup (name), + GINT_TO_POINTER (TRUE)); + g_signal_emit (monitor, signals[SIGNAL_ENABLED], 0, name); + } + } + else + { + g_object_unref (account); + /* We don't do anything for incomplete accounts */ + } + } + else if (g_str_has_suffix (entry->key, "/" MC_ACCOUNTS_GCONF_KEY_DELETED)) + { + /* if account is deleted, remove it */ + if (!mc_account_is_complete (account)) + { + if (GPOINTER_TO_INT (g_hash_table_lookup (priv->enabledness, name))) + { + g_signal_emit (monitor, signals[SIGNAL_DISABLED], 0, name); + } + g_hash_table_remove (priv->accounts, name); + g_hash_table_remove (priv->enabledness, name); + g_signal_emit (monitor, signals[SIGNAL_DELETED], 0, name); + } + } + else if (g_str_has_suffix (entry->key, "/" MC_ACCOUNTS_GCONF_KEY_ENABLED)) + { + if (entry->value != NULL && entry->value->type == GCONF_VALUE_BOOL) + { + if (gconf_value_get_bool (entry->value)) + { + g_hash_table_insert (priv->enabledness, g_strdup (name), + GINT_TO_POINTER (TRUE)); + g_signal_emit (monitor, signals[SIGNAL_ENABLED], 0, name); + } + else + { + g_hash_table_remove (priv->enabledness, name); + g_signal_emit (monitor, signals[SIGNAL_DISABLED], 0, name); + } + } + } + else if (!g_str_has_suffix (entry->key, "/" MC_ACCOUNTS_GCONF_KEY_AVATAR_TOKEN)) + { + /* Emit the rest as value changed signal */ + g_signal_emit (monitor, signals[SIGNAL_CHANGED], 0, name); + } + + /* If we are enabling/disabling account */ + g_free (name); +} + +static void +mc_account_monitor_init (McAccountMonitor *self) +{ + GError *error = NULL; + GConfClient *client = gconf_client_get_default (); + GSList *i, *dirs; + McAccountMonitorPrivate *priv; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, + MC_TYPE_ACCOUNT_MONITOR, McAccountMonitorPrivate); + priv = MC_ACCOUNT_MONITOR_PRIV (self); + priv->accounts = NULL; + priv->gconf_client = client; + dirs = gconf_client_all_dirs (client, MC_ACCOUNTS_GCONF_BASE, &error); + + if (NULL != error) + { + g_print ("Error: %s\n", error->message); + g_assert_not_reached (); + } + + priv->accounts = g_hash_table_new_full ( + g_str_hash, g_str_equal, (GDestroyNotify) g_free, g_object_unref); + priv->enabledness = g_hash_table_new_full ( + g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); + + for (i = dirs; NULL != i; i = i->next) + { + gchar *name = _account_name_from_key (i->data); + McAccount *account = _mc_account_new (name); + + /* Only pick up accounts which are not yet deleted. */ + if (mc_account_is_complete (account)) + { + g_hash_table_insert (priv->accounts, g_strdup (name), account); + + if (mc_account_is_enabled (account)) + g_hash_table_insert (priv->enabledness, g_strdup (name), + GINT_TO_POINTER (TRUE)); + } + else + { + g_object_unref (account); + } + g_free (i->data); + g_free (name); + } + + g_slist_free (dirs); + + gconf_client_add_dir ( + client, MC_ACCOUNTS_GCONF_BASE, GCONF_CLIENT_PRELOAD_NONE, &error); + + if (NULL != error) + { + g_print ("Error: %s\n", error->message); + g_assert_not_reached (); + } + + priv->gconf_connection = gconf_client_notify_add ( + client, MC_ACCOUNTS_GCONF_BASE, _gconf_notify_cb, self, NULL, &error); + + if (NULL != error) + { + g_print ("Error: %s\n", error->message); + g_assert_not_reached (); + } +} + +McAccount * +_mc_account_monitor_lookup (McAccountMonitor *monitor, const gchar *unique_name) +{ + McAccountMonitorPrivate *priv = MC_ACCOUNT_MONITOR_PRIV (monitor); + McAccount *ret; + + g_return_val_if_fail (unique_name != NULL, NULL); + g_return_val_if_fail (*unique_name != '\0', NULL); + + ret = g_hash_table_lookup (priv->accounts, unique_name); + + if (NULL != ret) + g_object_ref (ret); + + return ret; +} + +/** + * mc_account_monitor_new: + * + * Get a #McAccountMonitor object. The object is a singleton: it is created + * only if another instance of itself is not alive, otherwise the same instance + * is returned. + * + * Returns: the #McAccountMonitor object. + */ +McAccountMonitor * +mc_account_monitor_new (void) +{ + static McAccountMonitor *monitor = NULL; + + if (NULL == monitor) + { + monitor = g_object_new (MC_TYPE_ACCOUNT_MONITOR, NULL); + g_object_add_weak_pointer (G_OBJECT (monitor), (gpointer) &monitor); + } + + g_object_ref (monitor); + return monitor; +} + +static void +_list_append (gpointer key, gpointer value, gpointer user_data) +{ + GList **list = (GList **) user_data; + + *list = g_list_prepend (*list, value); +} + +GList * +_mc_account_monitor_list (McAccountMonitor *monitor) +{ + McAccountMonitorPrivate *priv = MC_ACCOUNT_MONITOR_PRIV (monitor); + GList *accounts = NULL; + + g_hash_table_foreach (priv->accounts, _list_append, &accounts); + return accounts; +} + +static void +merge_presences (gpointer key, McAccount *account, GArray *presences) +{ + const McPresence *account_presences; + gint i; + + if (!mc_account_is_enabled (account)) + return; + + account_presences = mc_account_get_supported_presences (account); + if (account_presences) + { + while (*account_presences) + { + /* check if this presence is already in the array */ + for (i = 0; i < presences->len; i++) + if (g_array_index (presences, McPresence, i) == *account_presences) + break; + if (i >= presences->len) /* no, it's not: let's add it */ + g_array_append_vals (presences, account_presences, 1); + account_presences++; + } + } +} + +/** + * mc_account_monitor_get_supported_presences: + * @monitor: The #McAccountMonitor. + * + * Get a list of all the presences supported in any account: a presence is + * considered as supported if there is at least one enabled account which + * supports it. Support fot the basic presences @MC_PRESENCE_AVAILABLE and + * @MC_PRESENCE_OFFLINE is taken for granted and therefore these presences are + * not returned. + * + * Returns: a NULL-terminated array of presences, which must be freed with + * g_free(). + */ +McPresence * +mc_account_monitor_get_supported_presences (McAccountMonitor *monitor) +{ + McAccountMonitorPrivate *priv = MC_ACCOUNT_MONITOR_PRIV (monitor); + GArray *presences; + McPresence *data; + + presences = g_array_new (TRUE, FALSE, sizeof (McPresence)); + g_hash_table_foreach (priv->accounts, (GHFunc)merge_presences, presences); + data = (McPresence *)presences->data; + g_array_free (presences, FALSE); + return data; +} + diff --git a/libmissioncontrol/mc-account-monitor.h b/libmissioncontrol/mc-account-monitor.h new file mode 100644 index 00000000..742117aa --- /dev/null +++ b/libmissioncontrol/mc-account-monitor.h @@ -0,0 +1,72 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MC_ACCOUNT_MONITOR_H__ +#define __MC_ACCOUNT_MONITOR_H__ + +#include <glib-object.h> +#include <libmissioncontrol/mission-control.h> + +G_BEGIN_DECLS + +#define MC_TYPE_ACCOUNT_MONITOR mc_account_monitor_get_type() + +#define MC_ACCOUNT_MONITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MC_TYPE_ACCOUNT_MONITOR, McAccountMonitor)) + +#define MC_ACCOUNT_MONITOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MC_TYPE_ACCOUNT_MONITOR, McAccountMonitorClass)) + +#define MC_IS_ACCOUNT_MONITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MC_TYPE_ACCOUNT_MONITOR)) + +#define MC_IS_ACCOUNT_MONITOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MC_TYPE_ACCOUNT_MONITOR)) + +#define MC_ACCOUNT_MONITOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MC_TYPE_ACCOUNT_MONITOR, McAccountMonitorClass)) + +typedef struct { + GObject parent; + gpointer priv; +} McAccountMonitor; + +typedef struct { + GObjectClass parent_class; +} McAccountMonitorClass; + +GType mc_account_monitor_get_type (void); + +McAccountMonitor* mc_account_monitor_new (void); + +McPresence * mc_account_monitor_get_supported_presences (McAccountMonitor * + monitor); + +G_END_DECLS + +#endif /* __MC_ACCOUNT_MONITOR_H__ */ diff --git a/libmissioncontrol/mc-account-priv.h b/libmissioncontrol/mc-account-priv.h new file mode 100644 index 00000000..c287c153 --- /dev/null +++ b/libmissioncontrol/mc-account-priv.h @@ -0,0 +1,42 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MC_ACCOUNT_PRIV_H__ +#define __MC_ACCOUNT_PRIV_H__ + +#define MC_ACCOUNTS_GCONF_BASE "/apps/telepathy/mc/accounts" +#define MC_ACCOUNTS_GCONF_KEY_DISPLAY_NAME "display_name" +#define MC_ACCOUNTS_GCONF_KEY_NORMALIZED_NAME "normalized_name" +#define MC_ACCOUNTS_GCONF_KEY_ENABLED "enabled" +#define MC_ACCOUNTS_GCONF_KEY_DELETED "deleted" +#define MC_ACCOUNTS_GCONF_KEY_PROFILE "profile" +#define MC_ACCOUNTS_GCONF_KEY_PARAM_ACCOUNT "account" +#define MC_ACCOUNTS_GCONF_KEY_AVATAR_TOKEN "avatar_token" +#define MC_ACCOUNTS_GCONF_KEY_AVATAR_MIME "avatar_mime" +#define MC_ACCOUNTS_GCONF_KEY_AVATAR_ID "avatar_id" +#define MC_ACCOUNTS_GCONF_KEY_DATA_DIR "data_dir" + +McAccount * _mc_account_new (const gchar *unique_name); + +#endif /* __MC_ACCOUNT_PRIV_H__ */ + diff --git a/libmissioncontrol/mc-account.c b/libmissioncontrol/mc-account.c new file mode 100644 index 00000000..16212d75 --- /dev/null +++ b/libmissioncontrol/mc-account.c @@ -0,0 +1,2136 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#define DBUS_API_SUBJECT_TO_CHANGE + +#include <dbus/dbus.h> +#include <errno.h> +#include <gconf/gconf-client.h> +#include <glib.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <glib/gstdio.h> + +#include "mc-account.h" +#include "mc-account-priv.h" +#include "mc-account-monitor.h" +#include "mc-account-monitor-priv.h" +#include "mc-profile.h" +#include <config.h> + +#define MC_ACCOUNTS_MAX 1024 +#define MC_AVATAR_FILENAME "avatar.bin" + +#define MC_ACCOUNT_PRIV(account) ((McAccountPrivate *)account->priv) + +G_DEFINE_TYPE (McAccount, mc_account, G_TYPE_OBJECT); + +typedef struct +{ + gchar *unique_name; + GSList *display_names; + GSList *normalized_names; +} McAccountPrivate; + +static gboolean mc_account_set_deleted (McAccount *account, gboolean deleted); +static gboolean mc_account_is_deleted (McAccount *account); + +static void +mc_account_finalize (GObject *object) +{ + McAccount *self = MC_ACCOUNT(object); + McAccountPrivate *priv = MC_ACCOUNT_PRIV (self); + + g_free (priv->unique_name); + g_slist_foreach (priv->display_names, (GFunc)g_free, NULL); + g_slist_free (priv->display_names); + g_slist_foreach (priv->normalized_names, (GFunc)g_free, NULL); + g_slist_free (priv->normalized_names); +} + +static void +mc_account_init (McAccount *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, + MC_TYPE_ACCOUNT, McAccountPrivate); +} + +void +mc_account_clear_cache (void) +{ +} + +McAccount * +_mc_account_new (const gchar *unique_name) +{ + McAccount *new; + new = (McAccount *)g_object_new (MC_TYPE_ACCOUNT, NULL); + MC_ACCOUNT_PRIV (new)->unique_name = g_strdup (unique_name); + + return new; +} + +static void +mc_account_class_init (McAccountClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (object_class, sizeof (McAccountPrivate)); + object_class->finalize = mc_account_finalize; +} + +/* Returns the data dir for the given account name. + * Returned string must be freed by caller. */ +static gchar * +get_account_data_path (const gchar *unique_name) +{ + const gchar *base; + + base = g_getenv ("MC_ACCOUNT_DIR"); + if (!base) + base = ACCOUNTS_DIR; + if (!base) + return NULL; + + if (base[0] == '~') + return g_build_filename (g_get_home_dir(), base + 1, unique_name, NULL); + else + return g_build_filename (base, unique_name, NULL); +} + +/* if key is NULL, returns the gconf dir for the given account name, + * otherwise returns the full key. prepends key with param- if + * param is TRUE. returned string must be freed by caller. */ +static gchar * +_mc_account_path (const gchar *unique_name, + const gchar *key, + gboolean param) +{ + if (key != NULL) + { + if (param) + { + return g_strconcat (MC_ACCOUNTS_GCONF_BASE, "/", + unique_name, "/param-", + key, NULL); + } + else + { + return g_strconcat (MC_ACCOUNTS_GCONF_BASE, "/", + unique_name, "/", + key, NULL); + } + } + else + { + return g_strconcat (MC_ACCOUNTS_GCONF_BASE, "/", + unique_name, NULL); + } +} + +static GConfValue * +_mc_account_gconf_get (McAccount *account, + const gchar *name, + gboolean param) +{ + GConfClient *client; + gchar *key; + GConfValue *value; + + g_return_val_if_fail (account != NULL, NULL); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + NULL); + g_return_val_if_fail (name != NULL, NULL); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, NULL); + + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + name, param); + value = gconf_client_get (client, key, NULL); + + g_object_unref (client); + g_free (key); + + return value; +} + +static gboolean +_mc_account_gconf_get_boolean (McAccount *account, + const gchar *name, + gboolean param, + gboolean *value) +{ + GConfValue *val; + + g_return_val_if_fail (value != NULL, FALSE); + + val = _mc_account_gconf_get (account, name, param); + + if (val == NULL) + { + return FALSE; + } + + if (val->type != GCONF_VALUE_BOOL) + { + gconf_value_free (val); + return FALSE; + } + + *value = gconf_value_get_bool (val); + gconf_value_free (val); + + return TRUE; +} + +static gboolean +_mc_account_gconf_get_int (McAccount *account, + const gchar *name, + gboolean param, + gint *value) +{ + GConfValue *val; + + g_return_val_if_fail (value != NULL, FALSE); + + val = _mc_account_gconf_get (account, name, param); + + if (val == NULL) + { + return FALSE; + } + + if (val->type != GCONF_VALUE_INT) + { + gconf_value_free (val); + return FALSE; + } + + *value = gconf_value_get_int (val); + gconf_value_free (val); + + return TRUE; +} + +static gboolean +_mc_account_gconf_get_string (McAccount *account, + const gchar *name, + gboolean param, + gchar **value) +{ + GConfValue *val; + + g_return_val_if_fail (value != NULL, FALSE); + + val = _mc_account_gconf_get (account, name, param); + + if (val == NULL) + { + return FALSE; + } + + if (val->type != GCONF_VALUE_STRING) + { + gconf_value_free (val); + return FALSE; + } + + *value = g_strdup (gconf_value_get_string (val)); + gconf_value_free (val); + + return TRUE; +} + +static gboolean +_mc_account_gconf_set_string (McAccount *account, const gchar *name, + const gchar *value) +{ + GConfClient *client; + gchar *key; + gboolean ok; + + g_return_val_if_fail (account != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + FALSE); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + name, FALSE); + ok = gconf_client_set_string (client, key, value, NULL); + + g_free (key); + g_object_unref (client); + + return ok; +} + +/** + * mc_account_lookup: + * @unique_name: The unique name of the account. + * + * Look-up an account from its unique name. The reference count of the returned + * account is not incremented. + * + * Return value: The requested #McAccount, or NULL if not found. + */ +McAccount * +mc_account_lookup (const gchar *unique_name) +{ + McAccountMonitor *monitor = mc_account_monitor_new (); + McAccount *ret = _mc_account_monitor_lookup (monitor, unique_name); + + g_object_unref (monitor); + return ret; +} + +gboolean +_filter_account (McAccount *acct, gpointer data) +{ + const gchar *compare_account; + gchar *gconf_account; + gboolean ret; + + g_return_val_if_fail (acct != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (acct)->unique_name != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + compare_account = (const gchar *) data; + + if (!_mc_account_gconf_get_string (acct, + MC_ACCOUNTS_GCONF_KEY_PARAM_ACCOUNT, + TRUE, &gconf_account)) + return FALSE; + + ret = (0 == strcmp(gconf_account, compare_account)); + + g_free (gconf_account); + + return ret; +} + +McAccount * +_free_all_but_one (GList *list) +{ + McAccount *ret = NULL; + + if (list != NULL) + { + GList *tmp; + + tmp = g_list_remove_link (list, list); + mc_accounts_list_free (tmp); + + ret = (McAccount *) list->data; + g_list_free (list); + } + + return ret; +} + +/** + * mc_account_lookup_with_profile: + * @profile: A #McProfile. + * @account: The name of the account. + * + * Look-up an account from its name in the given #McProfile. The reference + * count of the returned account is not incremented. + * + * Return value: The requested #McAccount, or NULL if not found. + */ +McAccount * +mc_account_lookup_with_profile (McProfile *profile, + const gchar *account) +{ + GList *accounts; + + g_return_val_if_fail (profile != NULL, NULL); + g_return_val_if_fail (account != NULL, NULL); + + accounts = mc_accounts_list_by_profile (profile); + accounts = mc_accounts_filter (accounts, _filter_account, (gpointer) account); + + return _free_all_but_one (accounts); +} + +/** + * mc_account_lookup_with_vcard_field: + * @vcard_field: The VCard field. + * @account: The name of the account. + * + * Look-up an account from its name in the given VCard field. The reference + * count of the returned account is not incremented. + * + * Return value: The requested #McAccount, or NULL if not found. + */ +McAccount * +mc_account_lookup_with_vcard_field (const gchar *vcard_field, + const gchar *account) +{ + GList *accounts; + + g_return_val_if_fail (vcard_field != NULL, NULL); + g_return_val_if_fail (account != NULL, NULL); + + accounts = mc_accounts_list_by_vcard_field (vcard_field); + accounts = mc_accounts_filter (accounts, _filter_account, (gpointer) account); + + return _free_all_but_one (accounts); +} + +/** + * mc_account_free: + * @account: The #McAccount. + * + * Free an account. + */ +void +mc_account_free (McAccount* account) +{ + g_object_unref (account); +} + +/** + * mc_account_create: + * @profile: A #McProfile. + * + * Create a new account of the given #McProfile. + * + * Return value: the newly created #McAccount. + */ +McAccount * +mc_account_create (McProfile *profile) +{ + McAccount *ret = NULL; + GConfClient *client; + const gchar *profile_name; + gchar *unique_name, *key, *data_dir = NULL; + guint i = 0; + gboolean ok; + + g_return_val_if_fail (profile != NULL, NULL); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, NULL); + + profile_name = mc_profile_get_unique_name (profile); + + /* find the first free account with this profile */ + unique_name = NULL; + while (unique_name == NULL && i < MC_ACCOUNTS_MAX) + { + gchar *path; + + unique_name = g_strdup_printf ("%s%u", profile_name, i); + + path = _mc_account_path (unique_name, NULL, FALSE); + if (gconf_client_dir_exists (client, path, NULL)) + { + g_free (unique_name); + unique_name = NULL; + i++; + } + g_free (path); + } + + if (unique_name == NULL) + goto OUT; + + key = _mc_account_path (unique_name, MC_ACCOUNTS_GCONF_KEY_PROFILE, FALSE); + ok = gconf_client_set_string (client, key, profile_name, NULL); + g_free (key); + + if (!ok) + goto OUT; + + /* create the directory for storing the binary objects (i.e., avatar) */ + data_dir = get_account_data_path (unique_name); + if (g_mkdir_with_parents (data_dir, 0777) != 0) + goto OUT; + /* and store it in GConf */ + key = _mc_account_path (unique_name, MC_ACCOUNTS_GCONF_KEY_DATA_DIR, FALSE); + ok = gconf_client_set_string (client, key, data_dir, NULL); + g_free (key); + + /* Account is disabled by default, because there is no guarantee + * the account is usable at this point. The one who created the + * account should enable it when its ready. + */ + ret = _mc_account_new (unique_name); + +OUT: + g_free (data_dir); + g_free (unique_name); + g_object_unref (client); + + return ret; +} + +static gchar * +_account_name_from_key (const gchar *key) +{ + guint base_len = strlen (MC_ACCOUNTS_GCONF_BASE); + const gchar *base, *slash; + + g_assert (key == strstr (key, MC_ACCOUNTS_GCONF_BASE)); + g_assert (strlen (key) > base_len + 1); + + base = key + base_len + 1; + slash = strchr (base, '/'); + + if (slash == NULL) + return g_strdup (base); + else + return g_strndup (base, slash - base); +} + +static gboolean +mc_account_expunge_deleted (gpointer user_data) +{ + GConfClient *client; + gchar *key; + GSList *entries, *tmp; + gboolean ok = TRUE; + GError *error = NULL; + GSList *i, *dirs; + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + dirs = gconf_client_all_dirs (client, MC_ACCOUNTS_GCONF_BASE, &error); + + if (NULL != error) + { + g_print ("Error: %s\n", error->message); + g_assert_not_reached (); + } + for (i = dirs; NULL != i; i = i->next) + { + gchar *unique_name = _account_name_from_key (i->data); + gchar *data_dir_str; + GDir *data_dir; + + McAccount *account = _mc_account_new (unique_name); + + if (!mc_account_is_deleted (account)) + { + g_object_unref (account); + g_free (i->data); + g_free (unique_name); + continue; + } + + key = _mc_account_path (unique_name, NULL, FALSE); + entries = gconf_client_all_entries (client, key, NULL); + g_free (key); + + for (tmp = entries; tmp != NULL; tmp = tmp->next) + { + GConfEntry *entry; + entry = (GConfEntry*) tmp->data; + + if (!gconf_client_unset (client, entry->key, NULL)) + ok = FALSE; + + gconf_entry_free (entry); + } + + data_dir_str = get_account_data_path (unique_name); + data_dir = g_dir_open (data_dir_str, 0, NULL); + if (data_dir) + { + const gchar *filename; + while ((filename = g_dir_read_name (data_dir)) != NULL) + { + gchar *path; + path = g_build_filename (data_dir_str, filename, NULL); + g_remove (path); + g_free (path); + } + g_dir_close (data_dir); + g_rmdir (data_dir_str); + } + g_free (data_dir_str); + + g_free (i->data); + g_free (unique_name); + g_slist_free (entries); + } + g_object_unref (client); + return FALSE; +} + +/** + * mc_account_delete: + * @account: The #McAccount. + * + * Delete the given account from the accounts configuration. The object itself + * remains valid and must be free separately. + * + * Return value: %TRUE if the account was deleted, %FALSE otherwise. + */ +gboolean +mc_account_delete (McAccount *account) +{ + gboolean ok; + + g_return_val_if_fail (account != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV(account)->unique_name != NULL, + FALSE); + mc_account_set_enabled (account, FALSE); + + ok = mc_account_set_deleted (account, TRUE); + + /* Expunge deleted accounts after 5 secs. We can't expunge the accounts + * immediately because there might be other people using the accounts + * and gconf signaling is really async. + */ + g_timeout_add (2000, (GSourceFunc)mc_account_expunge_deleted, NULL); + return ok; +} + +/** + * mc_accounts_list: + * + * Lists all configured accounts. + * + * Return value: a #GList of all accounts. Must be freed with + * #mc_accounts_list_free. + */ +GList * +mc_accounts_list (void) +{ + McAccountMonitor *monitor = mc_account_monitor_new (); + GList *ret, *i; + + ret = _mc_account_monitor_list (monitor); + + for (i = ret; NULL != i; i = i->next) + g_object_ref (G_OBJECT (i->data)); + + g_object_unref (monitor); + return ret; +} + +static gboolean +_filter_enabled (McAccount *acct, gpointer data) +{ + gboolean enabled; + + g_return_val_if_fail (acct != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (acct)->unique_name != NULL, FALSE); + + enabled = GPOINTER_TO_INT (data); + + return (mc_account_is_enabled (acct) == enabled); +} + +/** + * mc_accounts_list_by_enabled: + * @enabled: a boolean to select if enabled accounts should be returned. + * + * Lists all enabled/disabled accounts. + * + * Return value: a #GList of all the enabled accounts. Must be freed with + * #mc_accounts_list_free. + */ +GList * +mc_accounts_list_by_enabled (gboolean enabled) +{ + GList *ret; + + ret = mc_accounts_list (); + ret = mc_accounts_filter (ret, _filter_enabled, GINT_TO_POINTER (enabled)); + + return ret; +} + +static gboolean +_filter_profile (McAccount *acct, gpointer data) +{ + McProfile *profile; + gchar *profile_name; + gboolean ret; + + g_return_val_if_fail (acct != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (acct)->unique_name != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + profile = (McProfile *) data; + + if (!_mc_account_gconf_get_string (acct, MC_ACCOUNTS_GCONF_KEY_PROFILE, + FALSE, &profile_name)) + return FALSE; + + ret = (0 == strcmp (profile_name, mc_profile_get_unique_name (profile))); + g_free (profile_name); + + return ret; +} + +/** + * mc_accounts_list_by_profile: + * @profile: a #McProfile. + * + * Lists all accounts of a #McProfile. + * + * Return value: a #GList of the accounts. Must be freed with + * #mc_accounts_list_free. + */ +GList * +mc_accounts_list_by_profile (McProfile *profile) +{ + GList *ret; + + g_return_val_if_fail (profile != NULL, NULL); + + ret = mc_accounts_list (); + ret = mc_accounts_filter (ret, _filter_profile, profile); + + return ret; +} + +static gboolean +_filter_vcard_field (McAccount *acct, gpointer data) +{ + McProfile *profile; + const gchar *vcard_field; + const gchar *profile_vcard_field; + gboolean ret; + + g_return_val_if_fail (acct != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (acct)->unique_name != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + vcard_field = (const gchar *) data; + profile = mc_account_get_profile (acct); + + if (profile == NULL) + return FALSE; + + profile_vcard_field = mc_profile_get_vcard_field (profile); + + if (profile_vcard_field == NULL) + ret = FALSE; + else + ret = (0 == strcmp (vcard_field, profile_vcard_field)); + + mc_profile_free (profile); + return ret; +} + +/** + * mc_accounts_list_by_vcard_field: + * @vcard_field: the VCard field. + * + * Lists all accounts of a VCard field. + * + * Return value: a #GList of the accounts. Must be freed with + * #mc_accounts_list_free. + */ +GList * +mc_accounts_list_by_vcard_field (const gchar *vcard_field) +{ + GList *ret; + + ret = mc_accounts_list (); + ret = mc_accounts_filter (ret, _filter_vcard_field, (gpointer) vcard_field); + + return ret; +} + +/** + * mc_accounts_list_free: + * @list: A #GList of #McAccount. + * + * Frees the lists of accounts returned by the mc_accounts_list* family of + * functions. + */ +void +mc_accounts_list_free (GList *list) +{ + GList *i; + + for (i = list; NULL != i; i = i->next) + g_object_unref (G_OBJECT (i->data)); + + g_list_free (list); +} + +/** + * mc_accounts_filter: + * @accounts: a #GList of #McAccount. + * @filter: a #McAccountFilter function. + * @data: user data to be passed to the filter function. + * + * Filter a list of accounts according to whether a function returns TRUE, + * freeing the list and any accounts which are filtered out. + * + * Return value: a #GList of the accounts. Must be freed with + * #mc_accounts_list_free. + */ +GList * +mc_accounts_filter (GList *accounts, + McAccountFilter filter, + gpointer data) +{ + GList *tmp, *ret = NULL; + + for (tmp = accounts; tmp != NULL; tmp = tmp->next) + { + McAccount *account = (McAccount *) tmp->data; + + if (filter (account, data)) + { + ret = g_list_prepend (ret, account); + } + else + { + mc_account_free (account); + } + } + + g_list_free (accounts); + + return ret; +} + +/** + * mc_account_get_normalized_name: + * @account: The #McAccount. + * + * Gets the normalized name for the account. + * + * Return value: the normalized name (must free with #g_free), or NULL. + */ +const gchar * +mc_account_get_normalized_name (McAccount *account) +{ + McAccountPrivate *priv; + GSList *list; + gchar *name; + + g_return_val_if_fail (account != NULL, NULL); + priv = MC_ACCOUNT_PRIV (account); + + if (!_mc_account_gconf_get_string (account, + MC_ACCOUNTS_GCONF_KEY_NORMALIZED_NAME, + FALSE, &name)) + return NULL; + + if ((list = g_slist_find_custom(priv->normalized_names, name, + (GCompareFunc)strcmp)) != NULL) + { + g_free (name); + name = list->data; + } + else + { + priv->normalized_names = g_slist_prepend (priv->normalized_names, + name); + } + return name; +} + +/** + * mc_account_set_normalized_name: + * @account: The #McAccount. + * @name: The name to set. + * + * Sets the normalized name of the account. + * + * Return value: %TRUE, or %FALSE if some error occurs. + */ +gboolean +mc_account_set_normalized_name (McAccount *account, const gchar *name) +{ + return _mc_account_gconf_set_string (account, + MC_ACCOUNTS_GCONF_KEY_NORMALIZED_NAME, + name); +} + +/** + * mc_account_get_unique_name: + * @account: The #McAccount. + * + * Gets the unique name for the account. + * + * Return value: the unique name, or NULL. + */ +const gchar * +mc_account_get_unique_name (McAccount *account) +{ + g_return_val_if_fail (account != NULL, NULL); + + return MC_ACCOUNT_PRIV (account)->unique_name; +} + +/** + * mc_account_get_profile: + * @account: The #McAccount. + * + * Get the #McProfile this #McAccount belongs to. + * + * Return value: the #McProfile, or NULL. + */ +McProfile * +mc_account_get_profile (McAccount *account) +{ + McProfile *ret; + gchar *profile_name; + + g_return_val_if_fail (account != NULL, NULL); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + NULL); + + if (!_mc_account_gconf_get_string (account, MC_ACCOUNTS_GCONF_KEY_PROFILE, + FALSE, &profile_name)) + return NULL; + + ret = mc_profile_lookup (profile_name); + + g_free (profile_name); + + return ret; +} + +/** + * mc_account_get_display_name: + * @account: The #McAccount. + * + * Gets the display name for the account. + * + * Return value: the display name, or NULL. + */ +const gchar * +mc_account_get_display_name (McAccount *account) +{ + McAccountPrivate *priv; + GSList *list; + gchar *name; + + g_return_val_if_fail (account != NULL, NULL); + priv = MC_ACCOUNT_PRIV (account); + + if (!_mc_account_gconf_get_string (account, MC_ACCOUNTS_GCONF_KEY_DISPLAY_NAME, + FALSE, &name)) + return NULL; + + if ((list = g_slist_find_custom(priv->display_names, name, + (GCompareFunc)strcmp)) != NULL) + { + g_free (name); + name = list->data; + } + else + { + priv->display_names = g_slist_prepend (priv->display_names, name); + } + return name; +} + +/** + * mc_account_set_display_name: + * @account: The #McAccount. + * @name: The name to set. + * + * Sets the display name of the account. + * + * Return value: %TRUE, or %FALSE if some error occurs. + */ +gboolean +mc_account_set_display_name (McAccount *account, const gchar *name) +{ + return _mc_account_gconf_set_string (account, + MC_ACCOUNTS_GCONF_KEY_DISPLAY_NAME, + name); +} + +/** + * mc_account_is_enabled: + * @account: The #McAccount. + * + * Checks if the account is enabled. + * + * Return value: %TRUE if enabled, %FALSE otherwise. + */ +gboolean +mc_account_is_enabled (McAccount *account) +{ + gboolean enabled; + + g_return_val_if_fail (account != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, FALSE); + + if (!_mc_account_gconf_get_boolean (account, MC_ACCOUNTS_GCONF_KEY_ENABLED, + FALSE, &enabled)) + return FALSE; + + return enabled; +} + +static gboolean +mc_account_is_deleted (McAccount *account) +{ + gboolean deleted; + + g_return_val_if_fail (account != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, FALSE); + + if (!_mc_account_gconf_get_boolean (account, MC_ACCOUNTS_GCONF_KEY_DELETED, + FALSE, &deleted)) + return FALSE; + + return deleted; +} + +static gboolean +mc_account_set_deleted (McAccount *account, gboolean deleted) +{ + GConfClient *client; + gchar *key; + gboolean ok; + + g_return_val_if_fail (account != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, FALSE); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + MC_ACCOUNTS_GCONF_KEY_DELETED, FALSE); + ok = gconf_client_set_bool (client, key, deleted, NULL); + + g_free (key); + g_object_unref (client); + + return ok; +} + +/** + * mc_account_set_enabled: + * @account: The #McAccount. + * @enabled: whether the account must be enabled. + * + * Enables or disables an account. + * + * Return value: %TRUE, or %FALSE if some error occurred. + */ +gboolean +mc_account_set_enabled (McAccount *account, const gboolean enabled) +{ + GConfClient *client; + gchar *key; + gboolean ok; + + g_return_val_if_fail (account != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, FALSE); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + gconf_client_suggest_sync (client, NULL); + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + MC_ACCOUNTS_GCONF_KEY_ENABLED, FALSE); + ok = gconf_client_set_bool (client, key, enabled, NULL); + + g_free (key); + g_object_unref (client); + + return ok; +} + +/** + * mc_account_get_param_boolean: + * @account: The #McAccount. + * @name: the parameter to retrieve. + * @value: a pointer to the boolean variable. + * + * Gets a boolean parameter from the account settings. + * + * Return value: a #McAccountSettingState. + */ +McAccountSettingState +mc_account_get_param_boolean (McAccount *account, + const gchar *name, + gboolean *value) +{ + McAccountSettingState ret; + + g_return_val_if_fail (account != NULL, MC_ACCOUNT_SETTING_ABSENT); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + MC_ACCOUNT_SETTING_ABSENT); + g_return_val_if_fail (name != NULL, MC_ACCOUNT_SETTING_ABSENT); + g_return_val_if_fail (value != NULL, MC_ACCOUNT_SETTING_ABSENT); + + /* TODO: retreive type from protocol and check it matches */ + + ret = MC_ACCOUNT_SETTING_ABSENT; + + if (_mc_account_gconf_get_boolean (account, name, TRUE, value)) + { + ret = MC_ACCOUNT_SETTING_FROM_ACCOUNT; + } + else + { + McProfile *profile; + const gchar *def; + + profile = mc_account_get_profile (account); + def = mc_profile_get_default_setting (profile, name); + + if (def != NULL) + { + if (strcmp (def, "true") == 0 || strcmp (def, "1") == 0) + { + *value = TRUE; + ret = MC_ACCOUNT_SETTING_FROM_PROFILE; + } + else if (strcmp (def, "false") == 0 || strcmp (def, "0") == 0) + { + *value = FALSE; + ret = MC_ACCOUNT_SETTING_FROM_PROFILE; + } + else + { + g_warning ("%s: unable to parse boolean %s on account %s parameter %s", + G_STRFUNC, def, MC_ACCOUNT_PRIV (account)->unique_name, + name); + ret = MC_ACCOUNT_SETTING_ABSENT; + } + } + } + + return ret; +} + +static gboolean +_get_system_http_proxy (gboolean https, gchar **host, guint *port) +{ + gchar *ret_host; + guint ret_port; + GConfValue *value; + GConfClient *client = gconf_client_get_default (); + + g_return_val_if_fail (client != NULL, FALSE); + + if (!https) + { + /* Plain HTTP. If use_http_proxy is not true, give up. */ + + value = gconf_client_get ( + client, "/system/http_proxy/use_http_proxy", NULL); + + if (NULL == value) + goto NONE; + + if (GCONF_VALUE_BOOL != value->type) + { + gconf_value_free (value); + goto NONE; + } + + if (FALSE == gconf_value_get_bool (value)) + { + gconf_value_free (value); + goto NONE; + } + + gconf_value_free (value); + + /* If we're supposed to authenticate, give up. */ + + value = gconf_client_get ( + client, "/system/http_proxy/use_authentication", NULL); + + if (NULL == value) + goto NONE; + + if (GCONF_VALUE_BOOL != value->type) + { + gconf_value_free (value); + goto NONE; + } + + if (TRUE == gconf_value_get_bool (value)) + { + gconf_value_free (value); + goto NONE; + } + + gconf_value_free (value); + } + + /* If the proxy mode is not manual (i.e. it's none or automatic), give up. */ + + value = gconf_client_get (client, "/system/proxy/mode", NULL); + + if (NULL == value) + goto NONE; + + if (GCONF_VALUE_STRING != value->type) + { + gconf_value_free (value); + goto NONE; + } + + if (0 != strcmp ("manual", gconf_value_get_string (value))) + { + gconf_value_free (value); + goto NONE; + } + + gconf_value_free (value); + + if (https) + { + value = gconf_client_get (client, "/system/proxy/secure_host", NULL); + + if (NULL == value) + goto NONE; + + if (GCONF_VALUE_STRING != value->type) + { + gconf_value_free (value); + goto NONE; + } + + ret_host = g_strdup (gconf_value_get_string (value)); + gconf_value_free (value); + value = NULL; + + value = gconf_client_get (client, "/system/proxy/secure_port", NULL); + + if (NULL == value) + goto NONE; + + if (GCONF_VALUE_INT != value->type) + { + gconf_value_free (value); + goto NONE; + } + + ret_port = gconf_value_get_int (value); + gconf_value_free (value); + } + else + { + value = gconf_client_get (client, "/system/http_proxy/host", NULL); + + if (NULL == value) + goto NONE; + + if (GCONF_VALUE_STRING != value->type) + { + gconf_value_free (value); + goto NONE; + } + + ret_host = g_strdup (gconf_value_get_string (value)); + gconf_value_free (value); + + value = gconf_client_get (client, "/system/http_proxy/port", NULL); + + if (NULL == value) + goto NONE; + + if (GCONF_VALUE_INT != value->type) + { + gconf_value_free (value); + g_free (ret_host); + goto NONE; + } + + ret_port = gconf_value_get_int (value); + gconf_value_free (value); + } + + if (0 == strcmp ("", ret_host) || ret_port <= 0) + { + g_free (ret_host); + goto NONE; + } + + g_object_unref (client); + *host = ret_host; + *port = ret_port; + return TRUE; + +NONE: + g_object_unref (client); + return FALSE; +} + +/** + * mc_account_get_param_int: + * @account: The #McAccount. + * @name: the parameter to retrieve. + * @value: a pointer to the integer variable. + * + * Gets a integer parameter from the account settings. + * + * Return value: a #McAccountSettingState. + */ +McAccountSettingState +mc_account_get_param_int (McAccount *account, + const gchar *name, + gint *value) +{ + gchar *end; + glong long_val; + gint int_val; + McProfile *profile; + const gchar *def; + + g_return_val_if_fail (account != NULL, MC_ACCOUNT_SETTING_ABSENT); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + MC_ACCOUNT_SETTING_ABSENT); + g_return_val_if_fail (name != NULL, MC_ACCOUNT_SETTING_ABSENT); + g_return_val_if_fail (value != NULL, MC_ACCOUNT_SETTING_ABSENT); + + /* TODO: retreive type from protocol and check it matches */ + + if (_mc_account_gconf_get_int (account, name, TRUE, value)) + return MC_ACCOUNT_SETTING_FROM_ACCOUNT; + + profile = mc_account_get_profile (account); + def = mc_profile_get_default_setting (profile, name); + + if (def != NULL) + { + errno = 0; + long_val = strtol (def, &end, 10); + + if (*def == '\0' || *end != '\0') + { + g_warning ("%s: unable to parse integer %s on account %s parameter %s", + G_STRFUNC, def, MC_ACCOUNT_PRIV (account)->unique_name, + name); + return MC_ACCOUNT_SETTING_ABSENT; + } + + int_val = long_val; + + if (int_val != long_val || errno == ERANGE) + { + g_warning ("%s: integer %s out of range on account %s parameter %s", + G_STRFUNC, def, MC_ACCOUNT_PRIV (account)->unique_name, + name); + return MC_ACCOUNT_SETTING_ABSENT; + } + + *value = int_val; + return MC_ACCOUNT_SETTING_FROM_PROFILE; + } + + if (0 == strcmp(name, "http-proxy-port") || + 0 == strcmp(name, "https-proxy-port")) + { + gchar *host; + guint port; + gboolean https; + + if (0 == strcmp (name, "https-proxy-port")) + https = TRUE; + else + https = FALSE; + + if (_get_system_http_proxy (https, &host, &port)) + { + *value = port; + return MC_ACCOUNT_SETTING_FROM_PROXY; + } + } + + return MC_ACCOUNT_SETTING_ABSENT; +} + +/** + * mc_account_get_param_string: + * @account: The #McAccount. + * @name: the parameter to retrieve. + * @value: a pointer to the string variable which will receive the setting. + * + * Gets a string parameter from the account settings. The string will have to + * be freed with #g_free. + * + * Return value: a #McAccountSettingState. + */ +McAccountSettingState +mc_account_get_param_string (McAccount *account, + const gchar *name, + gchar **value) +{ + McProfile *profile; + const gchar *def; + + g_return_val_if_fail (account != NULL, MC_ACCOUNT_SETTING_ABSENT); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + MC_ACCOUNT_SETTING_ABSENT); + g_return_val_if_fail (name != NULL, MC_ACCOUNT_SETTING_ABSENT); + g_return_val_if_fail (value != NULL, MC_ACCOUNT_SETTING_ABSENT); + + /* TODO: retreive type from protocol and check it matches */ + + if (_mc_account_gconf_get_string (account, name, TRUE, value)) + return MC_ACCOUNT_SETTING_FROM_ACCOUNT; + + profile = mc_account_get_profile (account); + def = mc_profile_get_default_setting (profile, name); + + if (def != NULL) + { + *value = g_strdup (def); + return MC_ACCOUNT_SETTING_FROM_PROFILE; + } + + if (0 == strcmp(name, "http-proxy-server") || + 0 == strcmp(name, "https-proxy-server")) + { + gchar *host; + guint port; + gboolean https; + + if (0 == strcmp (name, "https-proxy-server")) + https = TRUE; + else + https = FALSE; + + if (_get_system_http_proxy (https, &host, &port)) + { + *value = host; + return MC_ACCOUNT_SETTING_FROM_PROXY; + } + } + + return MC_ACCOUNT_SETTING_ABSENT; +} + +/** + * mc_account_set_param_boolean: + * @account: The #McAccount. + * @name: the parameter to set. + * @value: a boolean value. + * + * Sets a boolean parameter in the account settings. + * + * Return value: %TRUE, or %FALSE if some error occurred. + */ +gboolean +mc_account_set_param_boolean (McAccount *account, + const gchar *name, + gboolean value) +{ + GConfClient *client; + gchar *key; + gboolean ok; + + g_return_val_if_fail (account != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + FALSE); + g_return_val_if_fail (name != NULL, FALSE); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, name, + TRUE); + ok = gconf_client_set_bool (client, key, value, NULL); + + g_free (key); + g_object_unref (client); + + return ok; +} + +/** + * mc_account_set_param_int: + * @account: The #McAccount. + * @name: the parameter to set. + * @value: a integer value. + * + * Sets a integer parameter in the account settings. + * + * Return value: %TRUE, or %FALSE if some error occurred. + */ +gboolean +mc_account_set_param_int (McAccount *account, + const gchar *name, + gint value) +{ + GConfClient *client; + gchar *key; + gboolean ok; + + g_return_val_if_fail (account != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + FALSE); + g_return_val_if_fail (name != NULL, FALSE); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, name, + TRUE); + ok = gconf_client_set_int (client, key, value, NULL); + + g_free (key); + g_object_unref (client); + + return ok; +} + +/** + * mc_account_set_param_string: + * @account: The #McAccount. + * @name: the parameter to set. + * @value: a string value. + * + * Sets a string parameter in the account settings. + * + * Return value: %TRUE, or %FALSE if some error occurred. + */ +gboolean +mc_account_set_param_string (McAccount *account, + const gchar *name, + const gchar *value) +{ + GConfClient *client; + gchar *key; + gboolean ok; + + g_return_val_if_fail (account != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + FALSE); + g_return_val_if_fail (name != NULL, FALSE); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, name, + TRUE); + ok = gconf_client_set_string (client, key, value, NULL); + + g_free (key); + g_object_unref (client); + + return ok; +} + +/** + * mc_account_unset_param: + * @account: The #McAccount. + * @name: the parameter to unset. + * + * Unsets (removes) a parameter from the account settings. + * + * Return value: %TRUE, or %FALSE if some error occurred. + */ +gboolean +mc_account_unset_param (McAccount *account, const gchar *name) +{ + McAccountPrivate *priv = MC_ACCOUNT_PRIV (account); + GConfClient *client; + gchar *key; + gboolean ok; + + g_return_val_if_fail (account != NULL, FALSE); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + FALSE); + g_return_val_if_fail (name != NULL, FALSE); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + key = _mc_account_path (priv->unique_name, name, TRUE); + ok = gconf_client_unset (client, key, NULL); + + g_free (key); + g_object_unref (client); + + return ok; +} + +static void +_g_value_free (gpointer data) +{ + GValue *value = (GValue *) data; + g_value_unset (value); + g_free (value); +} + +static void +_add_one_setting (McAccount *account, + McProtocolParam *param, + GHashTable *hash) +{ + GValue *value = NULL; + McAccountSettingState ret = MC_ACCOUNT_SETTING_ABSENT; + + g_return_if_fail (account != NULL); + g_return_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL); + g_return_if_fail (param != NULL); + g_return_if_fail (param->name != NULL); + g_return_if_fail (param->signature != NULL); + + switch (param->signature[0]) + { + case DBUS_TYPE_STRING: + { + char *tmp; + ret = mc_account_get_param_string (account, param->name, &tmp); + if (ret != MC_ACCOUNT_SETTING_ABSENT) + { + value = g_new0(GValue, 1); + g_value_init (value, G_TYPE_STRING); + g_value_take_string (value, tmp); + } + break; + } + case DBUS_TYPE_INT16: + case DBUS_TYPE_INT32: + { + gint tmp; + ret = mc_account_get_param_int (account, param->name, &tmp); + if (ret != MC_ACCOUNT_SETTING_ABSENT) + { + value = g_new0(GValue, 1); + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, tmp); + } + break; + } + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + { + gint tmp; + ret = mc_account_get_param_int (account, param->name, &tmp); + if (ret != MC_ACCOUNT_SETTING_ABSENT) + { + value = g_new0(GValue, 1); + g_value_init (value, G_TYPE_UINT); + g_value_set_uint (value, tmp); + } + break; + } + case DBUS_TYPE_BOOLEAN: + { + gboolean tmp; + ret = mc_account_get_param_boolean (account, param->name, &tmp); + if (ret != MC_ACCOUNT_SETTING_ABSENT) + { + value = g_new0(GValue, 1); + g_value_init (value, G_TYPE_BOOLEAN); + g_value_set_boolean (value, tmp); + } + break; + } + default: + g_warning ("%s: skipping parameter %s, unknown type %s", G_STRFUNC, param->name, param->signature); + } + + if (ret != MC_ACCOUNT_SETTING_ABSENT && hash != NULL) + { + g_return_if_fail (value != NULL); + g_hash_table_insert (hash, g_strdup (param->name), value); + } +} + +static gboolean +copy_file (const gchar *filename_in, const gchar *filename_out) +{ + FILE *f_in, *f_out; + char buffer[2048]; + size_t read, written; + gboolean ret = TRUE; + + f_in = fopen (filename_in, "r"); + f_out = fopen (filename_out, "w"); + if (!f_in || !f_out) return FALSE; + while ((read = fread (buffer, 1, sizeof (buffer), f_in)) > 0) + { + written = fwrite (buffer, 1, read, f_out); + if (written < read) + { + g_warning ("%s: fwrite failed (errno = %d)", + G_STRFUNC, errno); + ret = FALSE; + break; + } + } + if (ferror (f_in)) + { + g_warning ("%s: fread failed (errno = %d)", + G_STRFUNC, errno); + ret = FALSE; + } + fclose (f_in); + fclose (f_out); + return ret; +} + +/** + * mc_account_get_params: + * @account: The #McAccount. + * + * Gets all the parameters for this account. The returned hash table must be + * freed. + * + * Return value: a #GHashTable containing all the account settings, or NULL. + */ +GHashTable * +mc_account_get_params (McAccount *account) +{ + McProfile *profile = NULL; + McProtocol *protocol = NULL; + GSList *params, *tmp; + GHashTable *ret = NULL; + + g_return_val_if_fail (account != NULL, NULL); + g_return_val_if_fail (MC_ACCOUNT_PRIV (account)->unique_name != NULL, + NULL); + + profile = mc_account_get_profile (account); + if (profile == NULL) + { + g_debug ("%s: getting profile failed", G_STRFUNC); + goto OUT; + } + + protocol = mc_profile_get_protocol (profile); + if (protocol == NULL) + { + g_debug ("%s: getting protocol failed", G_STRFUNC); + goto OUT; + } + + ret = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) _g_value_free); + + params = mc_protocol_get_params (protocol); + + for (tmp = params; tmp != NULL; tmp = tmp->next) + _add_one_setting (account, (McProtocolParam *) tmp->data, ret); + + mc_protocol_free_params_list (params); + +OUT: + if (protocol) + mc_protocol_free (protocol); + + if (profile) + mc_profile_free (profile); + + return ret; +} + +/** + * mc_account_is_complete: + * @account: The #McAccount. + * + * Checks if all the mandatory parameters declared by the protocol are present + * in this account's settings. + * + * Return value: a gboolean. + */ +gboolean +mc_account_is_complete (McAccount *account) +{ + McProfile *profile = NULL; + McProtocol *protocol = NULL; + GSList *params = NULL, *tmp; + gboolean ret = TRUE; + + g_return_val_if_fail (account != NULL, FALSE); + + /* Check if account was expunged */ + if (MC_ACCOUNT_PRIV (account)->unique_name == NULL) + return FALSE; + + /* Check if account was deleted */ + if (mc_account_is_deleted (account)) + return FALSE; + + profile = mc_account_get_profile (account); + if (profile == NULL) + { + ret = FALSE; + goto OUT; + } + + protocol = mc_profile_get_protocol (profile); + if (protocol == NULL) + { + ret = FALSE; + goto OUT; + } + + params = mc_protocol_get_params (protocol); + + for (tmp = params; tmp != NULL; tmp = tmp->next) + { + McProtocolParam *param; + const gchar *def; + GConfValue *val; + + param = (McProtocolParam *) tmp->data; + + if (!(param->flags & MC_PROTOCOL_PARAM_REQUIRED)) + continue; + + if (param == NULL || param->name == NULL || param->signature == NULL) + { + ret = FALSE; + break; + } + + /* TODO: check this value can be mapped to the desired type */ + def = mc_profile_get_default_setting (profile, param->name); + if (def) + { + continue; + } + + val = _mc_account_gconf_get (account, param->name, TRUE); + if (val == NULL) + { + ret = FALSE; + break; + } + + /* TODO: unduplicate this type mapping */ + switch (param->signature[0]) + { + case DBUS_TYPE_BOOLEAN: + if (val->type != GCONF_VALUE_BOOL) + ret = FALSE; + break; + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + if (val->type != GCONF_VALUE_INT) + ret = FALSE; + break; + case DBUS_TYPE_STRING: + if (val->type != GCONF_VALUE_STRING) + ret = FALSE; + break; + default: + ret = FALSE; + } + + gconf_value_free (val); + + if (ret == FALSE) + break; + } + + mc_protocol_free_params_list (params); + +OUT: + if (profile != NULL) + mc_profile_free (profile); + + if (protocol != NULL) + mc_protocol_free (protocol); + + return ret; +} + +/** + * mc_account_get_supported_presences: + * @account: the #McAccount. + * + * Checks what presence states are supported by this account. + * + * Returns: a zero-terminated array listing all the supported #McPresence. + * It must not be freed. + */ +const McPresence * +mc_account_get_supported_presences (McAccount *account) +{ + McProfile *profile = mc_account_get_profile (account); + const McPresence *presences; + + presences = mc_profile_get_supported_presences (profile); + mc_profile_free (profile); + return presences; +} + +/* + * mc_account_supports_presence: + * @account: The #McAccount. + * @presence: The #McPresence. + * + * Tests whether the account supports the presence @presence. + * + * Returns: a #gboolean. + */ +gboolean +mc_account_supports_presence (McAccount *account, McPresence presence) +{ + McProfile *profile = mc_account_get_profile (account); + gboolean supported; + + supported = mc_profile_supports_presence (profile, presence); + mc_profile_free (profile); + return supported; +} + +/** + * mc_account_set_avatar: + * @account: The #McAccount. + * @filename: the path of the image file to be used as avatar. + * @mime_type: the MIME type of the image. + * + * Set the avatar for this account. If @filename is %NULL, the avatar is + * cleared. + * + * Return value: %TRUE, or %FALSE if some error occurred. + */ +gboolean +mc_account_set_avatar (McAccount *account, const gchar *filename, + const gchar *mime_type) +{ + gchar *data_dir, *filename_out; + GConfClient *client; + gboolean ret = TRUE; + gchar *key; + + g_return_val_if_fail (account != NULL, FALSE); + + data_dir = get_account_data_path (MC_ACCOUNT_PRIV(account)->unique_name); + filename_out = g_build_filename (data_dir, MC_AVATAR_FILENAME, NULL); + if (!g_file_test (data_dir, G_FILE_TEST_EXISTS)) + g_mkdir_with_parents (data_dir, 0777); + g_free (data_dir); + + if (filename) + { + /* copy the file in the account data directory */ + if (strcmp (filename_out, filename) != 0) + { + if (copy_file (filename, filename_out) == FALSE) + { + g_warning ("%s: copy_file failed", G_STRLOC); + g_free (filename_out); + return FALSE; + } + } + } + else + { + /* create an empty file; this will cause MC to clear the current + * avatar */ + FILE *f_out = fopen (filename_out, "w"); + fclose (f_out); + } + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + MC_ACCOUNTS_GCONF_KEY_AVATAR_TOKEN, FALSE); + ret = gconf_client_unset(client, key, NULL); + g_free (key); + if (!ret) goto error; + + /* put an ID for the avatar, so that listeners of the "account-changed" + * signal will be able to determine whether the avatar has changed without + * having to load the file */ + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + MC_ACCOUNTS_GCONF_KEY_AVATAR_ID, FALSE); + ret = gconf_client_set_int(client, key, g_random_int(), NULL); + g_free (key); + + if (mime_type) + { + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + MC_ACCOUNTS_GCONF_KEY_AVATAR_MIME, FALSE); + ret = gconf_client_set_string (client, key, mime_type, NULL); + g_free (key); + } + +error: + g_object_unref (client); + g_free (filename_out); + + return ret; +} + +/** + * mc_account_set_avatar_token: + * @account: The #McAccount. + * @token: string holding the Telepathy token for the avatar. + * + * Set the avatar token for this account. This function is to be used only by + * the mission-control server. + * + * Return value: %TRUE, or %FALSE if some error occurred. + */ +gboolean +mc_account_set_avatar_token (McAccount *account, const gchar *token) +{ + return _mc_account_gconf_set_string (account, + MC_ACCOUNTS_GCONF_KEY_AVATAR_TOKEN, + token); +} + +/** + * mc_account_set_avatar_mime_type: + * @account: The #McAccount. + * @mime_type: string holding the mime-type of the avatar. + * + * Set the avatar mime-type for this account. This function is to be used only + * by the mission-control server. + * + * Return value: %TRUE, or %FALSE if some error occurred. + */ +gboolean +mc_account_set_avatar_mime_type (McAccount *account, const gchar *mime_type) +{ + return _mc_account_gconf_set_string (account, + MC_ACCOUNTS_GCONF_KEY_AVATAR_MIME, + mime_type); +} + +/** + * mc_account_get_avatar: + * @account: The #McAccount. + * @filename: address of the variable to hold the path of the image file used + * as avatar. + * @mime_type: address of the variable for the MIME type of the image. + * @token: address of the variable for the Telepathy token of the avatar. + * + * Get the avatar for this account. Any of the parameters for the output can be + * NULL, if that information is not needed. + * + * Return value: %TRUE, or %FALSE if some error occurred. + */ +gboolean +mc_account_get_avatar (McAccount *account, gchar **filename, + gchar **mime_type, gchar **token) +{ + gchar *data_dir; + GConfClient *client; + gchar *key; + + g_return_val_if_fail (account != NULL, FALSE); + + if (filename) + { + data_dir = + get_account_data_path (MC_ACCOUNT_PRIV (account)->unique_name); + *filename = g_build_filename (data_dir, MC_AVATAR_FILENAME, NULL); + if (!g_file_test (data_dir, G_FILE_TEST_EXISTS)) + g_mkdir_with_parents (data_dir, 0777); + g_free (data_dir); + } + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + if (token) + { + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + MC_ACCOUNTS_GCONF_KEY_AVATAR_TOKEN, FALSE); + *token = gconf_client_get_string (client, key, NULL); + g_free (key); + } + + if (mime_type) + { + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + MC_ACCOUNTS_GCONF_KEY_AVATAR_MIME, FALSE); + *mime_type = gconf_client_get_string (client, key, NULL); + g_free (key); + } + + g_object_unref (client); + + return TRUE; +} + +/** + * mc_account_get_avatar_id: + * @account: The #McAccount. + * + * Get the avatar ID for this account. The ID is a number that changes + * everytime the avatar image changes. + * + * Returns: the avatar ID. + */ +gint +mc_account_get_avatar_id (McAccount *account) +{ + GConfClient *client; + gchar *key; + gint ret; + + g_return_val_if_fail (account != NULL, 0); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != NULL, 0); + + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + MC_ACCOUNTS_GCONF_KEY_AVATAR_ID, FALSE); + ret = gconf_client_get_int (client, key, NULL); + g_free (key); + + g_object_unref (client); + return ret; +} + +/** + * mc_account_reset_avatar_id: + * @account: The #McAccount. + * + * Calculates a new avatar ID for this account. This function is to be called + * when the avatar image file has been changed by a direct modification of its + * binary content. + * + * Returns: %TRUE, or %FALSE if some error occurred. + */ +gboolean +mc_account_reset_avatar_id (McAccount *account) +{ + GConfClient *client; + gchar *key; + gboolean ok; + + g_return_val_if_fail (account != FALSE, 0); + + client = gconf_client_get_default (); + g_return_val_if_fail (client != FALSE, 0); + + key = _mc_account_path (MC_ACCOUNT_PRIV (account)->unique_name, + MC_ACCOUNTS_GCONF_KEY_AVATAR_ID, FALSE); + ok = gconf_client_set_int (client, key, g_random_int(), NULL); + g_free (key); + + g_object_unref (client); + return ok; +} + diff --git a/libmissioncontrol/mc-account.h b/libmissioncontrol/mc-account.h new file mode 100644 index 00000000..a839d577 --- /dev/null +++ b/libmissioncontrol/mc-account.h @@ -0,0 +1,182 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MC_ACCOUNT_H__ +#define __MC_ACCOUNT_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define MC_TYPE_ACCOUNT mc_account_get_type() + +#define MC_ACCOUNT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MC_TYPE_ACCOUNT, McAccount)) + +#define MC_ACCOUNT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MC_TYPE_ACCOUNT, McAccountwClass)) + +#define MC_IS_ACCOUNT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MC_TYPE_ACCOUNT)) + +#define MC_IS_ACCOUNT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MC_TYPE_ACCOUNT)) + +#define MC_ACCOUNT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MC_TYPE_ACCOUNT, McAccountwClass)) + +typedef struct { + GObject parent; + gpointer priv; +} McAccount; + +typedef struct { + GObjectClass parent_class; +} McAccountClass; + +GType mc_account_get_type (void); + +McAccount* mc_account_new (void); + +typedef enum +{ + MC_ACCOUNT_SETTING_ABSENT = 0, + MC_ACCOUNT_SETTING_FROM_ACCOUNT, + MC_ACCOUNT_SETTING_FROM_PROFILE, + MC_ACCOUNT_SETTING_FROM_PROXY, +} McAccountSettingState; + +#include <libmissioncontrol/mc-profile.h> + +/* returns NULL if unique name does not exist */ +McAccount *mc_account_lookup (const gchar *unique_name); +McAccount *mc_account_lookup_with_profile (McProfile *profile, + const gchar *account); +McAccount *mc_account_lookup_with_vcard_field (const gchar *vcard_field, + const gchar *account); +void mc_account_free (McAccount* account); +void mc_account_clear_cache (void); + +/* returns newly-created account, enabled by default */ +McAccount *mc_account_create (McProfile *profile); + +/* this function only deletes the account from database, + * the account struct itself must be freed separately */ +gboolean mc_account_delete (McAccount *account); + +/* lists returned by these functions should be freed with + * mc_accounts_list_free */ +GList *mc_accounts_list (void); +GList *mc_accounts_list_by_enabled (gboolean enabled); +GList *mc_accounts_list_by_profile (McProfile *profile); +GList *mc_accounts_list_by_vcard_field (const gchar *vcard_field); +void mc_accounts_list_free (GList *list); + +/* filter a list of accounts according to whether a function returns TRUE, + * freeing the list and any accounts which are filtered out, and returning a + * new list which must be freed with mc_accounts_list_free. */ +typedef gboolean (*McAccountFilter) (McAccount *account, gpointer data); +GList *mc_accounts_filter (GList *accounts, + McAccountFilter filter, + gpointer data); + +/* a unique identifier string for this account */ +const gchar *mc_account_get_unique_name (McAccount *account); + +/* get profile */ +McProfile *mc_account_get_profile (McAccount *account); + +/* human-readable name */ +const gchar *mc_account_get_display_name (McAccount *account); +gboolean mc_account_set_display_name (McAccount *account, + const gchar *name); + +/* normalized name */ +const gchar *mc_account_get_normalized_name (McAccount *account); +gboolean mc_account_set_normalized_name (McAccount *account, + const gchar *name); + +/* whether account is enabled or disabled */ +gboolean mc_account_is_enabled (McAccount *account); +gboolean mc_account_set_enabled (McAccount *account, + const gboolean enabled); + +/* the following methods retrieve a parameter from the account or the + * default from the profile if the account does not set the value */ +McAccountSettingState mc_account_get_param_boolean (McAccount *account, + const gchar *name, + gboolean *value); +McAccountSettingState mc_account_get_param_int (McAccount *account, + const gchar *name, + gint *value); +McAccountSettingState mc_account_get_param_string (McAccount *account, + const gchar *name, + gchar **value); + +/* for every parameter (both optional and mandatory) defined by the + * protocol, get a hash table of the params from the account or + * the default profile. each setting is stored in a GValue. */ +GHashTable *mc_account_get_params (McAccount *account); + +/* Set functions. Returns true if success, else false is returned */ +gboolean mc_account_set_param_boolean (McAccount *account, + const gchar *name, + gboolean value); +gboolean mc_account_set_param_int (McAccount *account, + const gchar *name, + gint value); +gboolean mc_account_set_param_string (McAccount *account, + const gchar *name, + const gchar *value); + +gboolean mc_account_unset_param (McAccount *account, const gchar *name); + +/* returns TRUE if the account information, along with the profile defaults + * has all mandatory fields (declared by the protocol) set */ +gboolean mc_account_is_complete (McAccount *account); + +const McPresence *mc_account_get_supported_presences (McAccount *account); +gboolean mc_account_supports_presence (McAccount *account, + McPresence presence); + +gboolean mc_account_set_avatar (McAccount *account, const gchar *filename, + const gchar *mime_type); +gboolean mc_account_get_avatar (McAccount *account, gchar **filename, + gchar **mime_type, gchar **token); + +gboolean mc_account_set_avatar_token (McAccount *account, const gchar *token); +gboolean mc_account_set_avatar_mime_type (McAccount *account, + const gchar *mime_type); + +gint mc_account_get_avatar_id (McAccount *account); +gboolean mc_account_reset_avatar_id (McAccount *account); + +G_END_DECLS + +#endif /* __MC_ACCOUNT_H__ */ diff --git a/libmissioncontrol/mc-manager-priv.h b/libmissioncontrol/mc-manager-priv.h new file mode 100644 index 00000000..4950699d --- /dev/null +++ b/libmissioncontrol/mc-manager-priv.h @@ -0,0 +1,32 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MC_MANAGER_PRIV_H__ +#define __MC_MANAGER_PRIV_H__ + +#include "mc-protocol.h" + +McProtocol * +_mc_manager_protocol_lookup (McManager *manager, const gchar *name); + +#endif /* __MC_MANAGER_PRIV_H__ */ diff --git a/libmissioncontrol/mc-manager.c b/libmissioncontrol/mc-manager.c new file mode 100644 index 00000000..4e9e4440 --- /dev/null +++ b/libmissioncontrol/mc-manager.c @@ -0,0 +1,388 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <string.h> +#include <sys/types.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "mc-protocol.h" +#include "mc-protocol-priv.h" + +#include "mc-manager.h" +#include "mc-manager-priv.h" + +#define MANAGER_PATH "/usr/share/telepathy/managers" +#define MANAGER_SUFFIX ".manager" +#define MANAGER_SUFFIX_LEN 8 + +#define MC_MANAGER_PRIV(manager) ((McManagerPrivate *)manager->priv) + +G_DEFINE_TYPE (McManager, mc_manager, G_TYPE_OBJECT); + +static GHashTable *manager_cache = NULL; + +typedef struct { + gchar *unique_name; + gchar *bus_name; + gchar *object_path; + time_t mtime; + GSList *protocols; +} McManagerPrivate; + +static void +mc_manager_finalize (GObject *object) +{ + McManager *manager = MC_MANAGER(object); + McManagerPrivate *priv = MC_MANAGER_PRIV (manager); + GSList *i; + + g_free (priv->unique_name); + g_free (priv->bus_name); + g_free (priv->object_path); + + for (i = priv->protocols; NULL != i; i = i->next) + g_object_unref (G_OBJECT (i->data)); + + g_slist_free (priv->protocols); +} + +static void +mc_manager_class_init (McManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (object_class, sizeof (McManagerPrivate)); + object_class->finalize = mc_manager_finalize; +} + +static void +mc_manager_init (McManager *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, + MC_TYPE_MANAGER, McManagerPrivate); +} + +static McManager * +mc_manager_new (gchar *unique_name, gchar *bus_name, gchar *object_path, + GSList *protocols) +{ + McManager *new = g_object_new (MC_TYPE_MANAGER, NULL); + McManagerPrivate *priv = MC_MANAGER_PRIV (new); + + priv->unique_name = unique_name; + priv->bus_name = bus_name; + priv->object_path = object_path; + priv->protocols = protocols; + + return new; +} + +static const gchar * +_mc_manager_path (void) +{ + const gchar *ret = NULL; + + if (ret == NULL) + { + ret = g_getenv ("MC_MANAGER_DIR"); + if (ret == NULL) + ret = MANAGER_PATH; + } + + return ret; +} + +static gchar * +_mc_manager_filename (const gchar *unique_name) +{ + return g_strconcat (_mc_manager_path (), G_DIR_SEPARATOR_S, + unique_name, MANAGER_SUFFIX, NULL); +} + +#define PREFIX_PROTOCOL "Protocol " +#define PREFIX_PROTOCOL_LEN 9 +#define PREFIX_PROTOCOL_OLD "Proto " +#define PREFIX_PROTOCOL_OLD_LEN 6 + +static GSList * +_keyfile_get_protocols (GKeyFile *keyfile, const gchar *manager) +{ + GSList *protocols = NULL; + gchar **groups = NULL; + gchar **i; + + groups = g_key_file_get_groups (keyfile, NULL); + + for (i = groups; NULL != *i; i++) + { + const gchar *name = NULL; + + if (0 == strncmp (*i, PREFIX_PROTOCOL, PREFIX_PROTOCOL_LEN)) + name = *i + PREFIX_PROTOCOL_LEN; + else if (0 == strncmp (*i, PREFIX_PROTOCOL_OLD, PREFIX_PROTOCOL_OLD_LEN)) + name = *i + PREFIX_PROTOCOL_OLD_LEN; + + if (NULL != name) + { + McProtocol *protocol = _mc_protocol_from_keyfile (keyfile, + manager, *i, name); + + if (protocol) + protocols = g_slist_prepend (protocols, protocol); + } + } + + g_strfreev (groups); + return protocols; +} + +static McManager * +_mc_manager_from_file (const gchar *unique_name, const gchar *filename) +{ + GError *error; + GKeyFile *keyfile; + gchar *bus_name = NULL; + gchar *object_path = NULL; + GSList *protocols = NULL; + + keyfile = g_key_file_new (); + + if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error)) + { + g_debug ("%s: loading %s failed: %s", G_STRFUNC, filename, error->message); + g_error_free (error); + return NULL; + } + + bus_name = g_key_file_get_string ( + keyfile, "ConnectionManager", "BusName", NULL); + object_path = g_key_file_get_string ( + keyfile, "ConnectionManager", "ObjectPath", NULL); + + if (!bus_name || !object_path) + { + g_debug ("%s: failed to get name, bus name and object path from file", + G_STRFUNC); + g_free (bus_name); + g_free (object_path); + return NULL; + } + + protocols = _keyfile_get_protocols (keyfile, unique_name); + g_key_file_free (keyfile); + + return mc_manager_new (g_strdup (unique_name), bus_name, object_path, + protocols); +} + +static time_t +_mc_manager_get_mtime (McManager *manager) +{ + McManagerPrivate *priv = MC_MANAGER_PRIV (manager); + return priv->mtime; +} + +/** + * mc_manager_lookup: + * @unique_name: the unique name. + * + * Looks up for the #McManager having the given unique name. + * The returned object's reference count is incremented. + * + * Returns: the #McManager, or NULL if not found. + */ +McManager * +mc_manager_lookup (const gchar *unique_name) +{ + McManager *manager = NULL; + gchar *filename; + struct stat buf; + + g_return_val_if_fail (unique_name != NULL, NULL); + g_return_val_if_fail (*unique_name != '\0', NULL); + + filename = _mc_manager_filename (unique_name); + + if (0 != g_stat (filename, &buf)) + goto OUT; + + if (NULL == manager_cache) + manager_cache = g_hash_table_new_full ( + g_str_hash, g_str_equal, g_free, g_object_unref); + + manager = g_hash_table_lookup (manager_cache, unique_name); + + if (NULL != manager && _mc_manager_get_mtime (manager) >= buf.st_mtime) + { + g_object_ref (manager); + goto OUT; + } + + manager = _mc_manager_from_file (unique_name, filename); + + if (NULL != manager) + { + McManagerPrivate *priv; + priv = MC_MANAGER_PRIV (manager); + priv->mtime = buf.st_mtime; + g_hash_table_replace (manager_cache, g_strdup (unique_name), manager); + g_object_ref (manager); + } + +OUT: + g_free (filename); + return manager; +} + +/** + * mc_manager_free: + * @id: the #McManager. + * + * Frees (unrefs) the manager. + */ +void +mc_manager_free (McManager *id) +{ + g_return_if_fail (id != NULL); + + g_object_unref (id); +} + +/** + * mc_manager_clear_cache: + * + * Clears the managers cache. + */ +void +mc_manager_clear_cache(void) +{ + if (NULL == manager_cache) + return; + + g_hash_table_destroy(manager_cache); + manager_cache = NULL; +} + +/** + * mc_managers_list: + * + * Lists all configured managers. <emphasis>Currently this function returns + * only the "gabble" manager</emphasis>. + * + * Returns: a #GList of the managers, to be freed with #mc_managers_free_list. + */ +GList * +mc_managers_list (void) +{ + return g_list_prepend (NULL, mc_manager_lookup ("gabble")); +} + +/** + * mc_managers_free_list: + * @list: a #GList of #McManager. + * + * Frees a list of managers. + */ +void +mc_managers_free_list (GList *list) +{ + GList *tmp; + + for (tmp = list; tmp != NULL; tmp = tmp->next) + mc_manager_free ((McManager *) tmp->data); + + g_list_free (list); +} + +/** + * mc_manager_get_unique_name: + * @id: the #McManager. + * + * Gets the unique name of the manager. + * + * Returns: the unique name, as a string (not to be freed). + */ +const gchar * +mc_manager_get_unique_name (McManager *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return MC_MANAGER_PRIV (id)->unique_name; +} + +/** + * mc_manager_get_bus_name: + * @id: the #McManager. + * + * Gets the D-Bus bus name of the manager. + * + * Returns: the bus name, as a string (not to be freed). + */ +const gchar * +mc_manager_get_bus_name (McManager *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return MC_MANAGER_PRIV (id)->bus_name; +} + +/** + * mc_manager_get_object_path: + * @id: the #McManager. + * + * Gets the D-Bus object path of the manager. + * + * Returns: the object path, as a string (not to be freed). + */ +const gchar * +mc_manager_get_object_path (McManager *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return MC_MANAGER_PRIV (id)->object_path; +} + +McProtocol * +_mc_manager_protocol_lookup (McManager *manager, const gchar *name) +{ + GSList *i; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (*name != '\0', NULL); + + for (i = MC_MANAGER_PRIV (manager)->protocols; NULL != i; i++) + { + McProtocol *protocol = (McProtocol *) i->data; + + if (0 == strcmp (name, mc_protocol_get_name (protocol))) + { + g_object_ref (protocol); + return protocol; + } + } + + return NULL; +} + diff --git a/libmissioncontrol/mc-manager.h b/libmissioncontrol/mc-manager.h new file mode 100644 index 00000000..b7995bca --- /dev/null +++ b/libmissioncontrol/mc-manager.h @@ -0,0 +1,79 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MC_MANAGER_H__ +#define __MC_MANAGER_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define MC_TYPE_MANAGER mc_manager_get_type() + +#define MC_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MC_TYPE_MANAGER, McManager)) + +#define MC_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MC_TYPE_MANAGER, McManagerClass)) + +#define MC_IS_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MC_TYPE_MANAGER)) + +#define MC_IS_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MC_TYPE_MANAGER)) + +#define MC_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MC_TYPE_MANAGER, McManagerClass)) + +typedef struct { + GObject parent; + gpointer priv; +} McManager; + +typedef struct { + GObjectClass parent_class; +} McManagerClass; + +GType mc_manager_get_type (void); + +McManager *mc_manager_lookup (const gchar *unique_name); +void mc_manager_free (McManager *id); +void mc_manager_clear_cache (void); + +/* get all managers; returns a list of McManager *s */ +GList *mc_managers_list (void); +void mc_managers_free_list (GList *list); + +const gchar *mc_manager_get_unique_name (McManager *id); +const gchar *mc_manager_get_bus_name (McManager *id); +const gchar *mc_manager_get_object_path (McManager *id); + +G_END_DECLS + +#endif /* __MC_MANAGER_H__ */ diff --git a/libmissioncontrol/mc-profile.c b/libmissioncontrol/mc-profile.c new file mode 100644 index 00000000..b6be4acf --- /dev/null +++ b/libmissioncontrol/mc-profile.c @@ -0,0 +1,837 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <glib.h> +#include <glib/gstdio.h> +#include <string.h> +#include <sys/types.h> +#include <libintl.h> + +#include "mc-profile.h" +#include "mc-enum-types.h" +#include <config.h> + +#define PROFILE_SUFFIX ".profile" +#define PROFILE_SUFFIX_LEN 8 +#define PROFILE_GROUP "Profile" + +#define MC_PROFILE_PRIV(profile) ((McProfilePrivate *)profile->priv) + +G_DEFINE_TYPE (McProfile, mc_profile, G_TYPE_OBJECT); + +static GHashTable *profile_cache = NULL; + +const GDebugKey capabilities[] = { + { "chat-p2p", MC_PROFILE_CAPABILITY_CHAT_P2P }, + { "chat-room", MC_PROFILE_CAPABILITY_CHAT_ROOM }, + { "chat-room-list", MC_PROFILE_CAPABILITY_CHAT_ROOM_LIST }, + { "voice-p2p", MC_PROFILE_CAPABILITY_VOICE_P2P }, + { "contact-search", MC_PROFILE_CAPABILITY_CONTACT_SEARCH }, + { "split-account", MC_PROFILE_CAPABILITY_SPLIT_ACCOUNT }, + { "registration-ui", MC_PROFILE_CAPABILITY_REGISTRATION_UI }, + { "supports-avatars", MC_PROFILE_CAPABILITY_SUPPORTS_AVATARS }, +}; + +typedef struct { + gchar *unique_name; + gboolean loaded; + gchar *configuration_ui; + gchar *display_name; + gchar *icon_name; + gchar *branding_icon_name; + gchar *manager; + gchar *protocol; + gchar *vcard_field; + gchar *default_account_domain; + gboolean vcard_default; + McProfileCapabilityFlags capabilities; + GHashTable *default_settings; + GArray *supported_presences; + time_t mtime; +} McProfilePrivate; + +static void +mc_profile_init (McProfile *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, + MC_TYPE_PROFILE, McProfilePrivate); +} + +static void +mc_profile_finalize (GObject *object) +{ + McProfile *self = MC_PROFILE(object); + McProfilePrivate *priv; + g_return_if_fail (self != NULL); + + priv = MC_PROFILE_PRIV (self); + g_free (priv->unique_name); + g_free (priv->configuration_ui); + g_free (priv->display_name); + g_free (priv->icon_name); + g_free (priv->branding_icon_name); + g_free (priv->manager); + g_free (priv->protocol); + g_free (priv->vcard_field); + g_free (priv->default_account_domain); + g_hash_table_destroy (priv->default_settings); + g_array_free (priv->supported_presences, TRUE); +} + +static void +mc_profile_class_init (McProfileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (object_class, sizeof (McProfilePrivate)); + object_class->finalize = mc_profile_finalize; +} + +/** + * mc_profile_clear_cache: + * + * Clears the profiles cache. + */ +void +mc_profile_clear_cache(void) +{ + if (NULL == profile_cache) + return; + + g_hash_table_destroy(profile_cache); + profile_cache = NULL; +} + +static const gchar * _mc_profile_path (void); +static gchar * _mc_profile_filename (const gchar *name); + +static time_t +_mc_profile_mtime (McProfile *profile) +{ + McProfilePrivate *priv = MC_PROFILE_PRIV (profile); + return priv->mtime; +} + +static McProfile * +_mc_profile_new (const gchar *unique_name) +{ + McProfile *profile = NULL; + McProfilePrivate *priv; + struct stat buf; + gchar *filename = _mc_profile_filename (unique_name); + + if (0 != g_stat (filename, &buf)) + goto OUT; + + if (NULL == profile_cache) + profile_cache = g_hash_table_new_full ( + g_str_hash, g_str_equal, g_free, g_object_unref); + + profile = g_hash_table_lookup (profile_cache, unique_name); + + if (NULL != profile && _mc_profile_mtime (profile) >= buf.st_mtime) + { + g_object_ref (profile); + goto OUT; + } + + profile = (McProfile *) g_object_new (MC_TYPE_PROFILE, NULL); + priv = MC_PROFILE_PRIV (profile); + priv->unique_name = g_strdup (unique_name); + priv->mtime = buf.st_mtime; + g_hash_table_replace (profile_cache, g_strdup (unique_name), profile); + g_object_ref (profile); + +OUT: + g_free (filename); + return profile; +} + +static const gchar * +_mc_profile_path (void) +{ + const gchar *ret = NULL; + + if (ret == NULL) + { + ret = g_getenv ("MC_PROFILE_DIR"); + if (ret == NULL) + ret = PROFILES_DIR; + } + + return ret; +} + +static gchar * +_mc_profile_filename (const gchar *name) +{ + return g_strconcat (_mc_profile_path (), G_DIR_SEPARATOR_S, + name, PROFILE_SUFFIX, NULL); +} + +static gboolean +_mc_profile_load (McProfile *profile) +{ + gchar *filename; + GKeyFile *keyfile; + GError *error = NULL; + gchar *caps, *localization_domain; + gchar **keys, **tmp; + McProfilePrivate *priv; + gchar **presences_str; + GEnumClass *presences_class; + gsize length; + gint i; + + priv = MC_PROFILE_PRIV (profile); + + if (priv->loaded) + return TRUE; + + filename = _mc_profile_filename (priv->unique_name); + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error)) + { + g_debug ("%s: loading %s failed: %s", G_STRFUNC, filename, error->message); + g_error_free (error); + return FALSE; + } + + priv->configuration_ui = g_key_file_get_string (keyfile, PROFILE_GROUP, "ConfigurationUI", NULL); + priv->display_name = g_key_file_get_string (keyfile, PROFILE_GROUP, "DisplayName", NULL); + priv->icon_name = g_key_file_get_string (keyfile, PROFILE_GROUP, "IconName", NULL); + priv->branding_icon_name = g_key_file_get_string (keyfile, PROFILE_GROUP, "BrandingIconName", NULL); + priv->manager = g_key_file_get_string (keyfile, PROFILE_GROUP, "Manager", NULL); + priv->protocol = g_key_file_get_string (keyfile, PROFILE_GROUP, "Protocol", NULL); + priv->vcard_field = g_key_file_get_string (keyfile, PROFILE_GROUP, "VCardField", NULL); + priv->vcard_default = g_key_file_get_boolean (keyfile, PROFILE_GROUP, "VCardDefault", NULL); + priv->default_account_domain = g_key_file_get_string (keyfile, PROFILE_GROUP, "DefaultAccountDomain", NULL); + localization_domain = g_key_file_get_string (keyfile, PROFILE_GROUP, "LocalizationDomain", NULL); + if (localization_domain) + { + gchar *display_name; + + display_name = g_strdup (dgettext (localization_domain, + priv->display_name)); + g_free (priv->display_name); + priv->display_name = display_name; + g_free (localization_domain); + } + + g_key_file_set_list_separator (keyfile, ','); + presences_str = g_key_file_get_string_list (keyfile, PROFILE_GROUP, + "SupportedPresences", &length, + NULL); + if (!presences_str) length = 0; + presences_class = g_type_class_ref (MC_TYPE_PRESENCE); + priv->supported_presences = g_array_sized_new (TRUE, FALSE, + sizeof (McPresence), length); + for (i = 0; i < length; i++) + { + McPresence presence; + GEnumValue *value; + + value = g_enum_get_value_by_nick (presences_class, + presences_str[i]); + if (!value) + { + g_warning ("Unrecognized presence `%s'", presences_str[i]); + continue; + } + presence = value->value; + g_array_append_val (priv->supported_presences, presence); + } + g_type_class_unref (presences_class); + g_strfreev (presences_str); + + /* :D */ + caps = g_key_file_get_string (keyfile, PROFILE_GROUP, "Capabilities", NULL); + g_strdelimit (caps, " ,;", ':'); + priv->capabilities = g_parse_debug_string (caps, capabilities, sizeof (capabilities) / sizeof (GDebugKey)); + g_free (caps); + + priv->default_settings = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + keys = g_key_file_get_keys (keyfile, PROFILE_GROUP, 0, NULL); + for (tmp = keys; *tmp != NULL; tmp++) + { + gchar *key = *tmp; + if (0 == g_ascii_strncasecmp ("Default-", key, 8)) + { + gchar *k, *v; + k = g_strdup (key+8); + v = g_key_file_get_string (keyfile, PROFILE_GROUP, key, NULL); + g_hash_table_insert (MC_PROFILE_PRIV (profile)->default_settings, k, v); + } + } + g_strfreev (keys); + + g_key_file_free (keyfile); + g_free (filename); + + priv->loaded = TRUE; + return TRUE; +} + +/** + * mc_profile_lookup: + * @unique_name: The unique name of the profile. + * + * Get the profile whose unique name is the one specified. If no profile with + * that name exists, a new one is created. The returned object's reference + * count is incremented. + * + * Return value: The #McProfile. + */ +McProfile * +mc_profile_lookup (const gchar *unique_name) +{ + g_return_val_if_fail (unique_name != NULL, NULL); + g_return_val_if_fail (*unique_name != '\0', NULL); + + return _mc_profile_new (unique_name); +} + +/** + * mc_profile_lookup_default_for_vcard_field: + * @vcard_field: The vcard field. + * + * Get the profile whose vcard field is the one specified. + * The returned object's reference count is incremented. + * + * Return value: The #McProfile. + */ +McProfile * +mc_profile_lookup_default_for_vcard_field (const gchar *vcard_field) +{ + McProfile *ret = NULL; + GList *list, *tmp; + + g_return_val_if_fail (vcard_field != NULL, NULL); + g_return_val_if_fail (*vcard_field != '\0', NULL); + + list = mc_profiles_list (); + + for (tmp = list; + tmp != NULL; + tmp = tmp->next) + { + McProfile *cur = (McProfile *) tmp->data; + McProfilePrivate *priv = MC_PROFILE_PRIV (cur); + + /* free the profile if we've found the desired value, + * or we're unable to load this one */ + if (ret != NULL || + !_mc_profile_load (cur)) + { + mc_profile_free (cur); + continue; + } + + /* store the profile if it's the desired one, + * free it otherwise */ + if (priv->vcard_default && + 0 == strcmp (priv->vcard_field, vcard_field)) + { + ret = cur; + break; + } + else + { + mc_profile_free (cur); + } + } + + g_list_free (list); + + return ret; +} + +/** + * mc_profile_free: + * @id: The #McProfile. + * + * Frees (unref) the given profile. + */ +void +mc_profile_free (McProfile *id) +{ + g_object_unref (id); +} + +/** + * mc_profiles_list: + * + * Lists all configured profiles. + * + * Returns: a #GList of the profiles (must be freed with #mc_profiles_free_list). + */ +GList * +mc_profiles_list (void) +{ + GDir *dir; + GError *error = NULL; + GList *ret = NULL; + const gchar *filename; + + dir = g_dir_open(_mc_profile_path (), 0, &error); + if (!dir) + { + g_warning ("%s: unable to open directory %s: %s", + G_STRFUNC, _mc_profile_path (), error->message); + + g_error_free (error); + + return NULL; + } + + while ((filename = g_dir_read_name(dir)) != NULL) + { + gchar *unique_name; + McProfile *profile; + + if (!g_str_has_suffix (filename, PROFILE_SUFFIX)) + continue; + + unique_name = g_strndup (filename, strlen(filename) - PROFILE_SUFFIX_LEN); + profile = _mc_profile_new (unique_name); + g_free (unique_name); + + ret = g_list_prepend (ret, profile); + } + + g_dir_close (dir); + + return ret; +} + +/** + * mc_profiles_list_by_vcard_field: + * @vcard_field: The vcard field. + * + * Lists all configured profiles with the given vcard field.. + * + * Returns: a #GList of the profiles (must be freed with #mc_profiles_free_list). + */ +GList * +mc_profiles_list_by_vcard_field (const gchar *vcard_field) +{ + GList *all, *tmp, *ret; + + g_return_val_if_fail (vcard_field != NULL, NULL); + g_return_val_if_fail (*vcard_field != '\0', NULL); + + all = mc_profiles_list (); + ret = NULL; + + for (tmp = all; + tmp != NULL; + tmp = tmp->next) + { + McProfile *cur = (McProfile *) tmp->data; + McProfilePrivate *priv = MC_PROFILE_PRIV (cur); + + if (!_mc_profile_load (cur)) + { + mc_profile_free (cur); + continue; + } + + if (priv->vcard_field && 0 == strcmp (vcard_field, priv->vcard_field)) + { + ret = g_list_prepend (ret, cur); + } + else + { + mc_profile_free (cur); + } + } + + g_list_free (all); + + return ret; +} + +/** + * mc_profiles_free_list: + * @list: The #GList of #McProfile. + * + * Frees a list of profiles. + */ +void +mc_profiles_free_list (GList *list) +{ + GList *tmp; + + for (tmp = list; tmp != NULL; tmp = tmp->next) + { + McProfile *p = tmp->data; + mc_profile_free (p); + } + + g_list_free (list); +} + +/** + * mc_profile_get_unique_name: + * @id: The #McProfile. + * + * Get the unique name of the profile. + * + * Returns: a string representing the unique name (must not be freed). + */ +const gchar * +mc_profile_get_unique_name (McProfile *id) +{ + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + + return MC_PROFILE_PRIV(id)->unique_name; +} + +/** + * mc_profile_get_configuration_ui: + * @id: The #McProfile. + * + * Get the configuration ui of the profile. + * + * Returns: a string representing the configuration ui (must not be freed). + */ +const gchar * +mc_profile_get_configuration_ui (McProfile *id) +{ + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + + return MC_PROFILE_PRIV (id)->configuration_ui; +} + +/** + * mc_profile_get_display_name: + * @id: The #McProfile. + * + * Get the display name of the profile. + * + * Returns: a string representing the display name (must not be freed). + */ +const gchar * +mc_profile_get_display_name (McProfile *id) +{ + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + + return MC_PROFILE_PRIV (id)->display_name; +} + +/** + * mc_profile_get_icon_name: + * @id: The #McProfile. + * + * Get the icon name of the profile. + * + * Returns: a string representing the icon name (must not be freed). + */ +const gchar * +mc_profile_get_icon_name (McProfile *id) +{ + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + + return MC_PROFILE_PRIV (id)->icon_name; +} + +/** + * mc_profile_get_branding_icon_name: + * @id: The #McProfile. + * + * Get the branding icon name of the profile. + * + * Returns: a string representing the branding icon name (must not be freed). + */ +const gchar * +mc_profile_get_branding_icon_name (McProfile *id) +{ + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + + return MC_PROFILE_PRIV (id)->branding_icon_name; +} + +/** + * mc_profile_get_supported_presences: + * @id: The #McProfile. + * + * Checks what presence states are supported by this profile. + * + * Returns: a zero-terminated array listing all the supported #McPresence. + * It must not be freed. + */ +const McPresence * +mc_profile_get_supported_presences (McProfile *id) +{ + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + + return (McPresence *)(MC_PROFILE_PRIV (id)->supported_presences->data); +} + +/* + * mc_profile_supports_presence: + * @id: The #McProfile. + * @presence: The #McPresence. + * + * Tests whether the profile supports the presence @presence. + * + * Returns: a #gboolean. + */ +gboolean +mc_profile_supports_presence (McProfile *id, McPresence presence) +{ + const McPresence *presences; + + presences = mc_profile_get_supported_presences (id); + if (!presences) return FALSE; + + while (*presences) + { + if (*presences == presence) + return TRUE; + presences++; + } + return FALSE; +} + +/** + * mc_profile_get_protocol: + * @id: The #McProfile. + * + * gets the protocol in use for this profile. + * + * Returns: a #McProtocol, or NULL if some error occurs. + */ +McProtocol * +mc_profile_get_protocol (McProfile *id) +{ + McManager *manager; + McProtocol *protocol; + McProfilePrivate *priv = MC_PROFILE_PRIV (id); + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + + manager = mc_manager_lookup (priv->manager); + g_return_val_if_fail (manager != NULL, NULL); + + protocol = mc_protocol_lookup (manager, priv->protocol); + mc_manager_free (manager); + + g_return_val_if_fail (protocol != NULL, NULL); + return protocol; +} + +/** + * mc_profile_get_protocol_name: + * @id: The #McProfile. + * + * Get the protocol name of the profile. + * + * Returns: a string representing the protocol name (must not be freed). + */ +const gchar * +mc_profile_get_protocol_name (McProfile *id) +{ + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + + return MC_PROFILE_PRIV (id)->protocol; +} + +/** + * mc_profile_get_vcard_field: + * @id: The #McProfile. + * + * Get the vcard field of the profile. + * + * Returns: a string representing the vcard field (must not be freed). + */ +const gchar * +mc_profile_get_vcard_field (McProfile *id) +{ + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + + return MC_PROFILE_PRIV (id)->vcard_field; +} + +/** + * mc_profile_get_default_account_domain: + * @id: The #McProfile. + * + * Get the default account domain of the profile. + * + * Returns: a string representing the default account domain (must not be + * freed). + */ +const gchar * +mc_profile_get_default_account_domain (McProfile *id) +{ + McProfilePrivate *priv = MC_PROFILE_PRIV (id); + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + g_return_val_if_fail ( + priv->capabilities & MC_PROFILE_CAPABILITY_SPLIT_ACCOUNT, NULL); + + return priv->default_account_domain; +} + +/** + * mc_profile_is_default_for_vcard_field: + * @id: The #McProfile. + * + * Checks if this is the default profile for the given vcard field. + * + * Returns: a gboolean. + */ +gboolean +mc_profile_is_default_for_vcard_field (McProfile *id) +{ + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, FALSE); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, FALSE); + + return MC_PROFILE_PRIV (id)->vcard_default; +} + +/** + * mc_profile_get_capabilities: + * @id: The #McProfile. + * + * Gets the capabilities of this profile. + * + * Returns: a combination of #McProfileCapabilityFlags. + */ +McProfileCapabilityFlags +mc_profile_get_capabilities (McProfile *id) +{ + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, 0); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, 0); + + return MC_PROFILE_PRIV (id)->capabilities; +} + +/** + * mc_profile_get_default_setting: + * @id: The #McProfile. + * @setting: The setting for which default value has to be retrieved. + * + * Get the default value of a setting of the profile. + * + * Returns: a string representing the default setting (must not be freed). + */ +const gchar * +mc_profile_get_default_setting (McProfile *id, const gchar *setting) +{ + McProtocol *proto; + GSList *params, *tmp; + McProtocolParam *param; + const gchar *def; + gboolean profile_loaded; + + g_return_val_if_fail (id != NULL, NULL); + g_return_val_if_fail (setting != NULL, NULL); + g_return_val_if_fail (*setting != '\0', NULL); + + profile_loaded = _mc_profile_load (id); + g_return_val_if_fail (profile_loaded, NULL); + + def = (const gchar *) g_hash_table_lookup (MC_PROFILE_PRIV (id)->default_settings, setting); + + if (def) + { + return def; + } + + proto = mc_profile_get_protocol (id); + params = mc_protocol_get_params (proto); + + for (tmp = params; tmp != NULL; tmp = tmp->next) + { + param = (McProtocolParam *) tmp->data; + + if ((NULL != param) && (NULL != param->name) + && (0 == strcmp(param->name, setting))) + { + def = param->def; + } + } + + mc_protocol_free_params_list (params); + return def; +} diff --git a/libmissioncontrol/mc-profile.h b/libmissioncontrol/mc-profile.h new file mode 100644 index 00000000..becdef65 --- /dev/null +++ b/libmissioncontrol/mc-profile.h @@ -0,0 +1,119 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MC_PROFILE_H__ +#define __MC_PROFILE_H__ + +#include <glib.h> +#include <glib-object.h> + +#include <libmissioncontrol/mc-protocol.h> + +G_BEGIN_DECLS + +typedef enum +{ + MC_PROFILE_CAPABILITY_NONE = 0, + MC_PROFILE_CAPABILITY_CHAT_P2P = 1 << 0, + MC_PROFILE_CAPABILITY_CHAT_ROOM = 1 << 1, + MC_PROFILE_CAPABILITY_CHAT_ROOM_LIST = 1 << 2, + MC_PROFILE_CAPABILITY_VOICE_P2P = 1 << 3, + MC_PROFILE_CAPABILITY_CONTACT_SEARCH = 1 << 4, + MC_PROFILE_CAPABILITY_SPLIT_ACCOUNT = 1 << 5, + MC_PROFILE_CAPABILITY_REGISTRATION_UI = 1 << 6, + MC_PROFILE_CAPABILITY_SUPPORTS_AVATARS = 1 << 7, +} McProfileCapabilityFlags; + +typedef struct { + GObject parent; + gpointer priv; +} McProfile; + +#define MC_TYPE_PROFILE mc_profile_get_type() + +#define MC_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MC_TYPE_PROFILE, McProfile)) + +#define MC_PROFILE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MC_TYPE_PROFILE, McProfilewClass)) + +#define MC_IS_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MC_TYPE_PROFILE)) + +#define MC_IS_PROFILE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MC_TYPE_PROFILE)) + +#define MC_PROFILE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MC_TYPE_PROFILE, McProfilewClass)) + +typedef struct { + GObjectClass parent_class; +} McProfileClass; + +#include <libmissioncontrol/mission-control.h> + +GType mc_profile_get_type (void); + +McProfile* mc_profile_new (const gchar *unique_name); + +/* to find one profile */ +McProfile *mc_profile_lookup (const gchar *unique_name); +McProfile *mc_profile_lookup_default_for_vcard_field (const gchar *vcard_field); +void mc_profile_free (McProfile *id); +void mc_profile_clear_cache (void); + +/* to find many profiles */ +GList *mc_profiles_list (void); +GList *mc_profiles_list_by_vcard_field (const gchar *vcard_field); +void mc_profiles_free_list (GList *list); + +const gchar *mc_profile_get_unique_name (McProfile *id); +const gchar *mc_profile_get_configuration_ui (McProfile *id); +const gchar *mc_profile_get_display_name (McProfile *id); +const gchar *mc_profile_get_icon_name (McProfile *id); +const gchar *mc_profile_get_branding_icon_name (McProfile *id); +const gchar *mc_profile_get_vcard_field (McProfile *id); +const gchar *mc_profile_get_default_account_domain (McProfile *id); +const McPresence *mc_profile_get_supported_presences (McProfile *id); +gboolean mc_profile_supports_presence (McProfile *id, McPresence presence); +McProtocol *mc_profile_get_protocol (McProfile *id); + +/* only use this protocol name instead of the real McProfile if you do + * not care about being able to discover the correct connection manager + * and hence which options are valid when connecting a certain account. + * without the manager name also, the protocol name is not sufficient + * to look up an McProfile. this is intentional. */ +const gchar *mc_profile_get_protocol_name (McProfile *id); + +gboolean mc_profile_is_default_for_vcard_field (McProfile *id); +McProfileCapabilityFlags mc_profile_get_capabilities (McProfile *id); +const gchar *mc_profile_get_default_setting (McProfile *id, const gchar *setting); + +G_END_DECLS + +#endif /* __MC_PROFILE_H__ */ diff --git a/libmissioncontrol/mc-protocol-priv.h b/libmissioncontrol/mc-protocol-priv.h new file mode 100644 index 00000000..9be7739d --- /dev/null +++ b/libmissioncontrol/mc-protocol-priv.h @@ -0,0 +1,32 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MC_PROTOCOL_PRIV_H__ +#define __MC_PROTOCOL_PRIV_H__ + +McProtocol * _mc_protocol_from_keyfile ( + GKeyFile *keyfile, const gchar *manager_name, const gchar *group_name, + const gchar *unique_name); + +#endif /* __MC_PROTOCOL_PRIV_H__ */ + diff --git a/libmissioncontrol/mc-protocol.c b/libmissioncontrol/mc-protocol.c new file mode 100644 index 00000000..e6e79f71 --- /dev/null +++ b/libmissioncontrol/mc-protocol.c @@ -0,0 +1,367 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#define DBUS_API_SUBJECT_TO_CHANGE 1 + +#include <dbus/dbus.h> +#include <glib.h> +#include <string.h> + +#include "mc-manager.h" +#include "mc-manager-priv.h" + +#include "mc-protocol.h" +#include "mc-protocol-priv.h" + +#define MC_PROTOCOL_PRIV(protocol) ((McProtocolPrivate *)protocol->priv) + +G_DEFINE_TYPE (McProtocol, mc_protocol, G_TYPE_OBJECT); + +typedef struct +{ + gchar *manager; + gchar *name; + GSList *params; +} McProtocolPrivate; + +static void +mc_protocol_init (McProtocol *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, + MC_TYPE_PROTOCOL, McProtocolPrivate); +} + +static McProtocol * +_mc_protocol_new (const gchar *manager, const gchar *name, GSList *params) +{ + McProtocol *new = g_object_new (MC_TYPE_PROTOCOL, NULL); + McProtocolPrivate *priv = MC_PROTOCOL_PRIV (new); + + priv->manager = g_strdup (manager); + priv->name = g_strdup (name); + priv->params = params; + return new; +} + +static void +mc_protocol_finalize (GObject *object) +{ + McProtocol *protocol = MC_PROTOCOL (object); + McProtocolPrivate *priv = MC_PROTOCOL_PRIV (protocol); + McProtocolParam *param; + GSList *i; + + g_free (priv->manager); + g_free (priv->name); + + for (i = priv->params; NULL != i; i = i->next) + { + param = (McProtocolParam *) i->data; + + g_free (i->data); + } + + g_slist_free (priv->params); +} + +static void +mc_protocol_class_init (McProtocolClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (object_class, sizeof (McProtocolPrivate)); + object_class->finalize = mc_protocol_finalize; +} + +/** + * mc_protocol_lookup: + * @id: The #McManager. + * @protocol: The protocol name. + * + * Looks up the protocol having the given name in the manager's supported + * protocols. The returned object's reference count is incremented. + * + * Returns: the #McProtocol, or NULL if not found. + */ +McProtocol * +mc_protocol_lookup (McManager *manager, const gchar *protocol) +{ + return _mc_manager_protocol_lookup (manager, protocol); +} + +/** + * mc_protocol_free: + * @id: The #McProtocol. + * + * Frees (unrefs) the protocol. + */ +void +mc_protocol_free (McProtocol *id) +{ + g_return_if_fail (id != NULL); + + g_object_unref (id); +} + +/** + * mc_protocols_list: + * + * Lists all supported protocols. <emphasis>This currently lists all protocols + * supported by the "gabble" manager</emphasis>. + * + * Returns: a #GList of #McProtocol, to be freed with #mc_protocols_free_list. + */ +GList * +mc_protocols_list (void) +{ + return mc_protocols_list_by_manager (mc_manager_lookup ("gabble")); +} + +/** + * mc_protocols_list_by_manager: + * @id: a #McManager. + * + * Lists all protocols supported by the given manager. + * + * Returns: a #GList of #McProtocol, to be freed with #mc_protocols_free_list. + */ +GList * +mc_protocols_list_by_manager (McManager *id) +{ + return g_list_prepend (NULL, mc_protocol_lookup (id, "jabber")); +} + +/** + * mc_protocols_free_list: + * @list: a #GList of #McProtocol. + * + * Frees a list of protocols. + */ +void +mc_protocols_free_list (GList *list) +{ + GList *tmp; + + for (tmp = list; tmp != NULL; tmp = tmp->next) + mc_protocol_free ((McProtocol *) tmp->data); + + g_list_free (list); +} + +/** + * mc_protocol_get_manager: + * @id: The #McProtocol. + * + * Gets the manager for this protocol. + * + * Returns: the #McManager, or NULL if some error occurred. + */ +McManager * +mc_protocol_get_manager (McProtocol *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return mc_manager_lookup (MC_PROTOCOL_PRIV (id)->manager); +} + +/** + * mc_protocol_get_name: + * @id: The #McProtocol. + * + * Gets the name of this protocol. + * + * Returns: a string representing the name (not to be freed) + */ +const gchar * +mc_protocol_get_name (McProtocol *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return MC_PROTOCOL_PRIV (id)->name; +} + +/** + * mc_protocol_get_params: + * @protocol: The #McProtocol. + * + * Gets the parameters for this protocol. + * + * Returns: a #GList of #McProtocolParam, to be freed with + * #mc_protocol_free_params_list. + */ +GSList * +mc_protocol_get_params (McProtocol *protocol) +{ + McProtocolPrivate *priv = MC_PROTOCOL_PRIV (protocol); + + return g_slist_copy (priv->params); +} + +/** + * mc_protocol_free_params_list: + * @list: The #GList. + * + * Frees a list of #McProtocolParam. + */ +void +mc_protocol_free_params_list (GSList *list) +{ + g_slist_free (list); +} + +static McProtocolParam * +_parse_parameter (const gchar *name, const gchar *s) +{ + McProtocolParam *new; + gchar **bits; + gchar **i; + + bits = g_strsplit (s, " ", 0); + + if (NULL == *bits) + { + g_debug ("%s: param \"%s\" has no signature", G_STRFUNC, name); + return NULL; + } + + if (1 != strlen (*bits)) + { + g_debug ("%s: param \"%s\" has invalid signature", G_STRFUNC, name); + return NULL; + } + + new = g_new0 (McProtocolParam, 1); + new->name = g_strdup (name); + new->signature = g_strdup (*bits); + new->def = NULL; + + for (i = bits + 1; NULL != *i; i++) + { + if (0 == strcmp (*i, "required")) + new->flags |= MC_PROTOCOL_PARAM_REQUIRED; + else if (0 == strcmp (*i, "register")) + new->flags |= MC_PROTOCOL_PARAM_REGISTER; + else + g_debug ("%s: unrecognised parameter flag \"%s\"", G_STRFUNC, *i); + } + + g_strfreev (bits); + return new; +} + +#define PREFIX_PARAM "param-" +#define PREFIX_PARAM_LEN 6 +#define PREFIX_DEFAULT "default-" +#define PREFIX_DEFAULT_LEN 8 + +static gint +find_param_by_name_func(gconstpointer a, gconstpointer b) +{ + McProtocolParam *param = (McProtocolParam *) a; + const gchar *name = (const gchar *) b; + + return !((NULL != param->name) && (NULL != name) + && (0 == strcmp(param->name, name))); +} + +McProtocol * +_mc_protocol_from_keyfile (GKeyFile *keyfile, const gchar *manager_name, + const gchar *group_name, const gchar *name) +{ + GSList *params = NULL; + gchar **keys; + gchar **i; + + g_assert (name); + keys = g_key_file_get_keys (keyfile, group_name, NULL, NULL); + + if (!keys) + { + g_debug ("%s: failed to get keys from file", G_STRFUNC); + return NULL; + } + + for (i = keys; NULL != *i; i++) + { + McProtocolParam *param; + const gchar *name = *i; + gchar *value = g_key_file_get_string (keyfile, group_name, *i, NULL); + + if (0 == strncmp (*i, PREFIX_PARAM, PREFIX_PARAM_LEN)) + { + name += PREFIX_PARAM_LEN; + param = _parse_parameter (name, value); + + if (param) + params = g_slist_prepend (params, param); + } + else if (0 == strncmp (*i, PREFIX_DEFAULT, PREFIX_DEFAULT_LEN)) + { + GSList *node; + + name += PREFIX_DEFAULT_LEN; + node = g_slist_find_custom (params, name, find_param_by_name_func); + + if (node) + { + param = (McProtocolParam *) node->data; + + if (!param->def) + param->def = g_strdup(value); + else + g_warning("%s: encountered multiple default values for parameter \"%s\"", G_STRFUNC, name); + } + } + else + { + g_debug ("%s: unrecognised protocol key \"%s\"", G_STRFUNC, *i); + } + + g_free (value); + } + + g_strfreev (keys); + + return _mc_protocol_new (manager_name, name, params); +} + +/** + * mc_protocol_print: + * @protocol: the #McProtocol. + * + * Prints the protocol name and all protocol parameters via #g_print. + */ +void +mc_protocol_print (McProtocol *protocol) +{ + GSList *i; + + g_print ("protocol: %s\n", mc_protocol_get_name (protocol)); + + for (i = mc_protocol_get_params (protocol); NULL != i; i = i->next) + { + McProtocolParam *param = (McProtocolParam *) i->data; + + g_print (" %s:%s:%s\n", param->signature, param->name, param->def); + } +} + diff --git a/libmissioncontrol/mc-protocol.h b/libmissioncontrol/mc-protocol.h new file mode 100644 index 00000000..339f91d3 --- /dev/null +++ b/libmissioncontrol/mc-protocol.h @@ -0,0 +1,103 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MC_PROTOCOL_H__ +#define __MC_PROTOCOL_H__ + +#include <glib.h> +#include <glib-object.h> + +#include <libmissioncontrol/mc-manager.h> + +G_BEGIN_DECLS + +#define MC_TYPE_PROTOCOL mc_protocol_get_type() + +#define MC_PROTOCOL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MC_TYPE_PROTOCOL, McProtocol)) + +#define MC_PROTOCOL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MC_TYPE_PROTOCOL, McProtocolClass)) + +#define MC_IS_PROTOCOL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MC_TYPE_PROTOCOL)) + +#define MC_IS_PROTOCOL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MC_TYPE_PROTOCOL)) + +#define MC_PROTOCOL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MC_TYPE_PROTOCOL, McProtocolClass)) + +GType mc_protocol_get_type (void); + +typedef struct { + GObject parent; + gpointer priv; +} McProtocol; + +typedef struct { + GObjectClass parent_class; +} McProtocolClass; + +/* protocols are only unique within the context of a particular manager */ +McProtocol *mc_protocol_lookup (McManager *id, const gchar *protocol); +void mc_protocol_free (McProtocol *id); + +GList *mc_protocols_list (void); +GList *mc_protocols_list_by_manager (McManager *id); +void mc_protocols_free_list (GList *list); + +McManager *mc_protocol_get_manager (McProtocol *id); +const gchar *mc_protocol_get_name (McProtocol *id); + +enum +{ + MC_PROTOCOL_PARAM_REQUIRED = 1 << 0, + MC_PROTOCOL_PARAM_REGISTER = 1 << 1 +}; + +/* return type for params */ +typedef struct +{ + const gchar *name; + const gchar *signature; + const gchar *def; + guint flags; +} McProtocolParam; + +/* Returns list of McProtocolParam. */ +GSList *mc_protocol_get_params (McProtocol *protocol); + +/* Frees the lists above and all data */ +void mc_protocol_free_params_list (GSList *list); + +void mc_protocol_print (McProtocol *protocol); + +G_END_DECLS + +#endif /* __MC_PROTOCOL_H__ */ diff --git a/libmissioncontrol/mc.c b/libmissioncontrol/mc.c new file mode 100644 index 00000000..f78a44e8 --- /dev/null +++ b/libmissioncontrol/mc.c @@ -0,0 +1,50 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <gmodule.h> + +#include "mc.h" + +#define LIBRARY_FILE G_STRINGIFY(LIBDIR) "/libmissioncontrol-config.so." G_STRINGIFY(LIBVERSION) + +/** + * mc_make_resident: + * + * This function is a workaround for problems with mc getting loaded twice + * into the same process, such as when the control panel loads a plugin which + * uses mc after it has already been loaded and unloaded. In order to + * prevent g_type_register_static being called twice, this function can be + * called to make mc be redident in memory for the lifetime of the process. + */ + +void +mc_make_resident (void) +{ + GModule *module = g_module_open (LIBRARY_FILE, 0); + if (NULL == module) + { + g_critical("%s: g_module_open() failed: %s", G_STRFUNC, g_module_error()); + } + g_module_make_resident (module); +} + diff --git a/libmissioncontrol/mc.h b/libmissioncontrol/mc.h new file mode 100644 index 00000000..0dc16017 --- /dev/null +++ b/libmissioncontrol/mc.h @@ -0,0 +1,29 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +G_BEGIN_DECLS + +void mc_make_resident (void); + +G_END_DECLS + diff --git a/libmissioncontrol/mission-control-signals-marshal.list b/libmissioncontrol/mission-control-signals-marshal.list new file mode 100644 index 00000000..07acaf90 --- /dev/null +++ b/libmissioncontrol/mission-control-signals-marshal.list @@ -0,0 +1,5 @@ +VOID:UINT,UINT,UINT,STRING +VOID:STRING,UINT +VOID:UINT,UINT +VOID:UINT,STRING,UINT +VOID:UINT diff --git a/libmissioncontrol/mission-control.c b/libmissioncontrol/mission-control.c new file mode 100644 index 00000000..5639f84e --- /dev/null +++ b/libmissioncontrol/mission-control.c @@ -0,0 +1,1110 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "mc-client-lib-gen.h" +#include "mission-control.h" +#include "mission-control-signals-marshal.h" +#include <glib.h> +#include <string.h> + +static void _handle_mcd_errors (DBusGProxy * missioncontrol, guint serial, + gchar *client_id, guint reason, + gpointer userdata); + +static gboolean check_for_accounts (MissionControl * self); + +static GObjectClass *parent_class = NULL; +static guint operation_id; /* A simple counter for execution order tracking; + must be global per process */ +static GList *instances = NULL; +static DBusConnection *dbus_connection = NULL; +static gboolean mc_is_running = FALSE; + +/* Signals */ + +enum +{ + ERROR, + SERVICE_ENDED, + LAST_SIGNAL +}; + +static guint libmc_signals[LAST_SIGNAL] = { 0 }; + +struct dbus_cb_data { + McCallback callback; + gpointer user_data; +}; + +struct get_current_status_cb_data { + McGetCurrentStatusCallback callback; + gpointer user_data; +}; + +#define INVOKE_CALLBACK(mc, callback, data, code, ...) \ + if (callback) { \ + GError *error = NULL; \ + error = g_error_new (MC_ERROR, code, __VA_ARGS__); \ + callback (mc, error, data); \ + } + +static void +dbus_async_cb (DBusGProxy * proxy, GError * error, gpointer userdata) +{ + struct dbus_cb_data *cb_data = (struct dbus_cb_data *)userdata; + + if (error) + g_debug ("%s: Error: %s (%u)", G_STRFUNC, error->message, error->code); + + if (cb_data->callback) + cb_data->callback ((MissionControl *)proxy, error, cb_data->user_data); + else + { + /* there's callback to free the error; we must do that ourselves */ + if (error) + g_error_free (error); + } + + g_free (cb_data); +} + +static DBusHandlerResult +dbus_filter_func (DBusConnection *connection, + DBusMessage *message, + gpointer data) +{ + DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_signal (message, + "org.freedesktop.DBus", + "NameOwnerChanged")) { + const gchar *name = NULL; + const gchar *prev_owner = NULL; + const gchar *new_owner = NULL; + DBusError error = {0}; + + dbus_error_init (&error); + + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_STRING, + &name, + DBUS_TYPE_STRING, + &prev_owner, + DBUS_TYPE_STRING, + &new_owner, + DBUS_TYPE_INVALID)) { + + g_debug ("error: %s", error.message); + dbus_error_free (&error); + + return result; + } + + if (name && + strcmp (name, MISSION_CONTROL_SERVICE) == 0) + { + if (prev_owner && prev_owner[0] != '\0') + { + GList *list; + + for (list = instances; list; list = list->next) + g_signal_emit (list->data, libmc_signals[SERVICE_ENDED], 0); + } + mc_is_running = new_owner && (new_owner[0] != '\0'); + } + } + + return result; +} + +GQuark mission_control_error_quark (void) +{ + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string ("mission-control-quark"); + return quark; +} + +static void instance_finalized (gpointer data, GObject *object) +{ + instances = g_list_remove (instances, object); + if (!instances) + { + dbus_connection_remove_filter (dbus_connection, dbus_filter_func, + NULL); + dbus_connection_unref (dbus_connection); + dbus_connection = NULL; + } +} + +static void initialize_dbus_filter (DBusGConnection *connection) +{ + DBusError error; + + /* Add a filter to detect the service exits and emit the ServiceEnded + * signal accordingly */ + dbus_connection = dbus_g_connection_get_connection (connection); + dbus_connection_ref (dbus_connection); + dbus_error_init (&error); + dbus_connection_add_filter (dbus_connection, + dbus_filter_func, + NULL, NULL); + dbus_bus_add_match (dbus_connection, + "type='signal'," "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'", &error); + if (dbus_error_is_set (&error)) + { + g_warning ("Match rule adding failed"); + dbus_error_free (&error); + } + + mc_is_running = dbus_bus_name_has_owner (dbus_connection, + MISSION_CONTROL_SERVICE, NULL); +} + +static void +_missioncontrol_register_signal_marshallers (void) +{ + /* Register the AccountStatusChanged signal */ + dbus_g_object_register_marshaller + (mission_control_signals_marshal_VOID__UINT_UINT_UINT_STRING, + G_TYPE_NONE, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING, + G_TYPE_INVALID); + /* Register the error signal */ + dbus_g_object_register_marshaller + (mission_control_signals_marshal_VOID__UINT_STRING_UINT, G_TYPE_NONE, + G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID); + /* Register the PresenceStatusRequested/Actual signal */ + dbus_g_object_register_marshaller + (mission_control_signals_marshal_VOID__UINT, G_TYPE_NONE, G_TYPE_UINT, + G_TYPE_INVALID); + /* Register the StatusActual signal */ + dbus_g_object_register_marshaller + (mission_control_signals_marshal_VOID__UINT_UINT, G_TYPE_NONE, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID); + /* Register the UsedChannelsCountChanged signal */ + dbus_g_object_register_marshaller + (mission_control_signals_marshal_VOID__STRING_UINT, G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID); +} + +static void +mission_control_init (GTypeInstance * instance, gpointer g_class) +{ + MissionControl *self = MISSIONCONTROL (instance); + self->first_run = TRUE; +} + + +static void +mission_control_dispose (GObject * obj) +{ + MissionControl *self = MISSIONCONTROL (obj); + + if (self->first_run) + { + self->first_run = FALSE; + } + + if (G_OBJECT_CLASS (parent_class)->dispose) + { + G_OBJECT_CLASS (parent_class)->dispose (obj); + } +} + + +static void +mission_control_class_init (MissionControlClass * klass) +{ + GObjectClass *obj = G_OBJECT_CLASS (klass); + parent_class = g_type_class_peek_parent (klass); + + obj->set_property = parent_class->set_property; + obj->get_property = parent_class->get_property; + obj->dispose = mission_control_dispose; + _missioncontrol_register_signal_marshallers (); + + /** + * MissionControl::Error: + * @self: The #MissionControl object. + * @operation_id: The unique ID of the operation which caused the error. + * When this signal is emitted to report a failure in handling a channel, + * this parameter holds the same @operation_id returned by the channel + * request function call. + * @error_code: The #MCError code describing the error. + * + * This signal is emitted when an error is raised from the mission-control + * server. This is not raised in response to some API call failing (they + * already provide a way to report errors), but rather for informing the + * client of some unexpected event, such as a channel handler failing. + */ + libmc_signals[ERROR] = + g_signal_new ("Error", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + mission_control_signals_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + + /** + * MissionControl::ServiceEnded: + * @self: The #MissionControl object + * + * This signal is emitted when a mission-control server process has exited. + */ + libmc_signals[SERVICE_ENDED] = + g_signal_new ("ServiceEnded", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + + +GType +mission_control_get_type (void) +{ + static GType type = 0; + if (type == 0) + { + static const GTypeInfo info = { + sizeof (MissionControlClass), + NULL, + NULL, + (GClassInitFunc) mission_control_class_init, + NULL, + NULL, + sizeof (MissionControl), + 0, + (GInstanceInitFunc) mission_control_init + }; + type = g_type_register_static (DBUS_TYPE_G_PROXY, + "MissionControl", &info, 0); + } + return type; +} + + +/** + * mission_control_new: + * @connection: The D-BUS connection for this object + * + * Creates a new Mission Control client library object. + * + * Return value: A new mc (Mission Control) library object, or NULL if unsuccesful + */ +MissionControl * +mission_control_new (DBusGConnection * connection) +{ + g_return_val_if_fail (connection != NULL, NULL); + MissionControl *mc_obj = NULL; + + /* Create the proxy object that is used for performing + * the method calls on the Mission Control service */ + + mc_obj = g_object_new (MISSIONCONTROL_TYPE, + "name", MISSION_CONTROL_SERVICE, + "path", MISSION_CONTROL_PATH, + "interface", MISSION_CONTROL_IFACE, + "connection", connection, NULL); + if (!instances) + { + /* this is the first instance created in this process: + * perform some global initializations */ + initialize_dbus_filter (connection); + } + /* Add the object to the list of living MC instances, and add a watch for + * its finalization */ + instances = g_list_prepend (instances, mc_obj); + g_object_weak_ref (G_OBJECT (mc_obj), instance_finalized, NULL); + + dbus_g_proxy_add_signal (DBUS_G_PROXY (mc_obj), "AccountStatusChanged", + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, + G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_add_signal (DBUS_G_PROXY (mc_obj), "McdError", G_TYPE_UINT, + G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (DBUS_G_PROXY (mc_obj), "McdError", + G_CALLBACK (_handle_mcd_errors), mc_obj, NULL); + dbus_g_proxy_add_signal (DBUS_G_PROXY (mc_obj), "PresenceStatusRequested", + G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_add_signal (DBUS_G_PROXY (mc_obj), "PresenceStatusActual", + G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_add_signal (DBUS_G_PROXY (mc_obj), "UsedChannelsCountChanged", + G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_add_signal (DBUS_G_PROXY (mc_obj), "StatusActual", + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID); + return mc_obj; +} + + +/** + * mission_control_set_presence: + * @self: The #MissionControl object. + * @presence: Integer specifying the presence status code + * @message: Optional presence associated message + * @callback: a #McCallback function to be notified about any errors + * @user_data: data to be passed to the @callback function + * + * Sets presence for the accounts. + */ +void +mission_control_set_presence (MissionControl * self, + McPresence presence, const gchar * message, + McCallback callback, gpointer user_data) +{ + struct dbus_cb_data *cb_data; + + /* Check whether we have any accounts to set presence for */ + if (!check_for_accounts (self)) + { + INVOKE_CALLBACK (self, callback, user_data, MC_NO_ACCOUNTS_ERROR, " "); + return; + } + + cb_data = g_malloc (sizeof (struct dbus_cb_data)); + g_assert (cb_data != NULL); + cb_data->callback = callback; + cb_data->user_data = user_data; + mission_control_dbus_set_presence_async (DBUS_G_PROXY (self), + (gint) presence, message, + dbus_async_cb, cb_data); +} + +/** + * mission_control_get_presence: + * @self: The #MissionControl object. + * @error: address where an error can be returned, or NULL. + * + * Gets the currently requested presence status. + * + * Return value: The currently requested presence status + */ +McPresence +mission_control_get_presence (MissionControl * self, GError **error) +{ + /* To indicate failure, we set the presence to unset */ + McPresence presence = MC_PRESENCE_UNSET; + + /* Check whether Mission Control is running; if not, it's safe to + * say that we're offline without starting it to perform the + * query. */ + if (!mc_is_running) + { + g_debug ("%s: MC not running.", G_STRFUNC); + g_set_error (error, MC_ERROR, MC_DISCONNECTED_ERROR, "MC not running"); + return MC_PRESENCE_OFFLINE; + } + + if (!mission_control_dbus_get_presence (DBUS_G_PROXY (self), + &presence, error)) + { + presence = MC_PRESENCE_UNSET; + } + + return presence; +} + +/** + * mission_control_get_presence_actual: + * @self: The #MissionControl object. + * @error: address where an error can be returned, or NULL. + * + * Gets the actual presence status. + * + * Return value: The actual presence status + */ +McPresence +mission_control_get_presence_actual (MissionControl * self, GError **error) +{ + /* To indicate failure, we set the presence to unset */ + McPresence presence = MC_PRESENCE_UNSET; + + /* Check whether Mission Control is running; if not, it's safe to + * say that we're offline without starting it to perform the + * query. */ + if (!mc_is_running) + { + g_debug ("%s: MC not running.", G_STRFUNC); + g_set_error (error, MC_ERROR, MC_DISCONNECTED_ERROR, "MC not running"); + return MC_PRESENCE_OFFLINE; + } + + if (!mission_control_dbus_get_presence_actual (DBUS_G_PROXY (self), + &presence, error)) + { + presence = MC_PRESENCE_UNSET; + } + + return presence; +} + +/** + * mission_control_request_channel: + * @self: The #MissionControl object. + * @account: The account which will join a new channel or request + * joining to an existing channel + * @type: a D-Bus interface name representing base channel type + * @handle: The handle we want to initiate the communication with + * @handle_type: The type of the handle we are initiating the + * communication with. See #TelepathyHandleType + * @callback: a #McCallback function to be notified about any errors + * @user_data: data to be passed to the @callback function + * + * Requests creation of a new channel, or join to an existing channel. + * + * Return value: An operation ID which can be used to cancel the request using + * #mission_control_cancel_channel_request. + */ +guint +mission_control_request_channel (MissionControl * self, + McAccount * account, + const gchar * type, + guint handle, + TelepathyHandleType handle_type, + McCallback callback, + gpointer user_data) +{ + struct dbus_cb_data *cb_data; + const gchar *account_name = mc_account_get_unique_name (account); + operation_id++; + + if (account_name == NULL) + { + INVOKE_CALLBACK (self, callback, user_data, MC_INVALID_ACCOUNT_ERROR, + " "); + return operation_id; + } + + /* Check whether we have any accounts to request channel for */ + if (!check_for_accounts (self)) + { + INVOKE_CALLBACK (self, callback, user_data, MC_NO_ACCOUNTS_ERROR, " "); + return operation_id; + } + + cb_data = g_malloc (sizeof (struct dbus_cb_data)); + g_assert (cb_data != NULL); + cb_data->callback = callback; + cb_data->user_data = user_data; + mission_control_dbus_request_channel_async (DBUS_G_PROXY (self), + account_name, + type, handle, handle_type, + operation_id, + dbus_async_cb, + cb_data); + + return operation_id; +} + + +/** + * mission_control_request_channel_with_string_handle: + * @self: The #MissionControl object. + * @account: The account which will join a new channel or request joining to an + * existing channel + * @type: a D-Bus interface name representing base channel type + * @handle: The handle we want to initiate the communication with + * @handle_type: The type of the handle we are initiating the communication + * with. See #TelepathyHandleType + * @callback: a #McCallback function to be notified about any errors + * @user_data: data to be passed to the @callback function + * + * Requests creation of a new channel, or join to an existing channel. Differs + * from the plain #mission_control_request_channel by taking handles as + * strings, which will be resolved to integers by MC. + * + * Return value: An operation ID which can be used to cancel the request using + * #mission_control_cancel_channel_request. + */ +guint +mission_control_request_channel_with_string_handle (MissionControl * self, + McAccount * account, + const gchar * type, + const gchar * handle, + TelepathyHandleType + handle_type, + McCallback callback, + gpointer user_data) +{ + struct dbus_cb_data *cb_data; + operation_id++; + const gchar *account_name = mc_account_get_unique_name (account); + + if (account_name == NULL) + { + INVOKE_CALLBACK (self, callback, user_data, MC_INVALID_ACCOUNT_ERROR, + " "); + return operation_id; + } + + /* Check whether we have any accounts to request channel for */ + if (!check_for_accounts (self)) + { + INVOKE_CALLBACK (self, callback, user_data, MC_NO_ACCOUNTS_ERROR, " "); + return operation_id; + } + + cb_data = g_malloc (sizeof (struct dbus_cb_data)); + g_assert (cb_data != NULL); + cb_data->callback = callback; + cb_data->user_data = user_data; + mission_control_dbus_request_channel_with_string_handle_async + (DBUS_G_PROXY (self), account_name, type, handle, handle_type, + operation_id, + dbus_async_cb, cb_data); + + return operation_id; +} + +/** + * mission_control_cancel_channel_request: + * @self: The #MissionControl object. + * @operation_id: the operation id of the request to cancel, as returned + * by #mission_control_request_channel_with_string_handle. + * @error: address where an error can be returned, or NULL. + * + * Cancel a channel request; a process can only cancel the requests that were + * originated by itself. + * + * Returns: %TRUE on success, %FALSE otherwise. + */ +gboolean +mission_control_cancel_channel_request (MissionControl *self, + guint operation_id, + GError **error) +{ + return mission_control_dbus_cancel_channel_request (DBUS_G_PROXY (self), + operation_id, + error); +} + +/** + * mission_control_connect_all_with_default_presence: + * @self: The #MissionControl object. + * @callback: a #McCallback function to be notified about any errors + * @user_data: data to be passed to the @callback function + * + * Connect all accounts using default presence, + * or HIDDEN if default presence is OFFLINE. + * If accounts are already connected do nothing. + */ +void +mission_control_connect_all_with_default_presence (MissionControl * self, + McCallback callback, + gpointer user_data) +{ + struct dbus_cb_data *cb_data; + + /* Check whether we have any accounts to set presence for */ + if (!check_for_accounts (self)) + { + INVOKE_CALLBACK (self, callback, user_data, MC_NO_ACCOUNTS_ERROR, + " "); + return; + } + + cb_data = g_malloc (sizeof (struct dbus_cb_data)); + g_assert (cb_data != NULL); + cb_data->callback = callback; + cb_data->user_data = user_data; + mission_control_dbus_connect_all_with_default_presence_async + (DBUS_G_PROXY (self), + dbus_async_cb, cb_data); +} + +/** + * mission_control_get_connection_status: + * @self: The #MissionControl object. + * @account: The account whose connection status is inspected + * @error: address where an error can be returned, or NULL. + * + * Request a status code describing the status of the connection that the + * provided account currently uses. + * + * Return value: A status code describing the status of the specified connection + * eg. CONNECTED = 0, CONNECTING = 1, DISCONNECTED = 2 + */ +guint +mission_control_get_connection_status (MissionControl * self, + McAccount * account, + GError **error) +{ + /* XXX TP_CONN_STATUS_DISCONNECTED is used as an UNKNOWN status is not + * available */ + guint conn_status = TP_CONN_STATUS_DISCONNECTED; + const gchar *account_name = mc_account_get_unique_name (account); + + if (account_name == NULL) + { + g_set_error (error, MC_ERROR, MC_INVALID_ACCOUNT_ERROR, " "); + return conn_status; + } + + /* Check whether we have any accounts to connection status for */ + if (!check_for_accounts (self)) + { + g_set_error (error, MC_ERROR, MC_NO_ACCOUNTS_ERROR, " "); + return conn_status; + } + + /* Check whether Mission Control is running; if not, it's safe to + * say that we're offline. */ + if (!mc_is_running) + { + g_debug ("%s: MC not running.", G_STRFUNC); + g_set_error (error, MC_ERROR, MC_DISCONNECTED_ERROR, "MC not running"); + return TP_CONN_STATUS_DISCONNECTED; + } + + mission_control_dbus_get_connection_status (DBUS_G_PROXY (self), + account_name, + &conn_status, error); + return conn_status; +} + +/** + * mission_control_get_online_connections: + * @self: The #MissionControl object. + * @error: address where an error can be returned, or NULL. + * + * Request an array of strings representing the account names that have + * an active connection. + * + * Return value: A list of McAccounts corresponding to the online + * connections + */ +GSList * +mission_control_get_online_connections (MissionControl * self, GError **error) +{ + GSList *online_conns = NULL; + gchar **names = NULL; + + /* Check whether we have any accounts, otherwise we do not have + * connections either */ + + if (!check_for_accounts (self)) + { + g_set_error (error, MC_ERROR, MC_NO_ACCOUNTS_ERROR, " "); + return NULL; + } + + if (!mc_is_running) + { + g_debug ("%s: MC not running.", G_STRFUNC); + g_set_error (error, MC_ERROR, MC_NO_MATCHING_CONNECTION_ERROR, + "MC not running"); + return NULL; + } + + if (!mission_control_dbus_get_online_connections (DBUS_G_PROXY (self), + &names, error)) + { + return NULL; + } + /* Create McAccounts with all the account names */ + while (*names != NULL) + { + McAccount *acc = mc_account_lookup (*names); + + if (acc != NULL) + { + online_conns = g_slist_prepend (online_conns, acc); + } + *names++; + } + + return online_conns; +} + +/** + * mission_control_get_connection: + * @self: The #MissionControl object. + * @account: The account the connection is created for. + * @error: address where an error can be returned, or NULL. + * + * Gets a connection object for the specified account name. + * + * Return value: An existing TpConn object, NULL if the account is not connected + */ +TpConn * +mission_control_get_connection (MissionControl * self, McAccount * account, + GError **error) +{ + TpConn *tp_conn = NULL; + gchar *bus_name = NULL, *obj_path = NULL; + const gchar *account_name = mc_account_get_unique_name (account); + DBusGConnection *connection = NULL; + + if (account_name == NULL) + { + g_set_error (error, MC_ERROR, MC_INVALID_ACCOUNT_ERROR, " "); + return NULL; + } + + /* Check whether we have any accounts to request connection for */ + if (!check_for_accounts (self)) + { + g_set_error (error, MC_ERROR, MC_NO_ACCOUNTS_ERROR, " "); + return NULL; + } + + /* If MC isn't running there won't be any connections. */ + if (!mc_is_running) + { + g_debug ("%s: MC not running.", G_STRFUNC); + g_set_error (error, MC_ERROR, MC_DISCONNECTED_ERROR, "MC not running"); + return NULL; + } + + g_object_get (G_OBJECT (self), "connection", &connection, NULL); + + if (connection == NULL) + { + g_set_error (error, MC_ERROR, MC_DISCONNECTED_ERROR, + "Cannot get D-BUS connection"); + return NULL; + } + + /* Match the account name and corresponding connection parameters in + * Mission Control */ + + if (!mission_control_dbus_get_connection (DBUS_G_PROXY (self), account_name, + &bus_name, &obj_path, error)) + { + dbus_g_connection_unref (connection); + return NULL; + } + + /* Create a local copy of the TpConn object from the acquired information. + * We do not need to use the connect method via a connection manager, + * because the connection is already initialized by MissionControl. */ + + tp_conn = tp_conn_new (connection, bus_name, obj_path); + + if (tp_conn == NULL) + { + g_set_error (error, MC_ERROR, MC_DISCONNECTED_ERROR, + "Cannot get telepathy connection"); + } + + g_free (bus_name); + g_free (obj_path); + dbus_g_connection_unref (connection); + + return tp_conn; +} + +/** + * mission_control_get_account_for_connection: + * @self: The #MissionControl object. + * @connection: connection object to get the account for + * @error: address where an error can be returned, or NULL. + * + * Gets the account corresponding to the connection object. + * Note that as a result the caller owns a reference to the account object. + * + * Return value: The matching account object, NULL on error + */ +McAccount * +mission_control_get_account_for_connection (MissionControl * self, + TpConn * connection, + GError **error) +{ + const gchar *connection_object_path; + gchar *account_unique_name; + McAccount *account; + + /* Check whether Mission Control is running; if not, it's safe to + * say that there are no accounts or connections in that case + * without starting it to perform the query. */ + if (!mc_is_running) + { + g_debug ("%s: MC not running.", G_STRFUNC); + g_set_error (error, MC_ERROR, MC_DISCONNECTED_ERROR, "MC not running"); + return NULL; + } + + connection_object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (connection)); + + if (!mission_control_dbus_get_account_for_connection (DBUS_G_PROXY (self), + connection_object_path, + &account_unique_name, + error)) + { + g_warning ("%s: Getting account for the connection failed", G_STRFUNC); + return NULL; + } + + account = mc_account_lookup (account_unique_name); + + g_free (account_unique_name); + + return account; +} + +/** + * mission_control_get_used_channels_count: + * @self: The #MissionControl object. + * @type: Type of the counted channels as a GQuark (see the defines in + * tp-chan.h) + * @error: address where an error can be returned, or NULL. + * + * Counts the number of active channels of specified type. + * + * Return value: The number of channels currently in use (negative if the query + * fails) + */ +gint mission_control_get_used_channels_count(MissionControl *self, + GQuark type, GError **error) +{ + gint ret; + + /* Check whether Mission Control is running; if not, there should be + no active channels without starting it to perform the query. */ + + if (!mc_is_running) + { + g_set_error (error, MC_ERROR, MC_DISCONNECTED_ERROR, "MC not running"); + return 0; + } + + /* We'll have to convert the quark here to a string, because it will + not match the quarks in another process */ + + if (!mission_control_dbus_get_used_channels_count(DBUS_G_PROXY(self), + g_quark_to_string(type), + (gint *)&ret, + error)) + { + /* We'll have to make a difference between a failed request and 0 + channels in use */ + return -1; + } + + return ret; +} + +static void +get_current_status_cb (DBusGProxy * proxy, + McStatus status, + McPresence presence, + McPresence requested_presence, + GPtrArray *accounts_array, + GError *error, gpointer userdata) +{ + struct get_current_status_cb_data *cb_data = (struct get_current_status_cb_data *)userdata; + McAccountStatus *accounts, *account; + GType type; + gsize n_accounts; + gint i; + + if (error) + g_debug ("%s: Error: %s (%u)", G_STRFUNC, error->message, error->code); + + type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID); + accounts = g_new (McAccountStatus, accounts_array->len); + for (i = 0, account = accounts; i < accounts_array->len; i++, account++) + { + GValue account_value = { 0, }; + + g_value_init (&account_value, type); + g_value_take_boxed (&account_value, + g_ptr_array_index (accounts_array, i)); + dbus_g_type_struct_get (&account_value, + 0, &account->unique_name, + 1, &account->status, + 2, &account->presence, + 3, &account->reason, + G_MAXUINT); + g_value_unset (&account_value); + } + n_accounts = accounts_array->len; + + g_ptr_array_free (accounts_array, TRUE); + cb_data->callback ((MissionControl *)proxy, + status, presence, requested_presence, + accounts, n_accounts, + error, cb_data->user_data); + + g_free (cb_data); +} + +/** + * mission_control_get_current_status: + * @self: The #MissionControl object. + * @callback: a #McGetCurrentStatusCallback function which will be called with + * the requested information. + * @user_data: data to be passed to the @callback function + * + * Queries the status of all the enabled accounts, as well as the global + * presence and status. This information will be returned in the registered + * @callback, which will be resposible for freeing all the dynamic data. + */ +void +mission_control_get_current_status (MissionControl * self, + McGetCurrentStatusCallback callback, + gpointer user_data) +{ + struct get_current_status_cb_data *cb_data; + + /* Check whether Mission Control is running; if not, it's safe to + * say that we're offline without starting it to perform the + * query. */ + g_assert (callback != NULL); + if (!mc_is_running) + { + GError *error = NULL; + g_debug ("%s: MC not running.", G_STRFUNC); + error = g_error_new (MC_ERROR, MC_DISCONNECTED_ERROR, " "); + callback (self, 0, 0, 0, NULL, 0, error, user_data); + return; + } + + cb_data = g_malloc (sizeof (struct get_current_status_cb_data)); + g_assert (cb_data != NULL); + cb_data->callback = callback; + cb_data->user_data = user_data; + mission_control_dbus_get_current_status_async (DBUS_G_PROXY (self), + get_current_status_cb, + cb_data); +} + +/** + * mission_control_free_account_statuses: + * @accounts: The array of #McAccountStatus. + * + * Frees the @accounts array. + */ +void +mission_control_free_account_statuses (McAccountStatus *accounts) +{ + McAccountStatus *account; + + for (account = accounts; account != NULL; account++) + g_free (account->unique_name); + g_free (accounts); +} + + +/* We handle errors coming via MCD here. If the pid for the error + matches our pid, we will emit the signal, otherwise we just + silently ignore it to avoid other instances using libmissioncontrol + getting confused */ + +static void +_handle_mcd_errors (DBusGProxy * missioncontrol, guint serial, + gchar *client_id, + guint reason, gpointer userdata) +{ + MissionControl *self = (MissionControl *) userdata; + DBusGConnection *connection; + const gchar *self_client_id = NULL; + + g_object_get (G_OBJECT (missioncontrol), "connection", &connection, NULL); + + if (!connection) + return; + + self_client_id = dbus_bus_get_unique_name ( + dbus_g_connection_get_connection (connection)); + dbus_g_connection_unref (connection); + + g_debug ("%s: client id is %s (error comes for %s)", G_STRFUNC, self_client_id, client_id); + if (client_id == NULL || (self_client_id != NULL && + strcmp(client_id, self_client_id) == 0)) + { + g_signal_emit_by_name (self, "Error", serial, reason); + } +} + + +/* A helper function to determine if there are valid accounts. Mainly + useful for avoiding useless launches of Mission Control */ + +static gboolean +check_for_accounts (MissionControl * self) +{ + GList *enabled_accounts = mc_accounts_list_by_enabled (TRUE); + + /* Do we have any enabled accounts? If not, fail. */ + + /* ? Should we add another error definition for situations where we + * have accounts, but none of them are enabled? */ + + if (!enabled_accounts || g_list_length (enabled_accounts) == 0) + { + mc_accounts_list_free (enabled_accounts); + g_debug ("%s: No enabled accounts", G_STRFUNC); + return FALSE; + } + + mc_accounts_list_free (enabled_accounts); + return TRUE; +} + +/** + * mission_control_remote_avatar_changed: + * @self: the #MissionControl object. + * @connection: connection object which received the avatar update. + * @contact_id: the Telepathy self contact handle. + * @token: the Telepathy token for the new avatar. + * @error: address where an error can be returned, or NULL. + * + * This function is responsible for taking actions in response to the own + * avatar being received from the server. Depending on the situation, this + * function can update the local avatar in our #McAccount. + * + * Returns: %TRUE if success, %FALSE if some error occurred. + */ +gboolean +mission_control_remote_avatar_changed (MissionControl *self, + TpConn *connection, guint contact_id, + const gchar *token, GError **error) +{ + const gchar *connection_object_path; + + /* Check whether Mission Control is running; if not, it's safe to + * say that there are no accounts or connections in that case + * without starting it to perform the query. */ + if (!mc_is_running) + { + g_debug ("%s: MC not running.", G_STRFUNC); + g_set_error (error, MC_ERROR, MC_DISCONNECTED_ERROR, "MC not running"); + return FALSE; + } + + connection_object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (connection)); + + return mission_control_dbus_remote_avatar_changed (DBUS_G_PROXY (self), + connection_object_path, + contact_id, token, + error); +} + + diff --git a/libmissioncontrol/mission-control.h b/libmissioncontrol/mission-control.h new file mode 100644 index 00000000..1ea6f59c --- /dev/null +++ b/libmissioncontrol/mission-control.h @@ -0,0 +1,208 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MISSION_CONTROL_LIB_H +#define MISSION_CONTROL_LIB_H + +#ifndef DBUS_API_SUBJECT_TO_CHANGE +#define DBUS_API_SUBJECT_TO_CHANGE +#endif + +#include "dbus/dbus-glib.h" +#include "dbus/dbus-glib-lowlevel.h" +#include "dbus/dbus.h" +#include <libtelepathy/tp-constants.h> +#include <libtelepathy/tp-conn.h> +#include <libtelepathy/tp-chan.h> +#include <sys/types.h> +#include <unistd.h> + +#define MISSION_CONTROL_SERVICE "org.freedesktop.Telepathy.MissionControl" +#define MISSION_CONTROL_IFACE "org.freedesktop.Telepathy.MissionControl" +#define MISSION_CONTROL_PATH "/org/freedesktop/Telepathy/MissionControl" + +#define MISSIONCONTROL_TYPE (mission_control_get_type ()) + +#define MISSIONCONTROL(obj) (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), MISSIONCONTROL_TYPE, \ + MissionControl)) + +#define MISSIONCONTROL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \ + ((klass), MISSIONCONTROL_TYPE, \ + MissionControlClass)) + +#define IS_MISSIONCONTROL(obj) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), MISSIONCONTROL_TYPE)) + +#define IS_MISSIONCONTROL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE \ + ((klass), MISSIONCONTROL_TYPE)) + +#define MISSIONCONTROL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), MISSIONCONTROL_TYPE, \ + MissionControlClass)) + +#define MC_ERROR (mission_control_error_quark()) + +typedef struct _missioncontrol MissionControl; +typedef struct _missioncontrolclass MissionControlClass; + +typedef enum { + MC_DISCONNECTED_ERROR, + MC_INVALID_HANDLE_ERROR, + MC_NO_MATCHING_CONNECTION_ERROR, + MC_INVALID_ACCOUNT_ERROR, + MC_PRESENCE_FAILURE_ERROR, + MC_NO_ACCOUNTS_ERROR, + MC_NETWORK_ERROR, + MC_CONTACT_DOES_NOT_SUPPORT_VOICE_ERROR, + MC_LOWMEM_ERROR, + MC_CHANNEL_REQUEST_GENERIC_ERROR, + MC_CHANNEL_BANNED_ERROR, + MC_CHANNEL_FULL_ERROR, + MC_CHANNEL_INVITE_ONLY_ERROR, + MC_LAST_ERROR /*< skip >*/ +} MCError; + +typedef enum { + MC_PRESENCE_UNSET, + MC_PRESENCE_OFFLINE, + MC_PRESENCE_AVAILABLE, + MC_PRESENCE_AWAY, + MC_PRESENCE_EXTENDED_AWAY, + MC_PRESENCE_HIDDEN, + MC_PRESENCE_DO_NOT_DISTURB, + LAST_MC_PRESENCE /*< skip >*/ +} McPresence; + +typedef enum { + MC_STATUS_DISCONNECTED, + MC_STATUS_CONNECTING, + MC_STATUS_CONNECTED, +} McStatus; + +struct _missioncontrol +{ + DBusGProxy parent; + + gboolean first_run; +}; + + +struct _missioncontrolclass +{ + DBusGProxyClass parent_class; +}; + +typedef struct _McAccountStatus { + gchar *unique_name; + TelepathyConnectionStatus status; + McPresence presence; + TelepathyConnectionStatusReason reason; +} McAccountStatus; + +typedef void (*McCallback) (MissionControl *mc, + GError *error, + gpointer user_data); + +#include <libmissioncontrol/mc-account.h> + +GQuark mission_control_error_quark (void); +GType mission_control_get_type (void); + + +MissionControl *mission_control_new (DBusGConnection *connection); + +void mission_control_set_presence (MissionControl *self, + McPresence presence, + const gchar *message, + McCallback callback, + gpointer user_data); + +McPresence mission_control_get_presence (MissionControl *self, GError **error); +McPresence mission_control_get_presence_actual (MissionControl *self, + GError **error); + +guint mission_control_request_channel (MissionControl *self, + McAccount *account, + const gchar *type, + guint handle, + TelepathyHandleType handle_type, + McCallback callback, + gpointer user_data); + +guint mission_control_request_channel_with_string_handle (MissionControl *self, + McAccount *account, + const gchar *type, + const gchar *handle, + TelepathyHandleType handle_type, + McCallback callback, + gpointer user_data); + +gboolean mission_control_cancel_channel_request (MissionControl *self, + guint operation_id, + GError **error); + +void mission_control_connect_all_with_default_presence (MissionControl *self, + McCallback callback, + gpointer user_data); + +guint mission_control_get_connection_status (MissionControl *self, + McAccount *account, + GError **error); + +GSList *mission_control_get_online_connections (MissionControl *self, + GError **error); + +TpConn *mission_control_get_connection (MissionControl *self, + McAccount *account, GError **error); + +McAccount *mission_control_get_account_for_connection (MissionControl *self, + TpConn *connection, + GError **error); + +gint mission_control_get_used_channels_count (MissionControl *self, + GQuark type, GError **error); + +typedef void (*McGetCurrentStatusCallback) (MissionControl *mc, + McStatus status, + McPresence presence, + McPresence requested_presence, + McAccountStatus *accounts, + gsize n_accounts, + GError *error, + gpointer user_data); + +void mission_control_get_current_status (MissionControl *self, + McGetCurrentStatusCallback callback, + gpointer user_data); + +void mission_control_free_account_statuses (McAccountStatus *accounts); + +gboolean mission_control_remote_avatar_changed (MissionControl *self, + TpConn *connection, + guint contact_id, + const gchar *token, + GError **error); + +#endif diff --git a/libmissioncontrol/test.c b/libmissioncontrol/test.c new file mode 100644 index 00000000..fca3c561 --- /dev/null +++ b/libmissioncontrol/test.c @@ -0,0 +1,491 @@ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <utime.h> + +#include <glib.h> + +#include "mc.h" +#include "mc-account.h" +#include "mc-account-monitor.h" +#include "mc-profile.h" + +void print_profile (McProfile *profile) +{ + const gchar *name, *protocol_name; + McProtocol *protocol; + + g_assert (NULL != profile); + name = mc_profile_get_unique_name (profile); + protocol = mc_profile_get_protocol (profile); + protocol_name = mc_protocol_get_name (protocol); + printf ("profile: %s (%s)\n", name, protocol_name); +} + +void print_account (McAccount *account) +{ + const gchar *name = mc_account_get_unique_name (account); + printf ("account: %p (%s)\n", account, name); +} + +void print_manager (McManager *manager) +{ + const gchar *name = mc_manager_get_unique_name (manager); + printf ("manager: %p (%s)\n", manager, name); +} + +void print_protocol (McProtocol *protocol) +{ + printf ("protocol: %s/%s\n", + mc_manager_get_unique_name ( + mc_protocol_get_manager (protocol)), + mc_protocol_get_name (protocol)); +} + +void print_protocol_detailed (McProtocol *protocol) +{ + GSList *i; + + g_assert (NULL != protocol); + print_protocol (protocol); + + for (i = mc_protocol_get_params (protocol); NULL != i; i = i->next) + { + McProtocolParam *param = (McProtocolParam *) i->data; + + printf(" %s:%s\n", param->signature, param->name); + } +} + +void test_profile () +{ + McProfile *profile1, *profile2; + McProtocol *protocol; + const gchar * protocol_name; + + profile1 = mc_profile_lookup ("testprofile"); + g_assert (profile1); + g_assert (0 == strcmp ("testprofile", + mc_profile_get_unique_name (profile1))); + protocol_name = mc_profile_get_protocol_name (profile1); + g_assert (0 == strcmp ("testproto", protocol_name)); + protocol = mc_profile_get_protocol (profile1); + g_assert (protocol); + g_assert (0 == strcmp ("testproto", mc_protocol_get_name (protocol))); + profile2 = mc_profile_lookup ("testprofile"); + g_assert (profile1 == profile2); + + mc_profile_free (profile1); + mc_profile_free (profile2); +} + +void test_profile_list () +{ + GList *list, *i; + McProfile *profile1, *profile2; + + list = mc_profiles_list (); + g_assert (3 == g_list_length (list)); + i = list; + profile1 = (McProfile *) i->data; + i = i->next; + profile2 = (McProfile *) i->data; + g_assert (0 == strcmp ("jabber", + mc_profile_get_unique_name (profile1))); + g_assert (0 == strcmp ("google-talk", + mc_profile_get_unique_name (profile2))); + g_assert (0 == strcmp ("testprofile", + mc_profile_get_unique_name ((McProfile *) i->next->data))); + + mc_profiles_free_list (list); + + list = mc_profiles_list (); + g_assert (3 == g_list_length (list)); + i = list; + g_assert (profile1 == (McProfile *) i->data); + i = i->next; + g_assert (profile2 == (McProfile *) i->data); + + mc_profiles_free_list (list); +} + +void test_profile_stat () +{ + McProfile *profile1, *profile2; + + profile1 = mc_profile_lookup ("jabber"); + utime ("../test/jabber.profile", NULL); + profile2 = mc_profile_lookup("jabber"); + g_assert (profile1 != profile2); +} + +void check_account_param (gpointer key, gpointer value, gpointer data) +{ + if (0 == strcmp (key, "account")) + { + g_assert (G_VALUE_HOLDS_STRING (value)); + g_assert (0 == strcmp ("daf@foo", g_value_get_string (value))); + return; + } + + if (0 == strcmp (key, "password")) + { + g_assert (G_VALUE_HOLDS_STRING (value)); + g_assert (0 == strcmp ("badger", g_value_get_string (value))); + return; + } + + g_warning ("got unexpected parameter \"%s\" for account", (gchar *) key); +} + +void test_account() +{ + McAccount *account1, *account2; + + account1 = mc_account_lookup ("jabber1"); + g_assert (account1); + g_assert (0 == strcmp ("jabber1", mc_account_get_unique_name (account1))); + account2 = mc_account_lookup ("jabber1"); + g_assert (account2); + g_assert (account1 == account2); + + g_assert (mc_account_set_param_string(account1, "account", "daf@foo")); + g_assert (mc_account_set_param_string(account1, "password", "badger")); + + g_hash_table_foreach( + mc_account_get_params(account1), check_account_param, account1); + + mc_account_free(account1); + mc_account_free(account2); +} + +void print_accounts_list () +{ + GList *i, *accounts; + + accounts = mc_accounts_list (); + + for (i = accounts; NULL != i; i = i->next) + { + McAccount *account = (McAccount *) i->data; + const gchar *name = mc_account_get_unique_name (account); + const gchar *display_name = mc_account_get_display_name (account); + + if (display_name) + g_print (" %s (\"%s\")\n", name, display_name); + else + g_print (" %s\n", name); + } +} + +gint account_has_name (gconstpointer account_p, gconstpointer name_p) +{ + McAccount *account = (McAccount *) account_p; + const gchar *name = (gchar *) name_p; + + return strcmp (mc_account_get_unique_name (account), name); +} + +void test_mc_account_list () +{ + GList *accounts; + McAccount *account; + McProfile *profile; + const gchar *name; + + profile = mc_profile_lookup ("jabber"); + account = mc_account_create (profile); + sleep(1); + while (g_main_context_iteration (NULL, FALSE)); + name = mc_account_get_unique_name (account); + accounts = mc_accounts_list (); + g_assert (NULL != g_list_find_custom (accounts, name, account_has_name)); + mc_accounts_list_free (accounts); + mc_account_delete (account); +} + +void cb_account_created(McAccountMonitor *monitor, gchar *name, gpointer data) +{ + GSList **created = (GSList **) data; + *created = g_slist_append (*created, g_strdup (name)); + /*printf ("account created: %s\n", name); */ +} + +void cb_account_deleted(McAccountMonitor *monitor, gchar *name, gpointer data) +{ + GSList **deleted = (GSList **) data; + *deleted = g_slist_append (*deleted, g_strdup (name)); + /*printf ("account deleted: %s\n", name); */ +} + +void cb_account_enabled (McAccountMonitor *monitor, gchar *name, gpointer data) +{ + GSList **enabled = (GSList **) data; + *enabled = g_slist_append (*enabled, g_strdup (name)); + /* printf ("account enabled: %s\n", name); */ +} + +void cb_account_disabled (McAccountMonitor *monitor, gchar *name, gpointer data) +{ + GSList **disabled = (GSList **) data; + *disabled = g_slist_append (*disabled, g_strdup (name)); + /* printf ("account disabled: %s\n", name); */ +} + +void cb_account_changed (McAccountMonitor *monitor, gchar *name, gpointer data) +{ + GSList **disabled = (GSList **) data; + *disabled = g_slist_append (*disabled, g_strdup (name)); +} + +void test_account_monitor() +{ + McAccountMonitor *monitor; + McProfile *profile1, *profile2; + McAccount *account1, *account2; + GSList *created = NULL; + GSList *deleted = NULL; + GSList *enabled = NULL; + GSList *disabled = NULL; + GSList *changed = NULL; + const gchar *name1, *name2; + + monitor = mc_account_monitor_new (); + g_signal_connect (monitor, "account-created", (GCallback) cb_account_created, &created); + g_signal_connect (monitor, "account-deleted", (GCallback) cb_account_deleted, &deleted); + g_signal_connect (monitor, "account-enabled", (GCallback) cb_account_enabled, &enabled); + g_signal_connect (monitor, "account-disabled", (GCallback) cb_account_disabled, &disabled); + g_signal_connect (monitor, "account-changed", (GCallback) cb_account_changed, &changed); + + profile1 = mc_profile_lookup ("jabber"); + g_assert (NULL != profile1); + g_assert (NULL != mc_profile_get_protocol (profile1)); + + profile2 = mc_profile_lookup ("google-talk"); + g_assert (NULL != mc_profile_get_protocol (profile2)); + g_assert (NULL != profile2); + + /* test 1: creating */ + + account1 = mc_account_create (profile1); + name1 = mc_account_get_unique_name (account1); + /* printf ("new account: %s\n", name1);*/ + + account2 = mc_account_create (profile2); + name2 = mc_account_get_unique_name (account2); + /* printf ("new account: %s\n", name2); */ + + sleep(1); + while (g_main_context_iteration (NULL, FALSE)); + + g_assert (2 == g_slist_length (created)); + g_assert (0 == g_slist_length (deleted)); + g_assert (2 == g_slist_length (enabled)); + g_assert (0 == g_slist_length (disabled)); + g_assert (0 < g_slist_length (changed)); + + g_assert (NULL != g_slist_find_custom (created, name1, (GCompareFunc) strcmp)); + g_assert (NULL != g_slist_find_custom (created, name2, (GCompareFunc) strcmp)); + + g_assert (NULL != g_slist_find_custom (enabled, name1, (GCompareFunc) strcmp)); + g_assert (NULL != g_slist_find_custom (enabled, name2, (GCompareFunc) strcmp)); + + created = deleted = enabled = disabled = changed = NULL; + + /* test 2: disabling */ + + mc_account_set_enabled (account1, FALSE); + mc_account_set_enabled (account2, FALSE); + + sleep(1); + while (g_main_context_iteration (NULL, FALSE)); + + g_assert (0 == g_slist_length (created)); + g_assert (0 == g_slist_length (deleted)); + g_assert (0 == g_slist_length (enabled)); + g_assert (2 == g_slist_length (disabled)); + g_assert (0 == g_slist_length (changed)); + + g_assert (NULL != g_slist_find_custom (disabled, name1, (GCompareFunc) strcmp)); + g_assert (NULL != g_slist_find_custom (disabled, name2, (GCompareFunc) strcmp)); + + created = deleted = enabled = disabled = changed = NULL; + + /* test 3: re-enabling */ + + mc_account_set_enabled (account1, TRUE); + mc_account_set_enabled (account2, TRUE); + + sleep(1); + while (g_main_context_iteration (NULL, FALSE)); + + g_assert (0 == g_slist_length (created)); + g_assert (0 == g_slist_length (deleted)); + g_assert (2 == g_slist_length (enabled)); + g_assert (0 == g_slist_length (disabled)); + g_assert (0 == g_slist_length (changed)); + + g_assert (NULL != g_slist_find_custom (enabled, name1, (GCompareFunc) strcmp)); + g_assert (NULL != g_slist_find_custom (enabled, name2, (GCompareFunc) strcmp)); + + created = deleted = enabled = disabled = changed = NULL; + + /* test 4: deleting */ + + mc_account_delete (account2); + mc_account_delete (account1); + + sleep(1); + while (g_main_context_iteration (NULL, FALSE)); + + g_assert (0 == g_slist_length (created)); + g_assert (2 == g_slist_length (deleted)); + g_assert (0 == g_slist_length (enabled)); + g_assert (2 == g_slist_length (disabled)); + g_assert (0 < g_slist_length (changed)); + + + g_assert (NULL != g_slist_find_custom (deleted, name1, (GCompareFunc) strcmp)); + g_assert (NULL != g_slist_find_custom (deleted, name2, (GCompareFunc) strcmp)); + + mc_profile_free (profile1); + mc_profile_free (profile2); + mc_account_free (account1); + mc_account_free (account2); + g_object_unref (monitor); +} + +void test_manager() +{ + McManager *manager1, *manager2; + + manager1 = mc_manager_lookup ("testmanager"); + g_assert (manager1); + g_assert (0 == + strcmp ("testmanager", mc_manager_get_unique_name (manager1))); + + g_assert (0 == strcmp ("testmanager", + mc_manager_get_unique_name (manager1))); + g_assert (0 == strcmp ("org.freedesktop.Telepathy.ConnectionManager.test", + mc_manager_get_bus_name (manager1))); + g_assert (0 == strcmp ("/org/freedesktop/Telepathy/ConnectionManager/test", + mc_manager_get_object_path (manager1))); + + manager2 = mc_manager_lookup ("testmanager"); + g_assert (manager2); + g_assert (manager1 == manager2); + + mc_manager_free (manager1); + mc_manager_free (manager2); +} + +void test_protocol () +{ + McManager *manager1, *manager2; + McProtocol *protocol1, *protocol2; + GSList *params; + McProtocolParam expected_params[] = { + {"account", "s", NULL, + MC_PROTOCOL_PARAM_REQUIRED | MC_PROTOCOL_PARAM_REGISTER}, + {"password", "s", NULL, + MC_PROTOCOL_PARAM_REQUIRED | MC_PROTOCOL_PARAM_REGISTER}, + {"server", "s", NULL, + MC_PROTOCOL_PARAM_REQUIRED}, + {"port", "q", NULL, 0}, + {"register", "b", NULL, 0}, + {NULL, NULL, NULL, 0} + }, *i; + + manager1 = mc_manager_lookup ("testmanager"); + manager2 = mc_manager_lookup ("testmanager"); + protocol1 = mc_protocol_lookup (manager1, "testproto"); + protocol2 = mc_protocol_lookup (manager2, "testproto"); + g_assert (protocol1 == protocol2); + + params = mc_protocol_get_params (protocol1); + + for (i = expected_params; i->name; i++) + { + GSList *j; + gboolean found = FALSE; + + for (j = params; j; j = j->next) + { + McProtocolParam *p = (McProtocolParam *) j->data; + + if (0 == strcmp (i->name, p->name)) + { + found = TRUE; + g_assert (0 == strcmp (i->name, p->name)); + g_assert (0 == strcmp (i->signature, p->signature)); + } + } + + g_assert (found); + } + + mc_protocol_free_params_list (params); + + mc_manager_free (manager1); + mc_manager_free (manager2); + mc_protocol_free (protocol1); + mc_protocol_free (protocol2); +} + +int main () +{ + g_setenv ("MC_PROFILE_DIR", "../test", FALSE); + g_setenv ("MC_MANAGER_DIR", "../test", FALSE); + + g_type_init (); + + mc_make_resident (); + mc_make_resident (); + + test_profile (); + test_profile_list (); + test_profile_stat (); + test_account (); + test_mc_account_list (); + + /* this is a hack to workaround an apparent race condition when catching + * GConf signals in the process that caused them */ + sleep(1); + while (g_main_context_iteration (NULL, FALSE)); + + test_account_monitor (); + test_manager (); + test_protocol (); + + mc_profile_clear_cache (); + mc_account_clear_cache (); + mc_manager_clear_cache (); + + return 0; +} + + diff --git a/mission-control.pc.in b/mission-control.pc.in new file mode 100644 index 00000000..b51645dd --- /dev/null +++ b/mission-control.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +pluginlibdir=@pluginlibdir@ + +Name: mission-control +Description: Mission control filters interface library +Version: @VERSION@ +Libs: -L${libdir} -lmissioncontrol-server +Cflags: -I${includedir}/mission-control + diff --git a/server/Makefile.am b/server/Makefile.am new file mode 100644 index 00000000..0f0a862c --- /dev/null +++ b/server/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = $(DBUS_CFLAGS) $(TELEPATHY_CFLAGS) -I$(top_srcdir) \ + -I$(top_srcdir)/src \ + -DMC_DISABLE_DEPRECATED \ + -DLIBDIR="@libdir@" -DLIBVERSION="0" + +if HAVE_SERVER + +servicefiledir=$(prefix)/share/dbus-1/services +servicefile_DATA=org.freedesktop.Telepathy.MissionControl.service + +bin_PROGRAMS = mission-control +mission_control_SOURCES = mc-server.c +mission_control_LDADD = $(top_srcdir)/src/libmissioncontrol-server.la + +endif diff --git a/server/mc-server.c b/server/mc-server.c new file mode 100644 index 00000000..2d5117b8 --- /dev/null +++ b/server/mc-server.c @@ -0,0 +1,62 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <glib.h> +#include "mcd-service.h" + + +static void +on_abort (McdService * mcd) +{ + g_debug ("Exiting now ..."); + + mcd_debug_print_tree (mcd); + + g_object_unref (mcd); + g_debug ("MC now exits .. bye bye"); + exit (0); +} + +int +main (int argc, char **argv) +{ + McdService *mcd; + + g_type_init (); + + + mcd = mcd_service_new (); + + /* Listen for suicide notification */ + g_signal_connect_after (mcd, "abort", G_CALLBACK (on_abort), mcd); + + /* connect */ + mcd_mission_connect (MCD_MISSION (mcd)); + + mcd_service_run (MCD_OBJECT (mcd)); + + return 0; +} diff --git a/server/org.freedesktop.Telepathy.MissionControl.service.in b/server/org.freedesktop.Telepathy.MissionControl.service.in new file mode 100644 index 00000000..9ac6d7ac --- /dev/null +++ b/server/org.freedesktop.Telepathy.MissionControl.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.freedesktop.Telepathy.MissionControl +Exec=@prefix@/bin/mission-control diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..45d61e55 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,110 @@ +INCLUDES = $(GCONF_CFLAGS) $(TELEPATHY_CFLAGS) \ + -DMCD_DEFAULT_FILTER_PLUGIN_DIR=\"@pluginlibdir@\" \ + -DDBUS_API_SUBJECT_TO_CHANGE \ + -DMC_DISABLE_DEPRECATED -I$(top_srcdir) \ + -O0 -g -fno-omit-frame-pointer -Wall -Werror + +mission_control_includedir = $(includedir)/mission-control +mission_control_include = \ + mcd-debug.h \ + mcd-mission.h \ + mcd-operation.h \ + mcd-master.h \ + mcd-controller.h \ + mcd-manager.h \ + mcd-connection.h \ + mcd-presence-frame.h \ + mcd-channel.h \ + mcd-proxy.h \ + mcd-dispatcher.h \ + mcd-dispatcher-context.h \ + mcd-service.h \ + mcd-chan-handler.h + +mission_control_include_HEADERS = \ + $(mission_control_include) \ + mcd-enum-types.c \ + mcd-enum-types.h + +BUILT_SOURCES = \ + mcd-service-gen.h \ + mcd-signals-marshal.h \ + mcd-signals-marshal.c \ + mcd-enum-types.c \ + mcd-enum-types.h \ + stamp-mcd-enum-types.h + +CLEANFILES = $(BUILT_SOURCES) + +lib_LTLIBRARIES = libmissioncontrol-server.la + +libmissioncontrol_server_la_LIBADD = $(GCONF_LIBS) $(TELEPATHY_LIBS) \ + $(top_builddir)/libmissioncontrol/libmissioncontrol-config.la + +# we want to export symbols so that the plugins can see them +libmissioncontrol_server_la_LDFLAGS = -export-dynamic + +libmissioncontrol_server_la_SOURCES = \ + mcd-debug.c \ + mcd-enum-types.c \ + mcd-signals-marshal.c \ + mcd-mission.c \ + mcd-operation.c \ + mcd-controller.c \ + mcd-master.c \ + mcd-manager.c \ + mcd-connection.c \ + mcd-presence-frame.c \ + mcd-dispatcher.c \ + mcd-channel.c \ + mcd-service-gen.h \ + mcd-service.c \ + mcd-proxy.c \ + mcd-chan-handler.c + +# bin_PROGRAMS = mission-control +# mission_control_LDFLAGS = -export-dynamic +# +# mission_control_LDADD = $(GCONF_LIBS) $(TELEPATHY_LIBS) \ +# $(top_builddir)/libmissioncontrol/libmissioncontrol-config.la \ +# libmissioncontrol-server.la +# +# mission_control_SOURCES = \ +# mcd-main.c + +%-signals-marshal.h: %-signals-marshal.list Makefile + glib-genmarshal --header --prefix=$(subst -,_,$*)_marshal $< > $*-signals-marshal.h + +%-signals-marshal.c: %-signals-marshal.list Makefile + glib-genmarshal --body --prefix=$(subst -,_,$*)_marshal $< > $*-signals-marshal.c + +mcd-enum-types.h: stamp-mcd-enum-types.h + @true +stamp-mcd-enum-types.h: Makefile $(mission_control_include) mcd-enum-types.c + ( cd $(srcdir) && glib-mkenums \ + --fhead "#ifndef __MCD_ENUM_TYPES_H__\n#define __MCD_ENUM_TYPES_H__\n\n#include \"mcd-mission.h\"\n#include \"mcd-channel.h\"\n\nG_BEGIN_DECLS\n" \ + --fprod "/* enumerations from \"@filename@\" */\n" \ + --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define MCD_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ + --ftail "G_END_DECLS\n\n#endif /* __MCD_ENUM_TYPES_H__ */" \ + $(mission_control_include) ) >> xgen-geth \ + && (cmp -s xgen-geth mcd-enum-types.h || cp xgen-geth mcd-enum-types.h ) \ + && rm -f xgen-geth \ + && echo timestamp > $(@F) + +mcd-enum-types.c: Makefile + ( cd $(srcdir) && glib-mkenums \ + --fhead "#include \"mcd-enum-types.h\"\n#define g_intern_static_string(s) (s)\n" \ + --fprod "\n/* enumerations from \"@filename@\" */" \ + --ftail "\n#define __MCD_ENUM_TYPES_C__\n" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (g_intern_static_string (\"@EnumName@\"), values);\n }\n return etype;\n}\n" \ + $(mission_control_include) ) > xgen-getc \ + && cp xgen-getc mcd-enum-types.c \ + && rm -f xgen-getc + + +mcd-service-gen.h: ../xml/mcd-dbus-services.xml + dbus-binding-tool --mode=glib-server --prefix=mcd_service $< > $@ + +EXTRA_DIST = mcd-signals-marshal.list stamp-mcd-enum-types.h diff --git a/src/mcd-chan-handler.c b/src/mcd-chan-handler.c new file mode 100644 index 00000000..9c2fbf33 --- /dev/null +++ b/src/mcd-chan-handler.c @@ -0,0 +1,158 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <stdlib.h> +#include <string.h> +#include "mcd-chan-handler.h" +#include <config.h> + +#define FILE_SEPARATOR ',' +#define CH_FILE_SUFFIX ".chandler" +#define CH_FILE_CH_GROUP "ChannelHandler" + +static void +_mcd_channel_handler_free (McdChannelHandler *handler) +{ + g_free((gpointer) handler->bus_name); + g_free((gpointer) handler->obj_path); + g_free(handler); +} + +static inline void +_mcd_channel_handler_packer(GHashTable *handlers, gchar **string_list, + gsize list_length, gchar *bus_name, + TelepathyChannelMediaCapability capabilities, + gchar *object_path) +{ + gint i; + McdChannelHandler *handler; + + for (i = 0; i < list_length; i++) + { + handler = g_new(McdChannelHandler, 1); + handler->bus_name = bus_name; + handler->obj_path = object_path; + handler->capabilities = capabilities; + g_hash_table_insert(handlers, g_strdup(string_list[i]), handler); + } +} + +/* +* Read files from configuration file directory. +* This is used for Connection Manager and Channel Handler files. +*/ +static void +_mcd_channel_handlers_read_conf_files (GHashTable *handlers, + gchar *suffix, gchar *group) +{ + GError *error = NULL; + GKeyFile *file; + gchar **string_list; + gsize len; + GDir *dir; + const gchar *filename, *dirname; + gchar *absolute_filepath; + gchar *bus_name, *object_path; + TelepathyChannelMediaCapability capabilities; + + dirname = g_getenv ("MC_CHANDLERS_DIR"); + if (dirname == NULL) + dirname = CHANDLERS_DIR; + + /* Read the configuration file directory */ + if ((dir = g_dir_open(dirname, 0, &error)) == NULL) + { + g_error ("Error opening directory %s: %s", dirname, + error->message); + } + + while ((filename = g_dir_read_name(dir)) != NULL) + { + /* Skip the file if it doesn't contain the required file suffix */ + if (g_str_has_suffix(filename, suffix)) + { + absolute_filepath = g_build_filename(dirname, filename, NULL); + + file = g_key_file_new(); + if (!g_key_file_load_from_file + (file, absolute_filepath, G_KEY_FILE_NONE, &error)) + { + g_error ("%s", error->message); + } + g_key_file_set_list_separator(file, FILE_SEPARATOR); + + if (!(bus_name = g_key_file_get_string (file, group, + "BusName", &error))) + { + g_error ("%s: %s", absolute_filepath, error->message); + } + if (!(object_path = g_key_file_get_string(file, group, + "ObjectPath", &error))) + { + g_error ("%s: %s", absolute_filepath, error->message); + } + capabilities = g_key_file_get_integer(file, group, "TypeSpecificCapabilities", + &error); + if (error) + { + if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) + g_warning ("%s: Error parsing %s: %s", + G_STRFUNC, filename, error->message); + g_error_free(error); + error = NULL; + capabilities = 0; + } + + + if (!(string_list = g_key_file_get_string_list(file, group, "ChannelType", + &len, &error))) + { + g_error ("%s: %s", absolute_filepath, error->message); + } + + _mcd_channel_handler_packer(handlers, string_list, len, bus_name, + capabilities, object_path); + + g_strfreev(string_list); + g_key_file_free(file); + g_free(absolute_filepath); + } + } + g_dir_close(dir); +} + +GHashTable* +mcd_get_channel_handlers (void) +{ + GHashTable *handlers; + + handlers = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, + (GDestroyNotify)_mcd_channel_handler_free); + + /* Read Channel Handler files */ + _mcd_channel_handlers_read_conf_files (handlers, + CH_FILE_SUFFIX, CH_FILE_CH_GROUP); + return handlers; +} diff --git a/src/mcd-chan-handler.h b/src/mcd-chan-handler.h new file mode 100644 index 00000000..5c7f2d60 --- /dev/null +++ b/src/mcd-chan-handler.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_CHAN_HANDLER_H +#define MCD_CHAN_HANDLER_H + +#include <glib.h> +#include <libtelepathy/tp-constants.h> + +/* Channel handler */ + +typedef struct +{ + const gchar *bus_name; + const gchar *obj_path; + TelepathyChannelMediaCapability capabilities; +} McdChannelHandler; + +GHashTable* mcd_get_channel_handlers (void); + +#endif diff --git a/src/mcd-channel.c b/src/mcd-channel.c new file mode 100644 index 00000000..b72001bf --- /dev/null +++ b/src/mcd-channel.c @@ -0,0 +1,796 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <glib/gi18n.h> +#include <libtelepathy/tp-chan-iface-group-gen.h> +#include <libtelepathy/tp-constants.h> +#include <libtelepathy/tp-conn.h> + +#include "mcd-channel.h" +#include "mcd-enum-types.h" + +#define MCD_CHANNEL_PRIV(channel) (G_TYPE_INSTANCE_GET_PRIVATE ((channel), \ + MCD_TYPE_CHANNEL, \ + McdChannelPrivate)) + +G_DEFINE_TYPE (McdChannel, mcd_channel, MCD_TYPE_MISSION); + +typedef struct _McdChannelPrivate +{ + /* Channel info */ + gchar *channel_object_path; + gchar *channel_type; + GQuark channel_type_quark; + guint channel_handle; + TelepathyHandleType channel_handle_type; + gboolean outgoing; + + /* Channel created based on the above channel info */ + TpChan *tp_chan; + + /* Pending members */ + GArray *pending_local_members; + gboolean members_accepted; + + McdChannelStatus status; + gchar *channel_name; + + /* Requestor info */ + guint requestor_serial; + gchar *requestor_client_id; + + gboolean is_disposed; + +} McdChannelPrivate; + +enum _McdChannelSignalType +{ + STATUS_CHANGED, + MEMBERS_ACCEPTED, + LAST_SIGNAL +}; + +enum _McdChannelPropertyType +{ + PROP_CONNECTION=1, + PROP_TP_CHANNEL, + PROP_CHANNEL_STATUS, + PROP_CHANNEL_OBJECT_PATH, + PROP_CHANNEL_TYPE, + PROP_CHANNEL_TYPE_QUARK, + PROP_CHANNEL_HANDLE, + PROP_CHANNEL_HANDLE_TYPE, + PROP_OUTGOING, + PROP_REQUESTOR_SERIAL, + PROP_REQUESTOR_CLIENT_ID +}; + +static guint mcd_channel_signals[LAST_SIGNAL] = { 0 }; + +static void _mcd_channel_release_tp_channel (McdChannel *channel, + gboolean close_channel); + +static void +on_channel_members_changed (DBusGProxy * group_proxy, + const gchar * message, GArray * added, + GArray * removed, GArray * l_pending, + GArray * r_pending, guint actor, + guint reason, gpointer userdata) +{ + McdChannel *channel = MCD_CHANNEL (userdata); + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (userdata); + /* Local pending members? Add to the array and exit. */ + + if (l_pending && l_pending->len > 0) + { + int i; + /* FIXME: Add duplicity check */ + for (i = 0; i < l_pending->len; i++) + { + g_array_append_val (priv->pending_local_members, + (guint) g_array_index (l_pending, guint, i)); + g_debug ("Added handle %u to channel pending members", + (guint) g_array_index (l_pending, guint, i)); + } + } + + /* Added members? If any of them are in the local pending array, we can + * remove the lock restoration flag */ + + if (added && added->len > 0) + { + int i, j; + g_debug ("%u added members", added->len); + for (i = 0; i < added->len; i++) + { + guint added_member = g_array_index (added, guint, i); + + /* N^2 complexity is not good, however with VOIP calls we should + * not bump into significant number of members */ + + for (j = 0; j < priv->pending_local_members->len; j++) + { + if (added_member == + g_array_index (priv->pending_local_members, guint, i)) + { + g_debug + ("Pending local member added -> do not restore lock"); + g_debug + ("This should appear only when the call was accepted"); + /* mcd_object_get ()->filters_unlocked_tk_lock = FALSE; */ + priv->members_accepted = TRUE; + g_signal_emit_by_name (channel, "members-accepted"); + break; + } + } + } + } + /* FIXME: We should also remove members from the local pending + * array, even if we don't need the info */ +} + +static void +get_local_pending_cb (DBusGProxy * group_proxy, + GArray * l_pending, GError * error, gpointer userdata) +{ + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (userdata); + if (l_pending) + { + int i; + g_debug ("%u local pending members, adding", l_pending->len); + /* FIXME: Add duplicity check */ + for (i = 0; i < l_pending->len; i++) + { + g_array_append_val (priv->pending_local_members, + (guint) g_array_index (l_pending, guint, i)); + g_debug ("Added handle %u to channel pending members", + (guint) g_array_index (l_pending, guint, i)); + } + g_array_free (l_pending, TRUE); + } +} + +/* The callback is called on channel Closed signal */ +void +on_tp_channel_closed (DBusGProxy * tp_chan, gpointer userdata) +{ + McdChannel *channel = MCD_CHANNEL (userdata); + + _mcd_channel_release_tp_channel (channel, FALSE); + mcd_mission_abort (MCD_MISSION (channel)); + g_debug ("Channel closed"); +} + +static void proxy_destroyed (DBusGProxy *tp_chan, McdChannel *channel) +{ + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (channel); + + g_debug ("Channel proxy destroyed!"); + g_object_unref (tp_chan); + priv->tp_chan = NULL; + mcd_mission_abort (MCD_MISSION (channel)); + g_debug ("Channel closed"); +} + +static void +_mcd_channel_release_tp_channel (McdChannel *channel, gboolean close_channel) +{ + DBusGProxy *group_iface; + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (channel); + if (priv->tp_chan) + { + GError *error = NULL; + + g_debug ("%s: getting group_iface", G_STRFUNC); + group_iface = tp_chan_get_interface (priv->tp_chan, + TELEPATHY_CHAN_IFACE_GROUP_QUARK); + if (group_iface) + { + dbus_g_proxy_disconnect_signal (group_iface,"MembersChanged", + G_CALLBACK (on_channel_members_changed), + channel); + } + + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->tp_chan), + "Closed", + G_CALLBACK (on_tp_channel_closed), + channel); + g_signal_handlers_disconnect_by_func (G_OBJECT (priv->tp_chan), + G_CALLBACK (proxy_destroyed), + channel); + + if (close_channel && priv->channel_type_quark != TELEPATHY_CHAN_IFACE_CONTACTLIST_QUARK) + { + g_debug ("%s: Requesting telepathy to close the channel", G_STRFUNC); + tp_chan_close (DBUS_G_PROXY (priv->tp_chan), &error); + if (error) + { + g_warning ("%s: Request for channel close failed: %s", + G_STRFUNC, error->message); + g_error_free (error); + } + } + /* Destroy our proxy */ + g_object_unref (priv->tp_chan); + + priv->tp_chan = NULL; + } +} + +static void +_mcd_channel_set_property (GObject * obj, guint prop_id, + const GValue * val, GParamSpec * pspec) +{ + DBusGProxy *group_iface; + TpChan *tp_chan; + McdChannel *channel = MCD_CHANNEL (obj); + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (obj); + + switch (prop_id) + { + case PROP_CHANNEL_STATUS: + priv->status = g_value_get_enum (val); + g_signal_emit_by_name (channel, "status-changed", priv->status); + break; + case PROP_CONNECTION: + break; + case PROP_TP_CHANNEL: + tp_chan = g_value_get_object (val); + if (tp_chan) + { + g_return_if_fail (priv->channel_object_path != NULL); + g_return_if_fail (priv->channel_type != NULL); + g_return_if_fail (priv->channel_handle >= 0); + + /* FIXME: BUG in libtelepathy */ + /* g_return_if_fail (TELEPATHY_IS_CHAN (tp_chan)); */ + g_object_ref (tp_chan); + } + _mcd_channel_release_tp_channel (channel, TRUE); + priv->tp_chan = tp_chan; + if (priv->tp_chan) + { + group_iface = tp_chan_get_interface (priv->tp_chan, + TELEPATHY_CHAN_IFACE_GROUP_QUARK); + if (group_iface) + { + /* Setup channel watches */ + dbus_g_proxy_connect_signal (group_iface, "MembersChanged", + G_CALLBACK (on_channel_members_changed), + channel, NULL); + tp_chan_iface_group_get_local_pending_members_async (group_iface, + get_local_pending_cb, + channel); + } + /* We want to track the channel object closes, because we need to do + * some cleanups when it's gone */ + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_chan), "Closed", + G_CALLBACK (on_tp_channel_closed), + channel, NULL); + g_signal_connect (priv->tp_chan, "destroy", + G_CALLBACK (proxy_destroyed), channel); + + } + break; + case PROP_CHANNEL_OBJECT_PATH: + /* g_return_if_fail (g_value_get_string (val) != NULL); */ + g_free (priv->channel_object_path); + if (g_value_get_string (val) != NULL) + priv->channel_object_path = g_strdup (g_value_get_string (val)); + else + priv->channel_object_path = NULL; + break; + case PROP_CHANNEL_TYPE: + /* g_return_if_fail (g_value_get_string (val) != NULL); */ + g_free (priv->channel_type); + if (g_value_get_string (val) != NULL) + { + priv->channel_type = g_strdup (g_value_get_string (val)); + priv->channel_type_quark = g_quark_from_string (priv->channel_type); + } + else + { + priv->channel_type = NULL; + priv->channel_type_quark = 0; + } + break; + case PROP_CHANNEL_TYPE_QUARK: + break; + case PROP_CHANNEL_HANDLE: + priv->channel_handle = g_value_get_uint (val); + break; + case PROP_CHANNEL_HANDLE_TYPE: + priv->channel_handle_type = g_value_get_uint (val); + break; + case PROP_OUTGOING: + priv->outgoing = g_value_get_boolean (val); + break; + case PROP_REQUESTOR_SERIAL: + priv->requestor_serial = g_value_get_uint (val); + break; + case PROP_REQUESTOR_CLIENT_ID: + g_free (priv->requestor_client_id); + priv->requestor_client_id = g_value_dup_string (val); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_mcd_channel_get_property (GObject * obj, guint prop_id, + GValue * val, GParamSpec * pspec) +{ + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (obj); + + switch (prop_id) + { + case PROP_CONNECTION: + /* FIXME: This is presumetous and wrong */ + g_value_set_object (val, mcd_mission_get_parent (MCD_MISSION (obj))); + break; + case PROP_CHANNEL_STATUS: + g_value_set_enum (val, priv->status); + break; + case PROP_TP_CHANNEL: + g_value_set_object (val, priv->tp_chan); + break; + case PROP_CHANNEL_OBJECT_PATH: + g_value_set_string (val, priv->channel_object_path); + break; + case PROP_CHANNEL_TYPE: + g_value_set_string (val, priv->channel_type); + break; + case PROP_CHANNEL_TYPE_QUARK: + g_value_set_uint (val, priv->channel_type_quark); + break; + case PROP_CHANNEL_HANDLE: + g_value_set_uint (val, priv->channel_handle); + break; + case PROP_CHANNEL_HANDLE_TYPE: + g_value_set_uint (val, priv->channel_handle_type); + break; + case PROP_OUTGOING: + g_value_set_boolean (val, priv->outgoing); + break; + case PROP_REQUESTOR_SERIAL: + g_value_set_uint (val, priv->requestor_serial); + break; + case PROP_REQUESTOR_CLIENT_ID: + g_value_set_string (val, priv->requestor_client_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_mcd_channel_finalize (GObject * object) +{ + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (object); + + g_free (priv->channel_object_path); + g_free (priv->channel_type); + g_array_free (priv->pending_local_members, TRUE); + g_free (priv->requestor_client_id); + g_free (priv->channel_name); + + G_OBJECT_CLASS (mcd_channel_parent_class)->finalize (object); +} + +static void +_mcd_channel_dispose (GObject * object) +{ + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (object); + + if (priv->is_disposed) + return; + + priv->is_disposed = TRUE; + _mcd_channel_release_tp_channel (MCD_CHANNEL (object), TRUE); + G_OBJECT_CLASS (mcd_channel_parent_class)->dispose (object); +} + +static void +mcd_channel_class_init (McdChannelClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (object_class, sizeof (McdChannelPrivate)); + + object_class->finalize = _mcd_channel_finalize; + object_class->dispose = _mcd_channel_dispose; + object_class->set_property = _mcd_channel_set_property; + object_class->get_property = _mcd_channel_get_property; + + /* signals */ + mcd_channel_signals[STATUS_CHANGED] = + g_signal_new ("status-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdChannelClass, + status_changed_signal), + NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, + 1, G_TYPE_INT); + mcd_channel_signals[MEMBERS_ACCEPTED] = + g_signal_new ("members-accepted", G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdChannelClass, + members_accepted_signal), + NULL, + NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* properties */ + g_object_class_install_property (object_class, + PROP_CONNECTION, + g_param_spec_object ("connection", + _ ("McdConnection Object"), + _ ("McdConnection Object from which this channel was created"), + G_TYPE_OBJECT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_TP_CHANNEL, + g_param_spec_object ("tp-channel", + _ ("Telepathy Channel Object"), + _ ("Telepathy Channel Object wrapped by it"), + TELEPATHY_CHAN_TYPE, + G_PARAM_READWRITE /* | + G_PARAM_CONSTRUCT_ONLY */)); + g_object_class_install_property (object_class, PROP_CHANNEL_STATUS, + g_param_spec_enum ("channel-status", + _("Channel status"), + _("Channel status that indicates the state of channel"), + MCD_TYPE_CHANNEL_STATUS, + MCD_CHANNEL_PENDING, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_CHANNEL_OBJECT_PATH, + g_param_spec_string ("channel-object-path", + _ ("Channel dbus object path"), + _ ("DBus Bus name to use by us"), + NULL, + G_PARAM_READWRITE /*| + G_PARAM_CONSTRUCT_ONLY */)); + g_object_class_install_property (object_class, PROP_CHANNEL_TYPE, + g_param_spec_string ("channel-type", + _ ("Channel type"), + _ ("Telepathy channel type"), + NULL, + G_PARAM_READWRITE /*| + G_PARAM_CONSTRUCT_ONLY*/)); + g_object_class_install_property (object_class, PROP_CHANNEL_TYPE_QUARK, + g_param_spec_uint ("channel-type-quark", + _("Telepathy channel type in quark form"), + _("Telepathy channel type in quark form"), + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE /*| + G_PARAM_CONSTRUCT_ONLY*/)); + g_object_class_install_property (object_class, PROP_CHANNEL_HANDLE, + g_param_spec_uint ("channel-handle", + _("Telepathy channel handle"), + _("Telepathy channel handle"), + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE /*| + G_PARAM_CONSTRUCT_ONLY */)); + g_object_class_install_property (object_class, PROP_CHANNEL_HANDLE_TYPE, + g_param_spec_uint ("channel-handle-type", + _("Telepathy channel handle type"), + _("Telepathy channel handle type"), + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE /* | + G_PARAM_CONSTRUCT_ONLY */)); + g_object_class_install_property (object_class, PROP_OUTGOING, + g_param_spec_boolean ("outgoing", + _("Outgoing channel"), + _("True if the channel was requested by us"), + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_REQUESTOR_SERIAL, + g_param_spec_uint ("requestor-serial", + _("Requestor serial number"), + _("Requestor serial number"), + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_REQUESTOR_CLIENT_ID, + g_param_spec_string ("requestor-client-id", + _("Requestor client id"), + _("Requestor client id"), + NULL, G_PARAM_READWRITE)); +} + +static void +mcd_channel_init (McdChannel * obj) +{ + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (obj); + priv->pending_local_members = g_array_new (FALSE, FALSE, + sizeof (guint)); +} + +McdChannel * +mcd_channel_new (TpChan * tp_chan, const gchar *channel_object_path, + const gchar *channel_type, guint channel_handle, + TelepathyHandleType channel_handle_type, gboolean outgoing, + guint requestor_serial, const gchar *requestor_client_id) +{ + McdChannel *obj; + obj = MCD_CHANNEL (g_object_new (MCD_TYPE_CHANNEL, + "channel-object-path", channel_object_path, + "channel-type", channel_type, + "channel-handle", channel_handle, + "channel-handle-type", channel_handle_type, + "outgoing", outgoing, + "requestor-serial", requestor_serial, + "requestor-client-id", requestor_client_id, + "tp-channel", tp_chan, + NULL)); + return obj; +} + +void +mcd_channel_set_status (McdChannel *channel, McdChannelStatus status) +{ + g_return_if_fail(MCD_IS_CHANNEL(channel)); + g_object_set (channel, "channel-status", status, NULL); +} + +McdChannelStatus +mcd_channel_get_status (McdChannel *channel) +{ + return MCD_CHANNEL_PRIV (channel)->status; +} + +gboolean +mcd_channel_get_members_accepted (McdChannel *channel) +{ + return MCD_CHANNEL_PRIV (channel)->members_accepted; +} + +const gchar * +mcd_channel_get_channel_type (McdChannel *channel) +{ + return MCD_CHANNEL_PRIV (channel)->channel_type; +} + +GQuark +mcd_channel_get_channel_type_quark (McdChannel *channel) +{ + return MCD_CHANNEL_PRIV (channel)->channel_type_quark; +} + +const gchar * +mcd_channel_get_object_path (McdChannel *channel) +{ + return MCD_CHANNEL_PRIV (channel)->channel_object_path; +} + +guint +mcd_channel_get_handle (McdChannel *channel) +{ + return MCD_CHANNEL_PRIV (channel)->channel_handle; +} + +TelepathyHandleType +mcd_channel_get_handle_type (McdChannel *channel) +{ + return MCD_CHANNEL_PRIV (channel)->channel_handle_type; +} + +/* Similar to the another helper, but uses the array version (InspectHandles), free with g_strfreev*/ +static gchar ** +_contact_handles_to_strings (TpConn * conn, guint handle_type, + const GArray * handles) +{ + gchar **contact_addresses = NULL; + GError *error = NULL; + + tp_conn_inspect_handles (DBUS_G_PROXY (conn), handle_type, handles, + &contact_addresses, &error); + + if (error) + { + g_warning ("Error %s getting contacts for %u handles", + error->message, handles->len); + g_error_free (error); + } + + return contact_addresses; +} + +GPtrArray* +mcd_channel_get_members (McdChannel *channel) +{ + GObject *connection; + TpConn *tp_connection; + GPtrArray *members = NULL; + gchar *contact_address; + + g_return_val_if_fail(MCD_IS_CHANNEL(channel), NULL); + g_object_get (G_OBJECT (channel), "connection", + &connection, NULL); + g_object_get (connection, "tp-connection", + &tp_connection, NULL); + + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (channel); + + g_assert (priv->tp_chan != NULL); + + g_debug ("Creating members list"); + if (priv->channel_handle_type == TP_CONN_HANDLE_TYPE_CONTACT) + { + /* TODO: Now that we have only the multi-handle + * inspection call, we might be able to unify this + * and the case below */ + + gchar **addresses; + GArray *handles = + g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); + g_debug ("Single contact"); + + g_array_insert_val (handles, 0, + priv->channel_handle); + + addresses = + _contact_handles_to_strings (tp_connection, + priv->channel_handle_type, + handles); + g_array_free (handles, TRUE); + if (!addresses || !addresses[0]) + { + g_warning ("Unable to get contact address"); + } + else + { + contact_address = g_strdup (addresses[0]); + g_strfreev (addresses); + /*Creating the members array and adding contact_address there */ + members = g_ptr_array_new (); + g_ptr_array_add (members, contact_address); + } + } + + else /*Group channel */ + { + DBusGProxy *group_proxy; + GArray *contact_handles = NULL; + GError *error = NULL; + + g_debug ("Multiple contacts"); + + /* get a group proxy from the channel */ + group_proxy = tp_chan_get_interface (priv->tp_chan, + TELEPATHY_CHAN_IFACE_GROUP_QUARK); + + tp_chan_iface_group_get_members (DBUS_G_PROXY (group_proxy), + &contact_handles, &error); + + if (error) + { + g_warning ("Unable to get group members: %s", error->message); + g_error_free (error); + } + else if (!contact_handles || !contact_handles->len) + { + g_warning ("No contact handles"); + } + else + { /*Get the real user names */ + gchar **contact_addresses; + int i; + + members = g_ptr_array_new (); + + g_debug ("Transforming %i contacts into strings", + contact_handles->len); + + contact_addresses = + _contact_handles_to_strings (tp_connection, + TP_CONN_HANDLE_TYPE_CONTACT, + contact_handles); + + if (contact_addresses) + { + for (i = 0; i < contact_handles->len; i++) + { + g_ptr_array_add (members, + g_strdup (contact_addresses[i])); + } + g_strfreev (contact_addresses); + } + else + { + g_warning ("Unable to get contact address(multi)"); + } + } + + if (contact_handles) + { + g_array_free (contact_handles, TRUE); + } + } + + g_object_unref (connection); + g_object_unref (tp_connection); + return members; +} + +/** + * mcd_channel_get_name: + * @channel: the #McdChannel. + * + * Get the Telepathy name of @channel (calls InspectHandles on the channel + * handle). + * + * Returns: a const string holding the channel name. + */ +const gchar * +mcd_channel_get_name (McdChannel *channel) +{ + McdChannelPrivate *priv = MCD_CHANNEL_PRIV (channel); + + if (!priv->channel_name) + { + GObject *connection = NULL; + TpConn *tp_conn = NULL; + GArray *request_handles; + gchar **handle_names = NULL; + GError *error = NULL; + + request_handles = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (request_handles, priv->channel_handle); + + g_object_get (channel, "connection", &connection, NULL); + g_object_get (connection, "tp-connection", &tp_conn, NULL); + if (!tp_conn_inspect_handles (DBUS_G_PROXY (tp_conn), + priv->channel_handle_type, + request_handles, + &handle_names, &error)) + { + g_warning ("%s: InspectHandles failed: %s", + G_STRFUNC, error->message); + g_error_free (error); + } + else + priv->channel_name = handle_names[0]; + + g_object_unref (connection); + g_object_unref (tp_conn); + g_array_free (request_handles, TRUE); + /* free only the pointer array, not the string itself */ + g_free (handle_names); + } + + return priv->channel_name; +} + diff --git a/src/mcd-channel.h b/src/mcd-channel.h new file mode 100644 index 00000000..c668e508 --- /dev/null +++ b/src/mcd-channel.h @@ -0,0 +1,106 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_CHANNEL_H +#define MCD_CHANNEL_H + +#include <glib.h> +#include <glib-object.h> +#include <libtelepathy/tp-chan.h> +#include <libtelepathy/tp-constants.h> + +#include "mcd-mission.h" + +G_BEGIN_DECLS + +#define MCD_TYPE_CHANNEL (mcd_channel_get_type ()) +#define MCD_CHANNEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_CHANNEL, McdChannel)) +#define MCD_CHANNEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_CHANNEL, McdChannelClass)) +#define MCD_IS_CHANNEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_CHANNEL)) +#define MCD_IS_CHANNEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_CHANNEL)) +#define MCD_CHANNEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_CHANNEL, McdChannelClass)) + +typedef struct _McdChannel McdChannel; +typedef struct _McdChannelClass McdChannelClass; + +typedef enum +{ + MCD_CHANNEL_PENDING, /* Telepathy channel is not yet created */ + MCD_CHANNEL_DISPATCHING, /* Telepathy channel is created and waiting dispatch */ + MCD_CHANNEL_DISPATCHED, /* Channel has been dispatched to handler */ + MCD_CHANNEL_FAILED, /* Channel could not be dispached to handler, dying */ + +} McdChannelStatus; + +struct _McdChannel +{ + McdMission parent; +}; + +struct _McdChannelClass +{ + McdMissionClass parent_class; + + /* signals */ + void (*status_changed_signal) (McdChannel * channel, + McdChannelStatus status); + void (*members_accepted_signal) (McdChannel *channel); +}; + +struct mcd_channel_request +{ + const gchar *account_name; + const gchar *channel_type; + guint channel_handle; + const gchar *channel_handle_string; + gint channel_handle_type; + guint requestor_serial; + const gchar *requestor_client_id; +}; + +GType mcd_channel_get_type (void); + +McdChannel *mcd_channel_new (TpChan *channel, + const gchar *channel_object_path, + const gchar *channel_type, + guint channel_handle, + TelepathyHandleType channel_handle_type, + gboolean outgoing, + guint requestor_serial, + const gchar *requestor_client_id); + +void mcd_channel_set_status (McdChannel *channel, McdChannelStatus status); +McdChannelStatus mcd_channel_get_status (McdChannel * channel); +gboolean mcd_channel_get_members_accepted (McdChannel *channel); +const gchar* mcd_channel_get_channel_type (McdChannel *channel); +GQuark mcd_channel_get_channel_type_quark (McdChannel *channel); +const gchar* mcd_channel_get_object_path (McdChannel *channel); +guint mcd_channel_get_handle (McdChannel *channel); +TelepathyHandleType mcd_channel_get_handle_type (McdChannel *channel); +gint mcd_channel_get_flags (McdChannel *channel); +GPtrArray* mcd_channel_get_members (McdChannel *channel); +const gchar *mcd_channel_get_name (McdChannel *channel); + +G_END_DECLS +#endif /* MCD_CHANNEL_H */ diff --git a/src/mcd-connection.c b/src/mcd-connection.c new file mode 100644 index 00000000..9d2c0e35 --- /dev/null +++ b/src/mcd-connection.c @@ -0,0 +1,1980 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <string.h> +#include <sys/types.h> +#include <sched.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include <string.h> +#include <stdlib.h> +#include <dlfcn.h> + +#include <libmissioncontrol/mc-manager.h> +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-protocol.h> +#include <libmissioncontrol/mc-profile.h> +#include <libtelepathy/tp-connmgr.h> +#include <libtelepathy/tp-interfaces.h> +#include <libtelepathy/tp-constants.h> +#include <libtelepathy/tp-chan.h> +#include <libtelepathy/tp-conn.h> +#include <libtelepathy/tp-conn-gen.h> +#include <libtelepathy/tp-conn-iface-presence-gen.h> +#include <libtelepathy/tp-conn-iface-capabilities-gen.h> +#include <libtelepathy/tp-conn-iface-avatars-gen.h> +#include <libtelepathy/tp-helpers.h> + +#include "mcd-connection.h" +#include "mcd-channel.h" + +#define MAX_REF_PRESENCE 4 + +#define MCD_CONNECTION_PRIV(mcdconn) (G_TYPE_INSTANCE_GET_PRIVATE ((mcdconn), \ + MCD_TYPE_CONNECTION, \ + McdConnectionPrivate)) + +G_DEFINE_TYPE (McdConnection, mcd_connection, MCD_TYPE_OPERATION); + +/* Private */ +typedef struct +{ + /* DBUS connection */ + DBusGConnection *dbus_connection; + + /* DBus bus name */ + gchar *bus_name; + + /* Presence frame */ + McdPresenceFrame *presence_frame; + + /* Channel dispatcher */ + McdDispatcher *dispatcher; + + /* Account */ + McAccount *account; + + /* Associated profile */ + /* McProfile *profile; */ + + /* Telepathy connection manager */ + TpConnMgr *tp_conn_mgr; + + /* Telepathy connection */ + TpConn *tp_conn; + + /* Presence proxy */ + DBusGProxy *presence_proxy; + + DBusGProxy *avatars_proxy; + + /* Capabilities proxy */ + DBusGProxy *capabilities_proxy; + + /* Capabilities timer */ + guint capabilities_timer; + + guint reconnect_timer; /* timer for reconnection */ + guint reconnect_interval; + + /* Supported presences */ + GArray *recognized_presence_info_array; + struct presence_info *presence_to_set[LAST_MC_PRESENCE - 1]; + + /* List of pending channels which has been requested to telepathy, + * but telepathy hasn't yet responded with the channel object + */ + GHashTable *pending_channels; + + TelepathyConnectionStatusReason abort_reason; + gboolean got_capabilities; + + gboolean is_disposed; + +} McdConnectionPrivate; + +struct presence_info +{ + gchar *presence_str; + gboolean allow_message; +}; + +enum +{ + PROP_0, + PROP_DBUS_CONNECTION, + PROP_BUS_NAME, + PROP_TP_MANAGER, + PROP_TP_CONNECTION, + PROP_ACCOUNT, + PROP_PRESENCE_FRAME, + PROP_DISPATCHER, +}; + +/* This table lists the Telepathy well-known statuses and the corresponding + * McPresence values; the order in which the items appear is only important for + * those statuses which map to the same McPresence value: for them, the first + * ones will be preferred. */ +static const struct _presence_mapping { + gchar *presence_str; + McPresence mc_presence; +} presence_mapping[] = { + { "offline", MC_PRESENCE_OFFLINE }, + { "available", MC_PRESENCE_AVAILABLE }, + { "away", MC_PRESENCE_AWAY }, + { "xa", MC_PRESENCE_EXTENDED_AWAY }, + { "hidden", MC_PRESENCE_HIDDEN }, + { "dnd", MC_PRESENCE_DO_NOT_DISTURB }, + { "brb", MC_PRESENCE_AWAY }, + { "busy", MC_PRESENCE_DO_NOT_DISTURB }, + { NULL, 0 }, +}; + +static const McPresence fallback_presence + [LAST_MC_PRESENCE - 1][MAX_REF_PRESENCE] = { + { 0 }, /* MC_PRESENCE_OFFLINE */ + { 0 }, /* MC_PRESENCE_AVAILABLE */ + { MC_PRESENCE_AVAILABLE, 0 }, /* MC_PRESENCE_AWAY */ + { MC_PRESENCE_AWAY, MC_PRESENCE_AVAILABLE, 0 }, /* MC_PRESENCE_EXTENDED_AWAY */ + { MC_PRESENCE_DO_NOT_DISTURB, MC_PRESENCE_EXTENDED_AWAY, MC_PRESENCE_AVAILABLE, 0 }, /* MC_PRESENCE_HIDDEN */ + { 0 } /* MC_PRESENCE_DO_NOT_DISTURB */ +}; + +struct request_id { + guint requestor_serial; + const gchar *requestor_client_id; +}; + +struct capabilities_wait_data { + GError *error; /* error originally received when channel request failed */ + McdChannel *channel; + McdConnectionPrivate *priv; +}; + +static void mcd_async_request_chan_callback (DBusGProxy *proxy, + gchar *channel_path, + GError *error, + gpointer user_data); +static GError * map_tp_error_to_mc_error (McdChannel *channel, GError *tp_error); +static void _mcd_connection_setup (McdConnection * connection); +static void _mcd_connection_release_tp_connection (McdConnection *connection); + +static McPresence presence_str_to_enum (const gchar *presence_str) +{ + const struct _presence_mapping *mapping; + for (mapping = presence_mapping; mapping->presence_str; mapping++) + if (strcmp (presence_str, mapping->presence_str) == 0) + return mapping->mc_presence; + return MC_PRESENCE_UNSET; +} + +/* Free dynamic members and presence_info itself */ +static void +_mcd_connection_free_presence_info (McdConnection * conn) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (conn); + + if (priv->recognized_presence_info_array != NULL) + { + struct presence_info *pi; + gint i; + + for (i = 0; i < priv->recognized_presence_info_array->len; i++) + { + pi = &g_array_index (priv->recognized_presence_info_array, + struct presence_info, i); + g_free (pi->presence_str); + } + g_array_free (priv->recognized_presence_info_array, TRUE); + priv->recognized_presence_info_array = NULL; + } +} + +/* Fill empty presence_to_set elements with fallback presence values */ +static void +_mcd_connection_set_fallback_presences (McdConnection * connection, gint i) +{ + gint j; + McdConnectionPrivate *priv; + + g_return_if_fail (MCD_IS_CONNECTION (connection)); + + priv = MCD_CONNECTION_PRIV (connection); + + for (j = 0; j < MAX_REF_PRESENCE; j++) + { + struct presence_info *presence; + + presence = priv->presence_to_set[fallback_presence[i][j] - 1]; + if (presence != NULL) + { + priv->presence_to_set[i] = presence; + g_debug ("Fallback for McPresence %d set to %s", + i + 1, presence->presence_str); + return; + } + } +} + +/* Used for initializing recognized_presence_info_array. */ +static void +recognize_presence (gpointer key, gpointer value, gpointer user_data) +{ + guint telepathy_enum; + GHashTable *ht; + struct presence_info pi; + GValueArray *status; + McdConnectionPrivate *priv = (McdConnectionPrivate *) user_data; + gint j; + + status = (GValueArray *) value; + + /* Pull out the arguments of Telepathy GetStatuses */ + ht = (GHashTable *) g_value_get_boxed (g_value_array_get_nth (status, 3)); + pi.allow_message = g_hash_table_lookup (ht, "message") ? TRUE : FALSE; + + /* Look up which MC_PRESENCE this presence string corresponds */ + pi.presence_str = g_strdup ((const gchar *) key); + + j = presence_str_to_enum (pi.presence_str); + if (j == MC_PRESENCE_UNSET) + { + /* Didn't find match by comparing strings so map using the telepathy enum. */ + telepathy_enum = g_value_get_uint (g_value_array_get_nth (status, 0)); + switch (telepathy_enum) + { + case TP_CONN_PRESENCE_TYPE_OFFLINE: + j = MC_PRESENCE_OFFLINE; + break; + case TP_CONN_PRESENCE_TYPE_AVAILABLE: + j = MC_PRESENCE_AVAILABLE; + break; + case TP_CONN_PRESENCE_TYPE_AWAY: + j = MC_PRESENCE_AWAY; + break; + case TP_CONN_PRESENCE_TYPE_EXTENDED_AWAY: + j = MC_PRESENCE_EXTENDED_AWAY; + break; + case TP_CONN_PRESENCE_TYPE_HIDDEN: + j = MC_PRESENCE_HIDDEN; + break; + default: + g_debug ("Unknown Telepathy presence type. Presence %s " + "with Telepathy enum %d ignored.", pi.presence_str, + telepathy_enum); + g_free (pi.presence_str); + return; + break; + } + } + g_array_append_val (priv->recognized_presence_info_array, pi); +} + +static void +enable_well_known_presences (McdConnectionPrivate *priv) +{ + const struct _presence_mapping *mapping; + struct presence_info *pi; + + /* Loop the presence_mappinges; if one of the basic McPresences is not set, + * check if an mapping is supported by the connection and, if so, use it */ + for (mapping = presence_mapping; mapping->presence_str; mapping++) + { + if (priv->presence_to_set[mapping->mc_presence - 1] == NULL) + { + gint i; + /* see if this presence is supported by the connection */ + for (i = 0; i < priv->recognized_presence_info_array->len; i++) + { + pi = &g_array_index (priv->recognized_presence_info_array, + struct presence_info, i); + if (strcmp (pi->presence_str, mapping->presence_str) == 0) + { + g_debug ("Using %s status for McPresence %d", + mapping->presence_str, mapping->mc_presence); + /* Presence values used when setting the presence status */ + priv->presence_to_set[mapping->mc_presence - 1] = pi; + break; + } + } + } + } +} + +static void +_mcd_connection_set_presence (McdConnection * connection, + McPresence presence, + const gchar * presence_message) +{ + const gchar *presence_str; + GHashTable *presence_ht; + GHashTable *params_ht; + struct presence_info *supported_presence_info; + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + GError *error = NULL; + GValue msg_gval = { 0, }; + + if (!priv->tp_conn) + { + _mcd_connection_setup (connection); + return; + } + g_return_if_fail (TELEPATHY_IS_CONN (priv->tp_conn)); + g_return_if_fail (priv->bus_name != NULL); + + if (priv->presence_proxy == NULL) + { + GHashTable *status_hash; + guint i; + GError *error = NULL; + + /* Gets a reference to it */ + priv->presence_proxy = + tp_conn_get_interface (priv->tp_conn, + TELEPATHY_CONN_IFACE_PRESENCE_QUARK); + if (priv->presence_proxy == NULL) + { + g_warning ("%s: Account %s has no presence interface", G_STRFUNC, + mc_account_get_unique_name (priv->account)); + return; + } + g_object_add_weak_pointer (G_OBJECT (priv->presence_proxy), (gpointer *)&priv->presence_proxy); + + if (tp_conn_iface_presence_get_statuses (priv->presence_proxy, + &status_hash, &error) == FALSE) + { + g_warning ("%s: Get statuses failed for account %s: %s", G_STRFUNC, + mc_account_get_unique_name (priv->account), + error->message); + g_error_free (error); + return; + } + + /* Everything went well so pack the available presences into + * connection info + */ + /* Initialize presence info array and pointers for setting presences */ + for (i = 0; i < LAST_MC_PRESENCE - 1; i++) + priv->presence_to_set[i] = NULL; + priv->recognized_presence_info_array = + g_array_new (FALSE, FALSE, sizeof (struct presence_info)); + g_hash_table_foreach (status_hash, recognize_presence, priv); + g_hash_table_destroy (status_hash); + + enable_well_known_presences (priv); + + /* Set the fallback presence values */ + for (i = 0; i < LAST_MC_PRESENCE - 1; i++) + { + if (priv->presence_to_set[i] == NULL) + _mcd_connection_set_fallback_presences (connection, i); + } + } + + supported_presence_info = priv->presence_to_set[presence - 1]; + + if (supported_presence_info == NULL) + { + g_debug ("No matching supported presence found. " + "Account presence has not been changed."); + return; + } + + presence_str = g_strdup (supported_presence_info->presence_str); + presence = presence_str_to_enum (supported_presence_info->presence_str); + + /* Add the presence by libtelepathy */ + /* FIXME: what should we do when this is NULL? */ + if (presence_str != NULL) + { + presence_ht = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + params_ht = g_hash_table_new (g_str_hash, g_str_equal); + + /* + * Note that we silently ignore the message if Connection Manager + * doesn't support it for this presence state! + */ + if (supported_presence_info->allow_message && presence_message) + { + g_value_init (&msg_gval, G_TYPE_STRING); + g_value_set_string (&msg_gval, presence_message); + g_hash_table_insert (params_ht, "message", &msg_gval); + } + + g_hash_table_insert (presence_ht, (gpointer) presence_str, params_ht); + + if (tp_conn_iface_presence_set_status (priv->presence_proxy, + presence_ht, &error) == FALSE) + { + g_warning ("%s: Setting presence of %s to %s (%d) failed: %s", + G_STRFUNC, mc_account_get_unique_name (priv->account), + presence_str, presence, error->message); + } + else + { + mcd_presence_frame_set_account_presence (priv->presence_frame, + priv->account, + presence, + presence_message); + } + + if (supported_presence_info->allow_message && presence_message) + g_value_unset (&msg_gval); + + g_hash_table_destroy (presence_ht); + g_hash_table_destroy (params_ht); + } +} + + +/* This handler should update the presence of the tp_connection. + * Note, that the only presence transition not served by this function + * is getting to non-offline state since when presence is offline this object + * does not exist. + * + * So, here we just submit the request to the tp_connection object. The return off + * the operation is handled by (yet to be written) handler + */ +static void +on_presence_requested (McdPresenceFrame * presence_frame, + McPresence presence, + const gchar * presence_message, gpointer user_data) +{ + McdConnection *connection = MCD_CONNECTION (user_data); + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + GError *error = NULL; + + g_debug ("Presence requested: %d", presence); + if (presence == TP_CONN_PRESENCE_TYPE_OFFLINE || + presence == TP_CONN_PRESENCE_TYPE_UNSET) + { + /* Connection Proxy */ + priv->abort_reason = TP_CONN_STATUS_REASON_REQUESTED; + mcd_mission_disconnect (MCD_MISSION (connection)); + if (priv->tp_conn) + tp_conn_disconnect (DBUS_G_PROXY (priv->tp_conn), &error); + } + else + { + _mcd_connection_set_presence (connection, presence, presence_message); + } +} + +static void +_mcd_connection_new_channel_cb (DBusGProxy * tp_conn_proxy, + const gchar * chan_obj_path, + const gchar * chan_type, + guint handle_type, + guint handle, + gboolean suppress_handler, + McdConnection * connection) +{ + TpChan *tp_chan; + McdChannel *channel; + McdConnectionPrivate *priv; + + priv = MCD_CONNECTION_PRIV (connection); + + /* ignore all our own requests (they have always suppress_handler = 1) as + * well as other requests for which our intervention has not been requested + * */ + if (suppress_handler) return; + + /* g_return_if_fail (TELEPATHY_IS_CHAN (tp_chan)); */ + tp_chan = tp_chan_new (priv->dbus_connection, priv->bus_name, + chan_obj_path, chan_type, handle_type, handle); + + /* It's an incoming channel, so we create a new McdChannel for it */ + channel = mcd_channel_new (tp_chan, + chan_obj_path, + chan_type, + handle, + handle_type, + FALSE, /* incoming */ + 0, 0); /* There is no requestor, obviously */ + + mcd_operation_take_mission (MCD_OPERATION (connection), + MCD_MISSION (channel)); + + /* Channel about to be dispatched */ + mcd_channel_set_status (channel, MCD_CHANNEL_DISPATCHING); + + /* Dispatch the incoming channel */ + mcd_dispatcher_send (priv->dispatcher, channel); + + g_object_unref (tp_chan); +} + +static void +_foreach_channel_remove (McdMission * mission, McdOperation * operation) +{ + g_assert (MCD_IS_MISSION (mission)); + g_assert (MCD_IS_OPERATION (operation)); + + mcd_operation_remove_mission (operation, mission); +} + +static void +on_capabilities_changed (DBusGProxy *tp_conn_proxy, + GPtrArray *caps, McdChannel *channel) +{ + McdConnection *connection; + McdConnectionPrivate *priv; + gboolean found = FALSE; + GType type; + gchar *chan_type; + guint chan_handle, chan_handle_type; + DBusGProxyCall *call; + gint i; + + connection = g_object_get_data (G_OBJECT (channel), "temporary_connection"); + priv = MCD_CONNECTION_PRIV (connection); + + g_debug ("%s: got capabilities for channel %p handle %d, type %s", + G_STRFUNC, channel, mcd_channel_get_handle (channel), mcd_channel_get_channel_type (channel)); + type = dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, + G_TYPE_UINT, G_TYPE_INVALID); + for (i = 0; i < caps->len; i++) + { + GValue cap = { 0 }; + + g_value_init (&cap, type); + g_value_set_static_boxed (&cap, g_ptr_array_index(caps, i)); + dbus_g_type_struct_get (&cap, 0, &chan_handle, 1, &chan_type, G_MAXUINT); + if (chan_handle == mcd_channel_get_handle (channel) && + strcmp (chan_type, mcd_channel_get_channel_type (channel)) == 0) + { + found = TRUE; + break; + } + g_free (chan_type); + } + + if (!found) return; + /* Return also if the "tp_chan_call" data is set (which means that a + * request for this channel has already been made) */ + if (g_object_get_data (G_OBJECT (channel), "tp_chan_call") != NULL) + return; + chan_handle_type = mcd_channel_get_handle_type (channel); + g_debug ("%s: requesting channel again (type = %s, handle_type = %u, handle = %u)", + G_STRFUNC, chan_type, chan_handle_type, chan_handle); + call = tp_conn_request_channel_async(DBUS_G_PROXY(priv->tp_conn), + chan_type, + chan_handle_type, + chan_handle, TRUE, + mcd_async_request_chan_callback, + channel); + g_object_set_data (G_OBJECT (channel), "tp_chan_call", call); + g_free (chan_type); +} + +static gboolean +on_channel_capabilities_timeout (guint channel_handle, McdChannel *channel, + McdConnection *connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + struct capabilities_wait_data *cwd; + GError *mc_error; + + cwd = g_object_get_data (G_OBJECT (channel), "error_on_creation"); + if (!cwd) return FALSE; + + /* We reach this point if this channel was waiting for capabilities; we + * abort it and return the original error */ + g_debug ("%s: channel %p timed out, returning error!", G_STRFUNC, channel); + + mc_error = map_tp_error_to_mc_error (channel, cwd->error); + g_signal_emit_by_name (G_OBJECT(priv->dispatcher), "dispatch-failed", + channel, mc_error); + g_error_free (mc_error); + g_object_set_data (G_OBJECT (channel), "error_on_creation", NULL); + + /* No abort on channel, because we are the only one holding the only + * reference to this temporary channel. + */ + return TRUE; +} + +static gboolean +on_capabilities_timeout (McdConnection *connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + g_debug ("%s: got_capabilities is %d", G_STRFUNC, priv->got_capabilities); + priv->got_capabilities = TRUE; + g_hash_table_foreach_remove (priv->pending_channels, + (GHRFunc)on_channel_capabilities_timeout, + connection); + priv->capabilities_timer = 0; + return FALSE; +} + +static void +_mcd_connection_setup_capabilities (McdConnection *connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + GPtrArray *capabilities; + GError *error = NULL; + const gchar *remove = NULL; + + priv->capabilities_proxy = tp_conn_get_interface (priv->tp_conn, + TELEPATHY_CONN_IFACE_CAPABILITIES_QUARK); + if (!priv->capabilities_proxy) + { + g_debug ("%s: connection does not support capabilities interface", G_STRFUNC); + priv->got_capabilities = TRUE; + return; + } + g_object_add_weak_pointer (G_OBJECT (priv->capabilities_proxy), (gpointer *)&priv->capabilities_proxy); + capabilities = mcd_dispatcher_get_channel_capabilities (priv->dispatcher); + g_debug ("%s: advertising capabilities", G_STRFUNC); + tp_conn_iface_capabilities_advertise_capabilities (priv->capabilities_proxy, + capabilities, + &remove, NULL, + &error); + if (error) + { + g_warning ("%s: AdvertiseCapabilities failed: %s", G_STRFUNC, error->message); + g_error_free(error); + } + + if (priv->capabilities_timer) + { + g_warning ("This connection still has dangling capabilities timer on"); + g_source_remove (priv->capabilities_timer); + } + priv->capabilities_timer = + g_timeout_add (1000 * 5, (GSourceFunc)on_capabilities_timeout, connection); +} + +static void +_mcd_connection_get_normalized_name (McdConnectionPrivate *priv) +{ + GArray *handles; + gchar **names = NULL; + GError *error = NULL; + guint self_handle; + + tp_conn_get_self_handle (DBUS_G_PROXY (priv->tp_conn), &self_handle, + &error); + if (error) + { + g_warning ("%s: tp_conn_get_self_handle failed: %s", + G_STRFUNC, error->message); + g_error_free (error); + return; + } + + handles = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); + g_array_append_val (handles, self_handle); + tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn), + TP_CONN_HANDLE_TYPE_CONTACT, handles, + &names, &error); + g_array_free (handles, TRUE); + if (error) + { + g_warning ("%s: tp_conn_inspect_handles failed: %s", + G_STRFUNC, error->message); + g_error_free (error); + return; + } + if (names) + { + g_return_if_fail (names[0] != 0); + mc_account_set_normalized_name (priv->account, names[0]); + g_strfreev (names); + } +} + +static void +set_avatar_cb (DBusGProxy *proxy, char *token, GError *error, gpointer userdata) +{ + McdConnectionPrivate *priv = (McdConnectionPrivate *)userdata; + if (error) + { + g_warning ("%s: error: %s", G_STRFUNC, error->message); + g_error_free (error); + return; + } + g_debug ("%s: received token: %s", G_STRFUNC, token); + mc_account_set_avatar_token (priv->account, token); + g_free (token); +} + +static void +clear_avatar_cb (DBusGProxy *proxy, GError *error, gpointer userdata) +{ + gchar *filename = userdata; + if (!error) + { + g_debug ("%s: Clear avatar succeeded, removing %s", G_STRFUNC, filename); + g_remove (filename); + } + else + { + g_warning ("%s: error: %s", G_STRFUNC, error->message); + g_error_free (error); + } + g_free (filename); +} + +static void +_mcd_connection_setup_avatar (McdConnectionPrivate *priv) +{ + gchar *filename, *mime_type, *token; + GArray *avatar; + GError *error = NULL; + + priv->avatars_proxy = tp_conn_get_interface (priv->tp_conn, + TELEPATHY_CONN_IFACE_AVATARS_QUARK); + if (!priv->avatars_proxy) + { + g_debug ("%s: connection does not support avatar interface", G_STRFUNC); + return; + } + g_object_ref (priv->avatars_proxy); + if (!mc_account_get_avatar (priv->account, &filename, &mime_type, &token)) + { + g_debug ("%s: mc_account_get_avatar() returned FALSE", G_STRFUNC); + return; + } + + /* if the token is set, we have nothing to do */ + if (!token && filename && g_file_test (filename, G_FILE_TEST_EXISTS)) + { + avatar = g_array_new (FALSE, FALSE, 1); + g_return_if_fail (avatar != NULL); + if (g_file_get_contents (filename, &avatar->data, &avatar->len, &error)) + { + if (avatar->len > 0) + tp_conn_iface_avatars_set_avatar_async (priv->avatars_proxy, + avatar, mime_type, + set_avatar_cb, priv); + else + tp_conn_iface_avatars_clear_avatar_async(priv->avatars_proxy, + clear_avatar_cb, + g_strdup (filename)); + + } + else + { + g_debug ("%s: error reading %s: %s", G_STRFUNC, filename, error->message); + g_error_free (error); + } + g_array_free (avatar, TRUE); + } + + g_free (filename); + g_free (mime_type); + g_free (token); +} + +static gboolean +mcd_connection_reconnect (McdConnection *connection) +{ + g_debug ("%s: %p", G_STRFUNC, connection); + _mcd_connection_setup (connection); + return FALSE; +} + +static void +_mcd_connection_status_changed_cb (DBusGProxy * tp_conn_proxy, + TelepathyConnectionStatus conn_status, + TelepathyConnectionStatusReason + conn_reason, McdConnection * connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + g_debug ("%s: status_changed called from tp (%d)", G_STRFUNC, conn_status); + + switch (conn_status) + { + case TP_CONN_STATUS_CONNECTING: + mcd_presence_frame_set_account_status (priv->presence_frame, + priv->account, + conn_status, conn_reason); + priv->abort_reason = TP_CONN_STATUS_REASON_NONE_SPECIFIED; + break; + case TP_CONN_STATUS_CONNECTED: + { + const gchar *presence_message; + McPresence requested_presence; + + mcd_presence_frame_set_account_status (priv->presence_frame, + priv->account, + conn_status, conn_reason); + requested_presence = + mcd_presence_frame_get_requested_presence (priv->presence_frame); + presence_message = + mcd_presence_frame_get_requested_presence_message (priv->presence_frame); + _mcd_connection_set_presence (connection, requested_presence, + presence_message); + _mcd_connection_setup_capabilities (connection); + _mcd_connection_setup_avatar (priv); + _mcd_connection_get_normalized_name (priv); + priv->reconnect_interval = 30 * 1000; /* reset it to 30 seconds */ + } + break; + case TP_CONN_STATUS_DISCONNECTED: + /* Connection could die during account status updated if its + * manager is the only one holding the reference to it (manager will + * remove the connection from itself). To ensure we get a chance to + * emit abort signal (there could be others holding a ref to it), we + * will hold a temporary ref to it. + */ + priv->abort_reason = conn_reason; + + /* Destroy any pending timer */ + if (priv->capabilities_timer) + g_source_remove (priv->capabilities_timer); + priv->capabilities_timer = 0; + + /* Notify connection abort */ + if (conn_reason == TP_CONN_STATUS_REASON_NETWORK_ERROR || + conn_reason == TP_CONN_STATUS_REASON_NONE_SPECIFIED) + { + /* we were disconnected by a network error or by a gabble crash (in + * the latter case, we get NoneSpecified as a reason): don't abort + * the connection but try to reconnect later */ + _mcd_connection_release_tp_connection (connection); + priv->reconnect_timer = g_timeout_add (priv->reconnect_interval, + (GSourceFunc)mcd_connection_reconnect, + connection); + priv->reconnect_interval *= 2; + if (priv->reconnect_interval >= 30 * 60 * 1000) + /* no more than 30 minutes! */ + priv->reconnect_interval = 30 * 60 * 1000; + /* FIXME HACK: since we want presence-applet to immediately start + * displaying a blinking icon, we must set the account status to + * CONNECTING now */ + mcd_presence_frame_set_account_status (priv->presence_frame, + priv->account, + TP_CONN_STATUS_CONNECTING, + TP_CONN_STATUS_REASON_REQUESTED); + return; + } + + g_object_ref (connection); + /* Notify connection abort */ + mcd_mission_abort (MCD_MISSION (connection)); + g_object_unref (connection); + break; + default: + g_warning ("Unknown telepathy connection status"); + } +} + +static gint +disconnect_on_error_idle (McdConnection *connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + /* Just synthesize the missing signal. */ + _mcd_connection_status_changed_cb (DBUS_G_PROXY (priv->tp_conn), + TP_CONN_STATUS_DISCONNECTED, + TP_CONN_STATUS_REASON_NONE_SPECIFIED, + connection); + return FALSE; +} + +static void proxy_destroyed (DBusGProxy *tp_conn, McdConnection *connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + g_debug ("Proxy destroyed!"); + priv->tp_conn = NULL; + _mcd_connection_status_changed_cb (tp_conn, + TP_CONN_STATUS_DISCONNECTED, + TP_CONN_STATUS_REASON_NONE_SPECIFIED, + connection); + g_object_unref (tp_conn); +} + +static void +_mcd_connection_setup (McdConnection * connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + g_return_if_fail (priv->bus_name); + g_return_if_fail (priv->tp_conn_mgr); + g_return_if_fail (priv->account); + + if (priv->reconnect_timer) + { + g_source_remove (priv->reconnect_timer); + priv->reconnect_timer = 0; + } + + /* FIXME HACK: the correct test is + + if (mcd_connection_get_connection_status (connection) == + TP_CONN_STATUS_DISCONNECTED) + * but since we set the account status to CONNECTING as soon as we got + * disconnected by a network error, we must accept that status, too */ + if (mcd_connection_get_connection_status (connection) != + TP_CONN_STATUS_CONNECTED) + { + TelepathyConnectionStatus conn_status; + + /* FIXME: Can we do it easier? */ + GHashTable *params; + McProfile *profile; + const gchar *protocol_name; + const gchar *account_name; + + profile = mc_account_get_profile (priv->account); + protocol_name = mc_profile_get_protocol_name (profile); + account_name = mc_account_get_unique_name (priv->account); + + g_debug ("%s: Trying connect account: %s", + G_STRFUNC, (gchar *) account_name); + + params = mc_account_get_params (priv->account); + + /* FIXME: Libtelepathy has non-const on the name. Why's that? */ + priv->tp_conn = tp_connmgr_new_connection (priv->tp_conn_mgr, + params, + (gchar *) protocol_name); + g_hash_table_destroy (params); + g_object_unref (profile); + + mcd_presence_frame_set_account_status (priv->presence_frame, + priv->account, + TP_CONN_STATUS_CONNECTING, + TP_CONN_STATUS_REASON_REQUESTED); + if (!priv->tp_conn) + { + g_warning ("%s: tp_connmgr_new_connection returned NULL", G_STRFUNC); + mcd_presence_frame_set_account_status (priv->presence_frame, + priv->account, + TP_CONN_STATUS_DISCONNECTED, + TP_CONN_STATUS_REASON_NETWORK_ERROR); + return; + } + + /* Setup signals */ + g_signal_connect (priv->tp_conn, "destroy", + G_CALLBACK (proxy_destroyed), connection); + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", + G_CALLBACK + (_mcd_connection_new_channel_cb), + connection, NULL); + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_conn), + "StatusChanged", + G_CALLBACK + (_mcd_connection_status_changed_cb), + connection, NULL); + + /* We might have lost a StatusChanged signal before we connect to + * it because DISCONNECTION happened so fast. + */ + tp_conn_get_status (DBUS_G_PROXY (priv->tp_conn), &conn_status, NULL); + if (conn_status == TP_CONN_STATUS_DISCONNECTED) + { + g_debug ("Connection is in DISCONNECTED state!"); + g_idle_add ((GSourceFunc)disconnect_on_error_idle, connection); + } + } + else + { + g_debug ("%s: Not connecting because not disconnected (%i)", + G_STRFUNC, mcd_connection_get_connection_status (connection)); + return; + } +} + +static void +_mcd_connection_finalize (GObject * object) +{ + McdConnection *connection = MCD_CONNECTION (object); + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + g_free (priv->bus_name); + + _mcd_connection_free_presence_info (connection); + + G_OBJECT_CLASS (mcd_connection_parent_class)->finalize (object); +} + +static void +_mcd_connection_release_tp_connection (McdConnection *connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + g_debug ("%s(%p) called", G_STRFUNC, connection); + mcd_presence_frame_set_account_status (priv->presence_frame, + priv->account, + TP_CONN_STATUS_DISCONNECTED, + priv->abort_reason); + if (priv->tp_conn) + { + /* Disconnect signals */ + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->tp_conn), + "StatusChanged", + G_CALLBACK + (_mcd_connection_status_changed_cb), + connection); + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->tp_conn), + "NewChannel", + G_CALLBACK + (_mcd_connection_new_channel_cb), + connection); + g_signal_handlers_disconnect_by_func (G_OBJECT (priv->tp_conn), + G_CALLBACK (proxy_destroyed), + connection); + + + tp_conn_disconnect (DBUS_G_PROXY (priv->tp_conn), NULL); + g_object_unref (priv->tp_conn); + priv->tp_conn = NULL; + } + + /* the interface proxies obtained from this connection must be deleted, too + */ + if (priv->presence_proxy) + { + g_object_remove_weak_pointer (G_OBJECT(priv->presence_proxy), + (gpointer *)&priv->presence_proxy); + priv->presence_proxy = NULL; + } + if (priv->capabilities_proxy) + { + g_object_remove_weak_pointer (G_OBJECT(priv->capabilities_proxy), + (gpointer *)&priv->capabilities_proxy); + priv->capabilities_proxy = NULL; + } + if (priv->avatars_proxy) + { + g_object_unref (priv->avatars_proxy); + priv->avatars_proxy = NULL; + } + _mcd_connection_free_presence_info (connection); +} + +static void +_mcd_connection_dispose (GObject * object) +{ + McdConnection *connection = MCD_CONNECTION (object); + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + g_debug ("%s called for object %p", G_STRFUNC, object); + + if (priv->is_disposed) + { + return; + } + + priv->is_disposed = TRUE; + + /* Remove any pending source: timer and idle */ + g_source_remove_by_user_data (connection); + priv->capabilities_timer = 0; + + mcd_operation_foreach (MCD_OPERATION (connection), + (GFunc) _foreach_channel_remove, connection); + + /* Unref pending channels */ + g_hash_table_destroy (priv->pending_channels); + + _mcd_connection_release_tp_connection (connection); + + if (priv->presence_frame != NULL) + { + g_signal_handlers_disconnect_by_func (G_OBJECT + (priv->presence_frame), + G_CALLBACK + (on_presence_requested), object); + g_object_unref (priv->presence_frame); + priv->presence_frame = NULL; + } + + if (priv->account) + { + g_object_unref (priv->account); + priv->account = NULL; + } + + if (priv->tp_conn_mgr) + { + g_object_unref (priv->tp_conn_mgr); + priv->tp_conn_mgr = NULL; + } + + if (priv->dispatcher) + { + g_object_unref (priv->dispatcher); + priv->dispatcher = NULL; + } + + G_OBJECT_CLASS (mcd_connection_parent_class)->dispose (object); +} + +static void +_mcd_connection_set_property (GObject * obj, guint prop_id, + const GValue * val, GParamSpec * pspec) +{ + McdPresenceFrame *presence_frame; + McdDispatcher *dispatcher; + DBusGConnection *dbus_connection; + McAccount *account; + TpConnMgr *tp_conn_mgr; + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (obj); + + switch (prop_id) + { + case PROP_PRESENCE_FRAME: + presence_frame = g_value_get_object (val); + if (presence_frame) + { + g_return_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame)); + g_object_ref (presence_frame); + } + + if (priv->presence_frame) + { + g_signal_handlers_disconnect_by_func (G_OBJECT + (priv->presence_frame), + G_CALLBACK + (on_presence_requested), obj); + g_object_unref (priv->presence_frame); + } + priv->presence_frame = presence_frame; + if (priv->presence_frame) + { + g_signal_connect_after (G_OBJECT (priv->presence_frame), + "presence-requested", + G_CALLBACK (on_presence_requested), obj); + } + break; + case PROP_DISPATCHER: + dispatcher = g_value_get_object (val); + if (dispatcher) + { + g_return_if_fail (MCD_IS_DISPATCHER (dispatcher)); + g_object_ref (dispatcher); + } + if (priv->dispatcher) + { + g_object_unref (priv->dispatcher); + } + priv->dispatcher = dispatcher; + break; + case PROP_DBUS_CONNECTION: + dbus_connection = g_value_get_pointer (val); + dbus_g_connection_ref (dbus_connection); + if (priv->dbus_connection) + dbus_g_connection_unref (priv->dbus_connection); + priv->dbus_connection = dbus_connection; + break; + case PROP_BUS_NAME: + g_return_if_fail (g_value_get_string (val) != NULL); + g_free (priv->bus_name); + priv->bus_name = g_strdup (g_value_get_string (val)); + break; + case PROP_TP_MANAGER: + tp_conn_mgr = g_value_get_object (val); + g_return_if_fail (TELEPATHY_IS_CONNMGR (tp_conn_mgr)); + g_object_ref (tp_conn_mgr); + if (priv->tp_conn_mgr) + g_object_unref (priv->tp_conn_mgr); + priv->tp_conn_mgr = tp_conn_mgr; + break; + case PROP_ACCOUNT: + account = g_value_get_object (val); + g_return_if_fail (MC_IS_ACCOUNT (account)); + g_object_ref (account); + if (priv->account) + g_object_unref (priv->account); + priv->account = account; + _mcd_connection_setup (MCD_CONNECTION (obj)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_mcd_connection_get_property (GObject * obj, guint prop_id, + GValue * val, GParamSpec * pspec) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (obj); + + switch (prop_id) + { + case PROP_DBUS_CONNECTION: + g_value_set_pointer (val, priv->dbus_connection); + break; + case PROP_BUS_NAME: + g_value_set_string (val, priv->bus_name); + break; + case PROP_ACCOUNT: + g_value_set_object (val, priv->account); + break; + case PROP_TP_MANAGER: + g_value_set_object (val, priv->tp_conn_mgr); + break; + case PROP_TP_CONNECTION: + g_value_set_object (val, priv->tp_conn); + break; + case PROP_PRESENCE_FRAME: + g_value_set_object (val, priv->presence_frame); + break; + case PROP_DISPATCHER: + g_value_set_object (val, priv->dispatcher); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +mcd_connection_class_init (McdConnectionClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (object_class, sizeof (McdConnectionPrivate)); + + object_class->finalize = _mcd_connection_finalize; + object_class->dispose = _mcd_connection_dispose; + object_class->set_property = _mcd_connection_set_property; + object_class->get_property = _mcd_connection_get_property; + + /* Properties */ + g_object_class_install_property (object_class, + PROP_PRESENCE_FRAME, + g_param_spec_object ("presence-frame", + _ + ("Presence Frame Object"), + _ + ("Presence frame Object used by connections to update presence"), + MCD_TYPE_PRESENCE_FRAME, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_DISPATCHER, + g_param_spec_object ("dispatcher", + _("Dispatcher Object"), + _("Dispatcher to dispatch channels"), + MCD_TYPE_DISPATCHER, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_DBUS_CONNECTION, + g_param_spec_pointer ("dbus-connection", + _("DBus Connection"), + _ + ("DBus connection to use by us"), + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_BUS_NAME, + g_param_spec_string ("bus-name", + _("DBus Bus name"), + _ + ("DBus Bus name to use by us"), + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_TP_MANAGER, + g_param_spec_object ("tp-manager", + _ + ("Telepathy Manager Object"), + _ + ("Telepathy Manager Object which this connection uses"), + TELEPATHY_CONNMGR_TYPE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_TP_CONNECTION, + g_param_spec_object ("tp-connection", + _ + ("Telepathy Connection Object"), + _ + ("Telepathy Connection Object which this connection uses"), + TELEPATHY_CONN_TYPE, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, PROP_ACCOUNT, + g_param_spec_object ("account", + _("Account Object"), + _ + ("Account that will be used to create this connection"), + MC_TYPE_ACCOUNT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +release_channel_object (gpointer object) +{ + g_object_unref (object); +} + +static void +mcd_connection_init (McdConnection * connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + priv->pending_channels = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + release_channel_object); + priv->abort_reason = TP_CONN_STATUS_REASON_NONE_SPECIFIED; + + priv->reconnect_interval = 30 * 1000; /* 30 seconds */ +} + +/* Public methods */ + +/* Creates a new connection object. Increases a refcount of account. + * Uses mcd_get_manager function to get TpConnManager + */ +McdConnection * +mcd_connection_new (DBusGConnection * dbus_connection, + const gchar * bus_name, + TpConnMgr * tp_conn_mgr, + McAccount * account, + McdPresenceFrame * presence_frame, + McdDispatcher *dispatcher) +{ + McdConnection *mcdconn = NULL; + g_return_val_if_fail (dbus_connection != NULL, NULL); + g_return_val_if_fail (bus_name != NULL, NULL); + g_return_val_if_fail (TELEPATHY_IS_CONNMGR (tp_conn_mgr), NULL); + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame), NULL); + + mcdconn = g_object_new (MCD_TYPE_CONNECTION, + "dbus-connection", dbus_connection, + "bus-name", bus_name, + "tp-manager", tp_conn_mgr, + "presence-frame", presence_frame, + "dispatcher", dispatcher, + "account", account, NULL); + return mcdconn; +} + +/* Constant getters. These should probably be removed */ + +const McAccount * +mcd_connection_get_account (McdConnection * id) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (id); + return priv->account; +} + +TelepathyConnectionStatus +mcd_connection_get_connection_status (McdConnection * id) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (id); + return mcd_presence_frame_get_account_status (priv->presence_frame, + priv->account); +} + +gboolean +mcd_connection_get_telepathy_details (McdConnection * id, + gchar ** ret_servname, + gchar ** ret_objpath) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (id); + + g_return_val_if_fail (priv->tp_conn != NULL, FALSE); + g_return_val_if_fail (TELEPATHY_IS_CONN (priv->tp_conn), FALSE); + + /* Query the properties required for creation of identical TpConn object */ + *ret_objpath = + g_strdup (dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_conn))); + *ret_servname = + g_strdup (dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->tp_conn))); + + return TRUE; +} + +static GError * +map_tp_error_to_mc_error (McdChannel *channel, GError *tp_error) +{ + MCError mc_error_code = MC_CHANNEL_REQUEST_GENERIC_ERROR; + + g_warning ("Telepathy Error = %s", tp_error->message); + + /* TODO : Are there still more specific errors we need + * to distinguish? + */ + if (mcd_channel_get_channel_type_quark (channel) == + TELEPATHY_CHAN_IFACE_STREAMED_QUARK && + dbus_g_error_has_name(tp_error, "org.freedesktop.Telepathy.Error.NotAvailable")) + { + mc_error_code = MC_CONTACT_DOES_NOT_SUPPORT_VOICE_ERROR; + } + else if (dbus_g_error_has_name(tp_error, "org.freedesktop.Telepathy.Error.ChannelBanned")) + { + mc_error_code = MC_CHANNEL_BANNED_ERROR; + } + else if (dbus_g_error_has_name(tp_error, "org.freedesktop.Telepathy.Error.ChannelFull")) + { + mc_error_code = MC_CHANNEL_FULL_ERROR; + } + else if (dbus_g_error_has_name(tp_error, "org.freedesktop.Telepathy.Error.ChannelInviteOnly")) + { + mc_error_code = MC_CHANNEL_INVITE_ONLY_ERROR; + } + return g_error_new (MC_ERROR, mc_error_code, "Telepathy Error: %s", + tp_error->message); +} + +static void +remove_capabilities_refs (gpointer data) +{ + struct capabilities_wait_data *cwd = data; + + g_debug ("\n\n\n%s called\n\n\n", G_STRFUNC); + dbus_g_proxy_disconnect_signal (cwd->priv->capabilities_proxy, + "CapabilitiesChanged", + G_CALLBACK (on_capabilities_changed), + cwd->channel); + g_error_free (cwd->error); + g_free (cwd); +} + +static void +mcd_async_request_chan_callback (DBusGProxy *proxy, + gchar *channel_path, + GError *error, + gpointer user_data) +{ + McdChannel *channel; + McdConnection *connection; + McdConnectionPrivate *priv; + GError *error_on_creation; + struct capabilities_wait_data *cwd; + gchar *chan_type; + TelepathyHandleType chan_handle_type; + guint chan_handle; + TpChan *tp_chan; + /* We handle only the dbus errors */ + + /* ChannelRequestor *chan_req = (ChannelRequestor *)user_data; */ + channel = MCD_CHANNEL (user_data); /* the not-yet-added channel */ + connection = g_object_get_data (G_OBJECT (channel), "temporary_connection"); + g_object_steal_data (G_OBJECT (channel), "tp_chan_call"); + priv = MCD_CONNECTION_PRIV (connection); + + g_object_get (channel, + "channel-handle", &chan_handle, + "channel-handle-type", &chan_handle_type, + NULL); + + + cwd = g_object_get_data (G_OBJECT (channel), "error_on_creation"); + if (cwd) + { + error_on_creation = cwd->error; + g_object_set_data (G_OBJECT (channel), "error_on_creation", NULL); + } + else + error_on_creation = NULL; + + + if (error != NULL) + { + g_debug ("%s: Got error: %s", G_STRFUNC, error->message); + if (error_on_creation != NULL) + { + /* replace the error, so that the initial one is reported */ + g_error_free (error); + error = error_on_creation; + } + + if (priv->got_capabilities || error_on_creation) + { + /* Faild dispatch */ + GError *mc_error = map_tp_error_to_mc_error (channel, error); + g_signal_emit_by_name (G_OBJECT(priv->dispatcher), "dispatch-failed", + channel, mc_error); + g_error_free (mc_error); + g_error_free (error); + + /* No abort on channel, because we are the only one holding the only + * reference to this temporary channel. + * This should also unref the channel object + */ + g_hash_table_remove (priv->pending_channels, + GINT_TO_POINTER (chan_handle)); + } + else + { + /* the channel request has failed probably because we are just + * connected and we didn't recive the contact capabilities yet. In + * this case, wait for this contact's capabilities to arrive */ + g_debug ("%s: listening for remote capabilities on channel handle %d, type %d", + G_STRFUNC, chan_handle, mcd_channel_get_handle_type (channel)); + dbus_g_proxy_connect_signal (priv->capabilities_proxy, + "CapabilitiesChanged", + G_CALLBACK (on_capabilities_changed), + channel, NULL); + /* Store the error, we might need it later */ + cwd = g_malloc (sizeof (struct capabilities_wait_data)); + g_assert (cwd != NULL); + cwd->error = error; + cwd->channel = channel; + cwd->priv = priv; + g_object_set_data_full (G_OBJECT (channel), "error_on_creation", cwd, + remove_capabilities_refs); + } + return; + } + + if (channel_path == NULL) + { + GError *mc_error; + g_warning ("Returned channel_path from telepathy is NULL"); + + mc_error = g_error_new (MC_ERROR, + MC_CHANNEL_REQUEST_GENERIC_ERROR, + "Returned channel_path from telepathy is NULL"); + g_signal_emit_by_name (G_OBJECT(priv->dispatcher), + "dispatch-failed", channel, mc_error); + g_error_free (mc_error); + + /* No abort on channel, because we are the only one holding the only + * reference to this temporary channel. + * This should also unref the channel object + */ + g_hash_table_remove (priv->pending_channels, + GINT_TO_POINTER (chan_handle)); + return; + } + + /* Everything here is well and fine. We can create the channel. */ + channel = g_hash_table_lookup (priv->pending_channels, + GUINT_TO_POINTER (chan_handle)); + if (!channel) + { + g_warning ("%s: channel not found among the pending ones", G_STRFUNC); + return; + } + + g_object_get (channel, + "channel-type", &chan_type, + NULL); + tp_chan = tp_chan_new (priv->dbus_connection, priv->bus_name, + channel_path, chan_type, chan_handle_type, chan_handle); + g_object_set (channel, + "channel-object-path", channel_path, + "tp-channel", tp_chan, + NULL); + + /* The channel is no longer pending. 'stealing' because want the + * channel ownership. + */ + g_hash_table_steal (priv->pending_channels, + GINT_TO_POINTER (chan_handle)); + mcd_operation_take_mission (MCD_OPERATION (connection), + MCD_MISSION (channel)); + + /* Channel about to be dispatched */ + mcd_channel_set_status (channel, MCD_CHANNEL_DISPATCHING); + + /* Dispatch the incoming channel */ + mcd_dispatcher_send (priv->dispatcher, channel); + + g_free (chan_type); + g_free (channel_path); + g_object_unref (tp_chan); +} + +static void +mcd_async_request_handle_callback(DBusGProxy *proxy, GArray *handles, + GError *error, gpointer user_data) +{ + McdChannel *channel, *existing_channel; + McdConnection *connection; + McdConnectionPrivate *priv; + guint chan_handle, chan_handle_type; + const gchar *chan_type; + const GList *channels; + DBusGProxyCall *call; + + channel = MCD_CHANNEL (user_data); + connection = g_object_get_data (G_OBJECT (channel), "temporary_connection"); + priv = MCD_CONNECTION_PRIV (connection); + + if (error != NULL) + { + GError *mc_error; + g_warning ("Could not map string handle to a valid handle!: %s", + error->message); + + /* Fail dispatch */ + mc_error = g_error_new (MC_ERROR, MC_INVALID_HANDLE_ERROR, + "Could not map string handle to a valid handle!: %s", + error->message); + g_signal_emit_by_name (priv->dispatcher, "dispatch-failed", + channel, mc_error); + g_error_free (mc_error); + g_error_free(error); + + /* No abort, because we are the only one holding the only reference + * to this temporary channel + */ + g_object_unref (channel); + return; + } + + chan_type = mcd_channel_get_channel_type (channel), + chan_handle_type = mcd_channel_get_handle_type (channel), + chan_handle = g_array_index (handles, guint, 0); + g_array_free(handles, TRUE); + + g_debug ("Got handle %u", chan_handle); + + /* Is the handle we got valid? */ + if (chan_handle == 0) + { + GError *mc_error; + g_warning ("Could not map the string to a valid handle!"); + + /* Fail dispatch */ + mc_error = g_error_new (MC_ERROR, MC_INVALID_HANDLE_ERROR, + "Could not map string handle to a valid handle!"); + g_signal_emit_by_name (priv->dispatcher, "dispatch-failed", + channel, mc_error); + g_error_free (mc_error); + + /* No abort, because we are the only one holding the only reference + * to this temporary channel + */ + g_object_unref (channel); + return; + } + + /* Check if a telepathy channel has already been created; this could happen + * in the case we had a chat window open, the UI crashed and now the same + * channel is requested. */ + channels = mcd_operation_get_missions (MCD_OPERATION (connection)); + while (channels) + { + existing_channel = MCD_CHANNEL (channels->data); + g_debug ("Chan: %d, handle type %d, channel type %s", + mcd_channel_get_handle (existing_channel), + mcd_channel_get_handle_type (existing_channel), + mcd_channel_get_channel_type (existing_channel)); + if (chan_handle == mcd_channel_get_handle (existing_channel) && + strcmp(chan_type, + mcd_channel_get_channel_type (existing_channel)) == 0) + { + guint requestor_serial; + gchar *requestor_client_id; + + g_debug ("%s: Channel already existing, returning old one", G_STRFUNC); + /* we retrieve the information we need from the newly created + * channel object and set them to the "old" channel */ + g_object_get (channel, + "requestor-serial", &requestor_serial, + "requestor-client-id", &requestor_client_id, + NULL); + g_object_set (existing_channel, + "requestor-serial", requestor_serial, + "requestor-client-id", requestor_client_id, + NULL); + g_free (requestor_client_id); + /* we no longer need the new channel */ + g_object_unref (channel); + /* notify the dispatcher again */ + mcd_dispatcher_send (priv->dispatcher, existing_channel); + return; + } + channels = channels->next; + } + + /* Update our newly acquired information */ + g_object_set (channel, "channel-handle", chan_handle, NULL); + + /* The channel is temporary and stays in priv->pending_channels until + * a telepathy channel for it is created + */ + g_hash_table_insert (priv->pending_channels, + GINT_TO_POINTER (chan_handle), channel); + + /* Now, request the corresponding telepathy channel. */ + call = tp_conn_request_channel_async(DBUS_G_PROXY(proxy), + mcd_channel_get_channel_type (channel), + mcd_channel_get_handle_type (channel), + chan_handle, TRUE, + mcd_async_request_chan_callback, + channel); + g_object_set_data (G_OBJECT (channel), "tp_chan_call", call); +} + +gboolean +mcd_connection_request_channel (McdConnection *connection, + const struct mcd_channel_request *req, + GError ** error) +{ + McdChannel *channel; + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + g_return_val_if_fail (priv->tp_conn != NULL, FALSE); + g_return_val_if_fail (TELEPATHY_IS_CONN (priv->tp_conn), FALSE); + + /* The channel is temporary */ + channel = mcd_channel_new (NULL, + NULL, + req->channel_type, + req->channel_handle, + req->channel_handle_type, + TRUE, /* outgoing */ + req->requestor_serial, + req->requestor_client_id); + + /* We do not add the channel in connection until tp_channel is created */ + g_object_set_data (G_OBJECT (channel), "temporary_connection", connection); + + if (req->channel_handle) + { + DBusGProxyCall *call; + /* the channel stays in priv->pending_channels until a telepathy + * channel for it is created */ + g_hash_table_insert (priv->pending_channels, + GINT_TO_POINTER (req->channel_handle), channel); + + call = tp_conn_request_channel_async(DBUS_G_PROXY(priv->tp_conn), + req->channel_type, + req->channel_handle_type, + req->channel_handle, TRUE, + mcd_async_request_chan_callback, + channel); + g_object_set_data (G_OBJECT (channel), "tp_chan_call", call); + } + else + { + /* if channel handle is 0, this means that the channel was requested by + * a string handle; in that case, we must first request a channel + * handle for it */ + const gchar *name_array[2]; + g_assert (req->channel_handle_string != NULL); + + name_array[0] = req->channel_handle_string; + name_array[1] = NULL; + + /* Channel is temporary and will enter priv->pending_channels list + * only when we successfully resolve the handle. */ + tp_conn_request_handles_async (DBUS_G_PROXY(priv->tp_conn), + req->channel_handle_type, + name_array, + mcd_async_request_handle_callback, + channel); + } + return TRUE; +} + +static gboolean +channel_matches_request (gpointer key, McdChannel *channel, + struct request_id *req_id) +{ + guint requestor_serial; + gchar *requestor_client_id; + gboolean found; + + g_object_get (channel, + "requestor-serial", &requestor_serial, + "requestor-client-id", &requestor_client_id, + NULL); + if (requestor_serial == req_id->requestor_serial && + strcmp (requestor_client_id, req_id->requestor_client_id) == 0) + found = TRUE; + else + found = FALSE; + g_free (requestor_client_id); + return found; +} + +gboolean +mcd_connection_cancel_channel_request (McdConnection *connection, + guint operation_id, + const gchar *requestor_client_id, + GError **error) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + struct request_id req_id; + const GList *channels, *node; + McdChannel *channel; + + /* first, see if the channel is in the list of the pending channels */ + req_id.requestor_serial = operation_id; + req_id.requestor_client_id = requestor_client_id; + channel = g_hash_table_find (priv->pending_channels, + (GHRFunc)channel_matches_request, + &req_id); + if (channel) + { + guint chan_handle; + DBusGProxyCall *call; + + g_debug ("%s: requested channel found in the pending_channels list (%p)", G_STRFUNC, channel); + g_object_get (channel, + "channel-handle", &chan_handle, + NULL); + /* check for pending dbus calls to be cancelled */ + call = g_object_get_data (G_OBJECT (channel), "tp_chan_call"); + dbus_g_proxy_cancel_call (DBUS_G_PROXY(priv->tp_conn), call); + /* No abort on channel, because we are the only one holding the only + * reference to this temporary channel. + * This should also unref the channel object. + * No other actions needed; if a NewChannel signal will be emitted for + * this channel, it will be ignored since it has the suppress_handler + * flag set. + */ + g_hash_table_remove (priv->pending_channels, + GINT_TO_POINTER (chan_handle)); + return TRUE; + } + + /* the channel might already have a TpChan created for it, and either be in + * the dispatcher or be already handled. Aborting in any case. */ + channels = mcd_operation_get_missions (MCD_OPERATION (connection)); + if (!channels) return FALSE; + + for (node = channels; node; node = node->next) + { + guint chan_requestor_serial; + gchar *chan_requestor_client_id; + + channel = MCD_CHANNEL (node->data); + g_object_get (channel, + "requestor-serial", &chan_requestor_serial, + "requestor-client-id", &chan_requestor_client_id, + NULL); + if (chan_requestor_serial == operation_id && + strcmp (chan_requestor_client_id, requestor_client_id) == 0) + { + g_debug ("%s: requested channel found (%p)", G_STRFUNC, channel); + mcd_mission_abort (MCD_MISSION (channel)); + g_free (chan_requestor_client_id); + return TRUE; + } + g_free (chan_requestor_client_id); + } + g_debug ("%s: requested channel not found!", G_STRFUNC); + return FALSE; +} + +static void +request_avatar_cb (DBusGProxy *proxy, GArray *avatar, char *mime_type, + GError *error, gpointer userdata) +{ + McdConnectionPrivate *priv = (McdConnectionPrivate *)userdata; + gchar *filename; + if (error) + { + g_warning ("%s: error: %s", G_STRFUNC, error->message); + g_error_free (error); + return; + } + g_debug ("%s: received mime-type: %s", G_STRFUNC, mime_type); + if (mc_account_get_avatar (priv->account, &filename, NULL, NULL)) + { + g_file_set_contents (filename, avatar->data, avatar->len, NULL); + mc_account_set_avatar_mime_type (priv->account, mime_type); + mc_account_reset_avatar_id (priv->account); + g_free (filename); + } + g_free (mime_type); +} + +/** mcd_connection_remote_avatar_changed: + * @connection: the #McdConnection. + * @contact_id: the own contact id in Telepathy. + * @token: the new avatar token. + * + * This function is to be called when Telepathy signals that our own avatar has + * been updated. It takes care of checking if the remote avatar has to be + * retrieved and stored in the account. + * + * Returns: %TRUE if the local avatar has been updated. + */ +gboolean mcd_connection_remote_avatar_changed (McdConnection *connection, + guint contact_id, + const gchar *token) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + gchar *prev_token; + gboolean changed = FALSE; + + if (!priv->avatars_proxy) + { + g_warning ("%s: avatar proxy is gone", G_STRFUNC); + return FALSE; + } + + if (!mc_account_get_avatar (priv->account, NULL, NULL, &prev_token)) + return FALSE; + + if (!prev_token || strcmp (token, prev_token) != 0) + { + g_debug ("%s: avatar has changed", G_STRFUNC); + /* the avatar has changed, let's retrieve the new one */ + tp_conn_iface_avatars_request_avatar_async (priv->avatars_proxy, contact_id, + request_avatar_cb, + priv); + mc_account_set_avatar_token (priv->account, token); + changed = TRUE; + } + g_free (prev_token); + return changed; +} + +void +mcd_connection_close (McdConnection *connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + priv->abort_reason = TP_CONN_STATUS_REASON_REQUESTED; + mcd_mission_abort (MCD_MISSION (connection)); +} + +/** + * mcd_connection_account_changed: + * @connection: the #McdConnection. + * + * This function must be called when the account this connection refers to is + * modified. + * Note: ideally, this should be a private method and the connection should + * monitor the account itself; but since the monitoring is already done by + * McdMaster for all accounts, it seemed more convenient to let it call this + * function whenever needed. + */ +void +mcd_connection_account_changed (McdConnection *connection) +{ + McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); + + /* setup the avatar (if it has not been changed, this function does + * nothing) */ + if (priv->tp_conn) + _mcd_connection_setup_avatar (priv); + /* TODO: same for display name, aka alias */ +} + diff --git a/src/mcd-connection.h b/src/mcd-connection.h new file mode 100644 index 00000000..28e6518c --- /dev/null +++ b/src/mcd-connection.h @@ -0,0 +1,91 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MCD_CONNECTION_H__ +#define __MCD_CONNECTION_H__ + +#include <glib.h> +#include <glib-object.h> +#include <libmissioncontrol/mc-account.h> +#include <libtelepathy/tp-connmgr.h> + +#include "mcd-operation.h" + +G_BEGIN_DECLS +#define MCD_TYPE_CONNECTION (mcd_connection_get_type()) +#define MCD_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MCD_TYPE_CONNECTION, McdConnection)) +#define MCD_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MCD_TYPE_CONNECTION, McdConnectionClass)) +#define MCD_IS_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MCD_TYPE_CONNECTION)) +#define MCD_IS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MCD_TYPE_CONNECTION)) +#define MCD_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MCD_TYPE_CONNECTION, McdConnectionClass)) + typedef struct +{ + McdOperation parent; +} McdConnection; + +typedef struct +{ + McdOperationClass parent_class; +} McdConnectionClass; + +#include "mcd-presence-frame.h" +#include "mcd-dispatcher.h" + +GType mcd_connection_get_type (void); + +McdConnection *mcd_connection_new (DBusGConnection * dbus_connection, + const gchar * bus_name, + TpConnMgr * tp_conn_mgr, + McAccount * account, + McdPresenceFrame * presence_frame, + McdDispatcher *dispatcher); + +/* Return the connection's account */ +const McAccount *mcd_connection_get_account (McdConnection * connection); + +TelepathyConnectionStatus mcd_connection_get_connection_status (McdConnection + * connection); + +gboolean mcd_connection_request_channel (McdConnection *connection, + const struct mcd_channel_request *req, + GError ** error); + +gboolean mcd_connection_cancel_channel_request (McdConnection *connection, + guint operation_id, + const gchar *requestor_client_id, + GError **error); + +gboolean mcd_connection_get_telepathy_details (McdConnection * id, + gchar ** ret_servname, + gchar ** ret_objpath); + +gboolean mcd_connection_remote_avatar_changed (McdConnection *connection, + guint contact_id, + const gchar *token); +void mcd_connection_account_changed (McdConnection *connection); + +void mcd_connection_close (McdConnection *connection); + +G_END_DECLS +#endif /* __MCD_CONNECTION_H__ */ diff --git a/src/mcd-controller.c b/src/mcd-controller.c new file mode 100644 index 00000000..6a8e5faa --- /dev/null +++ b/src/mcd-controller.c @@ -0,0 +1,129 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "mcd-controller.h" + +/* Milliseconds to wait for Connectivity coming back up before exiting MC */ +#define EXIT_COUNTDOWN_TIME 5000 + +#define MCD_CONTROLLER_PRIV(controller) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((controller), \ + MCD_TYPE_CONTROLLER, \ + McdControllerPrivate)) + +/** + * McdController: + * This class implements the logic to control mission-control based on all + * external device events and states. It also controls mission-control + * life-cycle based on such events. + */ +G_DEFINE_TYPE (McdController, mcd_controller, MCD_TYPE_OPERATION); + +/* Private */ +typedef struct _McdControllerPrivate +{ + /* Current pending sleep timer */ + gint shutdown_timeout_id; + + gboolean is_disposed; +} McdControllerPrivate; + +static void +mcd_controller_class_init (McdControllerClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (object_class, sizeof (McdControllerPrivate)); +} + +static void +mcd_controller_init (McdController * obj) +{ +} + +/* Public */ + +McdController * +mcd_controller_new () +{ + McdController *obj; + obj = MCD_CONTROLLER (g_object_new (MCD_TYPE_CONTROLLER, NULL)); + return obj; +} + +static gboolean +_mcd_controller_exit_by_timeout (gpointer data) +{ + McdController *controller; + McdControllerPrivate *priv; + + controller = MCD_CONTROLLER (data); + priv = MCD_CONTROLLER_PRIV (controller); + + priv->shutdown_timeout_id = 0; + + /* Notify sucide */ + mcd_mission_abort (MCD_MISSION (controller)); + + return FALSE; +} + +void +mcd_controller_shutdown (McdController *controller, const gchar *reason) +{ + g_return_if_fail (MCD_IS_CONTROLLER (controller)); + + McdControllerPrivate *priv = MCD_CONTROLLER_PRIV (controller); + + if(!priv->shutdown_timeout_id) + { + g_debug ("MC will bail out because of \"%s\" out exit after %i", + reason ? reason : "No reason specified", + EXIT_COUNTDOWN_TIME); + + priv->shutdown_timeout_id = g_timeout_add (EXIT_COUNTDOWN_TIME, + _mcd_controller_exit_by_timeout, + controller); + } + else + { + g_debug ("Already shutting down. This one has the reason %s", + reason ? reason:"No reason specified"); + } + mcd_debug_print_tree (controller); +} + +void +mcd_controller_cancel_shutdown (McdController *controller) +{ + g_return_if_fail (MCD_IS_CONTROLLER (controller)); + + McdControllerPrivate *priv = MCD_CONTROLLER_PRIV (controller); + + if (priv->shutdown_timeout_id) + { + g_debug ("Cancelling exit timeout"); + g_source_remove (priv->shutdown_timeout_id); + priv->shutdown_timeout_id = 0; + } +} diff --git a/src/mcd-controller.h b/src/mcd-controller.h new file mode 100644 index 00000000..1695dcc7 --- /dev/null +++ b/src/mcd-controller.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_CONTROLLER_H +#define MCD_CONTROLLER_H + +#include <glib.h> +#include <glib-object.h> + +#include "mcd-operation.h" + +G_BEGIN_DECLS + +#define MCD_TYPE_CONTROLLER (mcd_controller_get_type ()) +#define MCD_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_CONTROLLER, McdController)) +#define MCD_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_CONTROLLER, McdControllerClass)) +#define MCD_IS_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_CONTROLLER)) +#define MCD_IS_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_CONTROLLER)) +#define MCD_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_CONTROLLER, McdControllerClass)) + +typedef struct _McdController McdController; +typedef struct _McdControllerClass McdControllerClass; + +struct _McdController +{ + McdOperation parent; +}; + +struct _McdControllerClass +{ + McdOperationClass parent_class; +}; + +GType mcd_controller_get_type (void); +McdController *mcd_controller_new (void); +void mcd_controller_shutdown (McdController *controller, const gchar *reason); +void mcd_controller_cancel_shutdown (McdController *controller); + +G_END_DECLS + +#endif /* MCD_CONTROLLER_H */ diff --git a/src/mcd-debug.c b/src/mcd-debug.c new file mode 100644 index 00000000..27a10cd9 --- /dev/null +++ b/src/mcd-debug.c @@ -0,0 +1,115 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <stdlib.h> +#include "mcd-debug.h" +#include "mcd-operation.h" + +static gint mc_debug = 0; + +void +mcd_debug_ref (gpointer obj, const gchar *filename, gint linenum) +{ + /* Function reference untouchable by macro processing */ + gpointer (*untouchable_ref) (gpointer object); + + untouchable_ref = g_object_ref; + if (mc_debug >= 2) + g_debug ("[%s:%d]: Referencing (%d) object %p of type %s", + filename, linenum, G_OBJECT (obj)->ref_count, + obj, G_OBJECT_TYPE_NAME(obj)); + untouchable_ref (obj); +} + +void +mcd_debug_unref (gpointer obj, const gchar *filename, gint linenum) +{ + void (*untouchable_unref) (gpointer object); + + untouchable_unref = g_object_unref; + if (mc_debug >= 2) + g_debug ("[%s:%d]: Unreferencing (%d) object %p of type %s", + filename, linenum, G_OBJECT (obj)->ref_count, obj, + G_OBJECT_TYPE_NAME(obj)); + untouchable_unref (obj); +} + +static void +mcd_debug_print_tree_real (gpointer object, gint level) +{ + GString *indent_str; + gchar *indent = " "; + gint i; + + indent_str = g_string_new (""); + + for (i = 0; i < level; i++) + { + g_string_append (indent_str, indent); + } + + g_debug ("%s%s (%p): %d", indent_str->str, + G_OBJECT_TYPE_NAME(object), object, G_OBJECT (object)->ref_count); + + if (MCD_IS_OPERATION (object)) + { + const GList *missions = mcd_operation_get_missions (MCD_OPERATION (object)); + const GList *node = missions; + while (node) + { + mcd_debug_print_tree_real (node->data, level + 1); + node = g_list_next (node); + } + } + g_string_free (indent_str, TRUE); +} + +void +mcd_debug_print_tree (gpointer object) +{ + g_return_if_fail (MCD_IS_MISSION (object)); + + if (mc_debug >= 2) + { + g_debug ("Object Hierarchy of object %p", object); + g_debug ("["); + mcd_debug_print_tree_real (object, 1); + g_debug ("]"); + } +} + +void mcd_debug_init () +{ + gchar *mc_debug_str; + + mc_debug_str = getenv ("MC_DEBUG"); + if (mc_debug_str) + mc_debug = atoi (mc_debug_str); +} + +inline gint mcd_debug_get_level () +{ + return mc_debug; +} + diff --git a/src/mcd-debug.h b/src/mcd-debug.h new file mode 100644 index 00000000..8b3318f0 --- /dev/null +++ b/src/mcd-debug.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MCD_DEBUG_H__ +#define __MCD_DEBUG_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define g_object_ref(obj) (mcd_debug_ref (obj, __FILE__, __LINE__)) +#define g_object_unref(obj) (mcd_debug_unref (obj, __FILE__, __LINE__)) + +void mcd_debug_init (); + +inline gint mcd_debug_get_level (); + +void mcd_debug_ref (gpointer obj, const gchar *filename, gint linenum); +void mcd_debug_unref (gpointer obj, const gchar *filename, gint linenum); + +void mcd_debug_print_tree (gpointer obj); + +G_END_DECLS + +#endif /* __MCD_DEBUG_H__ */ diff --git a/src/mcd-dispatcher-context.h b/src/mcd-dispatcher-context.h new file mode 100644 index 00000000..718220a5 --- /dev/null +++ b/src/mcd-dispatcher-context.h @@ -0,0 +1,138 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __MCD_DISPATCHER_CONTEXT_H__ +#define __MCD_DISPATCHER_CONTEXT_H__ + +#include "mcd-dispatcher.h" +#include "mcd-connection.h" +#include "mcd-chan-handler.h" + +G_BEGIN_DECLS + +#define MCD_PLUGIN_INIT_FUNC "mcd_filters_init" + +/* Filter flag definitions */ +#define MCD_FILTER_IN 1<<0 +#define MCD_FILTER_OUT 1<<1 + +/* The context of the current filter chain execution. Should be kept + * intact by filter implementation and passed transparently to + * getters/setters and state machine functions + */ +typedef struct _McdDispatcherContext McdDispatcherContext; + +/* Filter function type */ +typedef void (*filter_func_t) (McdDispatcherContext * ctx); + +/* Data structures and typedefs needed by pluginized filters */ +typedef void (*abort_function_t) (McdDispatcherContext * ctx); + +/* Requests the chain of filter functions for an unique combination of + * channel types and filter flags. + * + * @param channel_type: A quark representing a particular channel type + * @param filter_flags: The flags for the filter, such as incoming/outgoing + * @return A NULL-terminated array of filter function pointers + */ + +filter_func_t *mcd_dispatcher_get_filter_chain (McdDispatcher * dispatcher, + GQuark channel_type_quark, + guint filter_flags); + + +/* Indicates to Mission Control that we want to register a filter chain + * for a unique combination of channel type/filter flags. + * + * @param channel_type_quark: Quark indicating the channel type + * @param filter_flags: The flags for the filter, such as incoming/outgoing + * @param chain: The chain of filter functions to register + */ + +void mcd_dispatcher_register_filter_chain (McdDispatcher * dispatcher, + GQuark channel_type_quark, + guint filter_flags, + filter_func_t * chain); + +/* Indicates to Mission Control that we will not want to have a filter chain + * for particular unique channel type/filter flags combination anymore. + * + * @param channel_type_quark: Quark indicating the channel type + * @param filter_flags: The flags for the filter, such as incoming/outgoing + */ +void mcd_dispatcher_unregister_filter_chain (McdDispatcher * dispatcher, + GQuark channel_type_quark, + guint filter_flags); + +/* Context API section + * + * The use of gpointer is intentional; we want to make accessing the + * internals of the context restricted to make it unlikely that + * somebody shoots [him|her]self in the foot while doing fancy + * tricks. This also minimizes the amount of necessary includes. + */ + +/* Getters */ + +McdDispatcher* mcd_dispatcher_context_get_dispatcher (McdDispatcherContext * ctx); + +const TpChan *mcd_dispatcher_context_get_channel_object (McdDispatcherContext * ctx); + +const TpConn * mcd_dispatcher_context_get_connection_object (McdDispatcherContext * ctx); + +McdChannel * mcd_dispatcher_context_get_channel (McdDispatcherContext * ctx); + +const McdConnection * mcd_dispatcher_context_get_connection (McdDispatcherContext * ctx); + +McdChannelHandler * mcd_dispatcher_context_get_chan_handler (McdDispatcherContext * ctx); + +/*Returns an array of the gchar * addresses of participants in the channel*/ +GPtrArray *mcd_dispatcher_context_get_members (McdDispatcherContext * ctx); + +/*Filter-specifc data*/ +gpointer mcd_dispatcher_context_get_data (McdDispatcherContext * ctx); + + +/* Setters */ + +/* Abort function should be known only to the filter function. When + executed, filter function MUST set an abort fn as needed (such as + when implementing an async filter) */ + +void mcd_dispatcher_context_set_abort_fn (McdDispatcherContext * ctx, abort_function_t abort_fn); + +void mcd_dispatcher_context_set_data (McdDispatcherContext * ctx, gpointer data); + +/* Statemachine API section */ + +/* Will step through the state machine. + * @param ctx: The context + * @param result: The return code + */ + +void mcd_dispatcher_context_process (McdDispatcherContext * ctx, gboolean result); + +G_END_DECLS + +#endif diff --git a/src/mcd-dispatcher.c b/src/mcd-dispatcher.c new file mode 100644 index 00000000..1c6aa217 --- /dev/null +++ b/src/mcd-dispatcher.c @@ -0,0 +1,1185 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <dlfcn.h> +#include <glib/gi18n.h> +#include <libtelepathy/tp-chan-iface-group-gen.h> +#include <libtelepathy/tp-ch-gen.h> + +#include "mcd-signals-marshal.h" +#include "mcd-connection.h" +#include "mcd-channel.h" +#include "mcd-master.h" +#include "mcd-chan-handler.h" +#include "mcd-dispatcher-context.h" + +#define MCD_DISPATCHER_PRIV(dispatcher) (G_TYPE_INSTANCE_GET_PRIVATE ((dispatcher), \ + MCD_TYPE_DISPATCHER, \ + McdDispatcherPrivate)) + +G_DEFINE_TYPE (McdDispatcher, mcd_dispatcher, MCD_TYPE_MISSION); + +struct _McdDispatcherContext +{ + McdDispatcher *dispatcher; + + /*The actual channel */ + McdChannel *channel; + + /* State-machine internal data fields: */ + filter_func_t *chain; + + /* Next function in chain */ + guint next_func_index; + + /* Function to abort the current filter, if any */ + abort_function_t abort_fn; + + /*Filter-specific user data */ + gpointer data; + +}; + +typedef struct _McdDispatcherPrivate +{ + /* Pending state machine contexts */ + GSList *state_machine_list; + + /* All active channels */ + GList *channels; + + GSList *filter_dlhandles; + gchar *plugin_dir; + GData *interface_filters; + DBusGConnection *dbus_connection; + + /* Channel handlers */ + GHashTable *channel_handler_hash; + /* Array of channel handler's capabilities, stored as a GPtrArray for + * performance reasons */ + GPtrArray *channel_handler_caps; + + McdMaster *master; + + gboolean is_disposed; + +} McdDispatcherPrivate; + +struct iface_chains_t +{ + filter_func_t *chain_in; + filter_func_t *chain_out; +}; + +enum +{ + PROP_0, + PROP_PLUGIN_DIR, + PROP_DBUS_CONNECTION, + PROP_MCD_MASTER, +}; + +enum _McdDispatcherSignalType +{ + CHANNEL_ADDED, + CHANNEL_REMOVED, + DISPATCHED, + DISPATCH_FAILED, + LAST_SIGNAL +}; + +static guint mcd_dispatcher_signals[LAST_SIGNAL] = { 0 }; + +static void mcd_dispatcher_context_free (McdDispatcherContext * ctx); + +static void +_mcd_dispatcher_load_filters (McdDispatcher * dispatcher) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (dispatcher); + GDir *dir = NULL; + GError *error = NULL; + void *plugin_handle; + const gchar *name; + + dir = g_dir_open (priv->plugin_dir, 0, &error); + if (!dir) + { + g_debug ("Could not open plugin directory: %s", error->message); + g_error_free (error); + return; + } + + while ((name = g_dir_read_name (dir))) + { + plugin_handle = NULL; + gchar *path = NULL; + path = g_strconcat (priv->plugin_dir, G_DIR_SEPARATOR_S, name, NULL); + /* Skip directories */ + + if (g_file_test (path, G_FILE_TEST_IS_DIR)) + { + g_free (path); + continue; + } + /* Is it a library? If yes, add the name to list */ + + if (!g_str_has_suffix (path, ".so")) + { + g_free (path); + continue; + } + + /* ? Do we need to check more strictly than by using prefix-check? + * Probably not, as failure of dlopen will take care of things + * anyway? */ + + plugin_handle = dlopen (path, RTLD_NOW); + + if (plugin_handle != NULL) + { + void (*plugin_init) (McdDispatcher * dispatcher); + + priv->filter_dlhandles = g_slist_prepend (priv->filter_dlhandles, + plugin_handle); + + plugin_init = dlsym (plugin_handle, MCD_PLUGIN_INIT_FUNC); + if (plugin_init != NULL) + { + plugin_init (dispatcher); + } + else + { + g_debug ("Error opening filter plugin: %s: %s", path, + dlerror ()); + } + } + else + { + g_debug ("Could not open plugin %s because: %s", path, dlerror ()); + } + g_free (path); + } + g_dir_close (dir); + + return; +} + +static void +_mcd_dispatcher_unload_filters (McdDispatcher * dispatcher) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (dispatcher); + + if (priv->filter_dlhandles) + { + g_slist_foreach (priv->filter_dlhandles, (GFunc) dlclose, NULL); + g_slist_free (priv->filter_dlhandles); + priv->filter_dlhandles = NULL; + + g_datalist_clear (&priv->interface_filters); + priv->interface_filters = NULL; + } +} + +/* REGISTRATION/DEREGISTRATION of filters*/ + +/* A convenience function for acquiring the chain for particular channel +type and filter flag combination. */ + +static filter_func_t * +_mcd_dispatcher_get_filter_chain (McdDispatcher * dispatcher, + GQuark channel_type_quark, + guint filter_flags) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (dispatcher); + struct iface_chains_t *iface_chains; + filter_func_t *filter_chain = NULL; + + iface_chains = + (struct iface_chains_t *) + g_datalist_id_get_data (&(priv->interface_filters), channel_type_quark); + + if (iface_chains == NULL) + { + g_debug ("%s: No chains for interface %s", G_STRFUNC, + g_quark_to_string (channel_type_quark)); + } + else + switch (filter_flags) + { + case MCD_FILTER_IN: + filter_chain = iface_chains->chain_in; + break; + case MCD_FILTER_OUT: + filter_chain = iface_chains->chain_out; + break; + + default: + g_warning ("Unsupported filter flag value"); + break; + } + + return filter_chain; +} + +void +mcd_dispatcher_register_filter_chain (McdDispatcher * dispatcher, + GQuark channel_type_quark, + guint filter_flags, + filter_func_t * chain) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (dispatcher); + struct iface_chains_t *iface_chains = NULL; + + /* Check if the interface already has stored data, otherwise create it */ + + if (!(iface_chains = g_datalist_id_get_data (&(priv->interface_filters), + channel_type_quark))) + { + iface_chains = g_new0 (struct iface_chains_t, 1); + g_datalist_id_set_data_full (&(priv->interface_filters), + channel_type_quark, iface_chains, g_free); + } + + switch (filter_flags) + { + case MCD_FILTER_IN: + if (iface_chains->chain_in != NULL) + { + g_warning + ("Overwriting a previous chain without unregistering it!"); + } + iface_chains->chain_in = chain; + break; + case MCD_FILTER_OUT: + if (iface_chains->chain_out != NULL) + { + g_warning + ("Overwriting a previous chain without unregistering it!"); + } + iface_chains->chain_out = chain; + break; + default: + g_warning ("Unknown filter flag value!"); + } +} + +void +mcd_dispatcher_unregister_filter_chain (McdDispatcher * dispatcher, + GQuark channel_type_quark, + guint filter_flags) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (dispatcher); + + /* First, do we have anything registered for that channel type? */ + struct iface_chains_t *chains = + (struct iface_chains_t *) + g_datalist_id_get_data (&(priv->interface_filters), + channel_type_quark); + if (chains == NULL) + { + g_warning ("Attempting to unregister a nonexisting filter chain"); + return; + } + + switch (filter_flags) + { + case MCD_FILTER_IN: + /* No worries about memory leaks, as these are function pointers */ + chains->chain_in = NULL; + break; + case MCD_FILTER_OUT: + chains->chain_out = NULL; + break; + default: + g_warning ("Unknown filter flag value!"); + } + + /* Both chains are unregistered? We may as well free the struct then */ + + if (chains->chain_in == NULL && chains->chain_out == NULL) + { + /* ? Should we dlclose the plugin as well..? */ + g_datalist_id_remove_data (&(priv->interface_filters), + channel_type_quark); + g_free (chains); + } + + return; +} + +/* Returns # of times particular channel type has been used */ +gint +mcd_dispatcher_get_channel_type_usage (McdDispatcher * dispatcher, + GQuark chan_type_quark) +{ + GList *node; + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (dispatcher); + gint usage_counter = 0; + + node = priv->channels; + while (node) + { + McdChannel *chan = (McdChannel*) node->data; + if (chan && chan_type_quark == mcd_channel_get_channel_type_quark (chan)) + usage_counter++; + node = node->next; + } + + return usage_counter; +} + +/* The callback is called on channel Closed signal */ +static void +on_channel_abort_list (McdChannel *channel, McdDispatcher *dispatcher) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (dispatcher); + + g_debug ("Abort Channel; Removing channel from list"); + priv->channels = g_list_remove (priv->channels, channel); + g_signal_emit_by_name (dispatcher, "channel-removed", channel); + g_object_unref (channel); +} + +static void +on_master_abort (McdMaster *master, McdDispatcherPrivate *priv) +{ + g_object_unref (master); + priv->master = NULL; +} + +/* CHANNEL HANDLING */ + +/* Ensure that when the channelhandler dies, the channels do not be + * left around (e.g. when VOIP UI dies, the call used to hang + * around) + */ +static void +_mcd_dispatcher_channel_handler_destroy_cb (DBusGProxy * channelhandler, + gpointer userdata) +{ + McdChannel *channel; + + /* If the channel has already been destroyed, do not bother doing + * anything. */ + if (!userdata || !(G_IS_OBJECT (userdata)) || !(MCD_IS_CHANNEL (userdata))) + { + g_debug ("Channel has already been closed. No need to clean up."); + return; + } + + channel = MCD_CHANNEL (userdata); + + g_debug ("Channelhandler object been destroyed, chan still valid."); + mcd_mission_abort (MCD_MISSION (channel)); +} + +static void +disconnect_proxy_destry_cb (McdChannel *channel, DBusGProxy *channelhandler) +{ + g_signal_handlers_disconnect_by_func (channelhandler, + _mcd_dispatcher_channel_handler_destroy_cb, + channel); + g_object_unref (channelhandler); +} + +static void +cancel_proxy_call (McdChannel *channel, DBusGProxyCall *call) +{ + DBusGProxy *proxy = g_object_steal_data (G_OBJECT (channel), + "cancel_proxy_call_userdata"); + dbus_g_proxy_cancel_call (proxy, call); +} + +static void +_mcd_dispatcher_handle_channel_async_cb (DBusGProxy * proxy, GError * error, + gpointer userdata) +{ + McdDispatcherContext *context = userdata; + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (context->dispatcher); + McdChannel *channel; + + channel = mcd_dispatcher_context_get_channel (context); + McdChannelHandler *chandler = g_hash_table_lookup (priv->channel_handler_hash, + mcd_channel_get_channel_type (channel)); + + g_object_steal_data (G_OBJECT (channel), "cancel_proxy_call_userdata"); + g_signal_handlers_disconnect_matched (channel, G_SIGNAL_MATCH_FUNC, 0, 0, + NULL, cancel_proxy_call, NULL); + + /* We'll no longer need this proxy instance. */ + if (proxy && DBUS_IS_G_PROXY (proxy)) + { + g_object_unref (proxy); + } + + if (error != NULL) + { + GError *mc_error = NULL; + + g_warning ("Handle channel failed: %s", error->message); + + /* We can't reliably map channel handler error codes to MC error + * codes. So just using generic error message. + */ + mc_error = g_error_new (MC_ERROR, MC_CHANNEL_REQUEST_GENERIC_ERROR, + "Handle channel failed: %s", error->message); + + g_signal_emit_by_name (context->dispatcher, "dispatch-failed", + context->channel, mc_error); + + g_error_free (mc_error); + g_error_free (error); + if (context->channel) + mcd_mission_abort (MCD_MISSION (context->channel)); + return; + } + + /* In case the VOIP channel handler dies unexpectedly, we + * may end up in very confused state if we do + * nothing. Thus, we'll try to handle the death */ + + if (mcd_channel_get_channel_type_quark (channel) + == TELEPATHY_CHAN_IFACE_STREAMED_QUARK) + { + const McdConnection *connection; + DBusGConnection *dbus_connection; + GError *unique_proxy_error = NULL; + + connection = mcd_dispatcher_context_get_connection (context); + g_object_get (G_OBJECT (connection), "dbus-connection", &dbus_connection, NULL); + g_debug ("Aha! A streamed media channel, need to guard it."); + + DBusGProxy *unique_name_proxy = + dbus_g_proxy_new_for_name_owner (dbus_connection, + chandler->bus_name, + chandler->obj_path, + "org.freedesktop.Telepathy.ChannelHandler", + &unique_proxy_error); + if (unique_proxy_error == NULL) + { + g_debug ("Adding the destroy handler support."); + g_signal_connect (unique_name_proxy, + "destroy", + G_CALLBACK (_mcd_dispatcher_channel_handler_destroy_cb), + context->channel); + g_signal_connect (context->channel, "abort", + G_CALLBACK(disconnect_proxy_destry_cb), + unique_name_proxy); + } + } + + g_signal_emit_by_name (context->dispatcher, "dispatched", channel); + mcd_dispatcher_context_free (context); +} + +/* Happens at the end of successful filter chain execution (empty chain + * is always successful) + */ +static void +_mcd_dispatcher_start_channel_handler (McdDispatcherContext * context) +{ + McdChannelHandler *chandler; + McdDispatcherPrivate *priv; + McdChannel *channel; + + g_return_if_fail (context); + + priv = MCD_DISPATCHER_PRIV (context->dispatcher); + channel = mcd_dispatcher_context_get_channel (context); + + /* we need to know where's the channel handler and queue */ + /* drop from the queue */ + /*FIXME: Use Quarks in hashtable */ + chandler = + g_hash_table_lookup (priv->channel_handler_hash, + mcd_channel_get_channel_type (channel)); + if (chandler == NULL) + { + GError *mc_error; + g_debug ("No handler for channel type %s", + mcd_channel_get_channel_type (channel)); + + mc_error = g_error_new (MC_ERROR, MC_CHANNEL_REQUEST_GENERIC_ERROR, + "No handler for channel type %s", + mcd_channel_get_channel_type (channel)); + g_signal_emit_by_name (context->dispatcher, "dispatch-failed", channel, + mc_error); + g_error_free (mc_error); + } + else + { + DBusGProxyCall *call; + TpConn *tp_conn; + + const McdConnection *connection = mcd_dispatcher_context_get_connection (context); + DBusGConnection *dbus_connection; + + g_object_get (G_OBJECT (connection), + "dbus-connection", &dbus_connection, + "tp-connection", &tp_conn, NULL); + + DBusGProxy *handler_proxy = dbus_g_proxy_new_for_name (dbus_connection, + chandler->bus_name, + chandler->obj_path, + "org.freedesktop.Telepathy.ChannelHandler"); + + g_debug ("Starting chan handler (bus = %s, obj = '%s'): conn = %s, chan_type = %s," + " obj_path = %s, handle_type = %d, handle = %d", + chandler->bus_name, + chandler->obj_path, + dbus_g_proxy_get_path (DBUS_G_PROXY (tp_conn)), + mcd_channel_get_channel_type (channel), + mcd_channel_get_object_path (channel), + mcd_channel_get_handle_type (channel), + mcd_channel_get_handle (channel)); + + call = tp_ch_handle_channel_async (handler_proxy, + /*Connection bus */ + dbus_g_proxy_get_bus_name (DBUS_G_PROXY + (tp_conn)), + /*Connection path */ + dbus_g_proxy_get_path (DBUS_G_PROXY + (tp_conn)), + /*Channel type */ + mcd_channel_get_channel_type (channel), + /*Object path */ + mcd_channel_get_object_path (channel), + mcd_channel_get_handle_type (channel), + mcd_channel_get_handle (channel), + _mcd_dispatcher_handle_channel_async_cb, + context); + g_object_set_data (G_OBJECT (channel), "cancel_proxy_call_userdata", + handler_proxy); + g_signal_connect (channel, "abort", G_CALLBACK(cancel_proxy_call), + call); + g_object_unref (tp_conn); + } +} + +static void +_mcd_dispatcher_drop_channel_handler (McdDispatcherContext * context) +{ + g_return_if_fail(context); + + /* drop from the queue and close channel */ + + /* FIXME: The queue functionality is still missing. Add support for + it, once it's available. */ + + if (context->channel != NULL) + { + /* Context will be destroyed on this emission, so be careful + * not to access it after this. + */ + mcd_mission_abort (MCD_MISSION (context->channel)); + } +} + +/* STATE MACHINE */ + +static void +_mcd_dispatcher_leave_state_machine (McdDispatcherContext * context) +{ + McdDispatcherPrivate *priv = + MCD_DISPATCHER_PRIV (context->dispatcher); + + if (context->abort_fn) + context->abort_fn (context); + + /* _mcd_dispatcher_drop_channel_handler (context); */ + + priv->state_machine_list = + g_slist_remove (priv->state_machine_list, context); + + mcd_dispatcher_context_free (context); +} + +static void +on_channel_abort_context (McdChannel *channel, McdDispatcherContext *context) +{ + g_debug ("Abort Channel; Destroying state machine context."); + _mcd_dispatcher_leave_state_machine (context); +} + +/* Entering the state machine */ +static void +_mcd_dispatcher_enter_state_machine (McdDispatcher *dispatcher, + McdChannel *channel) +{ + McdDispatcherContext *context; + filter_func_t *chain; + GQuark chan_type_quark; + gboolean outgoing; + gint filter_flags; + + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (dispatcher); + + g_object_get (G_OBJECT (channel), + "channel-type-quark", &chan_type_quark, + "outgoing", &outgoing, + NULL); + + filter_flags = outgoing ? MCD_FILTER_OUT: MCD_FILTER_IN; + chain = _mcd_dispatcher_get_filter_chain (dispatcher, + chan_type_quark, + filter_flags); + + /* Preparing and filling the context */ + context = g_new0 (McdDispatcherContext, 1); + context->dispatcher = dispatcher; + context->channel = channel; + context->chain = chain; + + /* Context must be destroyed when the channel is destroyed */ + g_object_ref (channel); /* We hold separate refs for state machine */ + g_signal_connect (channel, "abort", G_CALLBACK (on_channel_abort_context), + context); + + if (chain) + { + g_debug ("entering state machine for channel of type: %s", + g_quark_to_string (chan_type_quark)); + + priv->state_machine_list = + g_slist_prepend (priv->state_machine_list, context); + mcd_dispatcher_context_process (context, TRUE); + } + else + { + g_debug ("No filters found for type %s, starting the channel handler", g_quark_to_string (chan_type_quark)); + _mcd_dispatcher_start_channel_handler (context); + } +} + +static gint +channel_on_state_machine (McdDispatcherContext *context, McdChannel *channel) +{ + return (context->channel == channel) ? 0 : 1; +} + +static void +_mcd_dispatcher_send (McdDispatcher * dispatcher, McdChannel * channel) +{ + McdDispatcherPrivate *priv; + g_return_if_fail (MCD_IS_DISPATCHER (dispatcher)); + g_return_if_fail (MCD_IS_CHANNEL (channel)); + + priv = MCD_DISPATCHER_PRIV (dispatcher); + + /* it can happen that this function gets called when the same channel has + * already entered the state machine or even when it has already been + * dispatched; so, check if this channel is already known to the + * dispatcher: */ + if (g_list_find (priv->channels, channel)) + { + McdDispatcherContext *context = NULL; + GSList *list; + g_debug ("%s: channel is already in dispatcher", G_STRFUNC); + + /* check if channel has already been dispatched (if it's still in the + * state machine list, this means that it hasn't) */ + list = g_slist_find_custom (priv->state_machine_list, channel, + (GCompareFunc)channel_on_state_machine); + if (list) context = list->data; + if (context) + { + gboolean outgoing; + g_debug ("%s: channel found in the state machine (%p)", G_STRFUNC, context); + g_object_get (G_OBJECT (channel), + "outgoing", &outgoing, + NULL); + + g_debug ("channel is %s", outgoing ? "outgoing" : "incoming"); + /* this channel has not been dispatched; we can get to this point if: + * 1) the channel is incoming (i.e. the contacts plugin icon is + * blinking) but the user didn't realize that and instead + * requested the same channel again; + * 2) theif channel is outgoing, and it was requested again before + * it could be created; I'm not sure this can really happen, + * though. In this case we don't have to do anything, just ignore + * this second request */ + if (!outgoing) + { + /* incoming channel: the state machine is probably stucked + * waiting for the user to acknowledge the channel. We bypass + * all that and instead launch the channel handler; this will + * free the context, but we still have to remove it from the + * machine state list ourselves. + * The filters should connect to the "dispatched" signal to + * catch this particular situation and clean-up gracefully. */ + _mcd_dispatcher_start_channel_handler (context); + priv->state_machine_list = + g_slist_remove(priv->state_machine_list, context); + + } + } + else + { + /* The channel was not found in the state machine, hence it must + * have been already dispatched. + * We could get to this point if the UI crashed while this channel + * was open, and now the user is requesting it again. just go straight + * and start the channel handler. */ + g_debug ("%s: channel is already dispatched, starting handler", G_STRFUNC); + /* Preparing and filling the context */ + context = g_new0 (McdDispatcherContext, 1); + context->dispatcher = dispatcher; + context->channel = channel; + + /* We must ref() the channel, because mcd_dispatcher_context_free() + * will unref() it */ + g_object_ref (channel); + _mcd_dispatcher_start_channel_handler (context); + } + return; + } + + /* Get hold of it in our all channels list */ + g_object_ref (channel); /* We hold separate refs for channels list */ + g_signal_connect (channel, "abort", G_CALLBACK (on_channel_abort_list), + dispatcher); + priv->channels = g_list_prepend (priv->channels, channel); + + g_signal_emit_by_name (dispatcher, "channel-added", channel); + _mcd_dispatcher_enter_state_machine (dispatcher, channel); +} + +static void +_mcd_dispatcher_set_property (GObject * obj, guint prop_id, + const GValue * val, GParamSpec * pspec) +{ + McdDispatcher *dispatcher = MCD_DISPATCHER (obj); + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (obj); + DBusGConnection *dbus_connection; + McdMaster *master; + + switch (prop_id) + { + case PROP_PLUGIN_DIR: + g_free (priv->plugin_dir); + + priv->plugin_dir = g_value_dup_string (val); + + _mcd_dispatcher_unload_filters (dispatcher); + _mcd_dispatcher_load_filters (dispatcher); + break; + case PROP_DBUS_CONNECTION: + dbus_connection = g_value_get_pointer (val); + dbus_g_connection_ref (dbus_connection); + if (priv->dbus_connection) + dbus_g_connection_unref (priv->dbus_connection); + priv->dbus_connection = dbus_connection; + break; + case PROP_MCD_MASTER: + master = g_value_get_object (val); + g_object_ref (G_OBJECT (master)); + if (priv->master) + { + g_signal_handlers_disconnect_by_func (G_OBJECT (master), G_CALLBACK (on_master_abort), NULL); + g_object_unref (priv->master); + } + priv->master = master; + g_signal_connect (G_OBJECT (master), "abort", G_CALLBACK (on_master_abort), priv); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_mcd_dispatcher_get_property (GObject * obj, guint prop_id, + GValue * val, GParamSpec * pspec) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (obj); + + switch (prop_id) + { + case PROP_PLUGIN_DIR: + g_value_set_string (val, priv->plugin_dir); + break; + case PROP_DBUS_CONNECTION: + g_value_set_pointer (val, priv->dbus_connection); + break; + case PROP_MCD_MASTER: + g_value_set_object (val, priv->master); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_mcd_dispatcher_finalize (GObject * object) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (object); + GType type; + gint i; + + g_hash_table_destroy (priv->channel_handler_hash); + + type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, + G_TYPE_UINT, G_TYPE_INVALID); + for (i = 0; i < priv->channel_handler_caps->len; i++) + g_boxed_free (type, g_ptr_array_index (priv->channel_handler_caps, i)); + g_ptr_array_free (priv->channel_handler_caps, TRUE); + + G_OBJECT_CLASS (mcd_dispatcher_parent_class)->finalize (object); +} + +static void +_mcd_dispatcher_dispose (GObject * object) +{ + McdDispatcher *dispatcher = MCD_DISPATCHER (object); + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (object); + + if (priv->is_disposed) + { + return; + } + priv->is_disposed = TRUE; + + if (priv->master) + { + g_object_unref (priv->master); + priv->master = NULL; + } + + if (priv->dbus_connection) + { + dbus_g_connection_unref (priv->dbus_connection); + priv->dbus_connection = NULL; + } + + if (priv->channels) + { + g_list_free (priv->channels); + priv->channels = NULL; + } + g_free (priv->plugin_dir); + priv->plugin_dir = NULL; + + _mcd_dispatcher_unload_filters (dispatcher); + G_OBJECT_CLASS (mcd_dispatcher_parent_class)->dispose (object); +} + +gboolean +mcd_dispatcher_send (McdDispatcher * dispatcher, McdChannel *channel) +{ + g_return_val_if_fail (MCD_IS_DISPATCHER (dispatcher), FALSE); + g_return_val_if_fail (MCD_IS_CHANNEL (channel), FALSE); + MCD_DISPATCHER_GET_CLASS (dispatcher)->send (dispatcher, channel); + return TRUE; +} + +static void +mcd_dispatcher_class_init (McdDispatcherClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (McdDispatcherPrivate)); + + object_class->set_property = _mcd_dispatcher_set_property; + object_class->get_property = _mcd_dispatcher_get_property; + object_class->finalize = _mcd_dispatcher_finalize; + object_class->dispose = _mcd_dispatcher_dispose; + klass->send = _mcd_dispatcher_send; + + mcd_dispatcher_signals[CHANNEL_ADDED] = + g_signal_new ("channel_added", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (McdDispatcherClass, + channel_added_signal), + NULL, NULL, mcd_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, MCD_TYPE_CHANNEL); + + mcd_dispatcher_signals[CHANNEL_REMOVED] = + g_signal_new ("channel_removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (McdDispatcherClass, + channel_removed_signal), + NULL, NULL, mcd_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, MCD_TYPE_CHANNEL); + + mcd_dispatcher_signals[DISPATCHED] = + g_signal_new ("dispatched", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (McdDispatcherClass, + dispatched_signal), + NULL, NULL, mcd_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, MCD_TYPE_CHANNEL); + + mcd_dispatcher_signals[DISPATCH_FAILED] = + g_signal_new ("dispatch-failed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (McdDispatcherClass, + dispatch_failed_signal), + NULL, NULL, mcd_marshal_VOID__OBJECT_POINTER, + G_TYPE_NONE, 2, MCD_TYPE_CHANNEL, G_TYPE_POINTER); + + /* Properties */ + g_object_class_install_property (object_class, + PROP_PLUGIN_DIR, + g_param_spec_string ("plugin-dir", + _("Plugin Directory"), + _("The Directory to load filter plugins from"), + MCD_DEFAULT_FILTER_PLUGIN_DIR, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_DBUS_CONNECTION, + g_param_spec_pointer ("dbus-connection", + _("DBus Connection"), + _("DBus connection to use by us"), + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_MCD_MASTER, + g_param_spec_object ("mcd-master", + _("McdMaster"), + _("McdMaster"), + MCD_TYPE_MASTER, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +_build_channel_capabilities (gchar *channel_type, McdChannelHandler *handler, + GPtrArray *capabilities) +{ + GValue cap = {0,}; + GType cap_type; + + cap_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, + G_TYPE_UINT, G_TYPE_INVALID); + g_value_init (&cap, cap_type); + g_value_take_boxed (&cap, dbus_g_type_specialized_construct (cap_type)); + + dbus_g_type_struct_set (&cap, + 0, channel_type, + 1, handler->capabilities, + G_MAXUINT); + + g_ptr_array_add (capabilities, g_value_get_boxed (&cap)); +} + + + +static void +mcd_dispatcher_init (McdDispatcher * dispatcher) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (dispatcher); + + priv->plugin_dir = g_strdup (MCD_DEFAULT_FILTER_PLUGIN_DIR); + + g_datalist_init (&(priv->interface_filters)); + + priv->channel_handler_hash = mcd_get_channel_handlers (); + + priv->channel_handler_caps = g_ptr_array_new(); + g_hash_table_foreach (priv->channel_handler_hash, + (GHFunc)_build_channel_capabilities, + priv->channel_handler_caps); + + _mcd_dispatcher_load_filters (dispatcher); +} + +McdDispatcher * +mcd_dispatcher_new (DBusGConnection * dbus_connection, McdMaster * master) +{ + McdDispatcher *obj; + obj = MCD_DISPATCHER (g_object_new (MCD_TYPE_DISPATCHER, + "dbus-connection", + dbus_connection, + "mcd-master", + master, + NULL)); + return obj; +} + +/* The new state machine walker function for pluginized filters*/ + +void +mcd_dispatcher_context_process (McdDispatcherContext * context, gboolean result) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (context->dispatcher); + + if (result) + { + /* Do we still have functions to go through? */ + if (context->chain[context->next_func_index]) + { + filter_func_t filter = context->chain[context->next_func_index]; + + context->next_func_index++; + + /*new state-new abort function*/ + context->abort_fn = NULL; + g_debug ("Next filter"); + filter (context); + return; /*State machine goes on...*/ + } + else + { + /* Context would be destroyed somewhere in this call */ + _mcd_dispatcher_start_channel_handler (context); + } + } + else + { + g_debug ("Filters failed, disposing request"); + + /* Some filter failed. The request shall not be handled. */ + /* Context would be destroyed somewhere in this call */ + _mcd_dispatcher_drop_channel_handler (context); + } + + /* FIXME: Should we remove the request in other cases? */ + priv->state_machine_list = + g_slist_remove(priv->state_machine_list, context); +} + +static void +mcd_dispatcher_context_free (McdDispatcherContext * context) +{ + /* FIXME: check for leaks */ + g_return_if_fail (context); + + /* Freeing context data */ + if (context->channel) + { + g_signal_handlers_disconnect_by_func (context->channel, + G_CALLBACK (on_channel_abort_context), + context); + g_object_unref (context->channel); + } + g_free (context); +} + +/* CONTEXT API */ + +/* Context getters */ +const TpChan * +mcd_dispatcher_context_get_channel_object (McdDispatcherContext * ctx) +{ + TpChan *tp_chan; + g_return_val_if_fail (ctx, 0); + g_object_get (G_OBJECT (ctx->channel), "tp-channel", &tp_chan, NULL); + g_object_unref (G_OBJECT (tp_chan)); + return tp_chan; +} + +McdDispatcher* +mcd_dispatcher_context_get_dispatcher (McdDispatcherContext * ctx) +{ + return ctx->dispatcher; +} + +const McdConnection * +mcd_dispatcher_context_get_connection (McdDispatcherContext * context) +{ + McdConnection *connection; + + g_return_val_if_fail (context, NULL); + + g_object_get (G_OBJECT (context->channel), + "connection", &connection, + NULL); + g_object_unref (G_OBJECT (connection)); + + return connection; +} + +const TpConn * +mcd_dispatcher_context_get_connection_object (McdDispatcherContext * ctx) +{ + const McdConnection *connection; + TpConn *tp_conn; + + connection = mcd_dispatcher_context_get_connection (ctx); + g_object_get (G_OBJECT (connection), "tp-connection", + &tp_conn, NULL); + + g_object_unref (tp_conn); + return tp_conn; +} + +McdChannel * +mcd_dispatcher_context_get_channel (McdDispatcherContext * ctx) +{ + g_return_val_if_fail (ctx, 0); + return ctx->channel; +} + +gpointer +mcd_dispatcher_context_get_data (McdDispatcherContext * ctx) +{ + g_return_val_if_fail (ctx, NULL); + return ctx->data; +} + +McdChannelHandler * +mcd_dispatcher_context_get_chan_handler (McdDispatcherContext * ctx) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (ctx->dispatcher); + McdChannel *channel; + + channel = mcd_dispatcher_context_get_channel (ctx); + return g_hash_table_lookup (priv->channel_handler_hash, + mcd_channel_get_channel_type (channel)); +} + +/*Returns an array of the participants in the channel*/ +GPtrArray * +mcd_dispatcher_context_get_members (McdDispatcherContext * ctx) +{ + return mcd_channel_get_members (ctx->channel); +} + +/* Context setters */ +void +mcd_dispatcher_context_set_abort_fn (McdDispatcherContext * ctx, abort_function_t abort_fn) +{ + g_return_if_fail (ctx); + ctx->abort_fn = abort_fn; +} + +void +mcd_dispatcher_context_set_data (McdDispatcherContext * ctx, gpointer data) +{ + g_return_if_fail (ctx); + ctx->data = data; +} + +GPtrArray *mcd_dispatcher_get_channel_capabilities (McdDispatcher * dispatcher) +{ + McdDispatcherPrivate *priv = MCD_DISPATCHER_PRIV (dispatcher); + + return priv->channel_handler_caps; +} diff --git a/src/mcd-dispatcher.h b/src/mcd-dispatcher.h new file mode 100644 index 00000000..6410adcf --- /dev/null +++ b/src/mcd-dispatcher.h @@ -0,0 +1,95 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_DISPATCHER_H +#define MCD_DISPATCHER_H + +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-profile.h> +#include <libtelepathy/tp-chan.h> +#include <libtelepathy/tp-conn.h> + +G_BEGIN_DECLS + +#define MCD_TYPE_DISPATCHER (mcd_dispatcher_get_type ()) +#define MCD_DISPATCHER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_DISPATCHER, McdDispatcher)) +#define MCD_DISPATCHER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_DISPATCHER, McdDispatcherClass)) +#define MCD_IS_DISPATCHER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_DISPATCHER)) +#define MCD_IS_DISPATCHER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_DISPATCHER)) +#define MCD_DISPATCHER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_DISPATCHER, McdDispatcherClass)) + +typedef struct _McdDispatcher McdDispatcher; +typedef struct _McdDispatcherClass McdDispatcherClass; +typedef struct _McdDispatcherStatus McdDispatcherStatus; + +#include "mcd-channel.h" +#include "mcd-master.h" + +struct _McdDispatcher +{ + McdMission parent; +}; + +struct _McdDispatcherClass +{ + McdMissionClass parent_class; + + /* signals */ + void (*channel_added_signal) (McdDispatcher *dispatcher, + McdChannel *channel); + void (*channel_removed_signal) (McdDispatcher *dispatcher, + McdChannel *channel); + void (*dispatched_signal) (McdDispatcher * dispatcher, + McdChannel * channel); + void (*dispatch_failed_signal) (McdDispatcher * dispatcher, + McdChannel * channel, + GError *error); + + /* virtual methods */ + void (*send) (McdDispatcher * dispatcher, McdChannel *channel); +}; + +GType mcd_dispatcher_get_type (void); + +McdDispatcher *mcd_dispatcher_new (DBusGConnection * dbus_connection, + McdMaster * master); + +gboolean mcd_dispatcher_send (McdDispatcher * dispatcher, McdChannel *channel); + +McdDispatcherStatus +mcd_dispatcher_get_status (McdDispatcher * dispatcher, McdChannel *channel); + +gint mcd_dispatcher_get_channel_type_usage (McdDispatcher * dispatcher, + GQuark chan_type_quark); + +/* retrieves the channel handlers' capabilities, in a format suitable for being + * used as a parameter for the telepathy "AdvertiseCapabilities" method */ +GPtrArray *mcd_dispatcher_get_channel_capabilities (McdDispatcher * dispatcher); + +G_END_DECLS + +#endif /* MCD_DISPATCHER_H */ diff --git a/src/mcd-filtering-int.h b/src/mcd-filtering-int.h new file mode 100644 index 00000000..d5436e38 --- /dev/null +++ b/src/mcd-filtering-int.h @@ -0,0 +1,17 @@ +#ifndef _MCD_FILTERS_INT_H +#define _MCD_FILTERS_INT_H + +#include "mcd-filtering.h" + +/* Internals of the filter-processing mechanism */ + +void dispose_state_machine_data (mcdf_context_t sm_ctx); +void start_channel_handler (mcdf_context_t ctx); +void dispose_channel_request (ChanHandlerReq * request); +void drop_channel_handler (mcdf_context_t ctx); +void enter_state_machine (guint request_id, ChanHandlerReq * chan_req); + + + + +#endif diff --git a/src/mcd-main.c b/src/mcd-main.c new file mode 100644 index 00000000..ac42d32e --- /dev/null +++ b/src/mcd-main.c @@ -0,0 +1,41 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <stdlib.h> +#include <glib.h> +#include "mcd-service.h" + +int +main (int argc, char **argv) +{ + McdService *mcd; + + g_type_init (); + + mcd = mcd_service_new (); + + mcd_service_run (MCD_OBJECT (mcd)); + + return 0; +} diff --git a/src/mcd-manager.c b/src/mcd-manager.c new file mode 100644 index 00000000..7121f881 --- /dev/null +++ b/src/mcd-manager.c @@ -0,0 +1,1065 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <string.h> +#include <glib/gi18n.h> +#include <libtelepathy/tp-connmgr.h> +#include <libtelepathy/tp-interfaces.h> +#include <libmissioncontrol/mission-control.h> + +#include "mcd-connection.h" +#include "mcd-manager.h" + +#define MCD_MANAGER_PRIV(manager) (G_TYPE_INSTANCE_GET_PRIVATE ((manager), \ + MCD_TYPE_MANAGER, \ + McdManagerPrivate)) + +G_DEFINE_TYPE (McdManager, mcd_manager, MCD_TYPE_OPERATION); + +typedef struct _McdManagerPrivate +{ + DBusGConnection *dbus_connection; + McManager *mc_manager; + McdPresenceFrame *presence_frame; + McdDispatcher *dispatcher; + TpConnMgr *tp_conn_mgr; + GList *accounts; + gboolean is_disposed; + gboolean delay_presence_request; + + /* Table of channels to create upon connection */ + GHashTable *requested_channels; +} McdManagerPrivate; + +enum +{ + PROP_0, + PROP_PRESENCE_FRAME, + PROP_DISPATCHER, + PROP_MC_MANAGER, + PROP_DBUS_CONNECTION, + PROP_ACCOUNTS +}; + +enum _McdManagerSignalType +{ + ACCOUNT_ADDED, + ACCOUNT_REMOVED, + LAST_SIGNAL +}; + +static guint mcd_manager_signals[LAST_SIGNAL] = { 0 }; + +static void abort_requested_channel (gchar *key, + struct mcd_channel_request *req, + McdManager *manager); + +static void +_mcd_manager_create_connection (McdManager * manager, McAccount * account) +{ + McdConnection *connection; + McdManagerPrivate *priv = MCD_MANAGER_PRIV (manager); + + g_return_if_fail (mcd_manager_get_account_connection (manager, account) == NULL); + + connection = mcd_connection_new (priv->dbus_connection, + mc_manager_get_bus_name (priv-> + mc_manager), + priv->tp_conn_mgr, account, + priv->presence_frame, + priv->dispatcher); + mcd_operation_take_mission (MCD_OPERATION (manager), + MCD_MISSION (connection)); + g_debug ("%s: Created a connection %p for account: %s", G_STRFUNC, + connection, mc_account_get_unique_name (account)); +} + +void +_mcd_manager_create_connections (McdManager * manager) +{ + GList *node; + McdManagerPrivate *priv = MCD_MANAGER_PRIV (manager); + + g_return_if_fail (TELEPATHY_IS_CONNMGR (priv->tp_conn_mgr)); + + for (node = priv->accounts; node; node = node->next) + { + /* Create a connection object for each account */ + McAccount *account = MC_ACCOUNT (node->data); + + /* Only create a new connection if there already is not one */ + if (!mcd_manager_get_account_connection (manager, account)) + { + _mcd_manager_create_connection (manager, account); + } + } +} + +static gint +_find_connection (gconstpointer data, gconstpointer user_data) +{ + McdConnection *connection = MCD_CONNECTION (data); + McAccount *account = MC_ACCOUNT (user_data); + McAccount *connection_account = NULL; + gint ret; + + g_object_get (G_OBJECT (connection), "account", &connection_account, NULL); + + if (connection_account == account) + { + ret = 0; + } + else + { + ret = 1; + } + + g_object_unref (G_OBJECT (connection_account)); + return ret; +} + +static gint +_find_connection_by_path (gconstpointer data, gconstpointer user_data) +{ + TpConn *tp_conn; + McdConnection *connection = MCD_CONNECTION (data); + const gchar *object_path = (const gchar *)user_data; + const gchar *conn_object_path = NULL; + gint ret; + + g_object_get (G_OBJECT (connection), "tp-connection", + &tp_conn, NULL); + if (!tp_conn) + return 1; + conn_object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (tp_conn)); + if (strcmp (conn_object_path, object_path) == 0) + { + ret = 0; + } + else + { + ret = 1; + } + + g_object_unref (G_OBJECT (tp_conn)); + return ret; +} + +static void +requested_channel_process (gchar *key, struct mcd_channel_request *req, + McdManager *manager) +{ + GError *error = NULL; + + g_debug ("%s: creating channel %s - %s - %s", G_STRFUNC, req->account_name, req->channel_type, req->channel_handle_string); + + if (!mcd_manager_request_channel (manager, req, &error)) + { + g_assert (error != NULL); + g_debug ("%s: channel request failed (%s)", G_STRFUNC, error->message); + g_error_free (error); + return; + } + g_assert (error == NULL); +} + +static void +on_presence_stable (McdPresenceFrame *presence_frame, + gboolean is_stable, McdManager *manager) +{ + McdManagerPrivate *priv = MCD_MANAGER_PRIV (manager); + + g_debug ("%s called", G_STRFUNC); + if (priv->requested_channels) + { + /* don't do anything until the presence frame is stable */ + g_debug ("presence frame is %sstable", mcd_presence_frame_is_stable (presence_frame) ? "" : "not "); + if (!is_stable) + return; + if (mcd_presence_frame_get_actual_presence (presence_frame) >= + MC_PRESENCE_AVAILABLE) + { + g_hash_table_foreach (priv->requested_channels, + (GHFunc)requested_channel_process, + manager); + } + else + { + /* We couldn't connect; signal an error to the channel requestors + */ + g_hash_table_foreach (priv->requested_channels, + (GHFunc)abort_requested_channel, + manager); + } + g_hash_table_destroy (priv->requested_channels); + priv->requested_channels = NULL; + } +} + +static gboolean +on_presence_requested_idle (gpointer data) +{ + McdManager *manager = MCD_MANAGER (data); + McdManagerPrivate *priv = MCD_MANAGER_PRIV (manager); + McPresence requested_presence = + mcd_presence_frame_get_requested_presence (priv->presence_frame); + McPresence actual_presence = + mcd_presence_frame_get_actual_presence (priv->presence_frame); + + g_debug ("%s: %d, %d", G_STRFUNC, requested_presence, + actual_presence); + if ((actual_presence == MC_PRESENCE_OFFLINE + || actual_presence == MC_PRESENCE_UNSET) + && (requested_presence != MC_PRESENCE_OFFLINE + && requested_presence != MC_PRESENCE_UNSET)) + { + if (!priv->tp_conn_mgr) + { + g_return_val_if_fail (MC_IS_MANAGER (priv->mc_manager), + FALSE); + + priv->tp_conn_mgr = + tp_connmgr_new (priv->dbus_connection, + mc_manager_get_bus_name (priv-> + mc_manager), + mc_manager_get_object_path (priv-> + mc_manager), + TP_IFACE_CONN_MGR_INTERFACE); + g_debug ("%s: Manager %s created", G_STRFUNC, + mc_manager_get_unique_name (priv->mc_manager)); + } + + _mcd_manager_create_connections (manager); + } + + return FALSE; +} + +static void +abort_requested_channel (gchar *key, struct mcd_channel_request *req, + McdManager *manager) +{ + McdManagerPrivate *priv = MCD_MANAGER_PRIV (manager); + McdChannel *channel; + GError *error; + + g_debug ("%s: aborting channel %s - %s - %s", G_STRFUNC, + req->account_name, req->channel_type, req->channel_handle_string); + error = g_error_new (MC_ERROR, MC_NETWORK_ERROR, + "Connection cancelled"); + /* we must create a channel object, just for delivering the error */ + channel = mcd_channel_new (NULL, + NULL, + req->channel_type, + 0, + req->channel_handle_type, + TRUE, /* outgoing */ + req->requestor_serial, + req->requestor_client_id); + g_signal_emit_by_name (priv->dispatcher, "dispatch-failed", + channel, error); + g_error_free (error); + /* this will actually destroy the channel object */ + g_object_unref (channel); +} + +static void +abort_requested_channels (McdManager *manager) +{ + McdManagerPrivate *priv = MCD_MANAGER_PRIV (manager); + + g_debug ("%s called %p", G_STRFUNC, priv->requested_channels); + g_hash_table_foreach (priv->requested_channels, + (GHFunc)abort_requested_channel, + manager); + g_hash_table_destroy (priv->requested_channels); + priv->requested_channels = NULL; +} + +static void +on_presence_requested (McdPresenceFrame * presence_frame, + McPresence presence, + const gchar * presence_message, gpointer data) +{ + McdManagerPrivate *priv; + + g_debug ("%s: Current connectivity status is %d", G_STRFUNC, + mcd_mission_is_connected (MCD_MISSION (data))); + + if (mcd_mission_is_connected (MCD_MISSION (data))) + { + on_presence_requested_idle(data); + } + else + { + priv = MCD_MANAGER_PRIV(data); + g_debug ("%s: Delaying call to on_presence_requested_idle", G_STRFUNC); + priv->delay_presence_request = TRUE; + + /* if we are offline and the user cancels the connection request, we + * must clean the requested channels and return an error to the UI for + * each of them. */ + if (presence == MC_PRESENCE_OFFLINE && priv->requested_channels != NULL) + abort_requested_channels (MCD_MANAGER (data)); + } +} + +/* FIXME: Until we have a proper serialization and deserialization, we will + * stick with killing all connections that were present before + * mission-control got control of telepathy managers + */ +/* Search the bus for already connected accounts and disconnect them. */ +static void +_mcd_manager_nuke_connections (McdManager *manager) +{ + McdManagerPrivate *priv; + char **names, **name; + DBusGProxy *proxy; + GError *error = NULL; + static gboolean already_nuked = FALSE; + + if (already_nuked) + return; /* We only nuke it once in process instance */ + already_nuked = TRUE; + + g_debug ("Nuking possible stale connections"); + + priv = MCD_MANAGER_PRIV (manager); + proxy = dbus_g_proxy_new_for_name(priv->dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + + if (!proxy) + { + g_warning ("Error creating proxy"); + return; + } + + if (!dbus_g_proxy_call(proxy, "ListNames", &error, G_TYPE_INVALID, + G_TYPE_STRV, &names, G_TYPE_INVALID)) + { + g_warning ("ListNames() failed: %s", error->message); + g_error_free (error); + g_object_unref (proxy); + return; + } + + g_object_unref(proxy); + + for (name = names; *name; name++) + { + if (strncmp(*name, "org.freedesktop.Telepathy.Connection.", + strlen("org.freedesktop.Telepathy.Connection.")) == 0) + { + gchar *path = g_strdelimit(g_strdup_printf("/%s", *name), ".", '/'); + + g_debug ("Trying to disconnect (%s), path=%s", *name, path); + + proxy = dbus_g_proxy_new_for_name(priv->dbus_connection, + *name, path, + TP_IFACE_CONN_INTERFACE); + + g_free(path); + + if (proxy) + { + if (!dbus_g_proxy_call(proxy, "Disconnect", &error, + G_TYPE_INVALID, G_TYPE_INVALID)) + { + g_warning ("Disconnect() failed: %s", error->message); + g_error_free(error); + error = NULL; + } + + g_object_unref(proxy); + } + else + { + g_warning ("Error creating proxy"); + } + } + } + g_strfreev(names); +} + +static void +requested_channel_free (struct mcd_channel_request *req) +{ + g_free ((gchar *)req->account_name); + g_free ((gchar *)req->channel_type); + g_free ((gchar *)req->channel_handle_string); + g_free ((gchar *)req->requestor_client_id); + g_free (req); +} + +static void +request_channel_delayed (McdManager *manager, + const struct mcd_channel_request *req) +{ + McdManagerPrivate *priv = MCD_MANAGER_PRIV (manager); + struct mcd_channel_request *req_cp; + gchar *key; + + g_debug ("%s: account %s, type %s, handle %s", G_STRFUNC, req->account_name, req->channel_type, req->channel_handle_string); + if (!priv->requested_channels) + priv->requested_channels = + g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) + requested_channel_free); + + if (req->channel_handle_string) + key = g_strdup_printf("%s\n%s\n%s", req->account_name, req->channel_type, + req->channel_handle_string); + else + key = g_strdup_printf("%s\n%s\n%u", req->account_name, req->channel_type, + req->channel_handle); + req_cp = g_malloc (sizeof (struct mcd_channel_request)); + memcpy(req_cp, req, sizeof (struct mcd_channel_request)); + req_cp->account_name = g_strdup (req->account_name); + req_cp->channel_type = g_strdup (req->channel_type); + req_cp->channel_handle_string = g_strdup (req->channel_handle_string); + req_cp->requestor_client_id = g_strdup (req->requestor_client_id); + g_hash_table_insert (priv->requested_channels, key, req_cp); + g_free (key); +} + +static void +_mcd_manager_set_presence_frame (McdManager *manager, McdPresenceFrame *presence_frame) +{ + McdManagerPrivate *priv = MCD_MANAGER_PRIV (manager); + if (presence_frame) + { + g_return_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame)); + g_object_ref (presence_frame); + } + + if (priv->presence_frame) + { + g_signal_handlers_disconnect_by_func (G_OBJECT + (priv->presence_frame), + G_CALLBACK + (on_presence_requested), manager); + g_signal_handlers_disconnect_by_func (priv->presence_frame, + G_CALLBACK + (on_presence_stable), + manager); + g_object_unref (priv->presence_frame); + } + priv->presence_frame = presence_frame; + if (priv->presence_frame) + { + g_signal_connect (G_OBJECT (priv->presence_frame), + "presence-requested", + G_CALLBACK (on_presence_requested), manager); + g_signal_connect (priv->presence_frame, "presence-stable", + G_CALLBACK (on_presence_stable), manager); + } +} + +static void +_mcd_manager_finalize (GObject * object) +{ + McdManagerPrivate *priv = MCD_MANAGER_PRIV (object); + + if (priv->requested_channels) + { + g_hash_table_destroy (priv->requested_channels); + priv->requested_channels = NULL; + } + G_OBJECT_CLASS (mcd_manager_parent_class)->finalize (object); +} + +static void +_mcd_manager_dispose (GObject * object) +{ + GList *node; + McdManagerPrivate *priv; + priv = MCD_MANAGER_PRIV (object); + + if (priv->is_disposed) + { + return; + } + + priv->is_disposed = TRUE; + + if (priv->accounts) + { + for (node = priv->accounts; node; node = node->next) + g_object_unref (G_OBJECT (node->data)); + g_list_free (priv->accounts); + priv->accounts = NULL; + } + + if (priv->dispatcher) + { + g_object_unref (priv->dispatcher); + priv->dispatcher = NULL; + } + + _mcd_manager_set_presence_frame (MCD_MANAGER (object), NULL); + + if (priv->dbus_connection) + { + dbus_g_connection_unref (priv->dbus_connection); + priv->dbus_connection = NULL; + } + + if (priv->mc_manager) + { + g_object_unref (priv->mc_manager); + priv->mc_manager = NULL; + } + + if (priv->tp_conn_mgr) + { + g_object_unref (priv->tp_conn_mgr); + priv->tp_conn_mgr = NULL; + } + + G_OBJECT_CLASS (mcd_manager_parent_class)->dispose (object); +} + +static void +_mcd_manager_connect (McdMission * mission) +{ + McdManagerPrivate *priv = MCD_MANAGER_PRIV (mission); + + g_debug ("%s: delay_presence_request = %d", G_STRFUNC, priv->delay_presence_request); + if (priv->delay_presence_request) + { + priv->delay_presence_request = FALSE; + g_idle_add (on_presence_requested_idle, mission); + g_debug ("%s: Added idle func on_presence_requested_idle", G_STRFUNC); + } + MCD_MISSION_CLASS (mcd_manager_parent_class)->connect (mission); +} + +static void +_mcd_manager_disconnect (McdMission * mission) +{ + GList *connections; + + g_debug ("%s(%p)", G_STRFUNC, mission); + MCD_MISSION_CLASS (mcd_manager_parent_class)->disconnect (mission); + + /* We now call mcd_mission_abort() on all child connections; but since this + * could modify the list of the children, we cannot just use + * mcd_operation_foreach(). Instead, make a copy of the list and work on + * that. */ + g_debug("manager tree before abort:"); + mcd_debug_print_tree(mission); + connections = g_list_copy ((GList *)mcd_operation_get_missions + (MCD_OPERATION (mission))); + g_list_foreach (connections, (GFunc) mcd_mission_abort, NULL); + g_list_free (connections); + g_debug("manager tree after abort:"); + mcd_debug_print_tree(mission); +} + +static void +_mcd_manager_set_property (GObject * obj, guint prop_id, + const GValue * val, GParamSpec * pspec) +{ + McdManagerPrivate *priv = MCD_MANAGER_PRIV (obj); + McdPresenceFrame *presence_frame; + McdDispatcher *dispatcher; + McManager *mc_manager; + DBusGConnection *dbus_connection; + + switch (prop_id) + { + case PROP_PRESENCE_FRAME: + presence_frame = g_value_get_object (val); + _mcd_manager_set_presence_frame (MCD_MANAGER (obj), presence_frame); + break; + case PROP_DISPATCHER: + dispatcher = g_value_get_object (val); + if (dispatcher) + { + g_return_if_fail (MCD_IS_DISPATCHER (dispatcher)); + g_object_ref (dispatcher); + } + if (priv->dispatcher) + { + g_object_unref (priv->dispatcher); + } + priv->dispatcher = dispatcher; + break; + case PROP_MC_MANAGER: + mc_manager = g_value_get_object (val); + g_return_if_fail (MC_IS_MANAGER (mc_manager)); + g_object_ref (mc_manager); + if (priv->mc_manager) + g_object_unref (priv->mc_manager); + priv->mc_manager = mc_manager; + break; + case PROP_DBUS_CONNECTION: + dbus_connection = g_value_get_pointer (val); + dbus_g_connection_ref (dbus_connection); + if (priv->dbus_connection) + dbus_g_connection_unref (priv->dbus_connection); + priv->dbus_connection = dbus_connection; + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_mcd_manager_get_property (GObject * obj, guint prop_id, + GValue * val, GParamSpec * pspec) +{ + McdManagerPrivate *priv = MCD_MANAGER_PRIV (obj); + + switch (prop_id) + { + case PROP_PRESENCE_FRAME: + g_value_set_object (val, priv->presence_frame); + break; + case PROP_DISPATCHER: + g_value_set_object (val, priv->dispatcher); + break; + case PROP_MC_MANAGER: + g_value_set_object (val, priv->mc_manager); + break; + case PROP_DBUS_CONNECTION: + g_value_set_pointer (val, priv->dbus_connection); + break; + case PROP_ACCOUNTS: + g_debug ("%s: accounts getting over-written", G_STRFUNC); + g_value_set_pointer (val, priv->accounts); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +mcd_manager_class_init (McdManagerClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + McdMissionClass *mission_class = MCD_MISSION_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (McdManagerPrivate)); + + object_class->finalize = _mcd_manager_finalize; + object_class->dispose = _mcd_manager_dispose; + object_class->set_property = _mcd_manager_set_property; + object_class->get_property = _mcd_manager_get_property; + + mission_class->connect = _mcd_manager_connect; + mission_class->disconnect = _mcd_manager_disconnect; + + /* signals */ + mcd_manager_signals[ACCOUNT_ADDED] = + g_signal_new ("account-added", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdManagerClass, account_added_signal), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + mcd_manager_signals[ACCOUNT_REMOVED] = + g_signal_new ("account-removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdManagerClass, account_removed_signal), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + /* Properties */ + g_object_class_install_property (object_class, + PROP_PRESENCE_FRAME, + g_param_spec_object ("presence-frame", + _ + ("Presence Frame Object"), + _ + ("Presence frame Object used by connections to update presence"), + MCD_TYPE_PRESENCE_FRAME, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_DISPATCHER, + g_param_spec_object ("dispatcher", + _ + ("Dispatcher Object"), + _ + ("Channel dispatcher object"), + MCD_TYPE_DISPATCHER, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_MC_MANAGER, + g_param_spec_object ("mc-manager", + _ + ("McManager Object"), + _ + ("McManager Object which this manager uses"), + MC_TYPE_MANAGER, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_DBUS_CONNECTION, + g_param_spec_pointer ("dbus-connection", + _("DBus Connection"), + _ + ("DBus connection to use by us"), + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, PROP_ACCOUNTS, + g_param_spec_pointer ("accounts", + _("Accounts"), + _ + ("List of accounts associated with this manager"), + G_PARAM_READABLE)); +} + +static void +mcd_manager_init (McdManager * manager) +{ + McdManagerPrivate *priv = MCD_MANAGER_PRIV (manager); + + priv->dbus_connection = NULL; +} + +/* Public methods */ + +McdManager * +mcd_manager_new (McManager * mc_manager, + McdPresenceFrame * pframe, + McdDispatcher *dispatcher, + DBusGConnection * dbus_connection) +{ + McdManager *obj; + obj = MCD_MANAGER (g_object_new (MCD_TYPE_MANAGER, + "mc-manager", mc_manager, + "presence-frame", pframe, + "dispatcher", dispatcher, + "dbus-connection", dbus_connection, NULL)); + _mcd_manager_nuke_connections (obj); + return obj; +} + +gboolean +mcd_manager_can_handle_account (McdManager * manager, McAccount *account) +{ + McdManagerPrivate *priv; + McProfile *profile; + McProtocol *protocol; + McManager *mc_manager; + gboolean ret; + + g_return_val_if_fail (MCD_IS_MANAGER (manager), FALSE); + g_return_val_if_fail (account != NULL, FALSE); + + priv = MCD_MANAGER_PRIV (manager); + + profile = account ? mc_account_get_profile (account) : NULL; + protocol = profile ? mc_profile_get_protocol (profile) : NULL; + mc_manager = protocol ? mc_protocol_get_manager (protocol) : NULL; + + if (priv->mc_manager == mc_manager) + { + ret = TRUE; + } + else + { + ret = FALSE; + } + + if (profile) + g_object_unref (profile); + if (protocol) + g_object_unref (protocol); + if (mc_manager) + g_object_unref (mc_manager); + + return ret; +} + +McAccount * +mcd_manager_get_account_by_name (McdManager * manager, + const gchar * account_name) +{ + GList *node; + McdManagerPrivate *priv; + + g_return_val_if_fail (MCD_IS_MANAGER (manager), FALSE); + g_return_val_if_fail (account_name != NULL, FALSE); + + priv = MCD_MANAGER_PRIV (manager); + + node = priv->accounts; + while (node) + { + if (strcmp (mc_account_get_unique_name (MC_ACCOUNT (node->data)), + account_name) == 0) + return MC_ACCOUNT (node->data); + node = node->next; + } + return NULL; +} + +gboolean +mcd_manager_add_account (McdManager * manager, McAccount * account) +{ + McdManagerPrivate *priv; + McdConnection *connection; + McPresence actual_presence; + + g_return_val_if_fail (MCD_IS_MANAGER (manager), FALSE); + g_return_val_if_fail (MC_IS_ACCOUNT (account), FALSE); + + priv = MCD_MANAGER_PRIV (manager); + + /* Make sure this account can be handled by this manager */ + g_return_val_if_fail (mcd_manager_can_handle_account (manager, account), + FALSE); + + /* Check if the account is already added */ + if (g_list_find (priv->accounts, account)) + return FALSE; + + g_object_ref (account); + g_debug ("%s: %u accounts in total", G_STRFUNC, g_list_length (priv->accounts)); + g_debug ("%s: adding account %p", G_STRFUNC, account); + priv->accounts = g_list_prepend (priv->accounts, account); + g_debug ("%s: %u accounts in total", G_STRFUNC, g_list_length (priv->accounts)); + + actual_presence = + mcd_presence_frame_get_actual_presence (priv->presence_frame); + + connection = mcd_manager_get_account_connection (manager, account); + if (!connection) + { + /* if presence is not offline or unset, we must create the + * connection for this new account */ + if ((actual_presence != MC_PRESENCE_OFFLINE && + actual_presence != MC_PRESENCE_UNSET)) + { + /* Also create the telepathy connection manager if not already + * created */ + if (!priv->tp_conn_mgr) + { + g_return_val_if_fail (MC_IS_MANAGER (priv->mc_manager), + FALSE); + + priv->tp_conn_mgr = + tp_connmgr_new (priv->dbus_connection, + mc_manager_get_bus_name (priv-> + mc_manager), + mc_manager_get_object_path (priv-> + mc_manager), + TP_IFACE_CONN_MGR_INTERFACE); + g_debug ("%s: Manager %s created", G_STRFUNC, + mc_manager_get_unique_name (priv->mc_manager)); + } + + _mcd_manager_create_connection (manager, account); + } + } + + g_signal_emit_by_name (manager, "account-added", account); + return TRUE; +} + +gboolean +mcd_manager_remove_account (McdManager * manager, McAccount * account) +{ + McdManagerPrivate *priv; + McdConnection *connection; + + g_return_val_if_fail (MCD_IS_MANAGER (manager), FALSE); + g_return_val_if_fail (MC_IS_ACCOUNT (account), FALSE); + + priv = MCD_MANAGER_PRIV (manager); + + if (!g_list_find (priv->accounts, account)) + return FALSE; + + connection = mcd_manager_get_account_connection (manager, account); + if (connection != NULL) + { + mcd_connection_close (connection); + } + + g_debug ("%s: %u accounts in total", G_STRFUNC, g_list_length (priv->accounts)); + g_debug ("%s: removing account %p", G_STRFUNC, account); + priv->accounts = g_list_remove (priv->accounts, account); + g_debug ("%s: %u accounts in total", G_STRFUNC, g_list_length (priv->accounts)); + g_signal_emit_by_name (manager, "account-removed", account); + + /* Account is unrefed after signal emission to prevent it being dead + * when the signal was emitted. + */ + g_object_unref (account); + + if (priv->accounts == NULL) + { + /* If we don't have any accounts, we don't have the right to live + * anymore. */ + g_debug ("%s: commiting suicide", G_STRFUNC); + mcd_mission_abort (MCD_MISSION (manager)); + } + + return TRUE; +} + +const GList * +mcd_manager_get_accounts (McdManager * manager) +{ + return MCD_MANAGER_PRIV (manager)->accounts; +} + +McdConnection * +mcd_manager_get_account_connection (McdManager * manager, + McAccount * account) +{ + const GList *connections; + const GList *node; + + connections = mcd_operation_get_missions (MCD_OPERATION (manager)); + node = g_list_find_custom ((GList*)connections, account, _find_connection); + + if (node != NULL) + { + return MCD_CONNECTION (node->data); + } + + else + { + return NULL; + } +} + +McdConnection * +mcd_manager_get_connection (McdManager * manager, const gchar *object_path) +{ + const GList *connections; + const GList *node; + + connections = mcd_operation_get_missions (MCD_OPERATION (manager)); + node = g_list_find_custom ((GList*)connections, object_path, + _find_connection_by_path); + + if (node != NULL) + { + return MCD_CONNECTION (node->data); + } + + else + { + return NULL; + } +} + +gboolean +mcd_manager_request_channel (McdManager *manager, + const struct mcd_channel_request *req, + GError ** error) +{ + McAccount *account; + McdConnection *connection; + + account = mcd_manager_get_account_by_name (manager, req->account_name); + if (!account) + { + /* ERROR here */ + if (error) + { + g_set_error (error, MC_ERROR, MC_NO_MATCHING_CONNECTION_ERROR, + "No matching account found for account name '%s'", + req->account_name); + g_warning ("No matching account found for account name '%s'", + req->account_name); + } + return FALSE; + } + + connection = mcd_manager_get_account_connection (manager, account); + if (!connection) + { + McdManagerPrivate *priv = MCD_MANAGER_PRIV (manager); + + g_debug ("%s: mcd-manager has connectivity status = %d", G_STRFUNC, mcd_mission_is_connected (MCD_MISSION (manager))); + if (!mcd_mission_is_connected (MCD_MISSION (manager)) || + (mcd_presence_frame_get_actual_presence (priv->presence_frame) <= MC_PRESENCE_AVAILABLE && + !mcd_presence_frame_is_stable (priv->presence_frame)) + ) + { + request_channel_delayed (manager, req); + return TRUE; + } + /* ERROR here */ + if (error) + { + g_set_error (error, MC_ERROR, MC_NO_MATCHING_CONNECTION_ERROR, + "No matching connection found for account name '%s'", + req->account_name); + g_warning ("%s: No matching connection found for account name '%s'", + G_STRFUNC, req->account_name); + } + return FALSE; + } + else if (mcd_connection_get_connection_status (connection) != + TP_CONN_STATUS_CONNECTED) + { + g_debug ("%s: connection is not connected", G_STRFUNC); + request_channel_delayed (manager, req); + return TRUE; + } + + if (!mcd_connection_request_channel (connection, req, error)) + { + g_assert (error == NULL || *error != NULL); + return FALSE; + } + g_assert (error == NULL || *error == NULL); + return TRUE; +} + +gboolean +mcd_manager_cancel_channel_request (McdManager *manager, guint operation_id, + const gchar *requestor_client_id, + GError **error) +{ + const GList *connections, *node; + + connections = mcd_operation_get_missions (MCD_OPERATION (manager)); + if (!connections) return FALSE; + + for (node = connections; node; node = node->next) + { + if (mcd_connection_cancel_channel_request (MCD_CONNECTION (node->data), + operation_id, + requestor_client_id, + error)) + return TRUE; + } + return FALSE; +} + diff --git a/src/mcd-manager.h b/src/mcd-manager.h new file mode 100644 index 00000000..342198e5 --- /dev/null +++ b/src/mcd-manager.h @@ -0,0 +1,92 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_MANAGER_H +#define MCD_MANAGER_H + +#include <glib.h> +#include <glib-object.h> +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-manager.h> + +#include "mcd-connection.h" +#include "mcd-operation.h" +#include "mcd-presence-frame.h" +#include "mcd-dispatcher.h" + +G_BEGIN_DECLS + +#define MCD_TYPE_MANAGER (mcd_manager_get_type ()) +#define MCD_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_MANAGER, McdManager)) +#define MCD_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_MANAGER, McdManagerClass)) +#define MCD_IS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_MANAGER)) +#define MCD_IS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_MANAGER)) +#define MCD_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_MANAGER, McdManagerClass)) + +typedef struct _McdManager McdManager; +typedef struct _McdManagerClass McdManagerClass; + +struct _McdManager +{ + McdOperation parent; +}; + +struct _McdManagerClass +{ + McdOperationClass parent_class; + + /* signals */ + void (*account_added_signal) (McdManager * manager, McAccount * account); + void (*account_removed_signal) (McdManager * manager, + McAccount * account); +}; + +GType mcd_manager_get_type (void); +McdManager *mcd_manager_new (McManager * mc_manager, + McdPresenceFrame * pframe, + McdDispatcher *dispatcher, + DBusGConnection * dbus_connection); + +gboolean mcd_manager_add_account (McdManager * manager, McAccount * account); +gboolean mcd_manager_can_handle_account (McdManager * manager, + McAccount *account); +McAccount* mcd_manager_get_account_by_name (McdManager * manager, + const gchar * account_name); +gboolean mcd_manager_remove_account (McdManager * manager, + McAccount * account); +const GList *mcd_manager_get_accounts (McdManager * manager); +McdConnection *mcd_manager_get_account_connection (McdManager * manager, + McAccount * account); +gboolean mcd_manager_request_channel (McdManager *manager, + const struct mcd_channel_request *req, + GError ** error); + +gboolean mcd_manager_cancel_channel_request (McdManager *manager, guint operation_id, + const gchar *requestor_client_pid, GError **error); + +McdConnection *mcd_manager_get_connection (McdManager *manager, + const gchar *object_path); + +G_END_DECLS +#endif /* MCD_MANAGER_H */ diff --git a/src/mcd-master.c b/src/mcd-master.c new file mode 100644 index 00000000..8dfd618f --- /dev/null +++ b/src/mcd-master.c @@ -0,0 +1,1150 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <glib/gi18n.h> +#include <gconf/gconf-client.h> +#include <libmissioncontrol/mc-manager.h> +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-profile.h> +#include <libmissioncontrol/mc-account-monitor.h> +#include <libmissioncontrol/mission-control.h> + +#include "mcd-master.h" +#include "mcd-presence-frame.h" +#include "mcd-proxy.h" +#include "mcd-manager.h" +#include "mcd-dispatcher.h" + +#define MCD_MASTER_PRIV(master) (G_TYPE_INSTANCE_GET_PRIVATE ((master), \ + MCD_TYPE_MASTER, \ + McdMasterPrivate)) + +/** + * McdMaster: + * This class implements actual mission-control. It keeps track of + * individual account presence and connection states in a McdPresenceFrame + * member object, which is available as a property. + * + * The McdPresenceFrame object could be easily utilized for + * any presence releated events and actions, either within this class or + * any other class subclassing it or using it. + * + * It is basically a container for all McdManager objects and + * takes care of their management. It also takes care of sleep and awake + * cycles (e.g. translates to auto away somewhere down the hierarchy). + * + * McdMaster is a subclass of McdConroller, which essentially means it + * is subject to all device control. + */ +G_DEFINE_TYPE (McdMaster, mcd_master, MCD_TYPE_CONTROLLER); + +typedef struct _McdMasterPrivate +{ + McdPresenceFrame *presence_frame; + McdDispatcher *dispatcher; + McdProxy *proxy; + McPresence awake_presence; + const gchar *awake_presence_message; + McPresence default_presence; + + /* We create this for our member objects */ + DBusGConnection *dbus_connection; + + /* Monitor for account enabling/disabling events */ + McAccountMonitor *account_monitor; + + /* if this flag is set, presence should go offline when all conversations + * are closed */ + gboolean offline_on_idle; + GHashTable *clients_needing_presence; + + gboolean is_disposed; +} McdMasterPrivate; + +enum +{ + PROP_0, + PROP_PRESENCE_FRAME, + PROP_DBUS_CONNECTION, + PROP_DISPATCHER, + PROP_DEFAULT_PRESENCE, +}; + +static void +_mcd_master_init_managers (McdMaster * master) +{ + GList *acct, *acct_head; + GHashTable *mc_managers; + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + + /* FIXME: Should get only _supported_ protocols */ + /* Only enabled accounts are read in */ + + mc_managers = g_hash_table_new (g_direct_hash, g_direct_equal); + + /* Deal with all enabled accounts */ + acct_head = mc_accounts_list_by_enabled (TRUE); + + /* Let the presence frame know what accounts we have */ + mcd_presence_frame_set_accounts (priv->presence_frame, acct_head); + + for (acct = acct_head; acct; acct = g_list_next (acct)) + { + McAccount *account; + McProfile *profile; + McProtocol *protocol; + McManager *mc_manager; + + account = acct ? acct->data : NULL; + profile = account ? mc_account_get_profile (account) : NULL; + protocol = profile ? mc_profile_get_protocol (profile) : NULL; + mc_manager = protocol ? mc_protocol_get_manager (protocol) : NULL; + + if (mc_manager) + { + McdManager *manager; + manager = g_hash_table_lookup (mc_managers, mc_manager); + + if (!manager) + { + manager = + mcd_manager_new (mc_manager, priv->presence_frame, + priv->dispatcher, priv->dbus_connection); + g_hash_table_insert (mc_managers, mc_manager, manager); + mcd_operation_take_mission (MCD_OPERATION (master), + MCD_MISSION (manager)); + } + mcd_manager_add_account (manager, account); + + g_debug ("%s: Added account:\n\tName\t\"%s\"\n\tProfile\t\"%s\"" + "\n\tProto\t\"%s\"\n\tManager\t\"%s\"", + G_STRFUNC, + mc_account_get_unique_name (account), + mc_profile_get_unique_name (profile), + mc_protocol_get_name (protocol), + mc_manager_get_unique_name (mc_manager)); + } + else + { + g_warning ("%s: Cannot add account:\n\tName\t\"%s\"\n\tProfile\t" + "\"%s\"\n\tProto\t\"%s\"\n\tManager\t\"%s\"", + G_STRFUNC, + account ? mc_account_get_unique_name (account) : + "NONE", + profile ? mc_profile_get_unique_name (profile) : + "NONE", + protocol ? mc_protocol_get_name (protocol) : "NONE", + mc_manager ? + mc_manager_get_unique_name (mc_manager) : "NONE"); + } + + if (profile) + g_object_unref (profile); + if (protocol) + g_object_unref (protocol); + if (mc_manager) + g_object_unref (mc_manager); + /* if (account) + g_object_unref (account); */ + } /*for */ + g_list_free (acct_head); + g_hash_table_destroy (mc_managers); +} + +static gint +_manager_has_account (McdManager * manager, McAccount * account) +{ + const GList *accounts; + const GList *account_node; + + accounts = mcd_manager_get_accounts (manager); + account_node = g_list_find ((GList *) accounts, account); + + if (account_node) + { + return 0; + } + + else + { + return 1; + } +} + +static McdManager * +_mcd_master_find_manager (McdMaster * master, McAccount * account) +{ + const GList *managers; + const GList *manager_node; + + managers = mcd_operation_get_missions (MCD_OPERATION (master)); + manager_node = + g_list_find_custom ((GList*)managers, account, + (GCompareFunc) _manager_has_account); + + if (manager_node) + { + return MCD_MANAGER (manager_node->data); + } + + else + { + return NULL; + } +} + +static gint +_is_manager_responsible (McdManager * manager, McAccount * account) +{ + gboolean can_handle = + mcd_manager_can_handle_account (manager, account); + + if (can_handle) + { + return 0; + } + else + { + return 1; + } +} + +static McdManager * +_mcd_master_find_potential_manager (McdMaster * master, McAccount * account) +{ + const GList *managers; + const GList *manager_node; + + managers = mcd_operation_get_missions (MCD_OPERATION (master)); + manager_node = + g_list_find_custom ((GList*)managers, account, + (GCompareFunc) _is_manager_responsible); + + if (manager_node) + { + return MCD_MANAGER (manager_node->data); + } + + else + { + return NULL; + } +} + +/* Reads in account's settings if they aren't in the hash table + already and (re)connects the account. */ +static void +_mcd_master_on_account_enabled (McAccountMonitor * monitor, + gchar * account_name, gpointer user_data) +{ + McdMaster *master = MCD_MASTER (user_data); + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + McdManager *manager; + McAccount *account; + + g_debug ("Account %s enabled", account_name); + + account = mc_account_lookup (account_name); + manager = _mcd_master_find_potential_manager (master, account); + + if (manager == NULL) + { + McProfile *profile; + McProtocol *protocol; + McManager *mc_manager; + + g_debug ("%s: manager not found, creating a new one", G_STRFUNC); + profile = account ? mc_account_get_profile (account) : NULL; + protocol = profile ? mc_profile_get_protocol (profile) : NULL; + mc_manager = protocol ? mc_protocol_get_manager (protocol) : NULL; + + if (mc_manager) + { + manager = + mcd_manager_new (mc_manager, priv->presence_frame, + priv->dispatcher, priv->dbus_connection); + mcd_operation_take_mission (MCD_OPERATION (master), + MCD_MISSION (manager)); + } + else + { + g_warning ("%s: Failed to get the manager for the account:" + "\n\tName\t\"%s\"\n\tProfile\t\"%s\"\n\tProto" + "\t\"%s\"\n\tManager\t\"%s\"", + G_STRFUNC, + account ? mc_account_get_unique_name (account) : + "NONE", + profile ? mc_profile_get_unique_name (profile) : + "NONE", + protocol ? mc_protocol_get_name (protocol) : "NONE", + mc_manager ? + mc_manager_get_unique_name (mc_manager) : "NONE"); + } + + if (profile) + g_object_unref (profile); + if (protocol) + g_object_unref (protocol); + if (mc_manager) + g_object_unref (mc_manager); + } + + if (manager != NULL) + { + g_debug ("adding account to manager and presence_frame"); + mcd_presence_frame_add_account (priv->presence_frame, account); + mcd_manager_add_account (manager, account); + } + + if (account) + g_object_unref (account); +} + +static void +_mcd_master_on_account_disabled (McAccountMonitor * monitor, + gchar * account_name, gpointer user_data) +{ + McdMaster *master = MCD_MASTER (user_data); + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + McdManager *manager; + McAccount *account; + + g_debug ("Account %s disabled", account_name); + + account = mc_account_lookup (account_name); + + manager = _mcd_master_find_manager (master, account); + + if (manager != NULL) + { + g_debug ("removing account from manager"); + mcd_manager_remove_account (manager, account); + } + + g_debug ("%s: removing account %s from presence_frame %p", + G_STRFUNC, + mc_account_get_unique_name (account), + priv->presence_frame); + mcd_presence_frame_remove_account (priv->presence_frame, account); + + if (account) + g_object_unref (account); +} + +static void +_mcd_master_on_account_changed (McAccountMonitor * monitor, + gchar * account_name, McdMaster *master) +{ + McdManager *manager; + McAccount *account; + + g_debug ("Account %s changed", account_name); + + account = mc_account_lookup (account_name); + if (!account) return; + manager = _mcd_master_find_manager (master, account); + + if (manager) + { + McdConnection *connection; + + connection = mcd_manager_get_account_connection (manager, account); + if (connection) + mcd_connection_account_changed (connection); + } + + g_object_unref (account); +} + +static void +_mcd_master_init_account_monitoring (McdMaster * master) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + + priv->account_monitor = mc_account_monitor_new (); + g_signal_connect (priv->account_monitor, + "account-enabled", + (GCallback) _mcd_master_on_account_enabled, master); + g_signal_connect (priv->account_monitor, + "account-disabled", + (GCallback) _mcd_master_on_account_disabled, master); + g_signal_connect (priv->account_monitor, + "account-changed", + (GCallback) _mcd_master_on_account_changed, master); +} + +static void +_mcd_master_dispose_account_monitoring (McdMaster * master) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + + g_signal_handlers_disconnect_by_func (priv->account_monitor, + (GCallback) _mcd_master_on_account_enabled, master); + g_signal_handlers_disconnect_by_func (priv->account_monitor, + (GCallback) _mcd_master_on_account_disabled, master); + g_signal_handlers_disconnect_by_func (priv->account_monitor, + (GCallback) _mcd_master_on_account_changed, master); + g_object_unref (priv->account_monitor); + priv->account_monitor = NULL; +} + +static gboolean +exists_supporting_invisible (McdMasterPrivate *priv) +{ + McPresence *presences, *presence; + gboolean found = FALSE; + + presences = + mc_account_monitor_get_supported_presences (priv->account_monitor); + for (presence = presences; *presence; presence++) + if (*presence == MC_PRESENCE_HIDDEN) + { + found = TRUE; + break; + } + g_free (presences); + return found; +} + +static McPresence +_get_default_presence (McdMasterPrivate *priv) +{ + McPresence presence = priv->default_presence; + + if (presence == MC_PRESENCE_OFFLINE) + { + /* Map offline to hidden if supported */ + presence = exists_supporting_invisible (priv)? + MC_PRESENCE_HIDDEN : MC_PRESENCE_AWAY; + } + + else if ((presence == MC_PRESENCE_HIDDEN) && + (exists_supporting_invisible (priv) == FALSE)) + { + /* Default presence was set to hidden/invisible but none of the + * accounts support it. Therefore use MC_PRESENCE_AWAY. */ + g_debug ("Default presence setting is hidden but none of the " + "accounts support it. Falling back to away."); + presence = MC_PRESENCE_AWAY; + } + + return presence; +} + +static DBusHandlerResult +dbus_filter_func (DBusConnection *connection, + DBusMessage *message, + gpointer data) +{ + DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + McdMasterPrivate *priv = (McdMasterPrivate *)data; + + if (dbus_message_is_signal (message, + "org.freedesktop.DBus", + "NameOwnerChanged")) { + const gchar *name = NULL; + const gchar *prev_owner = NULL; + const gchar *new_owner = NULL; + DBusError error = {0}; + + dbus_error_init (&error); + + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_STRING, + &name, + DBUS_TYPE_STRING, + &prev_owner, + DBUS_TYPE_STRING, + &new_owner, + DBUS_TYPE_INVALID)) { + + g_debug ("%s: error: %s", G_STRFUNC, error.message); + dbus_error_free (&error); + + return result; + } + + if (name && prev_owner && prev_owner[0] != '\0') + { + if (g_hash_table_lookup (priv->clients_needing_presence, prev_owner)) + { + g_debug ("Process %s which requested default presence is dead", prev_owner); + g_hash_table_remove (priv->clients_needing_presence, prev_owner); + if (g_hash_table_size (priv->clients_needing_presence) == 0 && + priv->offline_on_idle) + { + mcd_presence_frame_request_presence (priv->presence_frame, + MC_PRESENCE_OFFLINE, + "No active processes"); + } + } + } + } + + return result; +} + +static void +_mcd_master_connect (McdMission * mission) +{ + MCD_MISSION_CLASS (mcd_master_parent_class)->connect (mission); + /*if (mission->main_presence.presence_enum != MC_PRESENCE_OFFLINE) + * mcd_connect_all_accounts(mission); */ + +} + +static void +_mcd_master_disconnect (McdMission * mission) +{ + g_debug ("%s", G_STRFUNC); + + MCD_MISSION_CLASS (mcd_master_parent_class)->disconnect (mission); +} + +static void +_mcd_master_finalize (GObject * object) +{ + McdMaster *master; + master = MCD_MASTER (object); + G_OBJECT_CLASS (mcd_master_parent_class)->finalize (object); +} + +static void +_mcd_master_get_property (GObject * obj, guint prop_id, + GValue * val, GParamSpec * pspec) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (obj); + + switch (prop_id) + { + case PROP_PRESENCE_FRAME: + g_value_set_object (val, priv->presence_frame); + break; + case PROP_DISPATCHER: + g_value_set_object (val, priv->dispatcher); + break; + case PROP_DBUS_CONNECTION: + g_value_set_pointer (val, priv->dbus_connection); + break; + case PROP_DEFAULT_PRESENCE: + g_value_set_uint (val, priv->default_presence); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_mcd_master_set_property (GObject *obj, guint prop_id, + const GValue *val, GParamSpec *pspec) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (obj); + + switch (prop_id) + { + case PROP_DEFAULT_PRESENCE: + priv->default_presence = g_value_get_uint (val); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_mcd_master_set_flags (McdMission * mission, McdSystemFlags flags) +{ + McdSystemFlags idle_flag_old, idle_flag_new; + McdMasterPrivate *priv; + + g_return_if_fail (MCD_IS_MASTER (mission)); + priv = MCD_MASTER_PRIV (MCD_MASTER (mission)); + + idle_flag_old = MCD_MISSION_GET_FLAGS_MASKED (mission, MCD_SYSTEM_IDLE); + idle_flag_new = flags & MCD_SYSTEM_IDLE; + + if (idle_flag_old != idle_flag_new) + { + if (idle_flag_new) + { + /* Save the current presence first */ + priv->awake_presence = + mcd_presence_frame_get_actual_presence (priv->presence_frame); + if (priv->awake_presence != MC_PRESENCE_AVAILABLE) + return; + priv->awake_presence_message = + mcd_presence_frame_get_actual_presence_message (priv->presence_frame); + + mcd_presence_frame_request_presence (priv->presence_frame, + MC_PRESENCE_AWAY, NULL); + } + else + { + mcd_presence_frame_request_presence (priv->presence_frame, + priv->awake_presence, + priv->awake_presence_message); + } + } + MCD_MISSION_CLASS (mcd_master_parent_class)->set_flags (mission, flags); +} + +static void +_mcd_master_dispose (GObject * object) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (object); + + if (priv->is_disposed) + { + return; + } + priv->is_disposed = TRUE; + + g_hash_table_destroy (priv->clients_needing_presence); + + if (priv->dbus_connection) + { + dbus_connection_remove_filter (dbus_g_connection_get_connection + (priv->dbus_connection), + dbus_filter_func, priv); + + /* Flush all outgoing DBUS messages and signals */ + dbus_g_connection_flush (priv->dbus_connection); + dbus_g_connection_unref (priv->dbus_connection); + priv->dbus_connection = NULL; + } + + /* Don't unref() the dispatcher and the presence-frame: they will be + * unref()ed by the McdProxy */ + priv->dispatcher = NULL; + priv->presence_frame = NULL; + g_object_unref (priv->proxy); + + if (priv->account_monitor) + _mcd_master_dispose_account_monitoring (MCD_MASTER (object)); + + G_OBJECT_CLASS (mcd_master_parent_class)->dispose (object); +} + +static void +mcd_master_class_init (McdMasterClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + McdMissionClass *mission_class = MCD_MISSION_CLASS (klass); + g_type_class_add_private (object_class, sizeof (McdMasterPrivate)); + + object_class->finalize = _mcd_master_finalize; + object_class->get_property = _mcd_master_get_property; + object_class->set_property = _mcd_master_set_property; + object_class->dispose = _mcd_master_dispose; + + mission_class->connect = _mcd_master_connect; + mission_class->disconnect = _mcd_master_disconnect; + mission_class->set_flags = _mcd_master_set_flags; + + /* Properties */ + g_object_class_install_property (object_class, + PROP_PRESENCE_FRAME, + g_param_spec_object ("presence-frame", + _("Presence Frame Object"), + _("Presence frame Object used by connections to update presence"), + MCD_TYPE_PRESENCE_FRAME, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_DISPATCHER, + g_param_spec_object ("dispatcher", + _("Dispatcher Object"), + _("Dispatcher Object used to dispatch channels"), + MCD_TYPE_DISPATCHER, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_DBUS_CONNECTION, + g_param_spec_pointer ("dbus-connection", + _("D-Bus Connection"), + _("Connection to the D-Bus"), + G_PARAM_READABLE)); + g_object_class_install_property (object_class, PROP_DEFAULT_PRESENCE, + g_param_spec_uint ("default-presence", + _("Default presence"), + _("Default presence when connecting"), + 0, + LAST_MC_PRESENCE, + 0, + G_PARAM_READWRITE)); +} + +static void +install_dbus_filter (McdMasterPrivate *priv) +{ + DBusConnection *dbus_conn; + DBusError error; + + /* set up the NameOwnerChange filter */ + dbus_conn = dbus_g_connection_get_connection (priv->dbus_connection); + dbus_error_init (&error); + dbus_connection_add_filter (dbus_conn, + dbus_filter_func, + priv, NULL); + dbus_bus_add_match (dbus_conn, + "type='signal'," "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'", &error); + if (dbus_error_is_set (&error)) + { + g_warning ("Match rule adding failed"); + dbus_error_free (&error); + } +} + +static void +mcd_master_init (McdMaster * master) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + GError *error = NULL; + /* Initialize DBus connection */ + priv->dbus_connection = dbus_g_bus_get (DBUS_BUS_STARTER, &error); + if (priv->dbus_connection == NULL) + { + g_printerr ("Failed to open connection to bus: %s", error->message); + g_error_free (error); + return; + } + + install_dbus_filter (priv); + + priv->presence_frame = mcd_presence_frame_new (); + priv->dispatcher = mcd_dispatcher_new (priv->dbus_connection, master); + g_assert (MCD_IS_DISPATCHER (priv->dispatcher)); + /* propagate the signals to dispatcher and presence_frame, too */ + priv->proxy = mcd_proxy_new (MCD_MISSION (master)); + mcd_operation_take_mission (MCD_OPERATION (priv->proxy), + MCD_MISSION (priv->presence_frame)); + mcd_operation_take_mission (MCD_OPERATION (priv->proxy), + MCD_MISSION (priv->dispatcher)); + + _mcd_master_init_managers (master); + + /* Listen for account enable/disable events */ + _mcd_master_init_account_monitoring (master); + + priv->clients_needing_presence = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, NULL); +} + +McdMaster * +mcd_master_new (void) +{ + McdMaster *obj; + obj = MCD_MASTER (g_object_new (MCD_TYPE_MASTER, NULL)); + return obj; +} + +static void +mcd_master_set_offline_on_idle (McdMaster *master, gboolean offline_on_idle) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + + g_debug ("%s: setting offline_on_idle to %d", G_STRFUNC, offline_on_idle); + priv->offline_on_idle = offline_on_idle; +} + +void +mcd_master_request_presence (McdMaster * master, + McPresence presence, + const gchar * presence_message) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + + mcd_presence_frame_request_presence (priv->presence_frame, presence, + presence_message); + if (presence >= MC_PRESENCE_AVAILABLE) + mcd_master_set_offline_on_idle (master, FALSE); +} + +McPresence +mcd_master_get_actual_presence (McdMaster * master) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + + return mcd_presence_frame_get_actual_presence (priv->presence_frame); +} + +McPresence +mcd_master_get_requested_presence (McdMaster * master) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + + return mcd_presence_frame_get_requested_presence (priv->presence_frame); +} + +gboolean +mcd_master_set_default_presence (McdMaster * master, const gchar *client_id) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + McPresence presence; + + presence = _get_default_presence (priv); + if (presence == MC_PRESENCE_UNSET) + return FALSE; + + if (client_id) + { + if (g_hash_table_lookup (priv->clients_needing_presence, client_id) == NULL) + { + g_debug ("New process requesting default presence (%s)", client_id); + g_hash_table_insert (priv->clients_needing_presence, + g_strdup (client_id), GINT_TO_POINTER(1)); + } + } + + if (mcd_presence_frame_get_actual_presence (priv->presence_frame) + >= MC_PRESENCE_AVAILABLE || + !mcd_presence_frame_is_stable (priv->presence_frame) || + /* if we are not connected the presence frame will always be stable, + * but this doesn't mean we must accept this request; maybe another one + * is pending */ + (!mcd_mission_is_connected (MCD_MISSION (master)) && + mcd_presence_frame_get_requested_presence (priv->presence_frame) + >= MC_PRESENCE_AVAILABLE)) + { + g_warning ("%s: Default presence requested while connected or " + "already connecting", G_STRFUNC); + return FALSE; + } + mcd_master_set_offline_on_idle (master, TRUE); + mcd_presence_frame_request_presence (priv->presence_frame, presence, NULL); + return TRUE; +} + +TelepathyConnectionStatus +mcd_master_get_account_status (McdMaster * master, gchar * account_name) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + TelepathyConnectionStatus status; + McAccount *account; + + account = mc_account_lookup (account_name); + status = mcd_presence_frame_get_account_status (priv->presence_frame, + account); + g_object_unref (account); + return status; +} + +gboolean +mcd_master_get_online_connection_names (McdMaster * master, + gchar *** connected_names) +{ + GList *accounts; + gboolean ret; + + accounts = mc_accounts_list_by_enabled (TRUE); + + /* MC exits if there aren't any accounts */ + if (accounts) + { + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + GPtrArray *names = g_ptr_array_new (); + GList *account_node; + + /* Iterate through all connected accounts */ + for (account_node = accounts; account_node; + account_node = g_list_next (account_node)) + { + McAccount *account = account_node->data; + TelepathyConnectionStatus status; + + status = + mcd_presence_frame_get_account_status (priv->presence_frame, + account); + /* Ensure that only accounts that are actually conntected are added to + * the pointer array. */ + + if (status == TP_CONN_STATUS_CONNECTED) + { + g_ptr_array_add (names, + g_strdup (mc_account_get_unique_name + (account))); + } + } + + if (names->len != 0) + { + int i; + + /* Copy the collected names to the array of strings */ + *connected_names = + (gchar **) g_malloc0 (sizeof (gchar *) * (names->len + 1)); + for (i = 0; i < names->len; i++) + { + *(*connected_names + i) = g_ptr_array_index (names, i); + } + (*connected_names)[i] = NULL; + + ret = TRUE; + } + + else + { + ret = FALSE; + } + + g_ptr_array_free (names, TRUE); + g_list_free (accounts); + } + + else + { + ret = FALSE; + } + + return ret; +} + +gboolean +mcd_master_get_account_connection_details (McdMaster * master, + const gchar * account_name, + gchar ** servname, gchar ** objpath) +{ + McAccount *account; + McdManager *manager; + McdConnection *connection; + gboolean ret = FALSE; + + account = mc_account_lookup (account_name); + if (account) + { + manager = _mcd_master_find_manager (master, account); + connection = + manager ? mcd_manager_get_account_connection (manager, account) : NULL; + g_object_unref (account); + + if (connection) + ret = + mcd_connection_get_telepathy_details (connection, servname, + objpath); + } + + return ret; +} + +gboolean +mcd_master_request_channel (McdMaster *master, + const struct mcd_channel_request *req, + GError ** error) +{ + const GList *managers, *node; + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + + g_return_val_if_fail (MCD_IS_MASTER (master), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* Low memory ? */ + if (MCD_MISSION_GET_FLAGS_MASKED (MCD_MISSION (master), + MCD_SYSTEM_MEMORY_CONSERVED)) + { + g_warning ("Device is in lowmem state, will not create a channel"); + if (error) + g_set_error (error, MC_ERROR, MC_LOWMEM_ERROR, "Low memory"); + return FALSE; + } + + /* First find out the right manager */ + managers = mcd_operation_get_missions (MCD_OPERATION (master)); + + /* If there are no accounts, error */ + if (managers == NULL) + { + if (error) + { + g_set_error (error, MC_ERROR, MC_NO_ACCOUNTS_ERROR, + "No accounts configured"); + } + g_warning ("No accounts configured"); + + /* Nothing to do. Just exit */ + mcd_controller_shutdown (MCD_CONTROLLER (master), + "No accounts configured"); + return FALSE; + } + + /* make sure we are online, or will be */ + if (mcd_presence_frame_get_actual_presence (priv->presence_frame) <= MC_PRESENCE_AVAILABLE && + mcd_presence_frame_is_stable (priv->presence_frame)) + { + g_debug ("%s: requesting default presence", G_STRFUNC); + mcd_master_set_default_presence (master, NULL); + } + + node = managers; + while (node) + { + if (mcd_manager_get_account_by_name (MCD_MANAGER (node->data), + req->account_name)) + { + /* FIXME: handle error correctly */ + if (!mcd_manager_request_channel (MCD_MANAGER (node->data), + req, error)) + { + g_assert (error == NULL || *error != NULL); + return FALSE; + } + g_assert (error == NULL || *error == NULL); + return TRUE; + } + node = node->next; + } + + /* Manager not found */ + if (error) + { + g_set_error (error, MC_ERROR, MC_NO_MATCHING_CONNECTION_ERROR, + "No matching manager found for account %s", + req->account_name); + } + g_warning ("No matching manager found for account %s", req->account_name); + return FALSE; +} + +gboolean +mcd_master_cancel_channel_request (McdMaster *master, guint operation_id, + const gchar *requestor_client_id, + GError **error) +{ + const GList *managers, *node; + + g_return_val_if_fail (MCD_IS_MASTER (master), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* First find out the right manager */ + managers = mcd_operation_get_missions (MCD_OPERATION (master)); + if (!managers) return FALSE; + + for (node = managers; node; node = node->next) + { + if (mcd_manager_cancel_channel_request (MCD_MANAGER (node->data), + operation_id, + requestor_client_id, + error)) + return TRUE; + } + + return FALSE; +} + +gboolean +mcd_master_cancel_last_presence_request (McdMaster * master) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + + return mcd_presence_frame_cancel_last_request (priv->presence_frame); +} + +gboolean +mcd_master_get_used_channels_count (McdMaster *master, guint chan_type, + guint * ret, GError ** error) +{ + McdMasterPrivate *priv; + + g_return_val_if_fail (ret != NULL, FALSE); + + priv = MCD_MASTER_PRIV (master); + *ret = mcd_dispatcher_get_channel_type_usage (priv->dispatcher, + chan_type); + return TRUE; +} + +McdConnection * +mcd_master_get_connection (McdMaster *master, const gchar *object_path, + GError **error) +{ + McdConnection *connection; + const GList *managers, *node; + + g_return_val_if_fail (MCD_IS_MASTER (master), NULL); + + managers = mcd_operation_get_missions (MCD_OPERATION (master)); + + /* MC exits if there aren't any accounts */ + if (managers == NULL) + { + if (error) + { + g_set_error (error, MC_ERROR, MC_NO_ACCOUNTS_ERROR, + "No accounts configured"); + } + mcd_controller_shutdown (MCD_CONTROLLER (master), + "No accounts configured"); + return NULL; + } + + node = managers; + while (node) + { + connection = mcd_manager_get_connection (MCD_MANAGER (node->data), + object_path); + if (connection) + return connection; + node = node->next; + } + + /* Manager not found */ + if (error) + { + g_set_error (error, MC_ERROR, MC_NO_MATCHING_CONNECTION_ERROR, + "No matching manager found for connection '%s'", + object_path); + } + return NULL; +} + +gboolean +mcd_master_get_account_for_connection (McdMaster *master, + const gchar *object_path, + gchar **ret_unique_name, + GError **error) +{ + McdConnection *connection; + + connection = mcd_master_get_connection (master, object_path, error); + if (connection) + { + McAccount *account; + + g_object_get (G_OBJECT (connection), "account", &account, NULL); + *ret_unique_name = g_strdup (mc_account_get_unique_name (account)); + g_object_unref (G_OBJECT (account)); + return TRUE; + } + return FALSE; +} + +void +mcd_master_set_default_presence_setting (McdMaster *master, + McPresence presence) +{ + McdMasterPrivate *priv = MCD_MASTER_PRIV (master); + priv->default_presence = presence; +} + diff --git a/src/mcd-master.h b/src/mcd-master.h new file mode 100644 index 00000000..865da395 --- /dev/null +++ b/src/mcd-master.h @@ -0,0 +1,106 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_MASTER_H +#define MCD_MASTER_H + +#include <glib.h> +#include <glib-object.h> +#include "mcd-controller.h" + +G_BEGIN_DECLS +#define MCD_TYPE_MASTER (mcd_master_get_type ()) +#define MCD_MASTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_MASTER, McdMaster)) +#define MCD_MASTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_MASTER, McdMasterClass)) +#define MCD_IS_MASTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_MASTER)) +#define MCD_IS_MASTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_MASTER)) +#define MCD_MASTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_MASTER, McdMasterClass)) + +typedef struct _McdMaster McdMaster; +typedef struct _McdMasterClass McdMasterClass; + +#include <mcd-connection.h> + +struct _McdMaster +{ + McdController parent; +}; + +struct _McdMasterClass +{ + McdControllerClass parent_class; +}; + +struct mcd_channel_request; + +GType mcd_master_get_type (void); +McdMaster *mcd_master_new (void); +void mcd_master_request_presence (McdMaster * master, + McPresence presence, + const gchar * presence_message); + +McPresence mcd_master_get_actual_presence (McdMaster * master); + +McPresence mcd_master_get_requested_presence (McdMaster * master); + +gboolean mcd_master_set_default_presence (McdMaster * master, + const gchar *client_id); + +void mcd_master_set_default_presence_setting (McdMaster *master, + McPresence presence); + +TelepathyConnectionStatus mcd_master_get_account_status (McdMaster * master, + gchar * account_name); + +gboolean mcd_master_get_online_connection_names (McdMaster * master, + gchar *** connected_names); + +gboolean mcd_master_get_account_connection_details (McdMaster * master, + const gchar * account_name, + gchar ** servname, + gchar ** objpath); + +gboolean mcd_master_cancel_last_presence_request (McdMaster * master); + +gboolean mcd_master_request_channel (McdMaster *master, + const struct mcd_channel_request *req, + GError ** error); + +gboolean mcd_master_cancel_channel_request (McdMaster *master, + guint operation_id, + const gchar *requestor_client_id, + GError **error); + +gboolean mcd_master_get_used_channels_count (McdMaster *master, guint chan_type, + guint * ret, GError ** error); +McdConnection *mcd_master_get_connection (McdMaster *master, + const gchar *object_path, + GError **error); +gboolean mcd_master_get_account_for_connection (McdMaster *master, + const gchar *object_path, + gchar **ret_unique_name, + GError **error); + +G_END_DECLS +#endif /* MCD_MASTER_H */ diff --git a/src/mcd-mission.c b/src/mcd-mission.c new file mode 100644 index 00000000..6b02f166 --- /dev/null +++ b/src/mcd-mission.c @@ -0,0 +1,482 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <glib/gi18n.h> +#include "mcd-mission.h" +#include "mcd-enum-types.h" + +#define MCD_MISSION_PRIV(mission) (G_TYPE_INSTANCE_GET_PRIVATE ((mission), \ + MCD_TYPE_MISSION, \ + McdMissionPrivate)) + +/** + * McdMission: + * It is the base class for every object in mission-control. It defines + * a set of virtual functions and set of corresponding action signals. + * all virtual functions results in emission of their corresponding action + * signals. The virtual functions define states of the object, such + * as memory conserved state, connected state, locked state, low power state, + * lit state, sleeping state etc. Each of the object states can also be queried + * independently as properties. + * + * There are also some action signals such as abort, which is used to notify + * other objects holding hard references to it to release them (this object + * should then automatically die since all held references are released). It + * is mandatory for all other objects that hold a hard reference to it to + * listen for this signal and release the reference in signal handler. + * + * Concrete derived classes should override the sate methods to implement + * object specific state managements. + */ +G_DEFINE_TYPE (McdMission, mcd_mission, G_TYPE_OBJECT); + +/* Private */ + +typedef struct _McdMissionPrivate +{ + McdMission *parent; + McdSystemFlags flags; + McdMode mode; + + gboolean is_disposed; + +} McdMissionPrivate; + +enum _McdMissionSignalType +{ + CONNECTED, + DISCONNECTED, + FLAGS_CHANGED, + MODE_SET, + PARENT_SET, + ABORT, + LAST_SIGNAL +}; + +enum _McdMissionPropertyType +{ + PROP_0, + PROP_SYSTEM_FLAGS, + PROP_MODE, + PROP_PARENT +}; + +static guint mcd_mission_signals[LAST_SIGNAL] = { 0 }; + +static void +_mcd_mission_connect (McdMission * mission) +{ + McdMissionPrivate *priv; + + g_return_if_fail (MCD_IS_MISSION (mission)); + priv = MCD_MISSION_PRIV (mission); + + if (!mcd_mission_is_connected (mission)) + { + gint flags = mcd_mission_get_flags (mission); + flags |= MCD_SYSTEM_CONNECTED; + mcd_mission_set_flags (mission, flags); + g_signal_emit_by_name (mission, "connected"); + } +} + +static void +_mcd_mission_disconnect (McdMission * mission) +{ + McdMissionPrivate *priv; + + g_return_if_fail (MCD_IS_MISSION (mission)); + priv = MCD_MISSION_PRIV (mission); + + if (mcd_mission_is_connected (mission)) + { + gint flags = mcd_mission_get_flags (mission); + flags &= ~MCD_SYSTEM_CONNECTED; + mcd_mission_set_flags (mission, flags); + g_signal_emit_by_name (mission, "disconnected"); + } +} + +static void +_mcd_mission_set_flags (McdMission * mission, McdSystemFlags flags) +{ + McdMissionPrivate *priv; + + g_return_if_fail (MCD_IS_MISSION (mission)); + priv = MCD_MISSION_PRIV (mission); + + if (priv->flags != flags) + { + priv->flags = flags; + g_signal_emit_by_name (mission, "flags-changed", flags); + } +} + +static McdSystemFlags +_mcd_mission_get_flags (McdMission * mission) +{ + McdMissionPrivate *priv; + + g_return_val_if_fail (MCD_IS_MISSION (mission), MCD_MODE_UNKNOWN); + priv = MCD_MISSION_PRIV (mission); + + return priv->flags; +} + +static void +_mcd_mission_set_mode (McdMission * mission, McdMode mode) +{ + McdMissionPrivate *priv; + + g_return_if_fail (MCD_IS_MISSION (mission)); + priv = MCD_MISSION_PRIV (mission); + + if (priv->mode != mode) + { + priv->mode = mode; + + g_signal_emit_by_name (mission, "mode-set", mode); + } +} + +static McdMode +_mcd_mission_get_mode (McdMission * mission) +{ + McdMissionPrivate *priv; + + g_return_val_if_fail (MCD_IS_MISSION (mission), MCD_MODE_UNKNOWN); + priv = MCD_MISSION_PRIV (mission); + + return priv->mode; +} + +static void +on_parent_abort (McdMission *parent, McdMission *mission) +{ + g_debug ("%s called", G_STRFUNC); + mcd_mission_set_parent (mission, NULL); +} + +static void +_mcd_mission_set_parent (McdMission * mission, McdMission * parent) +{ + McdMissionPrivate *priv; + + g_return_if_fail (MCD_IS_MISSION (mission)); + g_return_if_fail ((parent == NULL) || MCD_IS_MISSION (parent)); + + priv = MCD_MISSION_PRIV (mission); + + g_debug ("%s: child = %p, parent = %p", G_STRFUNC, mission, parent); + + if (priv->parent) + { + g_signal_handlers_disconnect_by_func (priv->parent, + on_parent_abort, + mission); + } + + if (parent) + { + g_signal_connect (parent, "abort", + G_CALLBACK (on_parent_abort), + mission); + g_object_ref (parent); + } + + if (priv->parent) + { + g_object_unref (priv->parent); + } + + priv->parent = parent; + g_signal_emit_by_name (mission, "parent-set", parent); +} + +static void +_mcd_mission_abort (McdMission * mission) +{ + g_signal_emit_by_name (G_OBJECT (mission), "abort"); +} + +static void +_mcd_mission_dispose (GObject * object) +{ + McdMissionPrivate *priv; + g_return_if_fail (MCD_IS_MISSION (object)); + + priv = MCD_MISSION_PRIV (object); + + if (priv->is_disposed) + { + return; + } + + priv->is_disposed = TRUE; + + g_debug ("mission disposed %p", object); + if (priv->parent) + { + g_signal_handlers_disconnect_by_func (priv->parent, + on_parent_abort, + object); + g_object_unref (priv->parent); + priv->parent = NULL; + } + G_OBJECT_CLASS (mcd_mission_parent_class)->dispose (object); +} + +static void +_mcd_mission_finalize (GObject * object) +{ + g_debug ("mission finalized %p", object); + G_OBJECT_CLASS (mcd_mission_parent_class)->finalize (object); +} + +static void +_mcd_set_property (GObject * object, guint prop_id, const GValue * val, + GParamSpec * pspec) +{ + McdMission *mission = MCD_MISSION (object); + + switch (prop_id) + { + case PROP_PARENT: + mcd_mission_set_parent (mission, g_value_get_object (val)); + break; + case PROP_SYSTEM_FLAGS: + mcd_mission_set_flags (mission, g_value_get_enum (val)); + break; + case PROP_MODE: + mcd_mission_set_mode (mission, g_value_get_int (val)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_mcd_get_property (GObject * object, guint prop_id, GValue * val, + GParamSpec * pspec) +{ + McdMission *mission = MCD_MISSION (object); + + switch (prop_id) + { + case PROP_PARENT: + g_value_set_object (val, mcd_mission_get_parent (mission)); + break; + case PROP_SYSTEM_FLAGS: + g_value_set_enum (val, mcd_mission_get_flags (mission)); + break; + case PROP_MODE: + g_value_set_enum (val, mcd_mission_get_mode (mission)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +mcd_mission_class_init (McdMissionClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (object_class, sizeof (McdMissionPrivate)); + + /* virtual medthods */ + object_class->finalize = _mcd_mission_finalize; + object_class->dispose = _mcd_mission_dispose; + object_class->set_property = _mcd_set_property; + object_class->get_property = _mcd_get_property; + + /* virtual medthods */ + klass->abort = _mcd_mission_abort; + klass->connect = _mcd_mission_connect; + klass->disconnect = _mcd_mission_disconnect; + klass->set_flags = _mcd_mission_set_flags; + klass->get_flags = _mcd_mission_get_flags; + klass->set_mode = _mcd_mission_set_mode; + klass->get_mode = _mcd_mission_get_mode; + + klass->set_parent = _mcd_mission_set_parent; + + /* signals */ + mcd_mission_signals[ABORT] = + g_signal_new ("abort", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdMissionClass, abort_signal), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, + 0); + mcd_mission_signals[CONNECTED] = + g_signal_new ("connected", G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (McdMissionClass, + connected_signal), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, + 0); + mcd_mission_signals[DISCONNECTED] = + g_signal_new ("disconnected", G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (McdMissionClass, + disconnected_signal), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, + 0); + mcd_mission_signals[FLAGS_CHANGED] = + g_signal_new ("flags-changed", G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (McdMissionClass, + flags_changed_signal), + NULL, NULL, g_cclosure_marshal_VOID__ENUM, G_TYPE_NONE, + 1, MCD_TYPE_SYSTEM_FLAGS); + mcd_mission_signals[MODE_SET] = + g_signal_new ("mode-set", G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (McdMissionClass, + mode_set_signal), + NULL, NULL, g_cclosure_marshal_VOID__ENUM, G_TYPE_NONE, + 1, MCD_TYPE_MODE); + mcd_mission_signals[PARENT_SET] = + g_signal_new ("parent-set", G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (McdMissionClass, + parent_set_signal), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, + 0); + + /* Properties */ + g_object_class_install_property (object_class, + PROP_PARENT, + g_param_spec_object ("parent", + _("Parent Mission"), + _("Parent mission object to which this belongs"), + MCD_TYPE_MISSION, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_MODE, + g_param_spec_enum ("mode", + _("Platform-specific modes"), + _("Platform-specific modes"), + MCD_TYPE_MODE, + MCD_MODE_NORMAL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_SYSTEM_FLAGS, + g_param_spec_enum ("system-flags", + _("System flags"), + _("Mission control system flags"), + MCD_TYPE_MODE, + MCD_MODE_NORMAL, + G_PARAM_READWRITE)); +} + +static void +mcd_mission_init (McdMission * obj) +{ +} + +/* Public methods */ + +McdMission * +mcd_mission_new (void) +{ + McdMission *obj; + + obj = MCD_MISSION (g_object_new (MCD_TYPE_MISSION, NULL)); + + return obj; +} + +void +mcd_mission_connect (McdMission * mission) +{ + g_return_if_fail (MCD_IS_MISSION (mission)); + MCD_MISSION_GET_CLASS (mission)->connect (mission); +} + +void +mcd_mission_disconnect (McdMission * mission) +{ + g_return_if_fail (MCD_IS_MISSION (mission)); + MCD_MISSION_GET_CLASS (mission)->disconnect (mission); +} + +void +mcd_mission_abort (McdMission * mission) +{ + g_return_if_fail (MCD_IS_MISSION (mission)); + MCD_MISSION_GET_CLASS (mission)->abort (mission); +} + +McdSystemFlags +mcd_mission_get_flags (McdMission * mission) +{ + g_return_val_if_fail (MCD_IS_MISSION (mission), 0); + return MCD_MISSION_GET_CLASS (mission)->get_flags (mission); +} + +void +mcd_mission_set_flags (McdMission * mission, McdSystemFlags flags) +{ + g_return_if_fail (MCD_IS_MISSION (mission)); + MCD_MISSION_GET_CLASS (mission)->set_flags (mission, flags); +} + +McdMode +mcd_mission_get_mode (McdMission * mission) +{ + g_return_val_if_fail (MCD_IS_MISSION (mission), MCD_MODE_UNKNOWN); + return MCD_MISSION_GET_CLASS (mission)->get_mode (mission); +} + +void +mcd_mission_set_mode (McdMission * mission, McdMode mode) +{ + g_return_if_fail (MCD_IS_MISSION (mission)); + MCD_MISSION_GET_CLASS (mission)->set_mode (mission, mode); +} + +gboolean +mcd_mission_is_connected (McdMission * mission) +{ + McdMissionPrivate *priv; + + g_return_val_if_fail (MCD_IS_MISSION (mission), FALSE); + priv = MCD_MISSION_PRIV (mission); + + return (priv->flags & MCD_SYSTEM_CONNECTED); +} + +void +mcd_mission_set_parent (McdMission * mission, McdMission * parent) +{ + g_return_if_fail (MCD_IS_MISSION (mission)); + MCD_MISSION_GET_CLASS (mission)->set_parent (mission, parent); +} + +McdMission * +mcd_mission_get_parent (McdMission * mission) +{ + McdMissionPrivate *priv; + + g_return_val_if_fail (MCD_IS_MISSION (mission), NULL); + priv = MCD_MISSION_PRIV (mission); + + return priv->parent; +} diff --git a/src/mcd-mission.h b/src/mcd-mission.h new file mode 100644 index 00000000..0003e6a4 --- /dev/null +++ b/src/mcd-mission.h @@ -0,0 +1,125 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_MISSION_H +#define MCD_MISSION_H + +#include <glib.h> +#include <glib-object.h> +#include "mcd-debug.h" + +G_BEGIN_DECLS + +#define MCD_TYPE_MISSION (mcd_mission_get_type ()) +#define MCD_MISSION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_MISSION, McdMission)) +#define MCD_MISSION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_MISSION, McdMissionClass)) +#define MCD_IS_MISSION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_MISSION)) +#define MCD_IS_MISSION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_MISSION)) +#define MCD_MISSION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_MISSION, McdMissionClass)) + +#define MCD_MISSION_GET_FLAGS_MASKED(mission, flags) \ + (mcd_mission_get_flags (mission) & flags) + +#define MCD_MISSION_SET_FLAGS_MASKED(mission, flags) \ + (mcd_mission_set_flags (mission, mcd_mission_get_flags (mission) | flags)) + +#define MCD_MISSION_UNSET_FLAGS_MASKED(mission, flags) \ + (mcd_mission_set_flags (mission, mcd_mission_get_flags (mission) & (~flags))) + +typedef enum +{ + MCD_MODE_UNKNOWN, + MCD_MODE_NORMAL, + MCD_MODE_RESTRICTED, + MCD_MODE_CALL +} McdMode; + +typedef enum +{ + MCD_SYSTEM_CONNECTED = 1, + MCD_SYSTEM_MEMORY_CONSERVED = 1 << 1, + MCD_SYSTEM_POWER_CONSERVED = 1 << 2, + MCD_SYSTEM_SCREEN_BLANKED = 1 << 3, + MCD_SYSTEM_LOCKED = 1 << 4, + MCD_SYSTEM_IDLE = 1 << 5 +} McdSystemFlags; + +typedef struct _McdMission McdMission; +typedef struct _McdMissionClass McdMissionClass; + +struct _McdMission +{ + GObject parent; +}; + +struct _McdMissionClass +{ + GObjectClass parent_class; + + /* Signals */ + void (*parent_set_signal) (McdMission * mission, McdMission * parent); + void (*connected_signal) (McdMission * mission); + void (*disconnected_signal) (McdMission * mission); + + void (*flags_changed_signal) (McdMission *mission, McdSystemFlags flags); + void (*mode_set_signal) (McdMission * mission, McdMode mode); + + void (*abort_signal) (McdMission * mission); + + /* Virtual methods */ + void (*set_parent) (McdMission * mission, McdMission * parent); + + void (*connect) (McdMission * mission); + void (*disconnect) (McdMission * mission); + + void (*set_flags) (McdMission *mission, McdSystemFlags flags); + McdSystemFlags (*get_flags) (McdMission *mission); + + void (*set_mode) (McdMission * mission, McdMode mode); + McdMode (*get_mode) (McdMission * mission); + + void (*abort) (McdMission * mission); +}; + +GType mcd_mission_get_type (void); +McdMission *mcd_mission_new (void); + +gboolean mcd_mission_is_connected (McdMission * mission); + +McdMission *mcd_mission_get_parent (McdMission * mission); + +void mcd_mission_abort (McdMission * mission); +void mcd_mission_set_parent (McdMission * mission, McdMission * parent); + +void mcd_mission_connect (McdMission * mission); +void mcd_mission_disconnect (McdMission * mission); + +void mcd_mission_set_flags (McdMission * mission, McdSystemFlags flags); +McdSystemFlags mcd_mission_get_flags (McdMission * mission); + +void mcd_mission_set_mode (McdMission * mission, McdMode mode); +McdMode mcd_mission_get_mode (McdMission * mission); + +G_END_DECLS +#endif /* MCD_MISSION_H */ diff --git a/src/mcd-operation.c b/src/mcd-operation.c new file mode 100644 index 00000000..face18bb --- /dev/null +++ b/src/mcd-operation.c @@ -0,0 +1,313 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <glib/gi18n.h> +#include "mcd-operation.h" + +#define MCD_OPERATION_PRIV(operation) (G_TYPE_INSTANCE_GET_PRIVATE ((operation), \ + MCD_TYPE_OPERATION, \ + McdOperationPrivate)) + +/** + * McdOperation: + * This is a simple container class that can hold a list of mission objects + * as children. McdOperation makes sure that object states (see: McdMission) + * of the container are all proxied to the children. Children life cycles + * also managed by this class and parent-child relationship is correctly + * established. + */ +G_DEFINE_TYPE (McdOperation, mcd_operation, MCD_TYPE_MISSION); + +/* Private */ + +typedef struct _McdOperationPrivate +{ + GList *missions; + gboolean is_disposed; +} McdOperationPrivate; + +enum _McdOperationSignalType +{ + MISSION_TAKEN, + MISSION_REMOVED, + LAST_SIGNAL +}; + +static guint mcd_operation_signals[LAST_SIGNAL] = { 0 }; + +static void +on_mission_abort (McdMission *mission, McdOperation *operation) +{ + g_return_if_fail (MCD_IS_MISSION (mission)); + g_return_if_fail (MCD_IS_OPERATION (operation)); + mcd_operation_remove_mission (operation, mission); +} + +static void +_mcd_operation_disconnect_mission (McdMission *mission, McdOperation *operation) +{ + g_signal_handlers_disconnect_by_func (mission, + G_CALLBACK (on_mission_abort), + operation); +} + +static void +_mcd_operation_finalize (GObject * object) +{ + G_OBJECT_CLASS (mcd_operation_parent_class)->finalize (object); +} + +static void +_mcd_operation_child_unref (McdMission *child) +{ + g_object_unref (child); +} + +static void +_mcd_operation_abort (McdOperation * operation) +{ + const GList *node; + + g_debug ("Operation abort received, aborting all children"); + node = MCD_OPERATION_PRIV (operation)->missions; + while (node) + { + McdMission *mission = MCD_MISSION (node->data); + /* We don't want to hear it ourself so that we still hold the + * final reference to our children. + */ + g_signal_handlers_disconnect_by_func (mission, + G_CALLBACK (on_mission_abort), + operation); + mcd_mission_abort (mission); + + /* Restore the handler so that we continue listing for destroy + * notify for our children. + */ + g_signal_connect (mission, "abort", + G_CALLBACK (on_mission_abort), operation); + node = g_list_next (node); + } +} + +static void +_mcd_operation_dispose (GObject * object) +{ + McdOperationPrivate *priv = MCD_OPERATION_PRIV (object); + + if (priv->is_disposed) + { + return; + } + + priv->is_disposed = TRUE; + g_debug ("operation disposed"); + + g_signal_handlers_disconnect_by_func (object, + G_CALLBACK (_mcd_operation_abort), + NULL); + if (priv->missions) + { + g_list_foreach (priv->missions, + (GFunc) _mcd_operation_disconnect_mission, + object); + g_list_foreach (priv->missions, (GFunc) _mcd_operation_child_unref, + NULL); + g_list_free (priv->missions); + priv->missions = NULL; + } + G_OBJECT_CLASS (mcd_operation_parent_class)->dispose (object); +} + +static void +_mcd_operation_connect (McdMission * mission) +{ + McdOperationPrivate *priv = MCD_OPERATION_PRIV (mission); + g_list_foreach (priv->missions, (GFunc) mcd_mission_connect, NULL); + MCD_MISSION_CLASS (mcd_operation_parent_class)->connect (mission); +} + +static void +_mcd_operation_disconnect (McdMission * mission) +{ + McdOperationPrivate *priv = MCD_OPERATION_PRIV (mission); + g_list_foreach (priv->missions, (GFunc) mcd_mission_disconnect, NULL); + MCD_MISSION_CLASS (mcd_operation_parent_class)->disconnect (mission); +} + +static void +_mcd_operation_set_flags (McdMission * mission, McdSystemFlags flags) +{ + McdOperationPrivate *priv = MCD_OPERATION_PRIV (mission); + g_list_foreach (priv->missions, (GFunc) mcd_mission_set_flags, + GINT_TO_POINTER (flags)); + MCD_MISSION_CLASS (mcd_operation_parent_class)->set_flags (mission, flags); +} + +static void +_mcd_operation_set_mode (McdMission * mission, McdMode mode) +{ + McdOperationPrivate *priv = MCD_OPERATION_PRIV (mission); + g_list_foreach (priv->missions, (GFunc) mcd_mission_set_mode, + GINT_TO_POINTER (mode)); + MCD_MISSION_CLASS (mcd_operation_parent_class)->set_mode (mission, mode); +} + +static void +_mcd_operation_take_mission (McdOperation * operation, McdMission * mission) +{ + McdOperationPrivate *priv = MCD_OPERATION_PRIV (operation); + McdSystemFlags flags; + McdMode mode; + + priv->missions = g_list_prepend (priv->missions, mission); + mcd_mission_set_parent (mission, MCD_MISSION (operation)); + + if (mcd_mission_is_connected (MCD_MISSION (operation))) + mcd_mission_connect (mission); + flags = mcd_mission_get_flags (MCD_MISSION (operation)); + mcd_mission_set_flags (mission, flags); + + mode = mcd_mission_get_mode (MCD_MISSION (operation)); + mcd_mission_set_mode (mission, mode); + + g_signal_connect (mission, "abort", + G_CALLBACK (on_mission_abort), operation); + g_signal_emit_by_name (G_OBJECT (operation), "mission-taken", mission); +} + +static void +_mcd_operation_remove_mission (McdOperation * operation, McdMission * mission) +{ + McdOperationPrivate *priv = MCD_OPERATION_PRIV (operation); + + g_return_if_fail (g_list_find (priv->missions, mission) != NULL); + + _mcd_operation_disconnect_mission (mission, operation); + + priv->missions = g_list_remove (priv->missions, mission); + mcd_mission_set_parent (mission, NULL); + + g_signal_emit_by_name (G_OBJECT (operation), "mission-removed", mission); + + g_debug ("removing mission: %p", mission); + g_object_unref (mission); +} + +static void +mcd_operation_class_init (McdOperationClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + McdMissionClass *mission_class = MCD_MISSION_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (McdOperationPrivate)); + + object_class->finalize = _mcd_operation_finalize; + object_class->dispose = _mcd_operation_dispose; + + mission_class->connect = _mcd_operation_connect; + mission_class->disconnect = _mcd_operation_disconnect; + mission_class->set_flags = _mcd_operation_set_flags; + mission_class->set_mode = _mcd_operation_set_mode; + + klass->take_mission = _mcd_operation_take_mission; + klass->remove_mission = _mcd_operation_remove_mission; + + mcd_operation_signals[MISSION_TAKEN] = + g_signal_new ("mission-taken", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdOperationClass, + mission_taken_signal), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + mcd_operation_signals[MISSION_REMOVED] = + g_signal_new ("mission-removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdOperationClass, + mission_removed_signal), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); +} + +static void +mcd_operation_init (McdOperation * obj) +{ + McdOperationPrivate *priv = MCD_OPERATION_PRIV (obj); + priv->missions = NULL; + + /* Listen to self abort so that we can propagate it to our + * children + */ + g_signal_connect (obj, "abort", G_CALLBACK (_mcd_operation_abort), NULL); +} + +/* Public */ + +McdOperation * +mcd_operation_new (void) +{ + McdOperation *obj; + obj = MCD_OPERATION (g_object_new (MCD_TYPE_OPERATION, NULL)); + return obj; +} + +void +mcd_operation_take_mission (McdOperation * operation, McdMission * mission) +{ + g_return_if_fail (MCD_IS_OPERATION (operation)); + g_return_if_fail (MCD_IS_MISSION (mission)); + MCD_OPERATION_GET_CLASS (operation)->take_mission (operation, mission); +} + +void +mcd_operation_remove_mission (McdOperation * operation, McdMission * mission) +{ + g_return_if_fail (MCD_IS_OPERATION (operation)); + g_return_if_fail (MCD_IS_MISSION (mission)); + MCD_OPERATION_GET_CLASS (operation)->remove_mission (operation, mission); +} + +const GList * +mcd_operation_get_missions (McdOperation * operation) +{ + g_return_val_if_fail (MCD_IS_OPERATION (operation), NULL); + + McdOperationPrivate *priv = MCD_OPERATION_PRIV (operation); + return priv->missions; +} + +void +mcd_operation_foreach (McdOperation * operation, GFunc func, gpointer user_data) +{ + g_return_if_fail (MCD_IS_OPERATION (operation)); + + McdOperationPrivate *priv = MCD_OPERATION_PRIV (operation); + + g_list_foreach (priv->missions, (GFunc) func, user_data); +} diff --git a/src/mcd-operation.h b/src/mcd-operation.h new file mode 100644 index 00000000..ce61d51a --- /dev/null +++ b/src/mcd-operation.h @@ -0,0 +1,76 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_OPERATION_H +#define MCD_OPERATION_H + +#include <glib.h> +#include <glib-object.h> + +#include "mcd-mission.h" + +G_BEGIN_DECLS +#define MCD_TYPE_OPERATION (mcd_operation_get_type ()) +#define MCD_OPERATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_OPERATION, McdOperation)) +#define MCD_OPERATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_OPERATION, McdOperationClass)) +#define MCD_IS_OPERATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_OPERATION)) +#define MCD_IS_OPERATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_OPERATION)) +#define MCD_OPERATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_OPERATION, McdOperationClass)) +typedef struct _McdOperation McdOperation; +typedef struct _McdOperationClass McdOperationClass; + +struct _McdOperation +{ + McdMission parent; +}; + +struct _McdOperationClass +{ + McdMissionClass parent_class; + + /* signals */ + void (*mission_taken_signal) (McdOperation * operation, + McdMission * mission); + void (*mission_removed_signal) (McdOperation * operation, + McdMission * mission); + + /* virtual methods */ + void (*take_mission) (McdOperation * operation, McdMission * mission); + void (*remove_mission) (McdOperation * operation, McdMission * mission); +}; + +GType mcd_operation_get_type (void); +McdOperation *mcd_operation_new (void); + +/* Takes the ownership of mission */ +void mcd_operation_take_mission (McdOperation * operation, + McdMission * mission); +void mcd_operation_remove_mission (McdOperation * operation, + McdMission * mission); +void mcd_operation_foreach (McdOperation * operation, + GFunc func, gpointer user_data); +const GList * mcd_operation_get_missions (McdOperation * operation); + +G_END_DECLS +#endif /* MCD_OPERATION_H */ diff --git a/src/mcd-presence-frame.c b/src/mcd-presence-frame.c new file mode 100644 index 00000000..8c6307c8 --- /dev/null +++ b/src/mcd-presence-frame.c @@ -0,0 +1,884 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "mcd-signals-marshal.h" +#include "mcd-presence-frame.h" + +#define MCD_PRESENCE_FRAME_PRIV(pframe) (G_TYPE_INSTANCE_GET_PRIVATE ((pframe), \ + MCD_TYPE_PRESENCE_FRAME, \ + McdPresenceFramePrivate)) + +G_DEFINE_TYPE (McdPresenceFrame, mcd_presence_frame, MCD_TYPE_MISSION); + +typedef struct _McdPresence +{ + McPresence presence; + gchar *message; + TelepathyConnectionStatus connection_status; + TelepathyConnectionStatusReason connection_reason; +} McdPresence; + +typedef struct _McdPresenceFramePrivate +{ + McdPresence *requested_presence; + McdPresence *actual_presence; + McdPresence *last_presence; + GHashTable *account_presence; + + gboolean is_stable; +} McdPresenceFramePrivate; + +typedef struct _McdActualPresenceInfo { + McPresence presence; + McPresence requested_presence; + gboolean found; +} McdActualPresenceInfo; + +enum _McdPresenceFrameSignalType +{ + /* Request */ + PRESENCE_REQUESTED, + + /* Account specific changes */ + PRESENCE_CHANGED, + STATUS_CHANGED, + + /* Accumulated changes */ + PRESENCE_ACTUAL, + STATUS_ACTUAL, + PRESENCE_STABLE, + + LAST_SIGNAL +}; + +static guint mcd_presence_frame_signals[LAST_SIGNAL] = { 0 }; + +static McdPresence * +mcd_presence_new (McPresence tp_presence, + const gchar * presence_message, + TelepathyConnectionStatus connection_status, + TelepathyConnectionStatusReason connection_reason) +{ + McdPresence *presence = g_new0 (McdPresence, 1); + presence->presence = tp_presence; + if (presence_message) + { + presence->message = g_strdup (presence_message); + } + + else + { + presence->message = NULL; + } + + presence->connection_status = connection_status; + presence->connection_reason = connection_reason; + return presence; +} + +static void +mcd_presence_free (McdPresence * presence) +{ + g_free (presence->message); + g_free (presence); +} + +static McdPresence * +mcd_presence_copy (McdPresence * presence) +{ + return mcd_presence_new (presence->presence, + presence->message, + presence->connection_status, + presence->connection_reason); +} + +static void +_mcd_presence_frame_dispose (GObject * object) +{ + McdPresenceFramePrivate *priv; + + priv = MCD_PRESENCE_FRAME_PRIV (object); + + if (priv->account_presence) + { + g_hash_table_destroy (priv->account_presence); + priv->account_presence = NULL; + } + + G_OBJECT_CLASS (mcd_presence_frame_parent_class)->dispose (object); +} + +static void +_mcd_presence_frame_finalize (GObject * object) +{ + McdPresenceFrame *cobj; + McdPresenceFramePrivate *priv; + + cobj = MCD_PRESENCE_FRAME (object); + priv = MCD_PRESENCE_FRAME_PRIV (object); + + mcd_presence_free (priv->actual_presence); + if (priv->requested_presence) + mcd_presence_free (priv->requested_presence); + if (priv->last_presence) + mcd_presence_free (priv->last_presence); + + G_OBJECT_CLASS (mcd_presence_frame_parent_class)->finalize (object); +} + +static void +mcd_presence_frame_disconnect (McdMission *mission) +{ + McdPresenceFramePrivate *priv = MCD_PRESENCE_FRAME_PRIV (mission); + + /* If connectivity goes away MC will abort processing a presence request; + * so we must clear the requested-presence for consistency, or McdMaster + * will think we are still trying to go online. */ + if (priv->requested_presence) + { + mcd_presence_free (priv->requested_presence); + priv->requested_presence = NULL; + } +} + +static void +mcd_presence_frame_class_init (McdPresenceFrameClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + McdMissionClass *mission_class = MCD_MISSION_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (McdPresenceFramePrivate)); + + object_class->dispose = _mcd_presence_frame_dispose; + object_class->finalize = _mcd_presence_frame_finalize; + mission_class->disconnect = mcd_presence_frame_disconnect; + + /* FIXME: Telepathy doesn't currently registers it's enums to glib so we are compelled to register the + * signal handler's arguments as INTs below */ + /* signals */ + mcd_presence_frame_signals[PRESENCE_REQUESTED] = + g_signal_new ("presence-requested", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdPresenceFrameClass, + presence_requested_signal), + NULL, NULL, + mcd_marshal_VOID__INT_STRING, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_STRING); + mcd_presence_frame_signals[PRESENCE_CHANGED] = + g_signal_new ("presence-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdPresenceFrameClass, + presence_set_signal), + NULL, NULL, + mcd_marshal_VOID__OBJECT_INT_STRING, + G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_INT, G_TYPE_STRING); + mcd_presence_frame_signals[STATUS_CHANGED] = + g_signal_new ("status-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdPresenceFrameClass, + status_changed_signal), + NULL, NULL, + mcd_marshal_VOID__OBJECT_INT_INT, + G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_INT, G_TYPE_INT); + mcd_presence_frame_signals[PRESENCE_ACTUAL] = + g_signal_new ("presence-actual", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdPresenceFrameClass, + presence_actual_signal), + NULL, NULL, + mcd_marshal_VOID__INT_STRING, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_STRING); + mcd_presence_frame_signals[STATUS_ACTUAL] = + g_signal_new ("status-actual", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (McdPresenceFrameClass, + status_actual_signal), + NULL, NULL, + mcd_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + mcd_presence_frame_signals[PRESENCE_STABLE] = + g_signal_new ("presence-stable", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + mcd_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); +} + +static void +mcd_presence_frame_init (McdPresenceFrame * obj) +{ + McdPresenceFramePrivate *priv = MCD_PRESENCE_FRAME_PRIV (obj); + + priv->account_presence = + g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) mcd_presence_free); + + priv->actual_presence = mcd_presence_new (MC_PRESENCE_UNSET, + NULL, + TP_CONN_STATUS_DISCONNECTED, + TP_CONN_STATUS_REASON_NONE_SPECIFIED); + priv->requested_presence = NULL; + priv->last_presence = NULL; + + priv->is_stable = TRUE; +} + +/* Public */ + +McdPresenceFrame * +mcd_presence_frame_new (void) +{ + McdPresenceFrame *obj; + obj = MCD_PRESENCE_FRAME (g_object_new (MCD_TYPE_PRESENCE_FRAME, NULL)); + return obj; +} + +static void +_mcd_presence_frame_print_account (McAccount *account, McdPresence *presence, + McdPresenceFrame *presence_frame) +{ + g_debug (" Account: %s", mc_account_get_unique_name (account)); + if (presence->message) + { + g_debug (" Presence = %d, Status = %d, Message = %s", + presence->presence, presence->connection_status, + presence->message); + } + else + { + g_debug (" Presence = %d, Status = %d", + presence->presence, presence->connection_status); + } +} + +static void +_mcd_presence_frame_print (McdPresenceFrame *presence_frame) +{ + McdPresenceFramePrivate *priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + g_debug ("PresenceFrame state:"); + g_debug ("["); + g_hash_table_foreach (priv->account_presence, + (GHFunc)_mcd_presence_frame_print_account, + presence_frame); + g_debug ("]"); +} + +static void +_mcd_presence_frame_request_presence (McdPresenceFrame * presence_frame, + McPresence presence, + const gchar * presence_message) +{ + McdPresenceFramePrivate *priv; + TelepathyConnectionStatus status; + + g_return_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame)); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + if (priv->requested_presence) + { + mcd_presence_free (priv->requested_presence); + } + + if (presence == MC_PRESENCE_OFFLINE) + { + status = TP_CONN_STATUS_DISCONNECTED; + } + + else + { + status = TP_CONN_STATUS_CONNECTED; + } + + priv->requested_presence = mcd_presence_new (presence, presence_message, + status, + TP_CONN_STATUS_REASON_REQUESTED); + g_debug ("%s: Presence %d is being requested", G_STRFUNC, presence); + + g_signal_emit_by_name (presence_frame, "presence-requested", + presence, presence_message); +} + +void +mcd_presence_frame_request_presence (McdPresenceFrame * presence_frame, + McPresence presence, + const gchar * presence_message) +{ + McdPresenceFramePrivate *priv; + + g_return_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame)); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + if (priv->last_presence) + { + mcd_presence_free (priv->last_presence); + } + priv->last_presence = mcd_presence_copy (priv->actual_presence); + + g_debug ("%s: updated last_presence = %d, msg = %s", G_STRFUNC, + priv->last_presence->presence, + priv->last_presence->message); + + if (priv->last_presence->presence == MC_PRESENCE_UNSET) + { + priv->last_presence->presence = MC_PRESENCE_OFFLINE; + } + + if (mcd_debug_get_level() > 0) + { + g_debug ("Presence requested: %d", presence); + _mcd_presence_frame_print (presence_frame); + } + + _mcd_presence_frame_request_presence (presence_frame, presence, + presence_message); +} + +gboolean +mcd_presence_frame_cancel_last_request (McdPresenceFrame * presence_frame) +{ + McdPresenceFramePrivate *priv; + McPresence presence; + gchar *message; + + g_return_val_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame), FALSE); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + if (priv->last_presence != NULL) + { + presence = priv->last_presence->presence; + if (priv->last_presence->message) + { + message = g_strdup (priv->last_presence->message); + } + + else + { + message = NULL; + } + + mcd_presence_free (priv->last_presence); + priv->last_presence = NULL; + + _mcd_presence_frame_request_presence (presence_frame, presence, + message); + return TRUE; + } + + else + { + return FALSE; + } +} + +McPresence +mcd_presence_frame_get_requested_presence (McdPresenceFrame * presence_frame) +{ + McdPresenceFramePrivate *priv; + g_return_val_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame), + MC_PRESENCE_UNSET); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + if (priv->requested_presence) + return priv->requested_presence->presence; + else + return MC_PRESENCE_UNSET; +} + +const gchar * +mcd_presence_frame_get_requested_presence_message (McdPresenceFrame * + presence_frame) +{ + McdPresenceFramePrivate *priv; + g_return_val_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame), NULL); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + if (priv->requested_presence) + return priv->requested_presence->message; + else + return NULL; +} + +McPresence +mcd_presence_frame_get_actual_presence (McdPresenceFrame * presence_frame) +{ + McdPresenceFramePrivate *priv; + g_return_val_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame), + MC_PRESENCE_UNSET); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + return priv->actual_presence->presence; +} + +const gchar * +mcd_presence_frame_get_actual_presence_message (McdPresenceFrame * + presence_frame) +{ + McdPresenceFramePrivate *priv; + g_return_val_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame), NULL); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + return priv->actual_presence->message; +} + +static void +_mcd_presence_frame_update_actual_presences (gpointer key, + gpointer val, + McdActualPresenceInfo *info) +{ + McdPresence *account_presence = (McdPresence *) val; + + if (info->found) return; + if (account_presence->presence == info->requested_presence) + { + info->presence = account_presence->presence; + info->found = TRUE; + } + else if (info->presence < account_presence->presence) + info->presence = account_presence->presence; +} + +static void +_mcd_presence_frame_update_actual_presence (McdPresenceFrame * presence_frame, + const gchar * presence_message) +{ + McdPresenceFramePrivate *priv; + McdActualPresenceInfo pi; + McPresence old_presence; + TelepathyConnectionStatus connection_status; + TelepathyConnectionStatusReason connection_reason; + + g_debug ("%s called", G_STRFUNC); + + pi.presence = MC_PRESENCE_UNSET; + pi.requested_presence = mcd_presence_frame_get_requested_presence (presence_frame); + pi.found = FALSE; + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + g_hash_table_foreach (priv->account_presence, + (GHFunc)_mcd_presence_frame_update_actual_presences, + &pi); + + connection_status = priv->actual_presence->connection_status; + connection_reason = priv->actual_presence->connection_reason; + + old_presence = priv->actual_presence->presence; + mcd_presence_free (priv->actual_presence); + priv->actual_presence = mcd_presence_new (pi.presence, + presence_message, + connection_status, + connection_reason); + + g_debug ("%s: presence actual: %d", G_STRFUNC, pi.presence); + if (old_presence != pi.presence) + { + g_signal_emit_by_name (G_OBJECT (presence_frame), + "presence-actual", pi.presence, presence_message); + } +} + +void +mcd_presence_frame_set_account_presence (McdPresenceFrame * presence_frame, + McAccount * account, + McPresence + presence, + const gchar * presence_message) +{ + McdPresenceFramePrivate *priv; + McdPresence *account_presence; + + g_return_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame)); + + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + account_presence = g_hash_table_lookup (priv->account_presence, account); + + g_return_if_fail (account_presence != NULL); + if (account_presence->presence == presence) + { + g_debug ("%s: presence already set, not setting", G_STRFUNC); + return; + } + + g_debug ("%s: changing presence of account %s from %d to %d", + G_STRFUNC, mc_account_get_unique_name (account), + account_presence->presence, + presence); + account_presence->presence = presence; + g_free (account_presence->message); + account_presence->message = NULL; + if (presence_message) + account_presence->message = g_strdup (presence_message); + + g_signal_emit_by_name (presence_frame, "presence-changed", account, + presence, presence_message); + + _mcd_presence_frame_update_actual_presence (presence_frame, + presence_message); + + if (mcd_debug_get_level() > 0) + { + g_debug ("Presence Set for account: %s: %d", + mc_account_get_unique_name (account), + presence); + _mcd_presence_frame_print (presence_frame); + } + +} + +McPresence +mcd_presence_frame_get_account_presence (McdPresenceFrame * presence_frame, + McAccount * account) +{ + McdPresenceFramePrivate *priv; + McPresence ret_presence; + + g_return_val_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame), + MC_PRESENCE_UNSET); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + ret_presence = MC_PRESENCE_UNSET; + if (priv->account_presence) + { + McdPresence *presence; + presence = g_hash_table_lookup (priv->account_presence, account); + if (presence) + ret_presence = presence->presence; + } + return ret_presence; +} + +const gchar * +mcd_presence_frame_get_account_presence_message (McdPresenceFrame * + presence_frame, + McAccount * account) +{ + McdPresenceFramePrivate *priv; + + g_return_val_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame), NULL); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + if (priv->account_presence) + { + McdPresence *presence; + presence = g_hash_table_lookup (priv->account_presence, account); + if (presence) + return presence->message; + } + return NULL; +} + +static void +_mcd_presence_frame_update_actual_statuses (gpointer key, + gpointer val, gpointer user_data) +{ + McdPresence *account_presence = (McdPresence *) val; + TelepathyConnectionStatus *connection_status = + (TelepathyConnectionStatus *) user_data; + + if (*connection_status > account_presence->connection_status) + *connection_status = account_presence->connection_status; +} + +static void +_mcd_presence_frame_update_actual_status (McdPresenceFrame * presence_frame) +{ + McdPresenceFramePrivate *priv; + TelepathyConnectionStatus connection_status = TP_CONN_STATUS_DISCONNECTED; + TelepathyConnectionStatus old_connection_status; + TelepathyConnectionStatusReason connection_reason; + McPresence presence; + gchar *presence_message = NULL; + + g_debug ("%s called", G_STRFUNC); + + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + g_hash_table_foreach (priv->account_presence, + _mcd_presence_frame_update_actual_statuses, + &connection_status); + + connection_reason = priv->actual_presence->connection_reason; + presence = priv->actual_presence->presence; + if (priv->actual_presence->message) + presence_message = g_strdup (priv->actual_presence->message); + + old_connection_status = priv->actual_presence->connection_status; + mcd_presence_free (priv->actual_presence); + priv->actual_presence = mcd_presence_new (presence, + presence_message, + connection_status, + connection_reason); + + g_debug ("%s: status actual: %d", G_STRFUNC, connection_status); + if (old_connection_status != connection_status) + g_signal_emit_by_name (G_OBJECT (presence_frame), "status-actual", + connection_status); +} + +static void +_mcd_presence_frame_check_stable (McAccount *account, McdPresence *presence, gboolean *stable) +{ + g_debug ("%s: status = %d", G_STRFUNC, presence->connection_status); + if (presence->connection_status == TP_CONN_STATUS_CONNECTING) + *stable = FALSE; +} + +static void +_mcd_presence_frame_update_stable (McdPresenceFrame *presence_frame) +{ + McdPresenceFramePrivate *priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + g_debug ("%s: account_presence = %p", G_STRFUNC, priv->account_presence); + priv->is_stable = TRUE; + if (priv->account_presence) + g_hash_table_foreach (priv->account_presence, + (GHFunc)_mcd_presence_frame_check_stable, + &priv->is_stable); +} + +void +mcd_presence_frame_set_account_status (McdPresenceFrame * presence_frame, + McAccount * account, + TelepathyConnectionStatus + connection_status, + TelepathyConnectionStatusReason + connection_reason) +{ + McdPresenceFramePrivate *priv; + McdPresence *account_presence; + TelepathyConnectionStatus previous_status; + gboolean was_stable; + + g_return_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame)); + + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + account_presence = g_hash_table_lookup (priv->account_presence, account); + + if (account_presence == NULL) + { + g_warning ("%s: Can not find account presence for account %p (%s)", + G_STRFUNC, account, + mc_account_get_unique_name (account)); + return; + } + + previous_status = account_presence->connection_status; + account_presence->connection_status = connection_status; + account_presence->connection_reason = connection_reason; + + /* If no changed, then return */ + if (previous_status == connection_status) + return; + + if (connection_status == TP_CONN_STATUS_DISCONNECTED) + { + /* We first set UNSET presence */ + mcd_presence_frame_set_account_presence (presence_frame, account, + MC_PRESENCE_UNSET, + NULL); + /* and then set DISCONNECTED */ + g_signal_emit_by_name (presence_frame, "status-changed", account, + connection_status, connection_reason); + _mcd_presence_frame_update_actual_status (presence_frame); + } + else if (connection_status == TP_CONN_STATUS_CONNECTING) + { + g_signal_emit_by_name (presence_frame, "status-changed", account, + connection_status, connection_reason); + _mcd_presence_frame_update_actual_status (presence_frame); + } + else if (connection_status == TP_CONN_STATUS_CONNECTED) + { + /* We first set CONNECTED */ + g_signal_emit_by_name (presence_frame, "status-changed", account, + connection_status, connection_reason); + _mcd_presence_frame_update_actual_status (presence_frame); + + /* and then set AVAILABLE presence */ + mcd_presence_frame_set_account_presence (presence_frame, account, + MC_PRESENCE_AVAILABLE, + NULL); + } + + was_stable = priv->is_stable; + _mcd_presence_frame_update_stable (presence_frame); + if (was_stable != priv->is_stable || priv->is_stable) + g_signal_emit (presence_frame, + mcd_presence_frame_signals[PRESENCE_STABLE], + 0, priv->is_stable); + + if (mcd_debug_get_level() > 0) + { + g_debug ("%s: was stable = %d, is_stable = %d", G_STRFUNC, was_stable, priv->is_stable); + g_debug ("Account Status Set for account: %s: %d", mc_account_get_unique_name (account), + connection_status); + _mcd_presence_frame_print (presence_frame); + } +} + +TelepathyConnectionStatus +mcd_presence_frame_get_account_status (McdPresenceFrame * presence_frame, + McAccount * account) +{ + McdPresenceFramePrivate *priv; + TelepathyConnectionStatus conn_status; + + g_return_val_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame), + TP_CONN_STATUS_DISCONNECTED); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + conn_status = TP_CONN_STATUS_DISCONNECTED; + if (priv->account_presence) + { + McdPresence *presence; + presence = g_hash_table_lookup (priv->account_presence, account); + if (presence) + conn_status = presence->connection_status; + } + return conn_status; +} + +TelepathyConnectionStatusReason +mcd_presence_frame_get_account_status_reason (McdPresenceFrame * + presence_frame, + McAccount * account) +{ + McdPresenceFramePrivate *priv; + TelepathyConnectionStatusReason conn_reason; + + g_return_val_if_fail (MCD_IS_PRESENCE_FRAME (presence_frame), + TP_CONN_STATUS_REASON_NONE_SPECIFIED); + priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + conn_reason = TP_CONN_STATUS_REASON_NONE_SPECIFIED; + if (priv->account_presence) + { + McdPresence *presence; + presence = g_hash_table_lookup (priv->account_presence, account); + if (presence) + conn_reason = presence->connection_reason; + } + return conn_reason; +} + +void +mcd_presence_frame_set_accounts (McdPresenceFrame * presence_frame, + const GList * accounts) +{ + const GList *node; + McdPresenceFramePrivate *priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + if (priv->account_presence) + { + g_hash_table_destroy (priv->account_presence); + priv->account_presence = + g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) mcd_presence_free); + } + for (node = accounts; node; node = node->next) + { + g_object_ref (node->data); + g_hash_table_insert (priv->account_presence, node->data, + mcd_presence_new (MC_PRESENCE_UNSET, + NULL, + TP_CONN_STATUS_DISCONNECTED, + TP_CONN_STATUS_REASON_NONE_SPECIFIED)); + } +} + +gboolean +mcd_presence_frame_add_account (McdPresenceFrame * presence_frame, + McAccount * account) +{ + McdPresence *presence; + McdPresenceFramePrivate *priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + /* Let's see if we already have the account */ + presence = g_hash_table_lookup (priv->account_presence, account); + + if (presence != NULL) + { + return FALSE; + } + + presence = mcd_presence_new (MC_PRESENCE_UNSET, + NULL, + TP_CONN_STATUS_DISCONNECTED, + TP_CONN_STATUS_REASON_NONE_SPECIFIED); + g_object_ref (account); + g_hash_table_insert (priv->account_presence, + account, + presence); + + /*mcd_presence_frame_set_account_presence (presence_frame, account, + priv->actual_presence->presence, + NULL);*/ + return TRUE; +} + +gboolean +mcd_presence_frame_remove_account (McdPresenceFrame * presence_frame, + McAccount * account) +{ + McdPresence *presence; + McdPresenceFramePrivate *priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + + /* Let's see if we have the account */ + presence = g_hash_table_lookup (priv->account_presence, account); + + if (presence == NULL) + { + return FALSE; + } + + g_debug ("%s: removing account %s", G_STRFUNC, mc_account_get_unique_name (account)); + /*mcd_presence_frame_set_account_presence (presence_frame, account, + MC_PRESENCE_UNSET, + NULL);*/ + /*_mcd_presence_frame_update_actual_presence (presence_frame, NULL);*/ + g_hash_table_remove (priv->account_presence, account); + + return TRUE; +} + +/** + * mcd_presence_frame_is_stable: + * @presence_frame: The #McdPresenceFrame. + * + * Returns #TRUE if there isn't any account currently trying to connect. + */ +gboolean +mcd_presence_frame_is_stable (McdPresenceFrame *presence_frame) +{ + McdPresenceFramePrivate *priv = MCD_PRESENCE_FRAME_PRIV (presence_frame); + return priv->is_stable; +} + diff --git a/src/mcd-presence-frame.h b/src/mcd-presence-frame.h new file mode 100644 index 00000000..06b4d17f --- /dev/null +++ b/src/mcd-presence-frame.h @@ -0,0 +1,133 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_PRESENCE_FRAME_H +#define MCD_PRESENCE_FRAME_H + +#include <glib.h> +#include <glib-object.h> +#include <libmissioncontrol/mc-account.h> +#include <libtelepathy/tp-constants.h> +#include <libmissioncontrol/mission-control.h> + +#include "mcd-mission.h" + +G_BEGIN_DECLS +#define MCD_TYPE_PRESENCE_FRAME (mcd_presence_frame_get_type ()) +#define MCD_PRESENCE_FRAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_PRESENCE_FRAME, McdPresenceFrame)) +#define MCD_PRESENCE_FRAME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_PRESENCE_FRAME, McdPresenceFrameClass)) +#define MCD_IS_PRESENCE_FRAME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_PRESENCE_FRAME)) +#define MCD_IS_PRESENCE_FRAME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_PRESENCE_FRAME)) +#define MCD_PRESENCE_FRAME_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_PRESENCE_FRAME, McdPresenceFrameClass)) +typedef struct _McdPresenceFrame McdPresenceFrame; +typedef struct _McdPresenceFrameClass McdPresenceFrameClass; + +struct _McdPresenceFrame +{ + McdMission parent; +}; + +struct _McdPresenceFrameClass +{ + McdMissionClass parent_class; + + /* Signals */ + void (*presence_requested_signal) (McdPresenceFrame * presence_frame, + McPresence presence, + const gchar * presence_message); + + /* Account specific signals */ + void (*presence_set_signal) (McdPresenceFrame * presence_frame, + McAccount * account, + McPresence presence, + const gchar * presence_message); + void (*status_changed_signal) (McdPresenceFrame * presence_frame, + McAccount * account, + TelepathyConnectionStatus connection_status, + TelepathyConnectionStatusReason + connection_reason); + + /* Aggregate signals */ + void (*presence_actual_signal) (McdPresenceFrame * presence_frame, + McPresence presence, + const gchar * presence_message); + void (*status_actual_signal) (McdPresenceFrame * presence_frame, + TelepathyConnectionStatus status); +}; + +GType mcd_presence_frame_get_type (void); +McdPresenceFrame *mcd_presence_frame_new (void); + +void mcd_presence_frame_request_presence (McdPresenceFrame * presence_frame, + McPresence + presence, + const gchar * presence_message); + +McPresence mcd_presence_frame_get_requested_presence + (McdPresenceFrame * presence_frame); +const gchar *mcd_presence_frame_get_requested_presence_message + (McdPresenceFrame * presence_frame); + +McPresence mcd_presence_frame_get_actual_presence + (McdPresenceFrame * presence_frame); +const gchar *mcd_presence_frame_get_actual_presence_message + (McdPresenceFrame * presence_frame); + +void mcd_presence_frame_set_account_presence (McdPresenceFrame * + presence_frame, + McAccount * account, + McPresence + presence, + const gchar * presence_message); +void mcd_presence_frame_set_account_status (McdPresenceFrame * presence_frame, + McAccount * account, + TelepathyConnectionStatus + connection_status, + TelepathyConnectionStatusReason + connection_reason); + +McPresence mcd_presence_frame_get_account_presence + (McdPresenceFrame * presence_frame, McAccount * account); +const gchar *mcd_presence_frame_get_presence_message + (McdPresenceFrame * presence_frame, McAccount * account); +TelepathyConnectionStatus mcd_presence_frame_get_account_status + (McdPresenceFrame * presence_frame, McAccount * account); +TelepathyConnectionStatusReason mcd_presence_frame_get_account_status_reason + (McdPresenceFrame * presence_frame, McAccount * account); + +void mcd_presence_frame_set_accounts (McdPresenceFrame * presence_frame, + const GList * accounts); + +gboolean mcd_presence_frame_add_account (McdPresenceFrame * presence_frame, + McAccount * account); +gboolean mcd_presence_frame_remove_account (McdPresenceFrame * presence_frame, + McAccount * account); + +gboolean mcd_presence_frame_cancel_last_request (McdPresenceFrame * + presence_frame); + +gboolean mcd_presence_frame_is_stable (McdPresenceFrame *presence_frame); + +G_END_DECLS +#endif /* MCD_PRESENCE_FRAME_H */ diff --git a/src/mcd-proxy.c b/src/mcd-proxy.c new file mode 100644 index 00000000..ff163e1a --- /dev/null +++ b/src/mcd-proxy.c @@ -0,0 +1,226 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <glib/gi18n.h> +#include "mcd-proxy.h" + +#define MCD_PROXY_PRIV(proxy) (G_TYPE_INSTANCE_GET_PRIVATE ((proxy), \ + MCD_TYPE_PROXY, \ + McdProxyPrivate)) + +/** + * McdProxy: + * This is a simple container class that proxies the events from a proxy + * object to self container. + */ +G_DEFINE_TYPE (McdProxy, mcd_proxy, MCD_TYPE_OPERATION); + +/* Private */ + +typedef struct _McdProxyPrivate +{ + McdMission *proxy_object; + gboolean is_disposed; +} McdProxyPrivate; + +enum +{ + PROP_0, + PROP_PROXY_OBJECT +}; + +static void +_mcd_proxy_abort (McdProxy * proxy) +{ + /* Releases the reference */ + g_object_set (proxy, "proxy-object", NULL, NULL); + /* Propagate the "abort" event to our listeners */ + mcd_mission_abort (MCD_MISSION (proxy)); +} + +static void +_mcd_proxy_connect_signals (McdProxy * proxy) +{ + McdProxyPrivate *priv = MCD_PROXY_PRIV (proxy); + + g_signal_connect_swapped (priv->proxy_object, "connected", + G_CALLBACK (mcd_mission_connect), proxy); + g_signal_connect_swapped (priv->proxy_object, "disconnected", + G_CALLBACK (mcd_mission_disconnect), proxy); + g_signal_connect_swapped (priv->proxy_object, "flags-changed", + G_CALLBACK (mcd_mission_set_flags), proxy); + g_signal_connect_swapped (priv->proxy_object, "mode-set", + G_CALLBACK (mcd_mission_set_mode), proxy); + g_signal_connect_swapped (priv->proxy_object, "abort", + G_CALLBACK (_mcd_proxy_abort), proxy); +} + +static void +_mcd_proxy_disconnect_signals (McdProxy * proxy) +{ + McdProxyPrivate *priv = MCD_PROXY_PRIV (proxy); + + g_signal_handlers_disconnect_by_func (priv->proxy_object, + G_CALLBACK (mcd_mission_connect), + proxy); + g_signal_handlers_disconnect_by_func (priv->proxy_object, + G_CALLBACK (mcd_mission_disconnect), + proxy); + g_signal_handlers_disconnect_by_func (priv->proxy_object, + G_CALLBACK (mcd_mission_set_flags), + proxy); + g_signal_handlers_disconnect_by_func (priv->proxy_object, + G_CALLBACK (mcd_mission_set_mode), + proxy); + g_signal_handlers_disconnect_by_func (priv->proxy_object, + G_CALLBACK (_mcd_proxy_abort), proxy); +} + +static void +_mcd_proxy_finalize (GObject * object) +{ + G_OBJECT_CLASS (mcd_proxy_parent_class)->finalize (object); +} + +static void +_mcd_proxy_dispose (GObject * object) +{ + McdProxyPrivate *priv = MCD_PROXY_PRIV (object); + + if (priv->is_disposed) + { + return; + } + + priv->is_disposed = TRUE; + g_debug ("proxy disposed\n"); + + if (priv->proxy_object) + { + /* Disconnect proxy signals */ + _mcd_proxy_disconnect_signals (MCD_PROXY (object)); + g_object_unref (priv->proxy_object); + priv->proxy_object = NULL; + } + G_OBJECT_CLASS (mcd_proxy_parent_class)->dispose (object); +} + +static void +_mcd_proxy_set_property (GObject * obj, guint prop_id, + const GValue * val, GParamSpec * pspec) +{ + McdMission *proxy_object; + McdProxyPrivate *priv = MCD_PROXY_PRIV (obj); + + switch (prop_id) + { + case PROP_PROXY_OBJECT: + proxy_object = g_value_get_object (val); + if (proxy_object) + { + g_return_if_fail (MCD_IS_MISSION (proxy_object)); + g_object_ref (proxy_object); + } + + if (priv->proxy_object) + { + /* Disconnect proxy signals */ + _mcd_proxy_disconnect_signals (MCD_PROXY (obj)); + g_object_unref (priv->proxy_object); + } + priv->proxy_object = proxy_object; + if (priv->proxy_object) + { + /* Connect proxy signals */ + _mcd_proxy_connect_signals (MCD_PROXY (obj)); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_mcd_proxy_get_property (GObject * obj, guint prop_id, + GValue * val, GParamSpec * pspec) +{ + McdProxyPrivate *priv = MCD_PROXY_PRIV (obj); + + switch (prop_id) + { + case PROP_PROXY_OBJECT: + g_value_set_pointer (val, priv->proxy_object); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +mcd_proxy_class_init (McdProxyClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (McdProxyPrivate)); + + object_class->finalize = _mcd_proxy_finalize; + object_class->dispose = _mcd_proxy_dispose; + object_class->set_property = _mcd_proxy_set_property; + object_class->get_property = _mcd_proxy_get_property; + + g_object_class_install_property (object_class, + PROP_PROXY_OBJECT, + g_param_spec_object ("proxy-object", + _("Proxy object"), + _("Object to be monitored for McdMission signals"), + MCD_TYPE_MISSION, + G_PARAM_READWRITE)); +} + +static void +mcd_proxy_init (McdProxy * obj) +{ + McdProxyPrivate *priv = MCD_PROXY_PRIV (obj); + priv->proxy_object = NULL; +} + +/* Public */ + +McdProxy * +mcd_proxy_new (McdMission * proxy_object) +{ + McdProxy *obj; + obj = MCD_PROXY (g_object_new (MCD_TYPE_PROXY, "proxy-object", + proxy_object, NULL)); + return obj; +} + +const McdMission * +mcd_proxy_get_proxy_object (McdProxy * proxy) +{ + McdProxyPrivate *priv = MCD_PROXY_PRIV (proxy); + return priv->proxy_object; +} diff --git a/src/mcd-proxy.h b/src/mcd-proxy.h new file mode 100644 index 00000000..ed0aac6e --- /dev/null +++ b/src/mcd-proxy.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_PROXY_H +#define MCD_PROXY_H + +#include <glib.h> +#include <glib-object.h> + +#include "mcd-operation.h" + +G_BEGIN_DECLS +#define MCD_TYPE_PROXY (mcd_proxy_get_type ()) +#define MCD_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_PROXY, McdProxy)) +#define MCD_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_PROXY, McdProxyClass)) +#define MCD_IS_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_PROXY)) +#define MCD_IS_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_PROXY)) +#define MCD_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_PROXY, McdProxyClass)) +typedef struct _McdProxy McdProxy; +typedef struct _McdProxyClass McdProxyClass; + +struct _McdProxy +{ + McdOperation parent; +}; + +struct _McdProxyClass +{ + McdOperationClass parent_class; +}; + +GType mcd_proxy_get_type (void); +McdProxy *mcd_proxy_new (McdMission * proxy_mission); +const McdMission *mcd_proxy_get_proxy_object (McdProxy * proxy); + +G_END_DECLS +#endif /* MCD_PROXY_H */ diff --git a/src/mcd-service.c b/src/mcd-service.c new file mode 100644 index 00000000..690695f1 --- /dev/null +++ b/src/mcd-service.c @@ -0,0 +1,789 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <dbus/dbus.h> +#include <string.h> +#include <dlfcn.h> +#include <sched.h> +#include <libmissioncontrol/mission-control.h> +#include <stdlib.h> + +#include <glib.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> +#include <dbus/dbus.h> +#include <gconf/gconf-client.h> +#include <libtelepathy/tp-interfaces.h> +#include <libtelepathy/tp-constants.h> + +#include "mcd-signals-marshal.h" +#include "mcd-dispatcher.h" +#include "mcd-connection.h" +#include "mcd-service.h" + +/* DBus service specifics */ +#define MISSION_CONTROL_DBUS_SERVICE "org.freedesktop.Telepathy.MissionControl" +#define MISSION_CONTROL_DBUS_OBJECT "/org/freedesktop/Telepathy/MissionControl" +#define MISSION_CONTROL_DBUS_IFACE "org.freedesktop.Telepathy.MissionControl" + +/* Signals */ + +enum +{ + ACCOUNT_STATUS_CHANGED, + ERROR, + PRESENCE_STATUS_REQUESTED, + PRESENCE_STATUS_ACTUAL, + USED_CHANNELS_COUNT_CHANGED, + STATUS_ACTUAL, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; +static GObjectClass *parent_class = NULL; + +#define MCD_OBJECT_PRIV(mission) (G_TYPE_INSTANCE_GET_PRIVATE ((mission), \ + MCD_TYPE_SERVICE, \ + McdServicePrivate)) + +/** + * McdService: + * It is the frontline interface object that exposes mission-control to outside + * world through a dbus interface. It basically subclasses McdMaster and + * wraps up everything inside it and translate them into mission-control + * dbus interface. + */ +G_DEFINE_TYPE (McdService, mcd_service, MCD_TYPE_MASTER); + +/* Private */ + +typedef struct _McdServicePrivate +{ + McdPresenceFrame *presence_frame; + McdDispatcher *dispatcher; + + McStatus last_status; + + gboolean is_disposed; +} McdServicePrivate; + +#define MC_EMIT_ERROR_ASYNC(mi, err) \ + g_assert (err != NULL); \ + dbus_g_method_return_error (mi, err); \ + g_warning ("%s: Returning async error '%s'", G_STRFUNC, err->message); \ + g_error_free (err); + +#define MC_SHOW_ERROR(err) \ + g_assert ((err) != NULL); \ + g_warning ("%s: Returning error '%s'", G_STRFUNC, (err)->message); + +/* Dbus interface implementation */ +static gboolean +mcd_service_set_presence (GObject * obj, gint presence, gchar * message, + GError ** error) +{ + mcd_master_request_presence (MCD_MASTER (obj), presence, message); + return TRUE; +} + +static gboolean +mcd_service_get_presence (GObject *obj, gint *ret, GError **error) +{ + *ret = mcd_master_get_requested_presence (MCD_MASTER (obj)); + return TRUE; +} + +static gboolean +mcd_service_get_presence_actual (GObject *obj, gint *ret, GError **error) +{ + *ret = mcd_master_get_actual_presence (MCD_MASTER (obj)); + return TRUE; +} + +static void +mcd_service_connect_all_with_default_presence (GObject * obj, + DBusGMethodInvocation *mi) +{ + gchar *sender = dbus_g_method_get_sender (mi); + mcd_master_set_default_presence (MCD_MASTER (obj), sender); + g_free (sender); + dbus_g_method_return (mi); +} + +static gboolean +mcd_service_get_connection_status (GObject * obj, gchar * account_name, + guint * ret, GError ** error) +{ + *ret = mcd_master_get_account_status (MCD_MASTER (obj), account_name); + return TRUE; +} + +static gboolean +mcd_service_get_online_connections (GObject * obj, + gchar *** ret, GError ** error) +{ + return mcd_master_get_online_connection_names (MCD_MASTER (obj), ret); +} + +static gboolean +mcd_service_get_connection (GObject * obj, const gchar * account_name, + gchar ** ret_servname, + gchar ** ret_objpath, GError ** error) +{ + return mcd_master_get_account_connection_details (MCD_MASTER (obj), + account_name, + ret_servname, + ret_objpath); +} + +static void +mcd_service_request_channel (GObject * obj, + const gchar * account_name, + const gchar * type, + guint handle, + gint handle_type, + guint serial, + DBusGMethodInvocation *mi) +{ + struct mcd_channel_request req; + GError *err = NULL; + + memset (&req, 0, sizeof (req)); + req.account_name = account_name; + req.channel_type = type; + req.channel_handle = handle; + req.channel_handle_type = handle_type; + req.requestor_serial = serial; + req.requestor_client_id = dbus_g_method_get_sender (mi); + if (!mcd_master_request_channel (MCD_MASTER (obj), &req, &err)) + { + g_free ((gchar *)req.requestor_client_id); + MC_EMIT_ERROR_ASYNC (mi, err); + return; + } + g_free ((gchar *)req.requestor_client_id); + dbus_g_method_return (mi); +} + +static void +mcd_service_request_channel_with_string_handle (GObject * obj, + const gchar * account_name, + const gchar * type, + const gchar * handle, + gint handle_type, + guint serial, + DBusGMethodInvocation *mi) +{ + struct mcd_channel_request req; + GError *err = NULL; + + memset (&req, 0, sizeof (req)); + req.account_name = account_name; + req.channel_type = type; + req.channel_handle_string = handle; + req.channel_handle_type = handle_type; + req.requestor_serial = serial; + req.requestor_client_id = dbus_g_method_get_sender (mi); + mcd_controller_cancel_shutdown (MCD_CONTROLLER (obj)); + if (!mcd_master_request_channel (MCD_MASTER (obj), &req, &err)) + { + g_free ((gchar *)req.requestor_client_id); + MC_EMIT_ERROR_ASYNC (mi, err); + return; + } + g_free ((gchar *)req.requestor_client_id); + dbus_g_method_return (mi); +} + +static void +mcd_service_cancel_channel_request (GObject * obj, guint operation_id, + DBusGMethodInvocation *mi) +{ + GError *err = NULL; + gchar *sender = dbus_g_method_get_sender (mi); + g_debug ("%s (%u)", G_STRFUNC, operation_id); + if (!mcd_master_cancel_channel_request (MCD_MASTER (obj), operation_id, + sender, &err)) + { + g_warning ("%s: channel not found", G_STRFUNC); + g_free (sender); + dbus_g_method_return (mi); + return; + } + g_free (sender); + if (err) + { + MC_EMIT_ERROR_ASYNC (mi, err); + return; + } + dbus_g_method_return (mi); +} + +static gboolean +mcd_service_get_used_channels_count (GObject * obj, const gchar *chan_type, + guint * ret, GError ** error) +{ + if (!mcd_master_get_used_channels_count (MCD_MASTER (obj), + g_quark_from_string (chan_type), + ret, error)) + { + MC_SHOW_ERROR (*error); + return FALSE; + } + return TRUE; +} + +static gboolean +mcd_service_get_account_for_connection(GObject *obj, + const gchar *object_path, + gchar **ret_unique_name, + GError **error) +{ + g_debug ("%s: object_path = %s", __FUNCTION__, object_path); + + if (!mcd_master_get_account_for_connection (MCD_MASTER (obj), + object_path, + ret_unique_name, + error)) + { + MC_SHOW_ERROR (*error); + return FALSE; + } + return TRUE; +} + +static gboolean +mcd_service_get_current_status(GObject *obj, + McStatus *status, McPresence *presence, + McPresence *requested_presence, + GPtrArray **accounts, GError **error) +{ + McdServicePrivate *priv = MCD_OBJECT_PRIV (obj); + GList *account_list, *account_node; + GType type; + + *status = priv->last_status; + *presence = mcd_master_get_actual_presence (MCD_MASTER (obj)); + *requested_presence = mcd_master_get_requested_presence (MCD_MASTER (obj)); + *accounts = g_ptr_array_new (); + + type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID); + account_list = mc_accounts_list_by_enabled (TRUE); + for (account_node = account_list; account_node != NULL; + account_node = g_list_next (account_node)) + { + McAccount *account = account_node->data; + GValue account_data = { 0, }; + const gchar *name; + TelepathyConnectionStatus status; + TelepathyConnectionStatusReason reason; + McPresence presence; + + name = mc_account_get_unique_name (account); + status = mcd_presence_frame_get_account_status (priv->presence_frame, + account); + presence = + mcd_presence_frame_get_account_presence (priv->presence_frame, + account); + reason = + mcd_presence_frame_get_account_status_reason (priv->presence_frame, + account); + + g_value_init (&account_data, type); + g_value_take_boxed (&account_data, + dbus_g_type_specialized_construct (type)); + dbus_g_type_struct_set (&account_data, + 0, name, + 1, status, + 2, presence, + 3, reason, + G_MAXUINT); + + g_ptr_array_add (*accounts, g_value_get_boxed (&account_data)); + } + mc_accounts_list_free (account_list); + return TRUE; +} + +static void +mcd_service_remote_avatar_changed(GObject *obj, + const gchar *object_path, + guint contact_id, + const gchar *token, + DBusGMethodInvocation *mi) +{ + McdConnection *connection; + GError *error = NULL; + + g_debug ("%s: object_path = %s, id = %u, token = %s", __FUNCTION__, + object_path, contact_id, token); + + connection = mcd_master_get_connection (MCD_MASTER (obj), + object_path, &error); + if (!connection) + { + MC_EMIT_ERROR_ASYNC (mi, error); + return; + } + /* let the D-Bus call return immediately, there's no need for the caller to + * be blocked while we get the avatar */ + dbus_g_method_return (mi); + + mcd_connection_remote_avatar_changed (connection, contact_id, token); +} + +#include "mcd-service-gen.h" + +static void +mcd_register_dbus_object (McdService * obj) +{ + DBusError error; + DBusGConnection *connection; + + g_object_get (obj, "dbus-connection", &connection, NULL); + + dbus_error_init (&error); + + g_debug ("Requesting MC dbus service"); + + dbus_bus_request_name (dbus_g_connection_get_connection (connection), + MISSION_CONTROL_DBUS_SERVICE, 0, &error); + if (dbus_error_is_set (&error)) + { + g_error ("Service name '%s' is already in use - request failed", + MISSION_CONTROL_DBUS_SERVICE); + dbus_error_free (&error); + } + + g_debug ("Registering MC object"); + mcd_debug_print_tree (obj); + dbus_g_connection_register_g_object (connection, + MISSION_CONTROL_DBUS_OBJECT, + G_OBJECT (obj)); + g_debug ("Registered MC object"); + mcd_debug_print_tree (obj); +} + +static void +_on_account_status_changed (McdPresenceFrame * presence_frame, + McAccount * account, + TelepathyConnectionStatus connection_status, + TelepathyConnectionStatusReason connection_reason, + McdService * obj) +{ + McPresence presence = + mcd_presence_frame_get_account_presence (presence_frame, account); + + /* FIXME: Don't emit the CONNECTING state. The ui doesn handle it + * correctly yet. + */ + if (connection_status != TP_CONN_STATUS_CONNECTING) + { + /* Emit the AccountStatusChanged signal */ + g_debug ("Emitting account status changed for %s: status = %d, reason = %d", + mc_account_get_unique_name (account), connection_status, + connection_reason); + + g_signal_emit_by_name (G_OBJECT (obj), + "account-status-changed", connection_status, + presence, + connection_reason, + mc_account_get_unique_name (account)); + } +} + +static void +_on_account_presence_changed (McdPresenceFrame * presence_frame, + McAccount * account, + McPresence presence, + gchar * presence_message, McdService * obj) +{ + /* Emit the AccountStatusChanged signal */ + g_debug ("Emitting presence changed for %s: presence = %d, message = %s", + mc_account_get_unique_name (account), presence, + presence_message); + + g_signal_emit_by_name (G_OBJECT (obj), + "account-status-changed", + mcd_presence_frame_get_account_status + (presence_frame, account), presence, + mcd_presence_frame_get_account_status_reason + (presence_frame, account), + mc_account_get_unique_name (account)); +} + +static void +_on_presence_requested (McdPresenceFrame * presence_frame, + McPresence presence, + gchar * presence_message, McdService * obj) +{ + /* Begin shutdown if it is offline request */ + if (presence == MC_PRESENCE_OFFLINE || + presence == MC_PRESENCE_UNSET) + mcd_controller_shutdown (MCD_CONTROLLER (obj), + "Offline presence requested"); + else + /* If there is a presence request, make sure shutdown is canceled */ + mcd_controller_cancel_shutdown (MCD_CONTROLLER (obj)); + + /* Emit the AccountStatusChanged signal */ + g_signal_emit_by_name (G_OBJECT (obj), + "presence-status-requested", presence); +} + +static void +_on_presence_actual (McdPresenceFrame * presence_frame, + McPresence presence, + gchar * presence_message, McdService * obj) +{ + /* Emit the AccountStatusChanged signal */ + g_signal_emit_by_name (G_OBJECT (obj), "presence-status-actual", presence); +} + +static void +count_connections (McdOperation *manager, guint *connections) +{ + *connections += g_list_length ((GList *) + mcd_operation_get_missions (manager)); +} + +static void +_on_status_actual (McdPresenceFrame * presence_frame, + TelepathyConnectionStatus status, + McdService * obj) +{ + /* Everyone just got disconnected */ + if (status == TP_CONN_STATUS_DISCONNECTED) + { + guint connections = 0; + /* if there are no connections, then exit. Count the connections + * regardless of their status: we are supposing that if a connection is + * still there, then there is a good reason for it and we won't exit, + * even if it's not active (it might be trying to reconnect) */ + mcd_operation_foreach (MCD_OPERATION (obj), (GFunc)count_connections, + &connections); + if (connections == 0) + mcd_controller_shutdown (MCD_CONTROLLER (obj), "No connections"); + } +} + +static void +mcd_service_disconnect (McdMission *mission) +{ + MCD_MISSION_CLASS (mcd_service_parent_class)->disconnect (mission); + mcd_controller_shutdown (MCD_CONTROLLER (mission), "Disconnected"); +} + +static void +_on_presence_stable (McdPresenceFrame *presence_frame, gboolean is_stable, + McdService *service) +{ + McdServicePrivate *priv = MCD_OBJECT_PRIV (service); + McPresence req_presence; + McStatus status; + + req_presence = mcd_presence_frame_get_requested_presence (presence_frame); + if (is_stable) + { + if (mcd_presence_frame_get_actual_presence (presence_frame) >= + MC_PRESENCE_AVAILABLE) + status = MC_STATUS_CONNECTED; + else + status = MC_STATUS_DISCONNECTED; + } + else + status = MC_STATUS_CONNECTING; + + if (status != priv->last_status) + { + g_signal_emit (service, signals[STATUS_ACTUAL], 0, status, + req_presence); + priv->last_status = status; + } +} + +static void +_on_dispatcher_channel_added (McdDispatcher *dispatcher, + McdChannel *channel, McdService *obj) +{ + /* Nothing to do for now */ +} + +static void +_on_dispatcher_channel_removed (McdDispatcher *dispatcher, + McdChannel *channel, McdService *obj) +{ + const gchar *chan_type; + GQuark chan_type_quark; + gint usage; + + chan_type = mcd_channel_get_channel_type (channel); + chan_type_quark = mcd_channel_get_channel_type_quark (channel); + usage = mcd_dispatcher_get_channel_type_usage (dispatcher, + chan_type_quark); + + /* Signal that the channel count has changed */ + g_signal_emit_by_name (G_OBJECT (obj), + "used-channels-count-changed", + chan_type, usage); +} + +static void +_on_dispatcher_channel_dispatched (McdDispatcher *dispatcher, + McdChannel *channel, + McdService *obj) +{ + const gchar *chan_type; + GQuark chan_type_quark; + gint usage; + + chan_type = mcd_channel_get_channel_type (channel); + chan_type_quark = mcd_channel_get_channel_type_quark (channel); + usage = mcd_dispatcher_get_channel_type_usage (dispatcher, + chan_type_quark); + + /* Signal that the channel count has changed */ + g_signal_emit_by_name (G_OBJECT (obj), + "used-channels-count-changed", + chan_type, usage); +} + +static void +_on_dispatcher_channel_dispatch_failed (McdDispatcher *dispatcher, + McdChannel *channel, GError *error, + McdService *obj) +{ + guint requestor_serial; + gchar *requestor_client_id; + + g_debug ("%s", G_STRFUNC); + g_object_get (channel, "requestor-serial", &requestor_serial, + "requestor-client-id", &requestor_client_id, NULL); + + if (requestor_client_id) + { + g_signal_emit_by_name (obj, "mcd-error", requestor_serial, + requestor_client_id, error->code); + g_free (requestor_client_id); + } + + g_debug ("MC ERROR (channel request): %s", error->message); +} + +static void +mcd_dispose (GObject * obj) +{ + McdServicePrivate *priv; + McdService *self = MCD_OBJECT (obj); + + priv = MCD_OBJECT_PRIV (self); + + if (priv->is_disposed) + { + return; + } + + priv->is_disposed = TRUE; + + if (priv->presence_frame) + { + g_signal_handlers_disconnect_by_func (priv->presence_frame, + _on_account_status_changed, + self); + g_signal_handlers_disconnect_by_func (priv->presence_frame, + _on_account_presence_changed, + self); + g_signal_handlers_disconnect_by_func (priv->presence_frame, + _on_presence_requested, self); + g_signal_handlers_disconnect_by_func (priv->presence_frame, + _on_presence_actual, self); + g_signal_handlers_disconnect_by_func (priv->presence_frame, + _on_status_actual, self); + g_signal_handlers_disconnect_by_func (priv->presence_frame, + _on_presence_stable, self); + g_object_unref (priv->presence_frame); + } + + if (priv->dispatcher) + { + g_signal_handlers_disconnect_by_func (priv->dispatcher, + _on_dispatcher_channel_added, + self); + g_signal_handlers_disconnect_by_func (priv->dispatcher, + _on_dispatcher_channel_removed, + self); + g_signal_handlers_disconnect_by_func (priv->dispatcher, + _on_dispatcher_channel_dispatched, + self); + g_signal_handlers_disconnect_by_func (priv->dispatcher, + _on_dispatcher_channel_dispatch_failed, + self); + g_object_unref (priv->dispatcher); + } + + if (self->main_loop) + { + g_main_loop_quit (self->main_loop); + g_main_loop_unref (self->main_loop); + self->main_loop = NULL; + } + + if (G_OBJECT_CLASS (parent_class)->dispose) + { + G_OBJECT_CLASS (parent_class)->dispose (obj); + } +} + +static void +mcd_service_init (McdService * obj) +{ + McdServicePrivate *priv = MCD_OBJECT_PRIV (obj); + + obj->main_loop = g_main_loop_new (NULL, FALSE); + + priv->last_status = -1; + + g_object_get (obj, + "presence-frame", &priv->presence_frame, + "dispatcher", &priv->dispatcher, NULL); + + /* Setup presence signals */ + g_signal_connect (priv->presence_frame, "status-changed", + G_CALLBACK (_on_account_status_changed), obj); + g_signal_connect (priv->presence_frame, "presence-changed", + G_CALLBACK (_on_account_presence_changed), obj); + g_signal_connect (priv->presence_frame, "presence-requested", + G_CALLBACK (_on_presence_requested), obj); + g_signal_connect (priv->presence_frame, "presence-actual", + G_CALLBACK (_on_presence_actual), obj); + g_signal_connect (priv->presence_frame, "status-actual", + G_CALLBACK (_on_status_actual), obj); + g_signal_connect (priv->presence_frame, "presence-stable", + G_CALLBACK (_on_presence_stable), obj); + + /* Setup dispatcher signals */ + g_signal_connect (priv->dispatcher, "channel-added", + G_CALLBACK (_on_dispatcher_channel_added), obj); + g_signal_connect (priv->dispatcher, "channel-removed", + G_CALLBACK (_on_dispatcher_channel_removed), obj); + g_signal_connect (priv->dispatcher, "dispatched", + G_CALLBACK (_on_dispatcher_channel_dispatched), obj); + g_signal_connect (priv->dispatcher, "dispatch-failed", + G_CALLBACK (_on_dispatcher_channel_dispatch_failed), obj); + + mcd_register_dbus_object (obj); + mcd_debug_print_tree (obj); +} + +static void +mcd_service_class_init (McdServiceClass * self) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (self); + McdMissionClass *mission_class = MCD_MISSION_CLASS (self); + + parent_class = g_type_class_peek_parent (self); + gobject_class->dispose = mcd_dispose; + mission_class->disconnect = mcd_service_disconnect; + + g_type_class_add_private (gobject_class, sizeof (McdServicePrivate)); + + /* AccountStatusChanged signal */ + signals[ACCOUNT_STATUS_CHANGED] = + g_signal_new ("account-status-changed", + G_OBJECT_CLASS_TYPE (self), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, mcd_marshal_VOID__UINT_UINT_UINT_STRING, + G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_UINT, + G_TYPE_UINT, G_TYPE_STRING); + /* libmc request_error signal */ + signals[ERROR] = + g_signal_new ("mcd-error", + G_OBJECT_CLASS_TYPE (self), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, mcd_marshal_VOID__UINT_STRING_UINT, G_TYPE_NONE, + 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT); + /* PresenceStatusRequested signal */ + g_signal_new ("presence-status-requested", + G_OBJECT_CLASS_TYPE (self), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, mcd_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + /* PresenceStatusActual signal */ + g_signal_new ("presence-status-actual", + G_OBJECT_CLASS_TYPE (self), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, mcd_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + /* UsedChannelsCountChanged signal */ + signals[USED_CHANNELS_COUNT_CHANGED] = + g_signal_new ("used-channels-count-changed", + G_OBJECT_CLASS_TYPE (self), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, mcd_marshal_VOID__STRING_UINT, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_UINT); + /* StatusActual signal */ + signals[STATUS_ACTUAL] = + g_signal_new ("status-actual", + G_OBJECT_CLASS_TYPE (self), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, mcd_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (self), + &dbus_glib_mcd_service_object_info); +} + +McdService * +mcd_service_new (void) +{ + McdService *obj; + obj = g_object_new (MCD_TYPE_SERVICE, NULL); + return obj; +} + +void +mcd_service_run (McdService * self) +{ + g_main_loop_run (self->main_loop); +} + +/* FIXME: This is defined twice. The other definition is in + * libmissioncontrol/mission-control.c + */ +GQuark +mission_control_error_quark (void) +{ + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string ("mission-control-quark"); + return quark; +} + diff --git a/src/mcd-service.h b/src/mcd-service.h new file mode 100644 index 00000000..327e78f9 --- /dev/null +++ b/src/mcd-service.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MCD_OBJECT_H +#define MCD_OBJECT_H + +#include <glib.h> +#include <dbus/dbus-glib.h> +#include "mcd-master.h" + +#define MCD_TYPE_SERVICE (mcd_service_get_type ()) + +#define MCD_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), MCD_TYPE_SERVICE, \ + McdService)) + +#define MCD_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \ + ((klass), MCD_TYPE_SERVICE, \ + McdServiceClass)) + +#define MCD_IS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), MCD_TYPE_SERVICE)) + +#define MCD_IS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE \ + ((klass), MCD_TYPE_SERVICE)) + +#define MCD_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), MCD_TYPE_SERVICE, \ + McdServiceClass)) + +typedef struct _McdService McdService; +typedef struct _McdServiceClass McdServiceClass; + +struct _McdServiceClass +{ + McdMasterClass parent_class; +}; + + +struct _McdService +{ + McdMaster parent; + GMainLoop *main_loop; +}; + +GType mcd_service_get_type (void); + +McdService *mcd_service_new (void); +void mcd_service_run (McdService * self); + +#endif diff --git a/src/mcd-signals-marshal.list b/src/mcd-signals-marshal.list new file mode 100644 index 00000000..4ed420f3 --- /dev/null +++ b/src/mcd-signals-marshal.list @@ -0,0 +1,15 @@ +VOID:UINT,UINT,UINT,STRING +VOID:UINT,STRING,UINT +VOID:STRING,UINT +VOID:UINT +VOID:UINT,UINT +VOID:OBJECT,ENUM,STRING +VOID:ENUM,STRING +VOID:OBJECT,ENUM,ENUM +VOID:INT,STRING +VOID:INT +VOID:OBJECT,INT,STRING +VOID:OBJECT,INT,INT +VOID:OBJECT +VOID:OBJECT,POINTER +VOID:BOOLEAN diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 00000000..37fd44c7 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,12 @@ +INCLUDES = $(DBUS_CFLAGS) $(TELEPATHY_CFLAGS) -I$(top_srcdir) \ + -I$(top_srcdir)/src \ + -DMC_DISABLE_DEPRECATED \ + -DLIBDIR="@libdir@" -DLIBVERSION="0" + +if HAVE_TESTS + +bin_PROGRAMS = mc-client +mc_client_SOURCES = mc-client.c +mc_client_LDADD = $(top_srcdir)/libmissioncontrol/libmissioncontrol.la + +endif diff --git a/test/mc-client.c b/test/mc-client.c new file mode 100644 index 00000000..42dac402 --- /dev/null +++ b/test/mc-client.c @@ -0,0 +1,83 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 8 -*- */ +/* + * This file is part of mission-control + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Naba Kumar <naba.kumar@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <glib.h> +#include <dbus/dbus-glib.h> +#include "libmissioncontrol/mission-control.h" + + +static void +on_service_ended (MissionControl *mc) +{ + g_debug ("Mission control has ended"); +} + +static void +account_status_changed_cb (GObject *object, + TelepathyConnectionStatus status, + McPresence presence, + TelepathyConnectionStatusReason reason, + const gchar *account, + gpointer data) +{ + g_debug ("Account status changed: %s, status = %u, presence = %u, reason = %u", + account, status, presence, reason); +} + +static void mc_callback (MissionControl *mc, GError *error, gpointer data) +{ + if (error) + { + g_debug ("%s: got error code %u (%s), data is %p", G_STRFUNC, + error->code, error->message, data); + g_error_free (error); + } + else + g_debug ("%s: data is %p", G_STRFUNC, data); +} + +int main () +{ + MissionControl *mc; + DBusGConnection *dbus_conn = NULL; + GMainLoop *main_loop; + + g_type_init (); + dbus_conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + + mc = mission_control_new (dbus_conn); + g_signal_connect (mc, "ServiceEnded", G_CALLBACK (on_service_ended), NULL); + dbus_g_proxy_connect_signal (DBUS_G_PROXY (mc), + "AccountStatusChanged", + G_CALLBACK (account_status_changed_cb), + NULL, NULL); /* NULL is for a DestroyNotify */ + + mission_control_connect_all_with_default_presence (mc, mc_callback, NULL); + main_loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (main_loop); + + return 0; +} + + diff --git a/xml/Makefile.am b/xml/Makefile.am new file mode 100644 index 00000000..92dca715 --- /dev/null +++ b/xml/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = mcd-dbus-services.xml diff --git a/xml/mcd-dbus-services.xml b/xml/mcd-dbus-services.xml new file mode 100644 index 00000000..0e8ebc9e --- /dev/null +++ b/xml/mcd-dbus-services.xml @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/org/freedesktop/Telepathy/MissionControl"> + <interface name="org.freedesktop.Telepathy.MissionControl"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="mcd_service"/> + <annotation name="org.freedesktop.DBus.GLib.ClientCSymbol" + value="mission_control_dbus"/> + <method name="SetPresence"> + <arg type="u" name="presence" direction="in" /> + <arg type="s" name="message" direction="in" /> + </method> + <method name="GetPresence"> + <arg type="u" direction="out" /> + </method> + <method name="GetPresenceActual"> + <arg type="u" direction="out" /> + </method> + <method name="RequestChannel"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="s" name="account_name" direction="in" /> + <arg type="s" name="type" direction="in" /> + <arg type="u" name="handle" direction="in" /> + <arg type="i" name="handle_type" direction="in" /> + <arg type="u" name="serial" direction="in" /> + </method> + <method name="RequestChannelWithStringHandle"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="s" name="account_name" direction="in" /> + <arg type="s" name="type" direction="in" /> + <arg type="s" name="handle" direction="in" /> + <arg type="i" name="handle_type" direction="in" /> + <arg type="u" name="serial" direction="in" /> + </method> + <method name="CancelChannelRequest"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="u" name="operation_id" direction="in" /> + </method> + <method name="ConnectAllWithDefaultPresence"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + </method> + <method name="GetConnectionStatus"> + <arg type="s" name="account_name" direction="in" /> + <arg type="u" direction="out" /> + </method> + <method name="GetOnlineConnections"> + <arg type="as" direction="out" /> + </method> + <method name="GetConnection"> + <arg type="s" name="account_name" direction="in" /> + <arg type="s" direction="out" /> + <arg type="o" direction="out" /> + </method> + <method name="GetAccountForConnection"> + <arg type="s" direction="out" /> + <arg type="s" name="object_path" direction="in" /> + </method> + <method name="GetUsedChannelsCount"> + <arg type="u" direction="out" /> + <arg type="s" name="type" direction="in" /> + </method> + <method name="GetCurrentStatus"> + <arg type="u" name="status" direction="out" /> + <arg type="u" name="presence" direction="out" /> + <arg type="u" name="requested_presence" direction="out" /> + <arg type="a(suuu)" name="accounts" direction="out" /> + </method> + <method name="RemoteAvatarChanged"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="s" name="object_path" direction="in" /> + <arg type="u" name="contact_id" direction="in" /> + <arg type="s" name="token" direction="in" /> + </method> + <signal name="AccountStatusChanged"> + <arg type="u" name="status" /> + <arg type="u" name="presence" /> + <arg type="u" name="reason" /> + <arg type="s" name="account_id" /> + </signal> + <signal name="McdError"> + <arg type="u" name="serial" /> + <arg type="s" name="client_id" /> + <arg type="u" name="error_id" /> + </signal> + <signal name="PresenceStatusRequested"> + <arg type="u" name="presence" /> + </signal> + <signal name="PresenceStatusActual"> + <arg type="u" name="presence" /> + </signal> + <signal name="UsedChannelsCountChanged"> + <arg type="s" name="type" /> + <arg type="u" name="count" /> + </signal> + <signal name="StatusActual"> + <arg type="u" name="status" /> + <arg type="u" name="presence" /> + </signal> + </interface> +</node> |