summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlli Salli <olli.salli@collabora.co.uk>2007-01-29 15:47:36 +0000
committerOlli Salli <olli.salli@collabora.co.uk>2007-01-29 15:47:36 +0000
commit7a897b917168505139de0c726e96663e2c7ed977 (patch)
treeae8ec0815c2f66152812bdc85439c44bc708cd67
parent36e7ba52d4397763aca609c368bc6f2472b644a1 (diff)
downloadtelepathy-idle-7a897b917168505139de0c726e96663e2c7ed977.tar.gz
Initial import (migration from SF.net SVN)
20070129154736-9db4d-be80727a61507e6581870228122d0d2a7c12995e.gz
-rw-r--r--AUTHORS5
-rw-r--r--COPYING510
-rw-r--r--ChangeLog0
-rw-r--r--INSTALL236
-rw-r--r--Makefile.am4
-rw-r--r--NEWS0
-rw-r--r--README0
-rw-r--r--configure.ac84
-rw-r--r--data/.git-darcs-dir0
-rw-r--r--data/Makefile.am19
-rw-r--r--data/idle.manager19
-rw-r--r--data/org.freedesktop.Telepathy.ConnectionManager.idle.service.in3
-rw-r--r--generate/.git-darcs-dir0
-rw-r--r--generate/Makefile.am0
-rw-r--r--generate/README21
-rw-r--r--generate/do_src.sh27
-rw-r--r--generate/do_xml.sh15
-rw-r--r--generate/idle.def4
-rw-r--r--generate/src/.git-darcs-dir0
-rw-r--r--generate/src/idle-connection-manager-signals-marshal.list1
-rw-r--r--generate/src/idle-connection-manager.c202
-rw-r--r--generate/src/idle-connection-manager.h65
-rw-r--r--generate/src/idle-connection-signals-marshal.list5
-rw-r--r--generate/src/idle-connection.c521
-rw-r--r--generate/src/idle-connection.h81
-rw-r--r--generate/src/idle-im-channel-signals-marshal.list3
-rw-r--r--generate/src/idle-im-channel.c258
-rw-r--r--generate/src/idle-im-channel.h67
-rw-r--r--generate/src/idle-muc-channel-signals-marshal.list10
-rw-r--r--generate/src/idle-muc-channel.c595
-rw-r--r--generate/src/idle-muc-channel.h82
-rw-r--r--generate/src/telepathy-errors.h61
-rw-r--r--generate/xml-modified/.git-darcs-dir0
-rw-r--r--generate/xml-modified/idle-connection-manager.xml23
-rw-r--r--generate/xml-modified/idle-connection.xml100
-rw-r--r--generate/xml-modified/idle-im-channel.xml51
-rw-r--r--generate/xml-modified/idle-muc-channel.xml135
-rw-r--r--generate/xml-pristine/.git-darcs-dir0
-rw-r--r--generate/xml-pristine/idle-connection-manager.xml27
-rw-r--r--generate/xml-pristine/idle-connection.xml113
-rw-r--r--generate/xml-pristine/idle-im-channel.xml60
-rw-r--r--generate/xml-pristine/idle-muc-channel.xml138
-rw-r--r--m4/.git-darcs-dir0
-rw-r--r--m4/Makefile.am4
-rw-r--r--m4/as-ac-expand.m440
-rw-r--r--m4/as-compiler-flag.m433
-rw-r--r--m4/as-version.m466
-rw-r--r--src/.git-darcs-dir0
-rw-r--r--src/Makefile.am112
-rw-r--r--src/gheap.c142
-rw-r--r--src/gheap.h38
-rw-r--r--src/gintset.c353
-rw-r--r--src/gintset.h49
-rw-r--r--src/idle-connection-manager-signals-marshal.list1
-rw-r--r--src/idle-connection-manager.c559
-rw-r--r--src/idle-connection-manager.h64
-rw-r--r--src/idle-connection-signals-marshal.list4
-rw-r--r--src/idle-connection.c5377
-rw-r--r--src/idle-connection.h106
-rw-r--r--src/idle-dns-resolver.c566
-rw-r--r--src/idle-dns-resolver.h65
-rw-r--r--src/idle-handle-set.c195
-rw-r--r--src/idle-handle-set.h50
-rw-r--r--src/idle-handles-private.h53
-rw-r--r--src/idle-handles.c589
-rw-r--r--src/idle-handles.h56
-rw-r--r--src/idle-im-channel-signals-marshal.list4
-rw-r--r--src/idle-im-channel.c814
-rw-r--r--src/idle-im-channel.h69
-rw-r--r--src/idle-muc-channel-signals-marshal.list11
-rw-r--r--src/idle-muc-channel.c3474
-rw-r--r--src/idle-muc-channel.h114
-rw-r--r--src/idle-server-connection-iface-signals-marshal.list1
-rw-r--r--src/idle-server-connection-iface.c98
-rw-r--r--src/idle-server-connection-iface.h87
-rw-r--r--src/idle-server-connection-util.c104
-rw-r--r--src/idle-server-connection-util.h62
-rw-r--r--src/idle-server-connection.c797
-rw-r--r--src/idle-server-connection.h63
-rw-r--r--src/idle-ssl-server-connection.c766
-rw-r--r--src/idle-ssl-server-connection.h63
-rw-r--r--src/idle-version.h26
-rw-r--r--src/idle.c185
-rw-r--r--src/idle.h30
-rw-r--r--src/telepathy-constants.h117
-rw-r--r--src/telepathy-errors.c31
-rw-r--r--src/telepathy-errors.h60
-rw-r--r--src/telepathy-helpers.c63
-rw-r--r--src/telepathy-helpers.h34
-rw-r--r--src/telepathy-interfaces.h65
90 files changed, 19205 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..efa3128
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,5 @@
+Author:
+Olli Salli <olli.salli@collabora.co.uk>
+
+Nokia contact:
+Jussi Laako <jussi.laako@nokia.com>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..2d2d780
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,510 @@
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard. To achieve this, non-free programs must
+be allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least
+ three years, to give the same user the materials specified in
+ Subsection 6a, above, for a charge no more than the cost of
+ performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+ <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.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+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 0000000..e69de29
--- /dev/null
+++ b/ChangeLog
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..cc67d4a
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,4 @@
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = src data m4
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..be01730
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,84 @@
+AC_PREREQ([2.59])
+
+AC_INIT
+AC_CONFIG_MACRO_DIR([m4])
+
+AS_VERSION(telepathy-idle, TELEPATHY_IDLE_VERSION, 0, 0, 4, 0, WERROR="no", WERROR="no")
+
+AM_INIT_AUTOMAKE($PACKAGE, $VERSION)
+
+AM_PROG_LIBTOOL
+AM_CONFIG_HEADER(config.h)
+
+dnl check for tools
+AC_PROG_CC
+AC_PROG_CC_STDC
+AM_PROG_AS
+
+dnl decide on error flags
+AS_COMPILER_FLAG(-Wall, WALL="yes", WALL="no")
+
+if test "x$WALL" = "xyes"; then
+ ERROR_CFLAGS="-Wall"
+
+ if test "x$WERROR" = "xyes"; then
+ AS_COMPILER_FLAG(-Werror,ERROR_CFLAGS="$ERROR_CFLAGS -Werror",ERROR_CFLAGS="$ERROR_CFLAGS")
+ fi
+fi
+
+AC_SUBST(ERROR_CFLAGS)
+
+AC_HEADER_STDC([])
+AC_C_INLINE
+
+dnl GTK docs
+GTK_DOC_CHECK
+
+dnl Check for Glib
+PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.4, gobject-2.0 >= 2.4])
+
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
+GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`
+AC_SUBST(GLIB_GENMARSHAL)
+
+dnl Check for D-Bus
+PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.51, dbus-glib-1 >= 0.51])
+
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+dnl PKG_CHECK_MODULES(SOFIA_TOOLS, [sofia-tools >= 1.12],
+dnl [
+dnl SOFIA_CFLAGS=$SOFIA_TOOLS_CFLAGS
+dnl SOFIA_LIBS=$SOFIA_TOOLS_LIBS
+dnl ],
+dnl [
+dnl PKG_CHECK_MODULES(SOFIA_SIP_UA, [sofia-sip-ua >= 1.12],
+dnl [PKG_CHECK_MODULES(SOFIA_SIP_UA_GLIB, [sofia-sip-ua-glib >= 1.12],
+dnl [
+dnl SOFIA_CFLAGS="${SOFIA_SIP_UA_CFLAGS}${SOFIA_SIP_UA_GLIB_CFLAGS}"
+dnl SOFIA_LIBS="${SOFIA_SIP_UA_LIBS}${SOFIA_SIP_UA_GLIB_LIBS}"
+dnl ]]))
+dnl ])
+
+AC_SUBST(SOFIA_CFLAGS)
+AC_SUBST(SOFIA_LIBS)
+
+dnl Check for OpenSSL
+PKG_CHECK_MODULES(OPENSSL, [openssl >= 0.9.7])
+
+AC_SUBST(OPENSSL_CFLAGS)
+AC_SUBST(OPENSSL_LIBS)
+
+AS_AC_EXPAND(DATADIR, $datadir)
+DBUS_SERVICES_DIR="$DATADIR/dbus-1/services"
+AC_SUBST(DBUS_SERVICES_DIR)
+AC_DEFINE_UNQUOTED(DBUS_SERVICES_DIR, "$DBUS_SERVICES_DIR", [DBus services directory])
+
+AC_OUTPUT( Makefile \
+ src/Makefile \
+ m4/Makefile \
+ data/Makefile \
+)
diff --git a/data/.git-darcs-dir b/data/.git-darcs-dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/data/.git-darcs-dir
diff --git a/data/Makefile.am b/data/Makefile.am
new file mode 100644
index 0000000..72b0a80
--- /dev/null
+++ b/data/Makefile.am
@@ -0,0 +1,19 @@
+# Telepathy manager file
+managerdir = $(datadir)/telepathy/managers
+manager_DATA = idle.manager
+
+# Dbus service file
+BUILT_FILES = $(service_in_files:.service.in=.service)
+
+servicedir = $(DBUS_SERVICES_DIR)
+service_in_files = org.freedesktop.Telepathy.ConnectionManager.idle.service.in
+service_DATA = $(BUILT_FILES)
+CLEANFILES = $(BUILT_FILES)
+
+# Rule to make the service file with bindir expanded
+$(service_DATA): $(service_in_files) Makefile
+ @sed -e "s|\@bindir\@|$(bindir)|" $< > $@
+
+EXTRA_DIST= \
+ ${service_in_files} \
+ ${manager_DATA}
diff --git a/data/idle.manager b/data/idle.manager
new file mode 100644
index 0000000..b2cc6d1
--- /dev/null
+++ b/data/idle.manager
@@ -0,0 +1,19 @@
+[ConnectionManager]
+Name = idle
+BusName = org.freedesktop.Telepathy.ConnectionManager.idle
+ObjectPath = /org/freedesktop/Telepathy/ConnectionManager/idle
+
+[Protocol irc]
+param-account = s required register
+param-server = s required register
+param-fullname = s
+param-port = q
+param-password = s
+param-charset = s
+param-quit-message = s
+param-use-ssl = b
+default-port = 6667
+default-fullname = telepathy-idle user http://telepathy.freedesktop.org
+default-charset = UTF-8
+default-quit-message = So long and thanks for all the IRC - telepathy-idle IRC Connection Manager for Telepathy - http://telepathy.freedesktop.org
+default-use-ssl = false
diff --git a/data/org.freedesktop.Telepathy.ConnectionManager.idle.service.in b/data/org.freedesktop.Telepathy.ConnectionManager.idle.service.in
new file mode 100644
index 0000000..27afc08
--- /dev/null
+++ b/data/org.freedesktop.Telepathy.ConnectionManager.idle.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.Telepathy.ConnectionManager.idle
+Exec=@bindir@/telepathy-idle
diff --git a/generate/.git-darcs-dir b/generate/.git-darcs-dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/generate/.git-darcs-dir
diff --git a/generate/Makefile.am b/generate/Makefile.am
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/generate/Makefile.am
diff --git a/generate/README b/generate/README
new file mode 100644
index 0000000..0f00655
--- /dev/null
+++ b/generate/README
@@ -0,0 +1,21 @@
+Source code in this tree was originally generated from a magical gobject
+generation tool called gengobject.py. It takes D-Bus XML introspection data and
+outputs source, header and other files like signal marshallers. To read the XML
+from generate/xml-modified/ directory, and output new source in the
+generate/src/ directory, where changes can be reveiwed and applied to the src/
+directory by hand, run generate/do_src.sh.
+
+The XML is generated from the Python classes which are the current canonical
+definition of the Telepathy interfaces, according to the definitions in
+xml.def. Some manual modifications are necessary, to mark asynchronous methods,
+and to remove the Introspect methods which are implemented automatically by the
+bindings, so the pristine XML is kept in generate/xml-pristine/ and changed
+applied by hand to generate/xml-modified/. To generate this pristine XML, run
+generate/do_xml.sh.
+
+In both cases, applying the changes to the generated XML or src can be as easy
+as:
+ darcs diff -u generate/src | patch -d src
+Patch does a much better job of applying generated changes to the modified code
+than darcs did when pulling patches from seperate trees (xml -> generate ->
+live).
diff --git a/generate/do_src.sh b/generate/do_src.sh
new file mode 100644
index 0000000..08a0130
--- /dev/null
+++ b/generate/do_src.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+if [ `basename $PWD` == "generate" ]; then
+ TP=${TELEPATHY_PYTHON:=$PWD/../../telepathy-python}
+else
+ TP=${TELEPATHY_PYTHON:=$PWD/../telepathy-python}
+fi
+
+export PYTHONPATH=$TP:$PYTHONPATH
+
+test -d generate && cd generate
+cd src
+
+echo Generating IdleConnectionManager files ...
+python2.4 $TP/tools/gengobject.py ../xml-modified/idle-connection-manager.xml IdleConnectionManager
+
+echo Generating IdleConnection files ...
+python2.4 $TP/tools/gengobject.py ../xml-modified/idle-connection.xml IdleConnection
+
+echo Generating IdleIMChannel files ...
+python2.4 $TP/tools/gengobject.py ../xml-modified/idle-im-channel.xml IdleIMChannel
+
+echo Generating IdleMUCChannel files ...
+python2.4 $TP/tools/gengobject.py ../xml-modified/idle-muc-channel.xml IdleMUCChannel
+
+echo Generating error enums ...
+python2.4 $TP/tools/generrors.py
diff --git a/generate/do_xml.sh b/generate/do_xml.sh
new file mode 100644
index 0000000..fb20596
--- /dev/null
+++ b/generate/do_xml.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+if [ `basename $PWD` == "generate" ]; then
+ TP=${TELEPATHY_PYTHON:=$PWD/../../telepathy-python}
+else
+ TP=${TELEPATHY_PYTHON:=$PWD/../telepathy-python}
+fi
+
+export PYTHONPATH=$TP:$PYTHONPATH
+
+test -d generate && cd generate
+cd xml-pristine
+
+echo "Generating pristine XML in generate/xml-pristine..."
+python2.4 $TP/tools/genxml.py ../idle.def
diff --git a/generate/idle.def b/generate/idle.def
new file mode 100644
index 0000000..4adb05b
--- /dev/null
+++ b/generate/idle.def
@@ -0,0 +1,4 @@
+idle-connection-manager.xml IdleConnectionManager ConnectionManager
+idle-connection.xml IdleConnection Connection, ConnectionInterfaceCapabilities, ConnectionInterfacePresence, ConnectionInterfaceRenaming
+idle-im-channel.xml IdleIMChannel ChannelTypeText
+idle-muc-channel.xml IdleMUCChannel ChannelTypeText, ChannelInterfaceGroup, ChannelInterfacePassword, PropertiesInterface
diff --git a/generate/src/.git-darcs-dir b/generate/src/.git-darcs-dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/generate/src/.git-darcs-dir
diff --git a/generate/src/idle-connection-manager-signals-marshal.list b/generate/src/idle-connection-manager-signals-marshal.list
new file mode 100644
index 0000000..41e4027
--- /dev/null
+++ b/generate/src/idle-connection-manager-signals-marshal.list
@@ -0,0 +1 @@
+VOID:STRING,STRING,STRING
diff --git a/generate/src/idle-connection-manager.c b/generate/src/idle-connection-manager.c
new file mode 100644
index 0000000..a562b75
--- /dev/null
+++ b/generate/src/idle-connection-manager.c
@@ -0,0 +1,202 @@
+/*
+ * idle-connection-manager.c - Source for IdleConnectionManager
+ * Copyright (C) 2005 Collabora Ltd.
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "idle-connection-manager.h"
+#include "idle-connection-manager-signals-marshal.h"
+
+#include "idle-connection-manager-glue.h"
+
+G_DEFINE_TYPE(IdleConnectionManager, idle_connection_manager, G_TYPE_OBJECT)
+
+/* signal enum */
+enum
+{
+ NEW_CONNECTION,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/* private structure */
+typedef struct _IdleConnectionManagerPrivate IdleConnectionManagerPrivate;
+
+struct _IdleConnectionManagerPrivate
+{
+ gboolean dispose_has_run;
+};
+
+#define IDLE_CONNECTION_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_CONNECTION_MANAGER, IdleConnectionManagerPrivate))
+
+static void
+idle_connection_manager_init (IdleConnectionManager *obj)
+{
+ IdleConnectionManagerPrivate *priv = IDLE_CONNECTION_MANAGER_GET_PRIVATE (obj);
+
+ /* allocate any data required by the object here */
+}
+
+static void idle_connection_manager_dispose (GObject *object);
+static void idle_connection_manager_finalize (GObject *object);
+
+static void
+idle_connection_manager_class_init (IdleConnectionManagerClass *idle_connection_manager_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (idle_connection_manager_class);
+
+ g_type_class_add_private (idle_connection_manager_class, sizeof (IdleConnectionManagerPrivate));
+
+ object_class->dispose = idle_connection_manager_dispose;
+ object_class->finalize = idle_connection_manager_finalize;
+
+ signals[NEW_CONNECTION] =
+ g_signal_new ("new-connection",
+ G_OBJECT_CLASS_TYPE (idle_connection_manager_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_manager_marshal_VOID__STRING_STRING_STRING,
+ G_TYPE_NONE, 3, G_TYPE_STRING, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (idle_connection_manager_class), &dbus_glib_idle_connection_manager_object_info);
+}
+
+void
+idle_connection_manager_dispose (GObject *object)
+{
+ IdleConnectionManager *self = IDLE_CONNECTION_MANAGER (object);
+ IdleConnectionManagerPrivate *priv = IDLE_CONNECTION_MANAGER_GET_PRIVATE (self);
+
+ if (priv->dispose_has_run)
+ return;
+
+ priv->dispose_has_run = TRUE;
+
+ /* release any references held by the object here */
+
+ if (G_OBJECT_CLASS (idle_connection_manager_parent_class)->dispose)
+ G_OBJECT_CLASS (idle_connection_manager_parent_class)->dispose (object);
+}
+
+void
+idle_connection_manager_finalize (GObject *object)
+{
+ IdleConnectionManager *self = IDLE_CONNECTION_MANAGER (object);
+ IdleConnectionManagerPrivate *priv = IDLE_CONNECTION_MANAGER_GET_PRIVATE (self);
+
+ /* free any data held directly by the object here */
+
+ G_OBJECT_CLASS (idle_connection_manager_parent_class)->finalize (object);
+}
+
+
+
+/**
+ * idle_connection_manager_connect
+ *
+ * Implements DBus method Connect
+ * on interface org.freedesktop.Telepathy.ConnectionManager
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_manager_connect (IdleConnectionManager *obj, const gchar * proto, GHashTable * parameters, gchar ** ret, gchar ** ret1, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_manager_get_mandatory_parameters
+ *
+ * Implements DBus method GetMandatoryParameters
+ * on interface org.freedesktop.Telepathy.ConnectionManager
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_manager_get_mandatory_parameters (IdleConnectionManager *obj, const gchar * proto, GHashTable ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_manager_get_optional_parameters
+ *
+ * Implements DBus method GetOptionalParameters
+ * on interface org.freedesktop.Telepathy.ConnectionManager
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_manager_get_optional_parameters (IdleConnectionManager *obj, const gchar * proto, GHashTable ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_manager_get_parameter_defaults
+ *
+ * Implements DBus method GetParameterDefaults
+ * on interface org.freedesktop.Telepathy.ConnectionManager
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_manager_get_parameter_defaults (IdleConnectionManager *obj, const gchar * proto, GHashTable ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_manager_list_protocols
+ *
+ * Implements DBus method ListProtocols
+ * on interface org.freedesktop.Telepathy.ConnectionManager
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_manager_list_protocols (IdleConnectionManager *obj, gchar *** ret, GError **error)
+{
+ return TRUE;
+}
+
diff --git a/generate/src/idle-connection-manager.h b/generate/src/idle-connection-manager.h
new file mode 100644
index 0000000..715c5fe
--- /dev/null
+++ b/generate/src/idle-connection-manager.h
@@ -0,0 +1,65 @@
+/*
+ * idle-connection-manager.h - Header for IdleConnectionManager
+ * Copyright (C) 2005 Collabora Ltd.
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __IDLE_CONNECTION_MANAGER_H__
+#define __IDLE_CONNECTION_MANAGER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdleConnectionManager IdleConnectionManager;
+typedef struct _IdleConnectionManagerClass IdleConnectionManagerClass;
+
+struct _IdleConnectionManagerClass {
+ GObjectClass parent_class;
+};
+
+struct _IdleConnectionManager {
+ GObject parent;
+};
+
+GType idle_connection_manager_get_type(void);
+
+/* TYPE MACROS */
+#define IDLE_TYPE_CONNECTION_MANAGER \
+ (idle_connection_manager_get_type())
+#define IDLE_CONNECTION_MANAGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_CONNECTION_MANAGER, IdleConnectionManager))
+#define IDLE_CONNECTION_MANAGER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_CONNECTION_MANAGER, IdleConnectionManagerClass))
+#define IDLE_IS_CONNECTION_MANAGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_CONNECTION_MANAGER))
+#define IDLE_IS_CONNECTION_MANAGER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_CONNECTION_MANAGER))
+#define IDLE_CONNECTION_MANAGER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_CONNECTION_MANAGER, IdleConnectionManagerClass))
+
+
+gboolean idle_connection_manager_connect (IdleConnectionManager *obj, const gchar * proto, GHashTable * parameters, gchar ** ret, gchar ** ret1, GError **error);
+gboolean idle_connection_manager_get_mandatory_parameters (IdleConnectionManager *obj, const gchar * proto, GHashTable ** ret, GError **error);
+gboolean idle_connection_manager_get_optional_parameters (IdleConnectionManager *obj, const gchar * proto, GHashTable ** ret, GError **error);
+gboolean idle_connection_manager_get_parameter_defaults (IdleConnectionManager *obj, const gchar * proto, GHashTable ** ret, GError **error);
+gboolean idle_connection_manager_list_protocols (IdleConnectionManager *obj, gchar *** ret, GError **error);
+
+
+G_END_DECLS
+
+#endif /* #ifndef __IDLE_CONNECTION_MANAGER_H__*/
diff --git a/generate/src/idle-connection-signals-marshal.list b/generate/src/idle-connection-signals-marshal.list
new file mode 100644
index 0000000..2f728e5
--- /dev/null
+++ b/generate/src/idle-connection-signals-marshal.list
@@ -0,0 +1,5 @@
+VOID:INT,BOXED,BOXED
+VOID:STRING,STRING,INT,INT,BOOLEAN
+VOID:BOXED
+VOID:INT,INT
+VOID:INT,INT
diff --git a/generate/src/idle-connection.c b/generate/src/idle-connection.c
new file mode 100644
index 0000000..83af0a3
--- /dev/null
+++ b/generate/src/idle-connection.c
@@ -0,0 +1,521 @@
+/*
+ * idle-connection.c - Source for IdleConnection
+ * Copyright (C) 2005 Collabora Ltd.
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "idle-connection.h"
+#include "idle-connection-signals-marshal.h"
+
+#include "idle-connection-glue.h"
+
+G_DEFINE_TYPE(IdleConnection, idle_connection, G_TYPE_OBJECT)
+
+/* signal enum */
+enum
+{
+ CAPABILITIES_CHANGED,
+ NEW_CHANNEL,
+ PRESENCE_UPDATE,
+ RENAMED,
+ STATUS_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/* private structure */
+typedef struct _IdleConnectionPrivate IdleConnectionPrivate;
+
+struct _IdleConnectionPrivate
+{
+ gboolean dispose_has_run;
+};
+
+#define IDLE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_CONNECTION, IdleConnectionPrivate))
+
+static void
+idle_connection_init (IdleConnection *obj)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (obj);
+
+ /* allocate any data required by the object here */
+}
+
+static void idle_connection_dispose (GObject *object);
+static void idle_connection_finalize (GObject *object);
+
+static void
+idle_connection_class_init (IdleConnectionClass *idle_connection_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (idle_connection_class);
+
+ g_type_class_add_private (idle_connection_class, sizeof (IdleConnectionPrivate));
+
+ object_class->dispose = idle_connection_dispose;
+ object_class->finalize = idle_connection_finalize;
+
+ signals[CAPABILITIES_CHANGED] =
+ g_signal_new ("capabilities-changed",
+ G_OBJECT_CLASS_TYPE (idle_connection_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_marshal_VOID__INT_BOXED_BOXED,
+ G_TYPE_NONE, 3, G_TYPE_UINT, (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID)))), (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID)))));
+
+ signals[NEW_CHANNEL] =
+ g_signal_new ("new-channel",
+ G_OBJECT_CLASS_TYPE (idle_connection_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_marshal_VOID__STRING_STRING_INT_INT_BOOLEAN,
+ G_TYPE_NONE, 5, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN);
+
+ signals[PRESENCE_UPDATE] =
+ g_signal_new ("presence-update",
+ G_OBJECT_CLASS_TYPE (idle_connection_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, (dbus_g_type_get_map ("GHashTable", G_TYPE_UINT, (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)))), G_TYPE_INVALID)))));
+
+ signals[RENAMED] =
+ g_signal_new ("renamed",
+ G_OBJECT_CLASS_TYPE (idle_connection_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+ signals[STATUS_CHANGED] =
+ g_signal_new ("status-changed",
+ G_OBJECT_CLASS_TYPE (idle_connection_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (idle_connection_class), &dbus_glib_idle_connection_object_info);
+}
+
+void
+idle_connection_dispose (GObject *object)
+{
+ IdleConnection *self = IDLE_CONNECTION (object);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (self);
+
+ if (priv->dispose_has_run)
+ return;
+
+ priv->dispose_has_run = TRUE;
+
+ /* release any references held by the object here */
+
+ if (G_OBJECT_CLASS (idle_connection_parent_class)->dispose)
+ G_OBJECT_CLASS (idle_connection_parent_class)->dispose (object);
+}
+
+void
+idle_connection_finalize (GObject *object)
+{
+ IdleConnection *self = IDLE_CONNECTION (object);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (self);
+
+ /* free any data held directly by the object here */
+
+ G_OBJECT_CLASS (idle_connection_parent_class)->finalize (object);
+}
+
+
+
+/**
+ * idle_connection_add_status
+ *
+ * Implements DBus method AddStatus
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_add_status (IdleConnection *obj, const gchar * status, GHashTable * parms, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_advertise_capabilities
+ *
+ * Implements DBus method AdvertiseCapabilities
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_advertise_capabilities (IdleConnection *obj, const gchar ** add, const gchar ** remove, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_clear_status
+ *
+ * Implements DBus method ClearStatus
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_clear_status (IdleConnection *obj, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_disconnect
+ *
+ * Implements DBus method Disconnect
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_disconnect (IdleConnection *obj, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_get_capabilities
+ *
+ * Implements DBus method GetCapabilities
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_capabilities (IdleConnection *obj, guint handle, GPtrArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_get_interfaces
+ *
+ * Implements DBus method GetInterfaces
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_interfaces (IdleConnection *obj, gchar *** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_get_protocol
+ *
+ * Implements DBus method GetProtocol
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_protocol (IdleConnection *obj, gchar ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_get_self_handle
+ *
+ * Implements DBus method GetSelfHandle
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_self_handle (IdleConnection *obj, guint* ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_get_status
+ *
+ * Implements DBus method GetStatus
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_status (IdleConnection *obj, guint* ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_get_statuses
+ *
+ * Implements DBus method GetStatuses
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_statuses (IdleConnection *obj, GHashTable ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_hold_handle
+ *
+ * Implements DBus method HoldHandle
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @context: The DBUS invocation context to use to return values
+ * or throw an error.
+ */
+gboolean idle_connection_hold_handle (IdleConnection *obj, guint handle_type, guint handle, DBusGMethodInvocation *context)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_inspect_handle
+ *
+ * Implements DBus method InspectHandle
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_inspect_handle (IdleConnection *obj, guint handle_type, guint handle, gchar ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_list_channels
+ *
+ * Implements DBus method ListChannels
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_list_channels (IdleConnection *obj, GPtrArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_release_handle
+ *
+ * Implements DBus method ReleaseHandle
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @context: The DBUS invocation context to use to return values
+ * or throw an error.
+ */
+gboolean idle_connection_release_handle (IdleConnection *obj, guint handle_type, guint handle, DBusGMethodInvocation *context)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_remove_status
+ *
+ * Implements DBus method RemoveStatus
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_remove_status (IdleConnection *obj, const gchar * status, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_request_channel
+ *
+ * Implements DBus method RequestChannel
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_request_channel (IdleConnection *obj, const gchar * type, guint handle_type, guint handle, gboolean suppress_handler, gchar ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_request_handle
+ *
+ * Implements DBus method RequestHandle
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @context: The DBUS invocation context to use to return values
+ * or throw an error.
+ */
+gboolean idle_connection_request_handle (IdleConnection *obj, guint handle_type, const gchar * name, DBusGMethodInvocation *context)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_request_presence
+ *
+ * Implements DBus method RequestPresence
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_request_presence (IdleConnection *obj, const GArray * contacts, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_request_rename
+ *
+ * Implements DBus method RequestRename
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Renaming
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_request_rename (IdleConnection *obj, const gchar * name, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_set_last_activity_time
+ *
+ * Implements DBus method SetLastActivityTime
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_set_last_activity_time (IdleConnection *obj, guint time, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_set_status
+ *
+ * Implements DBus method SetStatus
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_set_status (IdleConnection *obj, GHashTable * statuses, GError **error)
+{
+ return TRUE;
+}
+
diff --git a/generate/src/idle-connection.h b/generate/src/idle-connection.h
new file mode 100644
index 0000000..4a503ad
--- /dev/null
+++ b/generate/src/idle-connection.h
@@ -0,0 +1,81 @@
+/*
+ * idle-connection.h - Header for IdleConnection
+ * Copyright (C) 2005 Collabora Ltd.
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __IDLE_CONNECTION_H__
+#define __IDLE_CONNECTION_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdleConnection IdleConnection;
+typedef struct _IdleConnectionClass IdleConnectionClass;
+
+struct _IdleConnectionClass {
+ GObjectClass parent_class;
+};
+
+struct _IdleConnection {
+ GObject parent;
+};
+
+GType idle_connection_get_type(void);
+
+/* TYPE MACROS */
+#define IDLE_TYPE_CONNECTION \
+ (idle_connection_get_type())
+#define IDLE_CONNECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_CONNECTION, IdleConnection))
+#define IDLE_CONNECTION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_CONNECTION, IdleConnectionClass))
+#define IDLE_IS_CONNECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_CONNECTION))
+#define IDLE_IS_CONNECTION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_CONNECTION))
+#define IDLE_CONNECTION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_CONNECTION, IdleConnectionClass))
+
+
+gboolean idle_connection_add_status (IdleConnection *obj, const gchar * status, GHashTable * parms, GError **error);
+gboolean idle_connection_advertise_capabilities (IdleConnection *obj, const gchar ** add, const gchar ** remove, GError **error);
+gboolean idle_connection_clear_status (IdleConnection *obj, GError **error);
+gboolean idle_connection_disconnect (IdleConnection *obj, GError **error);
+gboolean idle_connection_get_capabilities (IdleConnection *obj, guint handle, GPtrArray ** ret, GError **error);
+gboolean idle_connection_get_interfaces (IdleConnection *obj, gchar *** ret, GError **error);
+gboolean idle_connection_get_protocol (IdleConnection *obj, gchar ** ret, GError **error);
+gboolean idle_connection_get_self_handle (IdleConnection *obj, guint* ret, GError **error);
+gboolean idle_connection_get_status (IdleConnection *obj, guint* ret, GError **error);
+gboolean idle_connection_get_statuses (IdleConnection *obj, GHashTable ** ret, GError **error);
+gboolean idle_connection_hold_handle (IdleConnection *obj, guint handle_type, guint handle, DBusGMethodInvocation *context);
+gboolean idle_connection_inspect_handle (IdleConnection *obj, guint handle_type, guint handle, gchar ** ret, GError **error);
+gboolean idle_connection_list_channels (IdleConnection *obj, GPtrArray ** ret, GError **error);
+gboolean idle_connection_release_handle (IdleConnection *obj, guint handle_type, guint handle, DBusGMethodInvocation *context);
+gboolean idle_connection_remove_status (IdleConnection *obj, const gchar * status, GError **error);
+gboolean idle_connection_request_channel (IdleConnection *obj, const gchar * type, guint handle_type, guint handle, gboolean suppress_handler, gchar ** ret, GError **error);
+gboolean idle_connection_request_handle (IdleConnection *obj, guint handle_type, const gchar * name, DBusGMethodInvocation *context);
+gboolean idle_connection_request_presence (IdleConnection *obj, const GArray * contacts, GError **error);
+gboolean idle_connection_request_rename (IdleConnection *obj, const gchar * name, GError **error);
+gboolean idle_connection_set_last_activity_time (IdleConnection *obj, guint time, GError **error);
+gboolean idle_connection_set_status (IdleConnection *obj, GHashTable * statuses, GError **error);
+
+
+G_END_DECLS
+
+#endif /* #ifndef __IDLE_CONNECTION_H__*/
diff --git a/generate/src/idle-im-channel-signals-marshal.list b/generate/src/idle-im-channel-signals-marshal.list
new file mode 100644
index 0000000..b974b8a
--- /dev/null
+++ b/generate/src/idle-im-channel-signals-marshal.list
@@ -0,0 +1,3 @@
+VOID:VOID
+VOID:INT,INT,INT,INT,STRING
+VOID:INT,INT,STRING
diff --git a/generate/src/idle-im-channel.c b/generate/src/idle-im-channel.c
new file mode 100644
index 0000000..a5bf693
--- /dev/null
+++ b/generate/src/idle-im-channel.c
@@ -0,0 +1,258 @@
+/*
+ * idle-im-channel.c - Source for IdleIMChannel
+ * Copyright (C) 2005 Collabora Ltd.
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "idle-im-channel.h"
+#include "idle-im-channel-signals-marshal.h"
+
+#include "idle-im-channel-glue.h"
+
+G_DEFINE_TYPE(IdleIMChannel, idle_im_channel, G_TYPE_OBJECT)
+
+/* signal enum */
+enum
+{
+ CLOSED,
+ RECEIVED,
+ SENT,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/* private structure */
+typedef struct _IdleIMChannelPrivate IdleIMChannelPrivate;
+
+struct _IdleIMChannelPrivate
+{
+ gboolean dispose_has_run;
+};
+
+#define IDLE_IM_CHANNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_IM_CHANNEL, IdleIMChannelPrivate))
+
+static void
+idle_im_channel_init (IdleIMChannel *obj)
+{
+ IdleIMChannelPrivate *priv = IDLE_IM_CHANNEL_GET_PRIVATE (obj);
+
+ /* allocate any data required by the object here */
+}
+
+static void idle_im_channel_dispose (GObject *object);
+static void idle_im_channel_finalize (GObject *object);
+
+static void
+idle_im_channel_class_init (IdleIMChannelClass *idle_im_channel_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (idle_im_channel_class);
+
+ g_type_class_add_private (idle_im_channel_class, sizeof (IdleIMChannelPrivate));
+
+ object_class->dispose = idle_im_channel_dispose;
+ object_class->finalize = idle_im_channel_finalize;
+
+ signals[CLOSED] =
+ g_signal_new ("closed",
+ G_OBJECT_CLASS_TYPE (idle_im_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_im_channel_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[RECEIVED] =
+ g_signal_new ("received",
+ G_OBJECT_CLASS_TYPE (idle_im_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_im_channel_marshal_VOID__INT_INT_INT_INT_STRING,
+ G_TYPE_NONE, 5, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ signals[SENT] =
+ g_signal_new ("sent",
+ G_OBJECT_CLASS_TYPE (idle_im_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_im_channel_marshal_VOID__INT_INT_STRING,
+ G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (idle_im_channel_class), &dbus_glib_idle_im_channel_object_info);
+}
+
+void
+idle_im_channel_dispose (GObject *object)
+{
+ IdleIMChannel *self = IDLE_IM_CHANNEL (object);
+ IdleIMChannelPrivate *priv = IDLE_IM_CHANNEL_GET_PRIVATE (self);
+
+ if (priv->dispose_has_run)
+ return;
+
+ priv->dispose_has_run = TRUE;
+
+ /* release any references held by the object here */
+
+ if (G_OBJECT_CLASS (idle_im_channel_parent_class)->dispose)
+ G_OBJECT_CLASS (idle_im_channel_parent_class)->dispose (object);
+}
+
+void
+idle_im_channel_finalize (GObject *object)
+{
+ IdleIMChannel *self = IDLE_IM_CHANNEL (object);
+ IdleIMChannelPrivate *priv = IDLE_IM_CHANNEL_GET_PRIVATE (self);
+
+ /* free any data held directly by the object here */
+
+ G_OBJECT_CLASS (idle_im_channel_parent_class)->finalize (object);
+}
+
+
+
+/**
+ * idle_im_channel_acknowledge_pending_message
+ *
+ * Implements DBus method AcknowledgePendingMessage
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_acknowledge_pending_message (IdleIMChannel *obj, guint id, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_close
+ *
+ * Implements DBus method Close
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_close (IdleIMChannel *obj, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_get_channel_type
+ *
+ * Implements DBus method GetChannelType
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_get_channel_type (IdleIMChannel *obj, gchar ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_get_handle
+ *
+ * Implements DBus method GetHandle
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_get_handle (IdleIMChannel *obj, guint* ret, guint* ret1, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_get_interfaces
+ *
+ * Implements DBus method GetInterfaces
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_get_interfaces (IdleIMChannel *obj, gchar *** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_list_pending_messages
+ *
+ * Implements DBus method ListPendingMessages
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_list_pending_messages (IdleIMChannel *obj, GPtrArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_send
+ *
+ * Implements DBus method Send
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_send (IdleIMChannel *obj, guint type, const gchar * text, GError **error)
+{
+ return TRUE;
+}
+
diff --git a/generate/src/idle-im-channel.h b/generate/src/idle-im-channel.h
new file mode 100644
index 0000000..6623dbf
--- /dev/null
+++ b/generate/src/idle-im-channel.h
@@ -0,0 +1,67 @@
+/*
+ * idle-im-channel.h - Header for IdleIMChannel
+ * Copyright (C) 2005 Collabora Ltd.
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __IDLE_IM_CHANNEL_H__
+#define __IDLE_IM_CHANNEL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdleIMChannel IdleIMChannel;
+typedef struct _IdleIMChannelClass IdleIMChannelClass;
+
+struct _IdleIMChannelClass {
+ GObjectClass parent_class;
+};
+
+struct _IdleIMChannel {
+ GObject parent;
+};
+
+GType idle_im_channel_get_type(void);
+
+/* TYPE MACROS */
+#define IDLE_TYPE_IM_CHANNEL \
+ (idle_im_channel_get_type())
+#define IDLE_IM_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_IM_CHANNEL, IdleIMChannel))
+#define IDLE_IM_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_IM_CHANNEL, IdleIMChannelClass))
+#define IDLE_IS_IM_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_IM_CHANNEL))
+#define IDLE_IS_IM_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_IM_CHANNEL))
+#define IDLE_IM_CHANNEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_IM_CHANNEL, IdleIMChannelClass))
+
+
+gboolean idle_im_channel_acknowledge_pending_message (IdleIMChannel *obj, guint id, GError **error);
+gboolean idle_im_channel_close (IdleIMChannel *obj, GError **error);
+gboolean idle_im_channel_get_channel_type (IdleIMChannel *obj, gchar ** ret, GError **error);
+gboolean idle_im_channel_get_handle (IdleIMChannel *obj, guint* ret, guint* ret1, GError **error);
+gboolean idle_im_channel_get_interfaces (IdleIMChannel *obj, gchar *** ret, GError **error);
+gboolean idle_im_channel_list_pending_messages (IdleIMChannel *obj, GPtrArray ** ret, GError **error);
+gboolean idle_im_channel_send (IdleIMChannel *obj, guint type, const gchar * text, GError **error);
+
+
+G_END_DECLS
+
+#endif /* #ifndef __IDLE_IM_CHANNEL_H__*/
diff --git a/generate/src/idle-muc-channel-signals-marshal.list b/generate/src/idle-muc-channel-signals-marshal.list
new file mode 100644
index 0000000..a81dede
--- /dev/null
+++ b/generate/src/idle-muc-channel-signals-marshal.list
@@ -0,0 +1,10 @@
+VOID:VOID
+VOID:INT,INT
+VOID:VOID
+VOID:STRING,BOXED,BOXED,BOXED,BOXED
+VOID:INT,INT
+VOID:BOXED
+VOID:BOXED
+VOID:INT,INT,INT,INT,STRING
+VOID:INT,INT,INT,STRING
+VOID:INT,INT,STRING
diff --git a/generate/src/idle-muc-channel.c b/generate/src/idle-muc-channel.c
new file mode 100644
index 0000000..ae814d2
--- /dev/null
+++ b/generate/src/idle-muc-channel.c
@@ -0,0 +1,595 @@
+/*
+ * idle-muc-channel.c - Source for IdleMUCChannel
+ * Copyright (C) 2005 Collabora Ltd.
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "idle-muc-channel.h"
+#include "idle-muc-channel-signals-marshal.h"
+
+#include "idle-muc-channel-glue.h"
+
+G_DEFINE_TYPE(IdleMUCChannel, idle_muc_channel, G_TYPE_OBJECT)
+
+/* signal enum */
+enum
+{
+ CLOSED,
+ GROUP_FLAGS_CHANGED,
+ LOST_MESSAGE,
+ MEMBERS_CHANGED,
+ PASSWORD_FLAGS_CHANGED,
+ PROPERTIES_CHANGED,
+ PROPERTY_FLAGS_CHANGED,
+ RECEIVED,
+ SEND_ERROR,
+ SENT,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/* private structure */
+typedef struct _IdleMUCChannelPrivate IdleMUCChannelPrivate;
+
+struct _IdleMUCChannelPrivate
+{
+ gboolean dispose_has_run;
+};
+
+#define IDLE_MUC_CHANNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_MUC_CHANNEL, IdleMUCChannelPrivate))
+
+static void
+idle_muc_channel_init (IdleMUCChannel *obj)
+{
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE (obj);
+
+ /* allocate any data required by the object here */
+}
+
+static void idle_muc_channel_dispose (GObject *object);
+static void idle_muc_channel_finalize (GObject *object);
+
+static void
+idle_muc_channel_class_init (IdleMUCChannelClass *idle_muc_channel_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (idle_muc_channel_class);
+
+ g_type_class_add_private (idle_muc_channel_class, sizeof (IdleMUCChannelPrivate));
+
+ object_class->dispose = idle_muc_channel_dispose;
+ object_class->finalize = idle_muc_channel_finalize;
+
+ signals[CLOSED] =
+ g_signal_new ("closed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[GROUP_FLAGS_CHANGED] =
+ g_signal_new ("group-flags-changed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+ signals[LOST_MESSAGE] =
+ g_signal_new ("lost-message",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[MEMBERS_CHANGED] =
+ g_signal_new ("members-changed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__STRING_BOXED_BOXED_BOXED_BOXED,
+ G_TYPE_NONE, 5, G_TYPE_STRING, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY);
+
+ signals[PASSWORD_FLAGS_CHANGED] =
+ g_signal_new ("password-flags-changed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+ signals[PROPERTIES_CHANGED] =
+ g_signal_new ("properties-changed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_VALUE, G_TYPE_INVALID)))));
+
+ signals[PROPERTY_FLAGS_CHANGED] =
+ g_signal_new ("property-flags-changed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID)))));
+
+ signals[RECEIVED] =
+ g_signal_new ("received",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT_INT_INT_INT_STRING,
+ G_TYPE_NONE, 5, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ signals[SEND_ERROR] =
+ g_signal_new ("send-error",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT_INT_INT_STRING,
+ G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ signals[SENT] =
+ g_signal_new ("sent",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT_INT_STRING,
+ G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (idle_muc_channel_class), &dbus_glib_idle_muc_channel_object_info);
+}
+
+void
+idle_muc_channel_dispose (GObject *object)
+{
+ IdleMUCChannel *self = IDLE_MUC_CHANNEL (object);
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE (self);
+
+ if (priv->dispose_has_run)
+ return;
+
+ priv->dispose_has_run = TRUE;
+
+ /* release any references held by the object here */
+
+ if (G_OBJECT_CLASS (idle_muc_channel_parent_class)->dispose)
+ G_OBJECT_CLASS (idle_muc_channel_parent_class)->dispose (object);
+}
+
+void
+idle_muc_channel_finalize (GObject *object)
+{
+ IdleMUCChannel *self = IDLE_MUC_CHANNEL (object);
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE (self);
+
+ /* free any data held directly by the object here */
+
+ G_OBJECT_CLASS (idle_muc_channel_parent_class)->finalize (object);
+}
+
+
+
+/**
+ * idle_muc_channel_acknowledge_pending_message
+ *
+ * Implements DBus method AcknowledgePendingMessage
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_acknowledge_pending_message (IdleMUCChannel *obj, guint id, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_add_members
+ *
+ * Implements DBus method AddMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_add_members (IdleMUCChannel *obj, const GArray * contacts, const gchar * message, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_close
+ *
+ * Implements DBus method Close
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_close (IdleMUCChannel *obj, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_all_members
+ *
+ * Implements DBus method GetAllMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_all_members (IdleMUCChannel *obj, GArray ** ret, GArray ** ret1, GArray ** ret2, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_channel_type
+ *
+ * Implements DBus method GetChannelType
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_channel_type (IdleMUCChannel *obj, gchar ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_group_flags
+ *
+ * Implements DBus method GetGroupFlags
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_group_flags (IdleMUCChannel *obj, guint* ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_handle
+ *
+ * Implements DBus method GetHandle
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_handle (IdleMUCChannel *obj, guint* ret, guint* ret1, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_handle_owners
+ *
+ * Implements DBus method GetHandleOwners
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_handle_owners (IdleMUCChannel *obj, const GArray * handles, GArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_interfaces
+ *
+ * Implements DBus method GetInterfaces
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_interfaces (IdleMUCChannel *obj, gchar *** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_local_pending_members
+ *
+ * Implements DBus method GetLocalPendingMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_local_pending_members (IdleMUCChannel *obj, GArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_members
+ *
+ * Implements DBus method GetMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_members (IdleMUCChannel *obj, GArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_message_types
+ *
+ * Implements DBus method GetMessageTypes
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_message_types (IdleMUCChannel *obj, GArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_password_flags
+ *
+ * Implements DBus method GetPasswordFlags
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Password
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_password_flags (IdleMUCChannel *obj, guint* ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_properties
+ *
+ * Implements DBus method GetProperties
+ * on interface org.freedesktop.Telepathy.Properties
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_properties (IdleMUCChannel *obj, const GArray * properties, GPtrArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_remote_pending_members
+ *
+ * Implements DBus method GetRemotePendingMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_remote_pending_members (IdleMUCChannel *obj, GArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_self_handle
+ *
+ * Implements DBus method GetSelfHandle
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_self_handle (IdleMUCChannel *obj, guint* ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_list_pending_messages
+ *
+ * Implements DBus method ListPendingMessages
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_list_pending_messages (IdleMUCChannel *obj, GPtrArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_list_properties
+ *
+ * Implements DBus method ListProperties
+ * on interface org.freedesktop.Telepathy.Properties
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_list_properties (IdleMUCChannel *obj, GPtrArray ** ret, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_provide_password
+ *
+ * Implements DBus method ProvidePassword
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Password
+ *
+ * @context: The DBUS invocation context to use to return values
+ * or throw an error.
+ */
+gboolean idle_muc_channel_provide_password (IdleMUCChannel *obj, const gchar * password, DBusGMethodInvocation *context)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_remove_members
+ *
+ * Implements DBus method RemoveMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_remove_members (IdleMUCChannel *obj, const GArray * contacts, const gchar * message, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_send
+ *
+ * Implements DBus method Send
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_send (IdleMUCChannel *obj, guint type, const gchar * text, GError **error)
+{
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_set_properties
+ *
+ * Implements DBus method SetProperties
+ * on interface org.freedesktop.Telepathy.Properties
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_set_properties (IdleMUCChannel *obj, const GPtrArray * properties, GError **error)
+{
+ return TRUE;
+}
+
diff --git a/generate/src/idle-muc-channel.h b/generate/src/idle-muc-channel.h
new file mode 100644
index 0000000..76b635a
--- /dev/null
+++ b/generate/src/idle-muc-channel.h
@@ -0,0 +1,82 @@
+/*
+ * idle-muc-channel.h - Header for IdleMUCChannel
+ * Copyright (C) 2005 Collabora Ltd.
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __IDLE_MUC_CHANNEL_H__
+#define __IDLE_MUC_CHANNEL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdleMUCChannel IdleMUCChannel;
+typedef struct _IdleMUCChannelClass IdleMUCChannelClass;
+
+struct _IdleMUCChannelClass {
+ GObjectClass parent_class;
+};
+
+struct _IdleMUCChannel {
+ GObject parent;
+};
+
+GType idle_muc_channel_get_type(void);
+
+/* TYPE MACROS */
+#define IDLE_TYPE_MUC_CHANNEL \
+ (idle_muc_channel_get_type())
+#define IDLE_MUC_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_MUC_CHANNEL, IdleMUCChannel))
+#define IDLE_MUC_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_MUC_CHANNEL, IdleMUCChannelClass))
+#define IDLE_IS_MUC_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_MUC_CHANNEL))
+#define IDLE_IS_MUC_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_MUC_CHANNEL))
+#define IDLE_MUC_CHANNEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_MUC_CHANNEL, IdleMUCChannelClass))
+
+
+gboolean idle_muc_channel_acknowledge_pending_message (IdleMUCChannel *obj, guint id, GError **error);
+gboolean idle_muc_channel_add_members (IdleMUCChannel *obj, const GArray * contacts, const gchar * message, GError **error);
+gboolean idle_muc_channel_close (IdleMUCChannel *obj, GError **error);
+gboolean idle_muc_channel_get_all_members (IdleMUCChannel *obj, GArray ** ret, GArray ** ret1, GArray ** ret2, GError **error);
+gboolean idle_muc_channel_get_channel_type (IdleMUCChannel *obj, gchar ** ret, GError **error);
+gboolean idle_muc_channel_get_group_flags (IdleMUCChannel *obj, guint* ret, GError **error);
+gboolean idle_muc_channel_get_handle (IdleMUCChannel *obj, guint* ret, guint* ret1, GError **error);
+gboolean idle_muc_channel_get_handle_owners (IdleMUCChannel *obj, const GArray * handles, GArray ** ret, GError **error);
+gboolean idle_muc_channel_get_interfaces (IdleMUCChannel *obj, gchar *** ret, GError **error);
+gboolean idle_muc_channel_get_local_pending_members (IdleMUCChannel *obj, GArray ** ret, GError **error);
+gboolean idle_muc_channel_get_members (IdleMUCChannel *obj, GArray ** ret, GError **error);
+gboolean idle_muc_channel_get_message_types (IdleMUCChannel *obj, GArray ** ret, GError **error);
+gboolean idle_muc_channel_get_password_flags (IdleMUCChannel *obj, guint* ret, GError **error);
+gboolean idle_muc_channel_get_properties (IdleMUCChannel *obj, const GArray * properties, GPtrArray ** ret, GError **error);
+gboolean idle_muc_channel_get_remote_pending_members (IdleMUCChannel *obj, GArray ** ret, GError **error);
+gboolean idle_muc_channel_get_self_handle (IdleMUCChannel *obj, guint* ret, GError **error);
+gboolean idle_muc_channel_list_pending_messages (IdleMUCChannel *obj, GPtrArray ** ret, GError **error);
+gboolean idle_muc_channel_list_properties (IdleMUCChannel *obj, GPtrArray ** ret, GError **error);
+gboolean idle_muc_channel_provide_password (IdleMUCChannel *obj, const gchar * password, DBusGMethodInvocation *context);
+gboolean idle_muc_channel_remove_members (IdleMUCChannel *obj, const GArray * contacts, const gchar * message, GError **error);
+gboolean idle_muc_channel_send (IdleMUCChannel *obj, guint type, const gchar * text, GError **error);
+gboolean idle_muc_channel_set_properties (IdleMUCChannel *obj, const GPtrArray * properties, GError **error);
+
+
+G_END_DECLS
+
+#endif /* #ifndef __IDLE_MUC_CHANNEL_H__*/
diff --git a/generate/src/telepathy-errors.h b/generate/src/telepathy-errors.h
new file mode 100644
index 0000000..972dadb
--- /dev/null
+++ b/generate/src/telepathy-errors.h
@@ -0,0 +1,61 @@
+/*
+ * telepathy-errors.h - Header for Telepathy error types
+ * Copyright (C) 2005 Collabora Ltd.
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __TELEPATHY_ERRORS_H__
+#define __TELEPATHY_ERRORS_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ ChannelInviteOnly, /** The requested channel is invite only.
+ */
+ InvalidHandle, /** The contact name specified is unknown on this channel
+ * or connection.
+ */
+ ChannelBanned, /** You are banned from the channel.
+ */
+ Disconnected, /** The connection is not currently connected and cannot
+ * be used.
+ */
+ InvalidArgument, /** Raised when one of the provided arguments is invalid.
+ */
+ NetworkError, /** Raised when there is an error reading from or writing
+ * to the network.
+ */
+ PermissionDenied, /** The user is not permitted to perform the requested
+ * operation.
+ */
+ ChannelFull, /** The channel is full.
+ */
+ NotAvailable, /** Raised when the requested functionality is temporarily
+ * unavailable.
+ */
+ NotImplemented, /** Raised when the requested method, channel, etc is not
+ * available on this connection.
+ */
+} TelepathyErrors;
+
+
+G_END_DECLS
+
+#endif /* #ifndef __TELEPATHY_ERRORS_H__*/
diff --git a/generate/xml-modified/.git-darcs-dir b/generate/xml-modified/.git-darcs-dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/generate/xml-modified/.git-darcs-dir
diff --git a/generate/xml-modified/idle-connection-manager.xml b/generate/xml-modified/idle-connection-manager.xml
new file mode 100644
index 0000000..ff2422f
--- /dev/null
+++ b/generate/xml-modified/idle-connection-manager.xml
@@ -0,0 +1,23 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/IdleConnectionManager">
+ <interface name="org.freedesktop.Telepathy.ConnectionManager">
+ <method name="ListProtocols">
+ <arg direction="out" type="as" />
+ </method>
+ <signal name="NewConnection">
+ <arg type="s" name="bus_name" />
+ <arg type="o" name="object_path" />
+ <arg type="s" name="proto" />
+ </signal>
+ <method name="RequestConnection">
+ <arg direction="in" type="s" name="proto" />
+ <arg direction="in" type="a{sv}" name="parameters" />
+ <arg direction="out" type="s" />
+ <arg direction="out" type="o" />
+ </method>
+ <method name="GetParameters">
+ <arg direction="in" type="s" name="proto" />
+ <arg direction="out" type="a(susv)" />
+ </method>
+ </interface>
+</node>
diff --git a/generate/xml-modified/idle-connection.xml b/generate/xml-modified/idle-connection.xml
new file mode 100644
index 0000000..55fe633
--- /dev/null
+++ b/generate/xml-modified/idle-connection.xml
@@ -0,0 +1,100 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/IdleConnection">
+ <interface name="org.freedesktop.Telepathy.Connection">
+ <method name="GetInterfaces">
+ <arg direction="out" type="as" />
+ </method>
+ <method name="Connect">
+ </method>
+ <method name="Disconnect">
+ </method>
+ <method name="InspectHandles">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg direction="in" type="u" name="handle_type" />
+ <arg direction="in" type="au" name="handles" />
+ <arg direction="out" type="as" />
+ </method>
+ <method name="ReleaseHandles">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg direction="in" type="u" name="handle_type" />
+ <arg direction="in" type="au" name="handle" />
+ </method>
+ <method name="RequestChannel">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg direction="in" type="s" name="type" />
+ <arg direction="in" type="u" name="handle_type" />
+ <arg direction="in" type="u" name="handle" />
+ <arg direction="in" type="b" name="suppress_handler" />
+ <arg direction="out" type="o" />
+ </method>
+ <method name="RequestHandles">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg direction="in" type="u" name="handle_type" />
+ <arg direction="in" type="as" name="name" />
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetProtocol">
+ <arg direction="out" type="s" />
+ </method>
+ <method name="GetStatus">
+ <arg direction="out" type="u" />
+ </method>
+ <method name="ListChannels">
+ <arg direction="out" type="a(osuu)" />
+ </method>
+ <method name="HoldHandles">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg direction="in" type="u" name="handle_type" />
+ <arg direction="in" type="au" name="handle" />
+ </method>
+ <method name="GetSelfHandle">
+ <arg direction="out" type="u" />
+ </method>
+ <signal name="NewChannel">
+ <arg type="o" name="object_path" />
+ <arg type="s" name="channel_type" />
+ <arg type="u" name="handle_type" />
+ <arg type="u" name="handle" />
+ <arg type="b" name="suppress_handler" />
+ </signal>
+ <signal name="StatusChanged">
+ <arg type="u" name="status" />
+ <arg type="u" name="reason" />
+ </signal>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Connection.Interface.Presence">
+ <method name="RemoveStatus">
+ <arg direction="in" type="s" name="status" />
+ </method>
+ <method name="ClearStatus">
+ </method>
+ <method name="SetLastActivityTime">
+ <arg direction="in" type="u" name="time" />
+ </method>
+ <method name="AddStatus">
+ <arg direction="in" type="s" name="status" />
+ <arg direction="in" type="a{sv}" name="parms" />
+ </method>
+ <method name="RequestPresence">
+ <arg direction="in" type="au" name="contacts" />
+ </method>
+ <method name="GetStatuses">
+ <arg direction="out" type="a{s(ubba{ss})}" />
+ </method>
+ <signal name="PresenceUpdate">
+ <arg type="a{u(ua{sa{sv}})}" name="presence" />
+ </signal>
+ <method name="SetStatus">
+ <arg direction="in" type="a{sa{sv}}" name="statuses" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Connection.Interface.Renaming">
+ <signal name="Renamed">
+ <arg name="original" type="u" />
+ <arg name="new" type="u" />
+ </signal>
+ <method name="RequestRename">
+ <arg direction="in" name="name" type="s" />
+ </method>
+ </interface>
+</node>
diff --git a/generate/xml-modified/idle-im-channel.xml b/generate/xml-modified/idle-im-channel.xml
new file mode 100644
index 0000000..eda1a7d
--- /dev/null
+++ b/generate/xml-modified/idle-im-channel.xml
@@ -0,0 +1,51 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/IdleIMChannel">
+ <interface name="org.freedesktop.Telepathy.Channel.Type.Text">
+ <signal name="Received">
+ <arg type="u" name="id" />
+ <arg type="u" name="timestamp" />
+ <arg type="u" name="sender" />
+ <arg type="u" name="type" />
+ <arg type="u" name="flags" />
+ <arg type="s" name="text" />
+ </signal>
+ <method name="ListPendingMessages">
+ <arg direction="in" type="b" name="clear" />
+ <arg direction="out" type="a(uuuuus)" />
+ </method>
+ <method name="AcknowledgePendingMessages">
+ <arg direction="in" type="au" name="ids" />
+ </method>
+ <method name="Send">
+ <arg direction="in" type="u" name="type" />
+ <arg direction="in" type="s" name="text" />
+ </method>
+ <signal name="Sent">
+ <arg type="u" name="timestamp" />
+ <arg type="u" name="type" />
+ <arg type="s" name="text" />
+ </signal>
+ <signal name="SendError">
+ <arg type="u" name="error" />
+ <arg type="u" name="timestamp" />
+ <arg type="u" name="type" />
+ <arg type="s" name="text" />
+ </signal>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Channel">
+ <method name="GetHandle">
+ <arg direction="out" type="u" />
+ <arg direction="out" type="u" />
+ </method>
+ <method name="GetInterfaces">
+ <arg direction="out" type="as" />
+ </method>
+ <method name="GetChannelType">
+ <arg direction="out" type="s" />
+ </method>
+ <signal name="Closed">
+ </signal>
+ <method name="Close">
+ </method>
+ </interface>
+</node>
diff --git a/generate/xml-modified/idle-muc-channel.xml b/generate/xml-modified/idle-muc-channel.xml
new file mode 100644
index 0000000..ad74e83
--- /dev/null
+++ b/generate/xml-modified/idle-muc-channel.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" ?>
+<node name="/IdleMUCChannel">
+ <interface name="org.freedesktop.Telepathy.Channel">
+ <method name="Close">
+ </method>
+ <signal name="Closed">
+ </signal>
+ <method name="GetChannelType">
+ <arg direction="out" type="s" />
+ </method>
+ <method name="GetHandle">
+ <arg direction="out" type="u" />
+ <arg direction="out" type="u" />
+ </method>
+ <method name="GetInterfaces">
+ <arg direction="out" type="as" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Channel.Interface.Group">
+ <method name="AddMembers">
+ <arg direction="in" name="contacts" type="au" />
+ <arg direction="in" name="message" type="s" />
+ </method>
+ <method name="GetAllMembers">
+ <arg direction="out" type="au" />
+ <arg direction="out" type="au" />
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetGroupFlags">
+ <arg direction="out" type="u" />
+ </method>
+ <method name="GetHandleOwners">
+ <arg direction="in" name="handles" type="au" />
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetLocalPendingMembers">
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetMembers">
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetRemotePendingMembers">
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetSelfHandle">
+ <arg direction="out" type="u" />
+ </method>
+ <signal name="GroupFlagsChanged">
+ <arg name="added" type="u" />
+ <arg name="removed" type="u" />
+ </signal>
+ <signal name="MembersChanged">
+ <arg name="message" type="s" />
+ <arg name="added" type="au" />
+ <arg name="removed" type="au" />
+ <arg name="local_pending" type="au" />
+ <arg name="remote_pending" type="au" />
+ <arg name="actor" type="u" />
+ <arg name="reason" type="u" />
+ </signal>
+ <method name="RemoveMembers">
+ <arg direction="in" name="contacts" type="au" />
+ <arg direction="in" name="message" type="s" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Channel.Interface.Password">
+ <method name="GetPasswordFlags">
+ <arg direction="out" type="u" />
+ </method>
+ <signal name="PasswordFlagsChanged">
+ <arg name="added" type="u" />
+ <arg name="removed" type="u" />
+ </signal>
+ <method name="ProvidePassword">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg direction="in" name="password" type="s" />
+ <arg direction="out" type="b" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Channel.Type.Text">
+ <method name="AcknowledgePendingMessages">
+ <arg direction="in" name="id" type="au" />
+ </method>
+ <method name="GetMessageTypes">
+ <arg direction="out" type="au" />
+ </method>
+ <method name="ListPendingMessages">
+ <arg direction="in" name="clear" type="b" />
+ <arg direction="out" type="a(uuuuus)" />
+ </method>
+ <signal name="LostMessage">
+ </signal>
+ <signal name="Received">
+ <arg name="id" type="u" />
+ <arg name="timestamp" type="u" />
+ <arg name="sender" type="u" />
+ <arg name="type" type="u" />
+ <arg name="flags" type="u" />
+ <arg name="text" type="s" />
+ </signal>
+ <method name="Send">
+ <arg direction="in" name="type" type="u" />
+ <arg direction="in" name="text" type="s" />
+ </method>
+ <signal name="SendError">
+ <arg name="error" type="u" />
+ <arg name="timestamp" type="u" />
+ <arg name="type" type="u" />
+ <arg name="text" type="s" />
+ </signal>
+ <signal name="Sent">
+ <arg name="timestamp" type="u" />
+ <arg name="type" type="u" />
+ <arg name="text" type="s" />
+ </signal>
+ </interface>
+<interface name="org.freedesktop.Telepathy.Properties">
+ <method name="GetProperties">
+ <arg direction="in" name="properties" type="au" />
+ <arg direction="out" type="a(uv)" />
+ </method>
+ <method name="ListProperties">
+ <arg direction="out" type="a(ussu)" />
+ </method>
+ <signal name="PropertiesChanged">
+ <arg name="properties" type="a(uv)" />
+ </signal>
+ <signal name="PropertyFlagsChanged">
+ <arg name="properties" type="a(uu)" />
+ </signal>
+ <method name="SetProperties">
+ <arg direction="in" name="properties" type="a(uv)" />
+ </method>
+ </interface>
+</node>
diff --git a/generate/xml-pristine/.git-darcs-dir b/generate/xml-pristine/.git-darcs-dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/generate/xml-pristine/.git-darcs-dir
diff --git a/generate/xml-pristine/idle-connection-manager.xml b/generate/xml-pristine/idle-connection-manager.xml
new file mode 100644
index 0000000..9a9ce21
--- /dev/null
+++ b/generate/xml-pristine/idle-connection-manager.xml
@@ -0,0 +1,27 @@
+<node name="/IdleConnectionManager">
+ <interface name="org.freedesktop.DBus.Introspectable">
+ <method name="Introspect">
+ <arg direction="out" type="s" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.ConnectionManager">
+ <method name="GetParameters">
+ <arg direction="in" name="proto" type="s" />
+ <arg direction="out" type="a(susv)" />
+ </method>
+ <method name="ListProtocols">
+ <arg direction="out" type="as" />
+ </method>
+ <signal name="NewConnection">
+ <arg name="bus_name" type="s" />
+ <arg name="object_path" type="o" />
+ <arg name="proto" type="s" />
+ </signal>
+ <method name="RequestConnection">
+ <arg direction="in" name="proto" type="s" />
+ <arg direction="in" name="parameters" type="a{sv}" />
+ <arg direction="out" type="s" />
+ <arg direction="out" type="o" />
+ </method>
+ </interface>
+</node> \ No newline at end of file
diff --git a/generate/xml-pristine/idle-connection.xml b/generate/xml-pristine/idle-connection.xml
new file mode 100644
index 0000000..aaf4624
--- /dev/null
+++ b/generate/xml-pristine/idle-connection.xml
@@ -0,0 +1,113 @@
+<node name="/IdleConnection">
+ <interface name="org.freedesktop.DBus.Introspectable">
+ <method name="Introspect">
+ <arg direction="out" type="s" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Connection">
+ <method name="Connect">
+ </method>
+ <method name="Disconnect">
+ </method>
+ <method name="GetInterfaces">
+ <arg direction="out" type="as" />
+ </method>
+ <method name="GetProtocol">
+ <arg direction="out" type="s" />
+ </method>
+ <method name="GetSelfHandle">
+ <arg direction="out" type="u" />
+ </method>
+ <method name="GetStatus">
+ <arg direction="out" type="u" />
+ </method>
+ <method name="HoldHandles">
+ <arg direction="in" name="handle_type" type="u" />
+ <arg direction="in" name="handles" type="au" />
+ </method>
+ <method name="InspectHandles">
+ <arg direction="in" name="handle_type" type="u" />
+ <arg direction="in" name="handles" type="au" />
+ <arg direction="out" type="as" />
+ </method>
+ <method name="ListChannels">
+ <arg direction="out" type="a(osuu)" />
+ </method>
+ <signal name="NewChannel">
+ <arg name="object_path" type="o" />
+ <arg name="channel_type" type="s" />
+ <arg name="handle_type" type="u" />
+ <arg name="handle" type="u" />
+ <arg name="suppress_handler" type="b" />
+ </signal>
+ <method name="ReleaseHandles">
+ <arg direction="in" name="handle_type" type="u" />
+ <arg direction="in" name="handles" type="au" />
+ </method>
+ <method name="RequestChannel">
+ <arg direction="in" name="type" type="s" />
+ <arg direction="in" name="handle_type" type="u" />
+ <arg direction="in" name="handle" type="u" />
+ <arg direction="in" name="suppress_handler" type="b" />
+ <arg direction="out" type="o" />
+ </method>
+ <method name="RequestHandles">
+ <arg direction="in" name="handle_type" type="u" />
+ <arg direction="in" name="names" type="as" />
+ <arg direction="out" type="au" />
+ </method>
+ <signal name="StatusChanged">
+ <arg name="status" type="u" />
+ <arg name="reason" type="u" />
+ </signal>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Connection.Interface.Capabilities">
+ <method name="AdvertiseCapabilities">
+ <arg direction="in" name="add" type="as" />
+ <arg direction="in" name="remove" type="as" />
+ <arg direction="out" type="as" />
+ </method>
+ <signal name="CapabilitiesChanged">
+ <arg name="caps" type="a(usuu)" />
+ </signal>
+ <method name="GetCapabilities">
+ <arg direction="in" name="handles" type="au" />
+ <arg direction="out" type="a(usu)" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Connection.Interface.Presence">
+ <method name="AddStatus">
+ <arg direction="in" name="status" type="s" />
+ <arg direction="in" name="parms" type="a{sv}" />
+ </method>
+ <method name="ClearStatus">
+ </method>
+ <method name="GetStatuses">
+ <arg direction="out" type="a{s(ubba{ss})}" />
+ </method>
+ <signal name="PresenceUpdate">
+ <arg name="presence" type="a{u(ua{sa{sv}})}" />
+ </signal>
+ <method name="RemoveStatus">
+ <arg direction="in" name="status" type="s" />
+ </method>
+ <method name="RequestPresence">
+ <arg direction="in" name="contacts" type="au" />
+ </method>
+ <method name="SetLastActivityTime">
+ <arg direction="in" name="time" type="u" />
+ </method>
+ <method name="SetStatus">
+ <arg direction="in" name="statuses" type="a{sa{sv}}" />
+ </method>
+ </interface>
+<interface name="org.freedesktop.Telepathy.Connection.Interface.Renaming">
+ <signal name="Renamed">
+ <arg name="original" type="u" />
+ <arg name="new" type="u" />
+ </signal>
+ <method name="RequestRename">
+ <arg direction="in" name="name" type="s" />
+ </method>
+ </interface>
+ </node> \ No newline at end of file
diff --git a/generate/xml-pristine/idle-im-channel.xml b/generate/xml-pristine/idle-im-channel.xml
new file mode 100644
index 0000000..a5182e7
--- /dev/null
+++ b/generate/xml-pristine/idle-im-channel.xml
@@ -0,0 +1,60 @@
+<node name="/IdleIMChannel">
+ <interface name="org.freedesktop.DBus.Introspectable">
+ <method name="Introspect">
+ <arg direction="out" type="s" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Channel">
+ <method name="Close">
+ </method>
+ <signal name="Closed">
+ </signal>
+ <method name="GetChannelType">
+ <arg direction="out" type="s" />
+ </method>
+ <method name="GetHandle">
+ <arg direction="out" type="u" />
+ <arg direction="out" type="u" />
+ </method>
+ <method name="GetInterfaces">
+ <arg direction="out" type="as" />
+ </method>
+ </interface>
+<interface name="org.freedesktop.Telepathy.Channel.Type.Text">
+ <method name="AcknowledgePendingMessages">
+ <arg direction="in" name="ids" type="au" />
+ </method>
+ <method name="GetMessageTypes">
+ <arg direction="out" type="au" />
+ </method>
+ <method name="ListPendingMessages">
+ <arg direction="in" name="clear" type="b" />
+ <arg direction="out" type="a(uuuus)" />
+ </method>
+ <signal name="LostMessage">
+ </signal>
+ <signal name="Received">
+ <arg name="id" type="u" />
+ <arg name="timestamp" type="u" />
+ <arg name="sender" type="u" />
+ <arg name="type" type="u" />
+ <arg name="flags" type="u" />
+ <arg name="text" type="s" />
+ </signal>
+ <method name="Send">
+ <arg direction="in" name="type" type="u" />
+ <arg direction="in" name="text" type="s" />
+ </method>
+ <signal name="SendError">
+ <arg name="error" type="u" />
+ <arg name="timestamp" type="u" />
+ <arg name="type" type="u" />
+ <arg name="text" type="s" />
+ </signal>
+ <signal name="Sent">
+ <arg name="timestamp" type="u" />
+ <arg name="type" type="u" />
+ <arg name="text" type="s" />
+ </signal>
+ </interface>
+ </node> \ No newline at end of file
diff --git a/generate/xml-pristine/idle-muc-channel.xml b/generate/xml-pristine/idle-muc-channel.xml
new file mode 100644
index 0000000..6749978
--- /dev/null
+++ b/generate/xml-pristine/idle-muc-channel.xml
@@ -0,0 +1,138 @@
+<node name="/IdleMUCChannel">
+ <interface name="org.freedesktop.DBus.Introspectable">
+ <method name="Introspect">
+ <arg direction="out" type="s" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Channel">
+ <method name="Close">
+ </method>
+ <signal name="Closed">
+ </signal>
+ <method name="GetChannelType">
+ <arg direction="out" type="s" />
+ </method>
+ <method name="GetHandle">
+ <arg direction="out" type="u" />
+ <arg direction="out" type="u" />
+ </method>
+ <method name="GetInterfaces">
+ <arg direction="out" type="as" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Channel.Interface.Group">
+ <method name="AddMembers">
+ <arg direction="in" name="contacts" type="au" />
+ <arg direction="in" name="message" type="s" />
+ </method>
+ <method name="GetAllMembers">
+ <arg direction="out" type="au" />
+ <arg direction="out" type="au" />
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetGroupFlags">
+ <arg direction="out" type="u" />
+ </method>
+ <method name="GetHandleOwners">
+ <arg direction="in" name="handles" type="au" />
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetLocalPendingMembers">
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetMembers">
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetRemotePendingMembers">
+ <arg direction="out" type="au" />
+ </method>
+ <method name="GetSelfHandle">
+ <arg direction="out" type="u" />
+ </method>
+ <signal name="GroupFlagsChanged">
+ <arg name="added" type="u" />
+ <arg name="removed" type="u" />
+ </signal>
+ <signal name="MembersChanged">
+ <arg name="message" type="s" />
+ <arg name="added" type="au" />
+ <arg name="removed" type="au" />
+ <arg name="local_pending" type="au" />
+ <arg name="remote_pending" type="au" />
+ <arg name="actor" type="u" />
+ <arg name="reason" type="u" />
+ </signal>
+ <method name="RemoveMembers">
+ <arg direction="in" name="contacts" type="au" />
+ <arg direction="in" name="message" type="s" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Channel.Interface.Password">
+ <method name="GetPasswordFlags">
+ <arg direction="out" type="u" />
+ </method>
+ <signal name="PasswordFlagsChanged">
+ <arg name="added" type="u" />
+ <arg name="removed" type="u" />
+ </signal>
+ <method name="ProvidePassword">
+ <arg direction="in" name="password" type="s" />
+ <arg direction="out" type="b" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.Channel.Type.Text">
+ <method name="AcknowledgePendingMessages">
+ <arg direction="in" name="ids" type="au" />
+ </method>
+ <method name="GetMessageTypes">
+ <arg direction="out" type="au" />
+ </method>
+ <method name="ListPendingMessages">
+ <arg direction="in" name="clear" type="b" />
+ <arg direction="out" type="a(uuuus)" />
+ </method>
+ <signal name="LostMessage">
+ </signal>
+ <signal name="Received">
+ <arg name="id" type="u" />
+ <arg name="timestamp" type="u" />
+ <arg name="sender" type="u" />
+ <arg name="type" type="u" />
+ <arg name="flags" type="u" />
+ <arg name="text" type="s" />
+ </signal>
+ <method name="Send">
+ <arg direction="in" name="type" type="u" />
+ <arg direction="in" name="text" type="s" />
+ </method>
+ <signal name="SendError">
+ <arg name="error" type="u" />
+ <arg name="timestamp" type="u" />
+ <arg name="type" type="u" />
+ <arg name="text" type="s" />
+ </signal>
+ <signal name="Sent">
+ <arg name="timestamp" type="u" />
+ <arg name="type" type="u" />
+ <arg name="text" type="s" />
+ </signal>
+ </interface>
+<interface name="org.freedesktop.Telepathy.Properties">
+ <method name="GetProperties">
+ <arg direction="in" name="properties" type="au" />
+ <arg direction="out" type="a(uv)" />
+ </method>
+ <method name="ListProperties">
+ <arg direction="out" type="a(ussu)" />
+ </method>
+ <signal name="PropertiesChanged">
+ <arg name="properties" type="a(uv)" />
+ </signal>
+ <signal name="PropertyFlagsChanged">
+ <arg name="properties" type="a(uu)" />
+ </signal>
+ <method name="SetProperties">
+ <arg direction="in" name="properties" type="a(uv)" />
+ </method>
+ </interface>
+ </node> \ No newline at end of file
diff --git a/m4/.git-darcs-dir b/m4/.git-darcs-dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/m4/.git-darcs-dir
diff --git a/m4/Makefile.am b/m4/Makefile.am
new file mode 100644
index 0000000..259e3c3
--- /dev/null
+++ b/m4/Makefile.am
@@ -0,0 +1,4 @@
+EXTRA_DIST = \
+as-compiler-flag.m4 \
+as-version.m4 \
+as-ac-expand.m4
diff --git a/m4/as-ac-expand.m4 b/m4/as-ac-expand.m4
new file mode 100644
index 0000000..0c71173
--- /dev/null
+++ b/m4/as-ac-expand.m4
@@ -0,0 +1,40 @@
+dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR)
+dnl
+dnl example
+dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
+dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local
+
+AC_DEFUN([AS_AC_EXPAND],
+[
+ EXP_VAR=[$1]
+ FROM_VAR=[$2]
+
+ dnl first expand prefix and exec_prefix if necessary
+ prefix_save=$prefix
+ exec_prefix_save=$exec_prefix
+
+ dnl if no prefix given, then use /usr/local, the default prefix
+ if test "x$prefix" = "xNONE"; then
+ prefix=$ac_default_prefix
+ fi
+ dnl if no exec_prefix given, then use prefix
+ if test "x$exec_prefix" = "xNONE"; then
+ exec_prefix=$prefix
+ fi
+
+ full_var="$FROM_VAR"
+ dnl loop until it doesn't change anymore
+ while true; do
+ new_full_var="`eval echo $full_var`"
+ if test "x$new_full_var"="x$full_var"; then break; fi
+ full_var=$new_full_var
+ done
+
+ dnl clean up
+ full_var=$new_full_var
+ AC_SUBST([$1], "$full_var")
+
+ dnl restore prefix and exec_prefix
+ prefix=$prefix_save
+ exec_prefix=$exec_prefix_save
+])
diff --git a/m4/as-compiler-flag.m4 b/m4/as-compiler-flag.m4
new file mode 100644
index 0000000..605708a
--- /dev/null
+++ b/m4/as-compiler-flag.m4
@@ -0,0 +1,33 @@
+dnl as-compiler-flag.m4 0.1.0
+
+dnl autostars m4 macro for detection of compiler flags
+
+dnl David Schleef <ds@schleef.org>
+
+dnl $Id: as-compiler-flag.m4,v 1.1 2005/06/18 18:02:46 burgerman Exp $
+
+dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED])
+dnl Tries to compile with the given CFLAGS.
+dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags,
+dnl and ACTION-IF-NOT-ACCEPTED otherwise.
+
+AC_DEFUN([AS_COMPILER_FLAG],
+[
+ AC_MSG_CHECKING([to see if compiler understands $1])
+
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $1"
+
+ AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
+ CFLAGS="$save_CFLAGS"
+
+ if test "X$flag_ok" = Xyes ; then
+ $2
+ true
+ else
+ $3
+ true
+ fi
+ AC_MSG_RESULT([$flag_ok])
+])
+
diff --git a/m4/as-version.m4 b/m4/as-version.m4
new file mode 100644
index 0000000..defc887
--- /dev/null
+++ b/m4/as-version.m4
@@ -0,0 +1,66 @@
+dnl as-version.m4 0.1.0
+
+dnl autostars m4 macro for versioning
+
+dnl Thomas Vander Stichele <thomas at apestaart dot org>
+
+dnl $Id: as-version.m4,v 1.1 2005/06/18 18:02:46 burgerman Exp $
+
+dnl AS_VERSION(PACKAGE, PREFIX, MAJOR, MINOR, MICRO, NANO,
+dnl ACTION-IF-NO-NANO, [ACTION-IF-NANO])
+
+dnl example
+dnl AS_VERSION(gstreamer, GST_VERSION, 0, 3, 2,)
+dnl for a 0.3.2 release version
+
+dnl this macro
+dnl - defines [$PREFIX]_MAJOR, MINOR and MICRO
+dnl - if NANO is empty, then we're in release mode, else in cvs/dev mode
+dnl - defines [$PREFIX], VERSION, and [$PREFIX]_RELEASE
+dnl - executes the relevant action
+dnl - AC_SUBST's PACKAGE, VERSION, [$PREFIX] and [$PREFIX]_RELEASE
+dnl as well as the little ones
+dnl - doesn't call AM_INIT_AUTOMAKE anymore because it prevents
+dnl maintainer mode from running ok
+dnl
+dnl don't forget to put #undef [$2] and [$2]_RELEASE in acconfig.h
+dnl if you use acconfig.h
+
+AC_DEFUN([AS_VERSION],
+[
+ PACKAGE=[$1]
+ [$2]_MAJOR=[$3]
+ [$2]_MINOR=[$4]
+ [$2]_MICRO=[$5]
+ NANO=[$6]
+ [$2]_NANO=$NANO
+ if test "x$NANO" = "x" || test "x$NANO" = "x0";
+ then
+ AC_MSG_NOTICE(configuring [$1] for release)
+ VERSION=[$3].[$4].[$5]
+ [$2]_RELEASE=1
+ dnl execute action
+ ifelse([$7], , :, [$7])
+ else
+ AC_MSG_NOTICE(configuring [$1] for development with nano $NANO)
+ VERSION=[$3].[$4].[$5].$NANO
+ [$2]_RELEASE=0.`date +%Y%m%d.%H%M%S`
+ dnl execute action
+ ifelse([$8], , :, [$8])
+ fi
+
+ [$2]=$VERSION
+ AC_DEFINE_UNQUOTED([$2], "$[$2]", [Define the version])
+ AC_SUBST([$2])
+ AC_DEFINE_UNQUOTED([$2]_RELEASE, "$[$2]_RELEASE", [Define the release version])
+ AC_SUBST([$2]_RELEASE)
+
+ AC_SUBST([$2]_MAJOR)
+ AC_SUBST([$2]_MINOR)
+ AC_SUBST([$2]_MICRO)
+ AC_SUBST([$2]_NANO)
+ AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Define the package name])
+ AC_SUBST(PACKAGE)
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Define the version])
+ AC_SUBST(VERSION)
+])
diff --git a/src/.git-darcs-dir b/src/.git-darcs-dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/.git-darcs-dir
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..7552090
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,112 @@
+BUILT_SOURCES = \
+ idle-connection-manager-glue.h \
+ idle-connection-manager-signals-marshal.h \
+ idle-connection-manager-signals-marshal.c \
+ idle-connection-glue.h \
+ idle-connection-signals-marshal.h \
+ idle-connection-signals-marshal.c \
+ idle-im-channel-glue.h \
+ idle-im-channel-signals-marshal.h \
+ idle-im-channel-signals-marshal.c \
+ idle-muc-channel-glue.h \
+ idle-muc-channel-signals-marshal.h \
+ idle-muc-channel-signals-marshal.c \
+ telepathy-errors-enumtypes.h \
+ telepathy-errors-enumtypes.c \
+ idle-server-connection-iface-signals-marshal.h \
+ idle-server-connection-iface-signals-marshal.c \
+ config.h
+
+# correctly clean the generated source files
+CLEANFILES = $(BUILT_SOURCES)
+
+bin_PROGRAMS=telepathy-idle
+
+CORE_SOURCES = \
+ idle-connection-manager.h \
+ idle-connection.h \
+ idle-im-channel.h \
+ idle-muc-channel.h \
+ idle-handles.h \
+ idle-handle-set.h \
+ idle-handles-private.h \
+ gintset.h \
+ gheap.h \
+ telepathy-errors.h \
+ telepathy-helpers.h \
+ telepathy-constants.h \
+ telepathy-interfaces.h \
+ idle-server-connection-iface.h \
+ idle-server-connection.h \
+ idle-server-connection-util.h \
+ idle-dns-resolver.h \
+ idle-ssl-server-connection.h \
+ idle-connection-manager.c \
+ idle-connection.c \
+ idle-im-channel.c \
+ idle-muc-channel.c \
+ idle-handles.c \
+ idle-handle-set.c \
+ gintset.c \
+ gheap.c \
+ telepathy-errors.c \
+ telepathy-helpers.c \
+ idle-server-connection-iface.c \
+ idle-server-connection.c \
+ idle-server-connection-util.c \
+ idle-dns-resolver.c \
+ idle-ssl-server-connection.c \
+ idle-version.h \
+ $(BUILT_SOURCES)
+
+libidle_convenience_la_SOURCES = \
+ $(CORE_SOURCES)
+
+telepathy_idle_SOURCES = \
+ idle.c \
+ idle.h
+
+telepathy_idle_LDADD = libidle-convenience.la
+
+noinst_LTLIBRARIES = libidle-convenience.la
+
+AM_CFLAGS = $(ERROR_CFLAGS) @DBUS_CFLAGS@ @GLIB_CFLAGS@ @SOFIA_CFLAGS@ @OPENSSL_CFLAGS@
+AM_LDFLAGS = @DBUS_LIBS@ @GLIB_LIBS@ @SOFIA_LIBS@ @OPENSSL_LIBS@
+
+# rule to generate the binding headers
+%-glue.h: ../generate/xml-modified/%.xml Makefile
+ dbus-binding-tool --mode=glib-server --output=$@ --prefix=$(subst -,_,$*) $<
+
+%-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
+
+%-marshal.h: %-marshal.list Makefile
+ glib-genmarshal --header --prefix=$(subst -,_,$*)_marshal $< > $*-marshal.h
+
+%-marshal.c: %-marshal.list Makefile
+ glib-genmarshal --body --prefix=$(subst -,_,$*)_marshal $< > $*-marshal.c
+
+
+# rules for making the glib enum objects
+%-enumtypes.h: %.h Makefile
+ glib-mkenums \
+ --fhead "#ifndef __$(shell echo $* | tr [:lower:]- [:upper:]_)_ENUM_TYPES_H__\n#define __$(shell echo $* | tr [:lower:]- [:upper:]_)_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+ --fprod "/* enumerations from \"@filename@\" */\n" \
+ --vhead "GType @enum_name@_get_type (void);\n#define $(shell echo $* | tr [:lower:]- [:upper:]_ | sed 's/_.*//')_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
+ --ftail "G_END_DECLS\n\n#endif /* __$(shell echo $* | tr [:lower:]- [:upper:]_)_ENUM_TYPES_H__ */" \
+ $< > $@
+
+%-enumtypes.c: %.h Makefile
+ glib-mkenums \
+ --fhead "#include <$*.h>" \
+ --fprod "\n/* enumerations from \"@filename@\" */" \
+ --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@\", \"@VALUENAME@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \
+ $< > $@
+
+config.h:
+ echo '#define TELEPATHY_IDLE_VERSION "'@TELEPATHY_IDLE_VERSION@'"' > config.h
diff --git a/src/gheap.c b/src/gheap.c
new file mode 100644
index 0000000..917357e
--- /dev/null
+++ b/src/gheap.c
@@ -0,0 +1,142 @@
+/*
+ * Header file for GHeap
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 "gheap.h"
+
+#define DEFAULT_SIZE 64
+
+struct _GHeap
+{
+ GPtrArray *data;
+ GCompareFunc comparator;
+};
+
+GHeap *g_heap_new (GCompareFunc comparator)
+{
+ GHeap *ret = g_slice_new(GHeap);
+ g_assert (comparator != NULL);
+
+ ret->data = g_ptr_array_sized_new (DEFAULT_SIZE);
+ ret->comparator = comparator;
+
+ return ret;
+}
+
+void g_heap_destroy (GHeap *heap)
+{
+ g_return_if_fail (heap != NULL);
+
+ g_ptr_array_free (heap->data, TRUE);
+ g_slice_free (GHeap, heap);
+}
+
+void g_heap_clear (GHeap *heap)
+{
+ g_return_if_fail (heap != NULL);
+
+ g_ptr_array_free (heap->data, TRUE);
+ heap->data = g_ptr_array_sized_new (DEFAULT_SIZE);
+}
+
+#define HEAP_INDEX(heap, index) (g_ptr_array_index ((heap)->data, (index)-1))
+
+void g_heap_add (GHeap *heap, gpointer element)
+{
+ guint m;
+
+ g_return_if_fail (heap != NULL);
+
+ g_ptr_array_add (heap->data, element);
+ m = heap->data->len;
+ while (m != 1)
+ {
+ gpointer parent = HEAP_INDEX (heap, m/2);
+
+ if (heap->comparator (element, parent) == -1)
+ {
+ HEAP_INDEX (heap, m/2) = element;
+ HEAP_INDEX (heap, m) = parent;
+ m /= 2;
+ }
+ else
+ break;
+ }
+}
+
+gpointer g_heap_peek_first (GHeap *heap)
+{
+ g_return_val_if_fail (heap != NULL, NULL);
+
+ if (heap->data->len > 0)
+ return HEAP_INDEX (heap, 1);
+ else
+ return NULL;
+}
+
+gpointer g_heap_extract_first (GHeap *heap)
+{
+ gpointer ret;
+
+ g_return_val_if_fail (heap != NULL, NULL);
+
+ if (heap->data->len > 0)
+ {
+ guint m = heap->data->len;
+ guint i = 1, j;
+ ret = HEAP_INDEX (heap, 1);
+
+ HEAP_INDEX (heap, 1) = HEAP_INDEX (heap, m);
+
+ while (i*2 <= m)
+ {
+ /* select the child which is supposed to come FIRST */
+ if ((i*2+1 <= m)
+ && (heap->comparator (HEAP_INDEX (heap, i*2), HEAP_INDEX (heap, i*2+1)) == 1))
+ j = i*2+1;
+ else
+ j = i*2;
+
+ if (heap->comparator (HEAP_INDEX (heap, i), HEAP_INDEX (heap, j)) == 1)
+ {
+ gpointer tmp = HEAP_INDEX (heap, i);
+ HEAP_INDEX (heap, i) = HEAP_INDEX (heap, j);
+ HEAP_INDEX (heap, j) = tmp;
+ i = j;
+ }
+ else
+ break;
+ }
+
+ g_ptr_array_remove_index (heap->data, m-1);
+ }
+ else
+ ret = NULL;
+
+ return ret;
+}
+
+guint g_heap_size (GHeap *heap)
+{
+ g_return_val_if_fail (heap != NULL, 0);
+
+ return heap->data->len;
+}
+
diff --git a/src/gheap.h b/src/gheap.h
new file mode 100644
index 0000000..68bf4b9
--- /dev/null
+++ b/src/gheap.h
@@ -0,0 +1,38 @@
+/*
+ * Header file for GHeap
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __G_HEAP_H__
+#define __G_HEAP_H__
+
+#include <glib.h>
+
+typedef struct _GHeap GHeap;
+
+GHeap *g_heap_new (GCompareFunc comparator);
+void g_heap_destroy (GHeap *);
+void g_heap_clear (GHeap *);
+
+void g_heap_add (GHeap *heap, gpointer element);
+gpointer g_heap_peek_first (GHeap *heap);
+gpointer g_heap_extract_first (GHeap *heap);
+
+guint g_heap_size (GHeap *heap);
+
+#endif
diff --git a/src/gintset.c b/src/gintset.c
new file mode 100644
index 0000000..7076782
--- /dev/null
+++ b/src/gintset.c
@@ -0,0 +1,353 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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.h>
+#include "gintset.h"
+
+#define DEFAULT_SIZE 32
+#define DEFAULT_INCREMENT 8
+#define DEFAULT_INCREMENT_LOG2 3
+
+struct _GIntSet
+{
+ guint32 *bits;
+ guint size;
+};
+
+static GIntSet *
+_g_intset_new_with_size (guint size)
+{
+ GIntSet *set = g_slice_new(GIntSet);
+ set->bits = g_new0(guint32, size);
+ set->size = size;
+ return set;
+}
+
+GIntSet *
+g_intset_new ()
+{
+ return _g_intset_new_with_size (DEFAULT_SIZE);
+}
+
+/**
+ * g_intset_destroy:
+ * @set: set
+ *
+ * delete the set
+ */
+void
+g_intset_destroy (GIntSet *set)
+{
+ g_free (set->bits);
+ g_slice_free (GIntSet, set);
+}
+
+/**
+ * g_intset_add:
+ * @set: set
+ * @element: integer to add
+ *
+ * Add an integer into a GIntSet
+ */
+void
+g_intset_add (GIntSet *set, guint element)
+{
+ guint offset = element>>5;
+ guint newsize;
+ if (offset >= set->size)
+ {
+ newsize = ((offset>>DEFAULT_INCREMENT_LOG2) +1 ) << DEFAULT_INCREMENT_LOG2;
+ set->bits = g_renew(guint32, set->bits, newsize);
+ memset (set->bits + set->size, 0, sizeof(guint32) * (newsize - set->size));
+ set->size = newsize;
+ g_assert(offset<newsize);
+ }
+ set->bits[offset] = set->bits[offset] | (1<<(element & 0x1f));
+}
+
+/**
+ * g_intset_remove:
+ * @set: set
+ * @element: integer to add
+ *
+ * Remove an integer from a GIntSet
+ * Returns: TRUE if element was in set
+ */
+gboolean
+g_intset_remove (GIntSet *set, guint element)
+{
+ guint offset = element >>5;
+ guint mask = 1 << (element & 0x1f);
+ if (offset >= set->size)
+ return FALSE;
+ else if (! (set->bits[offset] & mask))
+ return FALSE;
+ else
+ {
+ set->bits[offset] &= ~mask;
+ return TRUE;
+ }
+}
+
+/**
+ * g_intset_is_member:
+ * @set: set
+ * @element: integer to test
+ *
+ * Tests if @element is a member of @set
+ * Returns: TRUE if element was in set
+ */
+gboolean
+g_intset_is_member (const GIntSet *set, guint element)
+{
+ guint offset = element >>5;
+ if (offset >= set->size)
+ return FALSE;
+ else
+ return (set->bits[offset] & (1 << (element & 0x1f)));
+}
+
+/**
+ * g_intset_foreach:
+ * @set: set
+ * @func: @GIntFunc to use to iterate the set
+ * @userdata: user data to pass to each call of @func
+ *
+ * Iterates every member of the set calling @func
+ */
+
+void
+g_intset_foreach (GIntSet *set, GIntFunc func, gpointer userdata)
+{
+ guint i, j;
+ for (i=0; i<set->size; i++)
+ {
+ if (set->bits[i])
+ {
+ for (j=0; j<32; j++)
+ {
+ if (set->bits[i] & 1<<j)
+ func(i*32 +j, userdata);
+ }
+ }
+ }
+}
+
+
+static void
+addint(guint32 i, gpointer data)
+{
+ GArray * array = (GArray *) data;
+ g_array_append_val (array, i);
+}
+
+/**
+ * g_intset_to_array:
+ * @set: set to convert
+ * Convert a gintset to an array, allocates the array for you, hence you
+ * must free it after use.
+ */
+GArray *
+g_intset_to_array (GIntSet *set)
+{
+ GArray *array;
+
+ array = g_array_new (FALSE, TRUE, sizeof (guint32));
+
+ g_intset_foreach(set, addint, array);
+ return array;
+}
+
+int
+g_intset_size(const GIntSet *set)
+{
+ int i,count=0;
+ guint32 n;
+
+ for (i=0; i< set->size ; i++)
+ {
+ n = set->bits[i];
+ n = n - ((n >> 1) & 033333333333) - ((n >> 2) & 011111111111);
+ count += ((n + (n >> 3)) & 030707070707) % 63;
+ }
+ return count;
+}
+
+gboolean
+g_intset_is_equal (const GIntSet *left, const GIntSet *right)
+{
+ const GIntSet *large, *small;
+ int i;
+
+ g_return_val_if_fail (left != NULL, FALSE);
+ g_return_val_if_fail (right != NULL, FALSE);
+
+ if (left->size > right->size)
+ {
+ large = left;
+ small = right;
+ }
+ else
+ {
+ large = right;
+ small = left;
+ }
+
+ for (i = 0; i < small->size; i++)
+ {
+ if (large->bits[i] != small->bits[i])
+ return FALSE;
+ }
+
+ for (i = small->size; i < large->size; i++)
+ {
+ if (large->bits[i] != 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+GIntSet *
+g_intset_copy (const GIntSet *orig)
+{
+ GIntSet *ret;
+
+ g_return_val_if_fail (orig != NULL, NULL);
+
+ ret = _g_intset_new_with_size (orig->size);
+ memcpy (ret->bits, orig->bits, (ret->size * sizeof(guint32)));
+
+ return ret;
+}
+
+GIntSet *
+g_intset_intersection (const GIntSet *left, const GIntSet *right)
+{
+ const GIntSet *large, *small;
+ GIntSet *ret;
+ int i;
+
+ g_return_val_if_fail (left != NULL, NULL);
+ g_return_val_if_fail (right != NULL, NULL);
+
+ if (left->size > right->size)
+ {
+ large = left;
+ small = right;
+ }
+ else
+ {
+ large = right;
+ small = left;
+ }
+
+ ret = g_intset_copy (small);
+
+ for (i = 0; i < ret->size; i++)
+ {
+ ret->bits[i] &= large->bits[i];
+ }
+
+ return ret;
+}
+
+GIntSet *
+g_intset_union (const GIntSet *left, const GIntSet *right)
+{
+ const GIntSet *large, *small;
+ GIntSet *ret;
+ int i;
+
+ g_return_val_if_fail (left != NULL, NULL);
+ g_return_val_if_fail (right != NULL, NULL);
+
+ if (left->size > right->size)
+ {
+ large = left;
+ small = right;
+ }
+ else
+ {
+ large = right;
+ small = left;
+ }
+
+ ret = g_intset_copy (large);
+
+ for (i = 0; i < small->size; i++)
+ {
+ ret->bits[i] |= small->bits[i];
+ }
+
+ return ret;
+}
+
+GIntSet *
+g_intset_difference (const GIntSet *left, const GIntSet *right)
+{
+ GIntSet *ret;
+ int i;
+
+ g_return_val_if_fail (left != NULL, NULL);
+ g_return_val_if_fail (right != NULL, NULL);
+
+ ret = g_intset_copy (left);
+
+ for (i = 0; i < MIN(right->size, left->size); i++)
+ {
+ ret->bits[i] &= ~right->bits[i];
+ }
+
+ return ret;
+}
+
+GIntSet *
+g_intset_symmetric_difference (const GIntSet *left, const GIntSet *right)
+{
+ const GIntSet *large, *small;
+ GIntSet *ret;
+ int i;
+
+ g_return_val_if_fail (left != NULL, NULL);
+ g_return_val_if_fail (right != NULL, NULL);
+
+ if (left->size > right->size)
+ {
+ large = left;
+ small = right;
+ }
+ else
+ {
+ large = right;
+ small = left;
+ }
+
+ ret = g_intset_copy (large);
+
+ for (i = 0; i < small->size; i++)
+ {
+ ret->bits[i] ^= small->bits[i];
+ }
+
+ return ret;
+}
+
diff --git a/src/gintset.h b/src/gintset.h
new file mode 100644
index 0000000..c83c7ff
--- /dev/null
+++ b/src/gintset.h
@@ -0,0 +1,49 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __G_INTSET_H__
+#define __G_INTSET_H__
+
+#include <glib-object.h>
+
+typedef struct _GIntSet GIntSet;
+typedef void (*GIntFunc)(guint i, gpointer userdata);
+
+GIntSet * g_intset_new ();
+void g_intset_destroy (GIntSet *);
+
+void g_intset_add (GIntSet *set, guint element);
+gboolean g_intset_remove (GIntSet *set, guint element);
+gboolean g_intset_is_member (const GIntSet *set, guint element);
+
+void g_intset_foreach (GIntSet *set, GIntFunc func, gpointer userdata);
+GArray* g_intset_to_array (GIntSet *set);
+
+int g_intset_size(const GIntSet *set);
+
+gboolean g_intset_is_equal (const GIntSet *left, const GIntSet *right);
+
+GIntSet *g_intset_copy (const GIntSet *orig);
+GIntSet *g_intset_intersection (const GIntSet *left, const GIntSet *right);
+GIntSet *g_intset_union (const GIntSet *left, const GIntSet *right);
+GIntSet *g_intset_difference (const GIntSet *left, const GIntSet *right);
+GIntSet *g_intset_symmetric_difference (const GIntSet *left, const GIntSet *right);
+
+#endif /*__G_INTSET_H__*/
diff --git a/src/idle-connection-manager-signals-marshal.list b/src/idle-connection-manager-signals-marshal.list
new file mode 100644
index 0000000..41e4027
--- /dev/null
+++ b/src/idle-connection-manager-signals-marshal.list
@@ -0,0 +1 @@
+VOID:STRING,STRING,STRING
diff --git a/src/idle-connection-manager.c b/src/idle-connection-manager.c
new file mode 100644
index 0000000..7a1575b
--- /dev/null
+++ b/src/idle-connection-manager.c
@@ -0,0 +1,559 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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-glib.h>
+#include <dbus/dbus-protocol.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "idle-connection.h"
+#include "telepathy-constants.h"
+#include "telepathy-errors.h"
+#include "telepathy-helpers.h"
+
+#include "idle-connection-manager.h"
+#include "idle-connection-manager-glue.h"
+#include "idle-connection-manager-signals-marshal.h"
+
+#define BUS_NAME "org.freedesktop.Telepathy.ConnectionManager.idle"
+#define OBJECT_PATH "/org/freedesktop/Telepathy/ConnectionManager/idle"
+
+G_DEFINE_TYPE(IdleConnectionManager, idle_connection_manager, G_TYPE_OBJECT)
+
+/* signal enum */
+enum
+{
+ NEW_CONNECTION,
+ NO_MORE_CONNECTIONS,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/* private structure */
+typedef struct _IdleConnectionManagerPrivate IdleConnectionManagerPrivate;
+
+struct _IdleConnectionManagerPrivate
+{
+ gboolean dispose_has_run;
+ GHashTable *connections;
+};
+
+#define IDLE_CONNECTION_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_CONNECTION_MANAGER, IdleConnectionManagerPrivate))
+
+static void
+idle_connection_manager_init (IdleConnectionManager *obj)
+{
+ IdleConnectionManagerPrivate *priv = IDLE_CONNECTION_MANAGER_GET_PRIVATE (obj);
+
+ priv->connections = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+static void idle_connection_manager_dispose (GObject *object);
+static void idle_connection_manager_finalize (GObject *object);
+
+static void
+idle_connection_manager_class_init (IdleConnectionManagerClass *idle_connection_manager_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (idle_connection_manager_class);
+
+ g_type_class_add_private (idle_connection_manager_class, sizeof (IdleConnectionManagerPrivate));
+
+ object_class->dispose = idle_connection_manager_dispose;
+ object_class->finalize = idle_connection_manager_finalize;
+
+ signals[NEW_CONNECTION] =
+ g_signal_new ("new-connection",
+ G_OBJECT_CLASS_TYPE (idle_connection_manager_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_manager_marshal_VOID__STRING_STRING_STRING,
+ G_TYPE_NONE, 3, G_TYPE_STRING, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING);
+
+ signals[NO_MORE_CONNECTIONS] =
+ g_signal_new ("no-more-connections",
+ G_OBJECT_CLASS_TYPE (idle_connection_manager_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (idle_connection_manager_class), &dbus_glib_idle_connection_manager_object_info);
+}
+
+void
+idle_connection_manager_dispose (GObject *object)
+{
+ IdleConnectionManager *self = IDLE_CONNECTION_MANAGER (object);
+ IdleConnectionManagerPrivate *priv = IDLE_CONNECTION_MANAGER_GET_PRIVATE (self);
+
+ if (priv->dispose_has_run)
+ return;
+
+ priv->dispose_has_run = TRUE;
+
+ /* release any references held by the object here */
+
+ if (G_OBJECT_CLASS (idle_connection_manager_parent_class)->dispose)
+ G_OBJECT_CLASS (idle_connection_manager_parent_class)->dispose (object);
+}
+
+void
+idle_connection_manager_finalize (GObject *object)
+{
+ IdleConnectionManager *self = IDLE_CONNECTION_MANAGER (object);
+ IdleConnectionManagerPrivate *priv = IDLE_CONNECTION_MANAGER_GET_PRIVATE (self);
+
+ g_hash_table_destroy(priv->connections);
+
+ G_OBJECT_CLASS (idle_connection_manager_parent_class)->finalize (object);
+}
+
+/* private data */
+
+typedef struct _IdleParams IdleParams;
+
+struct _IdleParams
+{
+ gchar *nickname;
+ gchar *server;
+ guint16 port;
+ gchar *password;
+ gchar *realname;
+ gchar *charset;
+ gchar *quit_message;
+ gboolean use_ssl;
+};
+
+typedef struct _IdleParamSpec IdleParamSpec;
+
+struct _IdleParamSpec
+{
+ const gchar *name;
+ const gchar *dtype;
+ const GType gtype;
+ guint flags;
+ const gpointer def;
+ const gsize offset;
+};
+
+static const IdleParamSpec irc_params[] =
+{
+ {"account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_REQUIRED, NULL, G_STRUCT_OFFSET(IdleParams, nickname)},
+ {"server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_REQUIRED, NULL, G_STRUCT_OFFSET(IdleParams, server)},
+ {"port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(6667), G_STRUCT_OFFSET(IdleParams, port)},
+ {"password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, G_STRUCT_OFFSET(IdleParams, password)},
+ {"fullname", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "telepathy-idle user http://telepathy.freedesktop.org", G_STRUCT_OFFSET(IdleParams, realname)},
+ {"charset", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "UTF-8", G_STRUCT_OFFSET(IdleParams, charset)},
+ {"quit-message",DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "So long and thanks for all the IRC - telepathy-idle IRC Connection Manager for Telepathy - http://telepathy.freedesktop.org", G_STRUCT_OFFSET(IdleParams, quit_message)},
+ {"use-ssl", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), G_STRUCT_OFFSET(IdleParams, use_ssl)},
+ {NULL, NULL, 0, 0, NULL, 0}
+};
+
+/* private methods */
+
+static gboolean get_parameters (const char *proto, const IdleParamSpec **params, GError **error)
+{
+ if (!strcmp(proto, "irc"))
+ {
+ *params = irc_params;
+
+ return TRUE;
+ }
+ else
+ {
+ g_debug("%s: unknown protocol %s", G_STRFUNC, proto);
+
+ *error = g_error_new (TELEPATHY_ERRORS, NotImplemented, "unknown protocol %s", proto);
+
+ return FALSE;
+ }
+}
+
+static void set_default_param (const IdleParamSpec *paramspec, IdleParams *params)
+{
+ switch (paramspec->dtype[0])
+ {
+ case DBUS_TYPE_STRING:
+ {
+ *((char **)((void *)params + paramspec->offset)) = g_strdup(paramspec->def);
+ break;
+ }
+ case DBUS_TYPE_UINT16:
+ {
+ *((guint16 *)((void *)params + paramspec->offset)) = GPOINTER_TO_INT(paramspec->def);
+ break;
+ }
+ case DBUS_TYPE_BOOLEAN:
+ {
+ *((gboolean *)((void *)params + paramspec->offset)) = GPOINTER_TO_INT(paramspec->def);
+ break;
+ }
+ default:
+ {
+ g_error("%s: unknown parameter type %s on parameter %s", G_STRFUNC, paramspec->dtype, paramspec->name);
+ break;
+ }
+ }
+}
+
+static gboolean set_param_from_value (const IdleParamSpec *paramspec, GValue *value, IdleParams *params, GError **error)
+{
+ if (G_VALUE_TYPE (value) != paramspec->gtype)
+ {
+ g_debug("%s: expected type %s for parameter %s, got %s", G_STRFUNC,
+ g_type_name (paramspec->gtype),
+ paramspec->name,
+ G_VALUE_TYPE_NAME (value));
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument,
+ "expected type %s for account parameter %s, got %s",
+ g_type_name (paramspec->gtype), paramspec->name,
+ G_VALUE_TYPE_NAME (value));
+ return FALSE;
+ }
+
+ switch (paramspec->dtype[0])
+ {
+ case DBUS_TYPE_STRING:
+ {
+ *((char **)((void *)params + paramspec->offset)) = g_value_dup_string (value);
+ break;
+ }
+ case DBUS_TYPE_UINT16:
+ {
+ *((guint16 *)((void *)params + paramspec->offset)) = g_value_get_uint (value);
+ break;
+ }
+ case DBUS_TYPE_BOOLEAN:
+ {
+ *((gboolean *)((void *)params + paramspec->offset)) = g_value_get_boolean (value);
+ break;
+ }
+ default:
+ {
+ g_error("%s: encountered unknown type %s on argument %s", G_STRFUNC, paramspec->dtype, paramspec->name);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean parse_parameters (const IdleParamSpec *paramspec, GHashTable *provided, IdleParams *params, GError **error)
+{
+ int params_left;
+ int i;
+
+ params_left = g_hash_table_size(provided);
+
+ for (i=0; paramspec[i].name; i++)
+ {
+ GValue *value;
+ value = g_hash_table_lookup(provided, paramspec[i].name);
+
+ if (!value)
+ {
+ if (paramspec[i].flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED)
+ {
+ g_debug("%s: missing REQUIRED param %s", G_STRFUNC, paramspec[i].name);
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument,
+ "missing REQUIRED account parameter %s",
+ paramspec[i].name);
+ return FALSE;
+ }
+ else
+ {
+ g_debug("%s: using default value for param %s",
+ G_STRFUNC, paramspec[i].name);
+ set_default_param(&(paramspec[i]), params);
+ }
+ }
+ else
+ {
+ if (!set_param_from_value(&(paramspec[i]), value, params, error))
+ {
+ return FALSE;
+ }
+
+ params_left--;
+
+ if (paramspec[i].gtype == G_TYPE_STRING)
+ {
+ g_debug("%s: new value %s for param %s", G_STRFUNC,
+ *((char **)((void *)params+paramspec[i].offset)), paramspec[i].name);
+ }
+ else
+ {
+ g_debug("%s: new value %u for param %s", G_STRFUNC,
+ *((guint *)((void *)params+paramspec[i].offset)), paramspec[i].name);
+
+ }
+ }
+ }
+
+ if (params_left)
+ {
+ g_debug("%s: %u unknown parameters given", G_STRFUNC, params_left);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void free_params (IdleParams *params)
+{
+ g_free(params->nickname);
+ g_free(params->server);
+ g_free(params->password);
+ g_free(params->realname);
+ g_free(params->charset);
+ g_free(params->quit_message);
+}
+
+static void connection_disconnected_cb(IdleConnection *conn, gpointer data)
+{
+ IdleConnectionManager *self = IDLE_CONNECTION_MANAGER(data);
+ IdleConnectionManagerPrivate *priv = IDLE_CONNECTION_MANAGER_GET_PRIVATE(self);
+
+ g_assert(g_hash_table_lookup(priv->connections, conn));
+ g_hash_table_remove(priv->connections, conn);
+
+ g_object_unref(conn);
+
+ g_debug("%s: unref'd connection", G_STRFUNC);
+
+ if (!g_hash_table_size(priv->connections))
+ {
+ g_signal_emit(self, signals[NO_MORE_CONNECTIONS], 0);
+ }
+}
+
+/* public methods */
+
+void
+_idle_connection_manager_register (IdleConnectionManager *self)
+{
+ DBusGConnection *bus;
+ DBusGProxy *bus_proxy;
+ GError *error = NULL;
+ guint request_name_result;
+
+ g_assert (IDLE_IS_CONNECTION_MANAGER (self));
+
+ bus = tp_get_bus ();
+ bus_proxy = tp_get_bus_proxy ();
+
+ if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error,
+ G_TYPE_STRING, BUS_NAME,
+ G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &request_name_result,
+ G_TYPE_INVALID))
+ g_error ("Failed to request bus name: %s", error->message);
+
+ if (request_name_result == DBUS_REQUEST_NAME_REPLY_EXISTS)
+ g_error ("Failed to acquire bus name, connection manager already running?");
+
+ dbus_g_connection_register_g_object (bus, OBJECT_PATH, G_OBJECT (self));
+}
+
+/* dbus-exported methods */
+
+/**
+ * idle_connection_manager_connect
+ *
+ * Implements DBus method Connect
+ * on interface org.freedesktop.Telepathy.ConnectionManager
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_manager_request_connection (IdleConnectionManager *obj,
+ const gchar *proto,
+ GHashTable *parameters,
+ gchar **bus_name,
+ gchar **object_path,
+ GError **error)
+{
+ IdleConnectionManager *self = IDLE_CONNECTION_MANAGER(obj);
+ IdleConnectionManagerPrivate *priv = IDLE_CONNECTION_MANAGER_GET_PRIVATE(self);
+ IdleConnection *conn;
+ const IdleParamSpec *paramspec;
+ IdleParams params = {NULL};
+
+ g_assert(IDLE_IS_CONNECTION_MANAGER(self));
+
+ if (!get_parameters(proto, &paramspec, error))
+ {
+ return FALSE;
+ }
+
+ if (!parse_parameters(paramspec, parameters, &params, error))
+ {
+ free_params(&params);
+ return FALSE;
+ }
+
+ conn = g_object_new(IDLE_TYPE_CONNECTION,
+ "protocol", proto,
+ "nickname", params.nickname,
+ "server", params.server,
+ "port", params.port,
+ "password", params.password,
+ "realname", params.realname,
+ "charset", params.charset,
+ "quit-message", params.quit_message,
+ "use-ssl", params.use_ssl,
+ NULL);
+
+ free_params(&params);
+
+ if (!_idle_connection_register(conn, bus_name, object_path, error))
+ {
+ g_debug("%s failed: %s", G_STRFUNC, (*error)->message);
+ goto lerror;
+ }
+
+ g_signal_connect(conn, "disconnected", G_CALLBACK(connection_disconnected_cb), self);
+ g_hash_table_insert(priv->connections, conn, GINT_TO_POINTER(TRUE));
+
+ g_signal_emit(obj, signals[NEW_CONNECTION], 0, *bus_name, *object_path, proto);
+
+ return TRUE;
+
+lerror:
+ if (conn)
+ {
+ g_object_unref(G_OBJECT(conn));
+ }
+
+ return FALSE;
+}
+
+gboolean idle_connection_manager_get_parameters (IdleConnectionManager *obj,
+ const gchar *proto,
+ GPtrArray **ret,
+ GError **error)
+{
+ const IdleParamSpec *params = NULL;
+ const IdleParamSpec *param;
+
+ if (!get_parameters(proto, &params, error))
+ {
+ return FALSE;
+ }
+
+ *ret = g_ptr_array_new();
+
+ for (param = params; param->name != NULL; param++)
+ {
+ GValue tp_param = {0, };
+ GValue *def;
+
+ def = g_new0(GValue, 1);
+ g_value_init(def, param->gtype);
+
+ g_value_init(&tp_param, dbus_g_type_get_struct ("GValueArray",
+ G_TYPE_STRING,
+ G_TYPE_UINT,
+ G_TYPE_STRING,
+ G_TYPE_VALUE,
+ G_TYPE_INVALID));
+
+ g_value_set_static_boxed(&tp_param,
+ dbus_g_type_specialized_construct(
+ dbus_g_type_get_struct ("GValueArray",
+ G_TYPE_STRING,
+ G_TYPE_UINT,
+ G_TYPE_STRING,
+ G_TYPE_VALUE,
+ G_TYPE_INVALID)));
+
+ if (param->def)
+ {
+ switch (param->gtype)
+ {
+ case G_TYPE_STRING:
+ {
+ g_value_set_static_string(def, param->def);
+ }
+ break;
+ case G_TYPE_UINT:
+ {
+ g_value_set_uint(def, GPOINTER_TO_UINT(param->def));
+ }
+ break;
+ case G_TYPE_BOOLEAN:
+ {
+ g_value_set_boolean(def, GPOINTER_TO_UINT(param->def));
+ }
+ break;
+ default:
+ {
+ g_assert_not_reached();
+ }
+ break;
+ }
+ }
+
+ dbus_g_type_struct_set(&tp_param,
+ 0, param->name,
+ 1, param->flags,
+ 2, param->dtype,
+ 3, def,
+ G_MAXUINT);
+
+ g_value_unset(def);
+ g_free(def);
+
+ g_ptr_array_add(*ret, g_value_get_boxed(&tp_param));
+ }
+
+ return TRUE;
+}
+
+/**
+ * idle_connection_manager_list_protocols
+ *
+ * Implements DBus method ListProtocols
+ * on interface org.freedesktop.Telepathy.ConnectionManager
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_manager_list_protocols (IdleConnectionManager *obj,
+ gchar *** ret,
+ GError **error)
+{
+ const char *protocols[] = {"irc", NULL};
+
+ *ret = g_strdupv((gchar **)(protocols));
+
+ return TRUE;
+}
+
diff --git a/src/idle-connection-manager.h b/src/idle-connection-manager.h
new file mode 100644
index 0000000..f98839f
--- /dev/null
+++ b/src/idle-connection-manager.h
@@ -0,0 +1,64 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_CONNECTION_MANAGER_H__
+#define __IDLE_CONNECTION_MANAGER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdleConnectionManager IdleConnectionManager;
+typedef struct _IdleConnectionManagerClass IdleConnectionManagerClass;
+
+struct _IdleConnectionManagerClass {
+ GObjectClass parent_class;
+};
+
+struct _IdleConnectionManager {
+ GObject parent;
+};
+
+GType idle_connection_manager_get_type(void);
+
+/* TYPE MACROS */
+#define IDLE_TYPE_CONNECTION_MANAGER \
+ (idle_connection_manager_get_type())
+#define IDLE_CONNECTION_MANAGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_CONNECTION_MANAGER, IdleConnectionManager))
+#define IDLE_CONNECTION_MANAGER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_CONNECTION_MANAGER, IdleConnectionManagerClass))
+#define IDLE_IS_CONNECTION_MANAGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_CONNECTION_MANAGER))
+#define IDLE_IS_CONNECTION_MANAGER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_CONNECTION_MANAGER))
+#define IDLE_CONNECTION_MANAGER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_CONNECTION_MANAGER, IdleConnectionManagerClass))
+
+void _idle_connection_manager_register (IdleConnectionManager *self);
+
+gboolean idle_connection_manager_request_connection (IdleConnectionManager *obj, const gchar * proto, GHashTable * parameters, gchar ** ret, gchar ** ret1, GError **error);
+gboolean idle_connection_manager_get_parameters (IdleConnectionManager *obj, const gchar * proto, GPtrArray **ret, GError **error);
+gboolean idle_connection_manager_list_protocols (IdleConnectionManager *obj, gchar *** ret, GError **error);
+
+
+G_END_DECLS
+
+#endif /* #ifndef __IDLE_CONNECTION_MANAGER_H__*/
diff --git a/src/idle-connection-signals-marshal.list b/src/idle-connection-signals-marshal.list
new file mode 100644
index 0000000..239c530
--- /dev/null
+++ b/src/idle-connection-signals-marshal.list
@@ -0,0 +1,4 @@
+VOID:INT,BOXED,BOXED
+VOID:STRING,STRING,INT,INT,BOOLEAN
+VOID:BOXED
+VOID:INT,INT
diff --git a/src/idle-connection.c b/src/idle-connection.c
new file mode 100644
index 0000000..a27dbda
--- /dev/null
+++ b/src/idle-connection.c
@@ -0,0 +1,5377 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+
+/* strnlen */
+#define __USE_GNU
+#include <string.h>
+#include <ctype.h>
+
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "telepathy-constants.h"
+#include "telepathy-errors.h"
+#include "telepathy-helpers.h"
+#include "telepathy-interfaces.h"
+
+#include "idle-handles.h"
+#include "idle-handle-set.h"
+#include "idle-im-channel.h"
+#include "idle-muc-channel.h"
+#include "gintset.h"
+
+#include "idle-server-connection.h"
+#include "idle-ssl-server-connection.h"
+#include "idle-server-connection-iface.h"
+#include "idle-server-connection-util.h"
+
+#include "idle-connection.h"
+#include "idle-connection-glue.h"
+#include "idle-connection-signals-marshal.h"
+
+#include "idle-version.h"
+
+#define BUS_NAME "org.freedesktop.Telepathy.Connection.idle"
+#define OBJECT_PATH "/org/freedesktop/Telepathy/Connection/idle"
+
+/*
+#define TP_CAPABILITY_PAIR_TYPE (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID))
+*/
+
+G_DEFINE_TYPE(IdleConnection, idle_connection, G_TYPE_OBJECT);
+
+#define IRC_PRESENCE_SHOW_AVAILABLE "available"
+#define IRC_PRESENCE_SHOW_AWAY "away"
+#define IRC_PRESENCE_SHOW_OFFLINE "offline"
+
+/* maximum IRC message payload */
+#define IRC_MSG_MAXLEN 510
+
+#define ERROR_IF_NOT_CONNECTED(CONN, PRIV, ERROR) \
+ if ((PRIV)->status != TP_CONN_STATUS_CONNECTED) \
+ { \
+ /* if (((PRIV)->conn.crashed)) socket_conn_close(CONN);*/ \
+ g_debug ("%s: rejected request as disconnected", G_STRFUNC); \
+ (ERROR) = g_error_new(TELEPATHY_ERRORS, NotAvailable, \
+ "Connection is disconnected"); \
+ return FALSE; \
+ }
+
+#define ERROR_IF_NOT_CONNECTED_ASYNC(CONN, PRIV, ERROR, CONTEXT) \
+ if ((PRIV)->status != TP_CONN_STATUS_CONNECTED) \
+ { \
+ /*if (((PRIV)->conn.crashed)) socket_conn_close(CONN); */\
+ g_debug ("%s: rejected request as disconnected", G_STRFUNC); \
+ (ERROR) = g_error_new(TELEPATHY_ERRORS, NotAvailable, \
+ "Connection is disconnected"); \
+ dbus_g_method_return_error ((CONTEXT), (ERROR)); \
+ g_error_free ((ERROR)); \
+ return FALSE; \
+ }
+
+struct _IdleContactPresence
+{
+ IdlePresenceState presence_state;
+ gchar *status_message;
+ guint last_activity;
+};
+
+#define idle_contact_presence_new() (g_slice_new(IdleContactPresence))
+#define idle_contact_presence_new0() (g_slice_new0(IdleContactPresence))
+
+typedef struct _IdleStatusInfo IdleStatusInfo;
+
+struct _IdleStatusInfo
+{
+ const gchar *name;
+ TpConnectionPresenceType presence_type;
+ const gboolean self, exclusive;
+};
+
+static const IdleStatusInfo idle_statuses[LAST_IDLE_PRESENCE_ENUM] =
+{
+ {IRC_PRESENCE_SHOW_AVAILABLE, TP_CONN_PRESENCE_TYPE_AVAILABLE, TRUE, TRUE},
+ {IRC_PRESENCE_SHOW_AWAY, TP_CONN_PRESENCE_TYPE_AWAY, TRUE, TRUE},
+ {IRC_PRESENCE_SHOW_OFFLINE, TP_CONN_PRESENCE_TYPE_OFFLINE, FALSE, TRUE}
+};
+
+void idle_contact_presence_free(IdleContactPresence *cp)
+{
+ g_free(cp->status_message);
+ g_slice_free(IdleContactPresence, cp);
+}
+
+typedef struct
+{
+ DBusGMethodInvocation *ctx;
+ gboolean suppress_handler;
+} chan_req_data;
+
+/* signal enum */
+enum
+{
+/* CAPABILITIES_CHANGED,*/
+ NEW_CHANNEL,
+ PRESENCE_UPDATE,
+ STATUS_CHANGED,
+ DISCONNECTED,
+ RENAMED,
+ LAST_SIGNAL_ENUM
+};
+
+enum
+{
+ PROP_PROTOCOL = 1,
+ PROP_NICKNAME,
+ PROP_SERVER,
+ PROP_PORT,
+ PROP_PASSWORD,
+ PROP_REALNAME,
+ PROP_CHARSET,
+ PROP_QUITMESSAGE,
+ PROP_USE_SSL,
+ LAST_PROPERTY_ENUM
+};
+
+static guint signals[LAST_SIGNAL_ENUM] = {0};
+
+/* private structure */
+typedef struct _IdleConnectionPrivate IdleConnectionPrivate;
+
+#if 0
+/*
+ * socket/stdio network connection
+ */
+typedef struct _socket_conn socket_conn;
+
+typedef void (*socket_conn_connected_cb_t)(IdleConnection *conn, gboolean success);
+typedef void (*socket_conn_disconnected_cb_t)(IdleConnection *conn, TpConnectionStatusReason reason);
+typedef void (*socket_conn_message_cb_t)(IdleConnection *conn, const gchar *msg);
+
+struct _socket_conn
+{
+ int fd;
+ /*FILE *stream;*/
+
+ /* we need to spawn a thread to play ping-pong with the server
+ * while it does that it also sends to the server whatever gets in out_queue
+ */
+
+ pthread_t thread;
+
+ /* output message queue */
+ GAsyncQueue *out_queue;
+
+ /* possible partial message saved from a earlier bunch */
+ gchar *msg_store;
+
+ /* if the comm thread should pause the sending of the queue for a while */
+ gboolean queue_wait;
+
+ /* if the comm thread should try and exit in a finite time */
+ gboolean exit;
+
+ /* if the comm thread has encountered trouble and wants to be cleaned up */
+ gboolean crashed;
+
+ /* callback to call after a connect attempt / a disconnect / a incoming message line */
+ socket_conn_connected_cb_t connected_cb;
+ socket_conn_disconnected_cb_t disconnected_cb;
+ socket_conn_message_cb_t message_cb;
+};
+#endif
+
+struct _IdleConnectionPrivate
+{
+ /*
+ * network connection
+ */
+
+ IdleServerConnectionIface *conn;
+ guint sconn_status;
+ guint sconn_timeout;
+
+ /*
+ * disconnect reason
+ */
+
+ TpConnectionStatusReason disconnect_reason;
+
+ /* telepathy properties */
+ char *protocol;
+
+ /* IRC connection properties */
+ char *nickname;
+ char *server;
+ guint port;
+ char *password;
+ char *realname;
+ char *charset;
+ char *quit_message;
+ gboolean use_ssl;
+
+ /* dbus object location */
+ char *bus_name;
+ char *object_path;
+
+ /* info about us in CTCP VERSION format */
+ char *ctcp_version_string;
+
+ /* connection status */
+ TpConnectionStatus status;
+
+ /* handles */
+ IdleHandleStorage *handles;
+ IdleHandle self_handle;
+
+ /* channel request contexts */
+ GHashTable *chan_req_ctxs;
+
+ /* channels (for queries and regular IRC channels respectively) */
+ GHashTable *im_channels;
+ GHashTable *muc_channels;
+
+ /* client handle set lists */
+ GData *cl_contact_handle_sets;
+ GData *cl_room_handle_sets;
+
+ /* Set of handles whose presence status we should poll and associated polling timer */
+ GIntSet *polled_presences;
+ guint presence_polling_timer_id;
+
+ /* presence query queue, unload timer and pending reply list */
+ GQueue *presence_queue;
+ guint presence_unload_timer_id;
+ GList *presence_reply_list;
+
+ /* if Disconnected has been emitted */
+ gboolean disconnected;
+
+ /* continuation line buffer */
+ gchar splitbuf[IRC_MSG_MAXLEN+3];
+
+ /* output message queue */
+ GQueue *msg_queue;
+
+ /* UNIX time the last message was sent on */
+ time_t last_msg_sent;
+
+ /* GSource id for message queue unloading timeout */
+ guint msg_queue_timeout;
+
+ /* if idle_connection_dispose has already run once */
+ gboolean dispose_has_run;
+};
+
+#define IDLE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_CONNECTION, IdleConnectionPrivate))
+
+static void
+idle_connection_init (IdleConnection *obj)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (obj);
+
+#if 0
+ priv->conn.fd = 0;
+/* priv->conn.stream = NULL; */
+ priv->conn.thread = (pthread_t)(0);
+ priv->conn.out_queue = NULL;
+ priv->conn.exit = FALSE;
+ priv->conn.crashed = FALSE;
+ priv->conn.msg_store = NULL;
+#endif
+
+ priv->port = 6667;
+ priv->password = NULL;
+ priv->realname = NULL;
+ priv->charset = NULL;
+ priv->quit_message = NULL;
+ priv->use_ssl = FALSE;
+
+ priv->conn = NULL;
+ priv->sconn_status = SERVER_CONNECTION_STATE_NOT_CONNECTED;
+
+ priv->ctcp_version_string = g_strdup_printf("telepathy-idle %s Telepathy IM/VoIP framework http://telepathy.freedesktop.org", TELEPATHY_IDLE_VERSION);
+
+ priv->handles = idle_handle_storage_new();
+ priv->chan_req_ctxs = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+ priv->im_channels = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
+ priv->muc_channels = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
+
+ priv->polled_presences = g_intset_new();
+ priv->presence_polling_timer_id = 0;
+
+ priv->presence_queue = g_queue_new();
+ priv->presence_unload_timer_id = 0;
+ priv->presence_reply_list = NULL;
+
+ priv->status = TP_CONN_STATUS_DISCONNECTED;
+
+ g_datalist_init(&(priv->cl_contact_handle_sets));
+ g_datalist_init(&(priv->cl_room_handle_sets));
+
+ memset(priv->splitbuf, 0, IRC_MSG_MAXLEN+3);
+ priv->msg_queue = g_queue_new();
+ priv->last_msg_sent = 0;
+ priv->msg_queue_timeout = 0;
+
+ priv->disconnected = FALSE;
+}
+
+static void idle_connection_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void idle_connection_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec);
+static void idle_connection_dispose (GObject *object);
+static void idle_connection_finalize (GObject *object);
+
+static void idle_connection_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ IdleConnection *self = (IdleConnection *)(obj);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
+
+ switch (prop_id)
+ {
+ case PROP_PROTOCOL:
+ {
+ g_free(priv->protocol);
+ priv->protocol = g_value_dup_string(value);
+ }
+ break;
+ case PROP_NICKNAME:
+ {
+ g_free(priv->nickname);
+ priv->nickname = g_value_dup_string(value);
+
+ if (strlen(priv->nickname) > 9)
+ {
+ char *p = g_utf8_offset_to_pointer (priv->nickname, 9);
+ *p = '\0';
+ g_debug("%s: nickname was longer than 9 characters; clipped to %s", G_STRFUNC, priv->nickname);
+ }
+ }
+ break;
+ case PROP_SERVER:
+ {
+ g_free(priv->server);
+ priv->server = g_value_dup_string(value);
+ }
+ break;
+ case PROP_PORT:
+ {
+ priv->port = g_value_get_uint(value);
+ }
+ break;
+ case PROP_PASSWORD:
+ {
+ g_free(priv->password);
+ priv->password = g_value_dup_string(value);
+ }
+ break;
+ case PROP_REALNAME:
+ {
+ g_free(priv->realname);
+ priv->realname = g_value_dup_string(value);
+ }
+ break;
+ case PROP_CHARSET:
+ {
+ g_free(priv->charset);
+ priv->charset = g_value_dup_string(value);
+ }
+ break;
+ case PROP_QUITMESSAGE:
+ {
+ g_free(priv->quit_message);
+ priv->quit_message = g_value_dup_string(value);
+ }
+ break;
+ case PROP_USE_SSL:
+ {
+ g_debug("%s: setting use_ssl to %u", G_STRFUNC, priv->use_ssl);
+ priv->use_ssl = g_value_get_boolean(value);
+ }
+ break;
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+ }
+ break;
+ }
+}
+
+static void idle_connection_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ IdleConnection *self = (IdleConnection *)(obj);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
+
+ switch (prop_id)
+ {
+ case PROP_PROTOCOL:
+ {
+ g_value_set_string(value, priv->protocol);
+ }
+ break;
+ case PROP_NICKNAME:
+ {
+ g_value_set_string(value, priv->nickname);
+ }
+ break;
+ case PROP_SERVER:
+ {
+ g_value_set_string(value, priv->server);
+ }
+ break;
+ case PROP_PORT:
+ {
+ g_value_set_uint(value, priv->port);
+ }
+ break;
+ case PROP_PASSWORD:
+ {
+ g_value_set_string(value, priv->password);
+ }
+ break;
+ case PROP_REALNAME:
+ {
+ g_value_set_string(value, priv->realname);
+ }
+ break;
+ case PROP_CHARSET:
+ {
+ g_value_set_string(value, priv->charset);
+ }
+ break;
+ case PROP_QUITMESSAGE:
+ {
+ g_value_set_string(value, priv->quit_message);
+ }
+ break;
+ case PROP_USE_SSL:
+ {
+ g_value_set_boolean(value, priv->use_ssl);
+ }
+ break;
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+ }
+ break;
+ }
+}
+
+static void
+idle_connection_class_init (IdleConnectionClass *idle_connection_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (idle_connection_class);
+ GParamSpec *param_spec;
+
+ object_class->set_property = idle_connection_set_property;
+ object_class->get_property = idle_connection_get_property;
+
+ g_type_class_add_private (idle_connection_class, sizeof (IdleConnectionPrivate));
+
+ object_class->dispose = idle_connection_dispose;
+ object_class->finalize = idle_connection_finalize;
+
+ /* params */
+
+ param_spec = g_param_spec_string ("protocol", "Telepathy identifier for protocol",
+ "Identifier string used when the protocol "
+ "name is required. Unused internally.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_PROTOCOL, param_spec);
+
+ param_spec = g_param_spec_string ("nickname", "IRC nickname",
+ "The nickname to be visible to others in IRC.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_NICKNAME, param_spec);
+
+ param_spec = g_param_spec_string ("server", "Hostname or IP of the IRC server to connect to",
+ "The server used when establishing the connection.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_SERVER, param_spec);
+
+ param_spec = g_param_spec_uint ("port", "IRC server port",
+ "The destination port used when establishing the connection.",
+ 0, G_MAXUINT16, 6667,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_PORT, param_spec);
+
+ param_spec = g_param_spec_string ("password", "Server password",
+ "Password to authenticate to the server with",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_PASSWORD, param_spec);
+
+ param_spec = g_param_spec_string ("realname", "Real name",
+ "The real name of the user connecting to IRC",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_REALNAME, param_spec);
+
+ param_spec = g_param_spec_string ("charset", "Character set",
+ "The character set to use to communicate with the outside world",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CHARSET, param_spec);
+
+ param_spec = g_param_spec_string ("quit-message", "Quit message",
+ "The quit message to send to the server when leaving IRC",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_QUITMESSAGE, param_spec);
+
+ param_spec = g_param_spec_boolean ("use-ssl", "Use SSL",
+ "If the connection should use a SSL tunneled socket connection",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_USE_SSL, param_spec);
+
+
+ /* signals */
+/*
+ signals[CAPABILITIES_CHANGED] =
+ g_signal_new ("capabilities-changed",
+ G_OBJECT_CLASS_TYPE (idle_connection_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_marshal_VOID__INT_BOXED_BOXED,
+ G_TYPE_NONE, 3, G_TYPE_UINT, (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID)))), (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID)))));
+ */
+
+ signals[NEW_CHANNEL] =
+ g_signal_new ("new-channel",
+ G_OBJECT_CLASS_TYPE (idle_connection_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_marshal_VOID__STRING_STRING_INT_INT_BOOLEAN,
+ G_TYPE_NONE, 5, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN);
+
+ signals[PRESENCE_UPDATE] =
+ g_signal_new ("presence-update",
+ G_OBJECT_CLASS_TYPE (idle_connection_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, (dbus_g_type_get_map ("GHashTable", G_TYPE_UINT, (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)))), G_TYPE_INVALID)))));
+
+ signals[STATUS_CHANGED] =
+ g_signal_new ("status-changed",
+ G_OBJECT_CLASS_TYPE (idle_connection_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+ signals[DISCONNECTED] =
+ g_signal_new("disconnected",
+ G_OBJECT_CLASS_TYPE(idle_connection_class),
+ G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[RENAMED] =
+ g_signal_new("renamed",
+ G_OBJECT_CLASS_TYPE(idle_connection_class),
+ G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_connection_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (idle_connection_class), &dbus_glib_idle_connection_object_info);
+}
+
+static void close_all_channels(IdleConnection *conn);
+/*static void socket_conn_close(IdleConnection *conn);*/
+
+void
+idle_connection_dispose (GObject *object)
+{
+ IdleConnection *self = IDLE_CONNECTION (object);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (self);
+ DBusGProxy *dbus_proxy;
+
+ dbus_proxy = tp_get_bus_proxy();
+
+ if (priv->dispose_has_run)
+ {
+ return;
+ }
+ else
+ {
+ priv->dispose_has_run = TRUE;
+ }
+
+ g_debug("%s called", G_STRFUNC);
+
+ if (priv->im_channels)
+ {
+ g_assert(g_hash_table_size(priv->im_channels) == 0);
+ g_hash_table_destroy(priv->im_channels);
+ }
+
+ if (priv->muc_channels)
+ {
+ g_assert(g_hash_table_size(priv->muc_channels) == 0);
+ g_hash_table_destroy(priv->muc_channels);
+ }
+
+ if (priv->presence_polling_timer_id)
+ {
+ g_source_remove(priv->presence_polling_timer_id);
+ priv->presence_polling_timer_id = 0;
+ }
+
+ if (priv->presence_unload_timer_id)
+ {
+ g_source_remove(priv->presence_unload_timer_id);
+ priv->presence_unload_timer_id = 0;
+ }
+
+ if (priv->msg_queue_timeout)
+ {
+ g_source_remove(priv->msg_queue_timeout);
+ priv->msg_queue_timeout = 0;
+ }
+
+ if (priv->sconn_timeout)
+ {
+ g_source_remove(priv->sconn_timeout);
+ priv->sconn_timeout = 0;
+ }
+
+ if (priv->conn != NULL)
+ {
+ g_warning("%s: connection was open when the object was deleted, it'll probably crash now...", G_STRFUNC);
+
+ g_object_unref(priv->conn);
+ priv->conn = NULL;
+ }
+
+ dbus_g_proxy_call_no_reply(dbus_proxy, "ReleaseName", G_TYPE_STRING, priv->bus_name, G_TYPE_INVALID);
+
+ if (G_OBJECT_CLASS (idle_connection_parent_class)->dispose)
+ G_OBJECT_CLASS (idle_connection_parent_class)->dispose (object);
+}
+
+static void free_helper(gpointer data, gpointer unused)
+{
+ return g_free(data);
+}
+
+void
+idle_connection_finalize (GObject *object)
+{
+ IdleConnection *self = IDLE_CONNECTION (object);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (self);
+ gchar *nick;
+ IdleOutputPendingMsg *msg;
+
+ g_free(priv->protocol);
+ g_free(priv->nickname);
+ g_free(priv->server);
+ g_free(priv->password);
+ g_free(priv->realname);
+ g_free(priv->charset);
+ g_free(priv->quit_message);
+
+ g_free(priv->bus_name);
+ g_free(priv->object_path);
+
+ g_free(priv->ctcp_version_string);
+
+ g_datalist_clear(&(priv->cl_contact_handle_sets));
+ g_datalist_clear(&(priv->cl_room_handle_sets));
+
+ g_hash_table_destroy(priv->chan_req_ctxs);
+
+ g_intset_destroy(priv->polled_presences);
+
+ while ((nick = g_queue_pop_head(priv->presence_queue)) != NULL)
+ {
+ g_free(nick);
+ }
+
+ g_queue_free(priv->presence_queue);
+
+ while ((msg = g_queue_pop_head(priv->msg_queue)) != NULL)
+ {
+ idle_output_pending_msg_free(msg);
+ }
+
+ g_queue_free(priv->msg_queue);
+
+ if (priv->presence_reply_list)
+ {
+ g_list_foreach(priv->presence_reply_list, free_helper, NULL);
+ g_list_free(priv->presence_reply_list);
+ }
+
+ G_OBJECT_CLASS (idle_connection_parent_class)->finalize (object);
+}
+
+#if 0
+void socket_conn_close(IdleConnection *conn)
+{
+ IdleConnectionPrivate *priv;
+
+ g_assert(conn != NULL);
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ if (priv->conn.fd)
+ {
+ int rc;
+
+ priv->conn.exit = TRUE;
+
+ if ((rc = pthread_join(priv->conn.thread, NULL)) != 0)
+ {
+ g_debug("%s: pthread_join returned %d", G_STRFUNC, rc);
+ }
+
+ g_async_queue_unref(priv->conn.out_queue);
+
+ close(priv->conn.fd);
+ priv->conn.fd = 0;
+ priv->conn.crashed = FALSE;
+ }
+ else
+ {
+ g_debug("%s: connection already closed!", G_STRFUNC);
+ }
+}
+#endif
+
+gboolean _idle_connection_register(IdleConnection *conn, gchar **bus_name, gchar **object_path, GError **error)
+{
+ DBusGConnection *bus;
+ DBusGProxy *bus_proxy;
+ IdleConnectionPrivate *priv;
+ const char *allowed_chars = "_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ gchar *safe_proto;
+ gchar *unique_name;
+ guint request_name_result;
+ GError *request_error;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ bus = tp_get_bus();
+ bus_proxy = tp_get_bus_proxy();
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ safe_proto = g_strdup(priv->protocol);
+ g_strcanon(safe_proto, allowed_chars, '_');
+
+ unique_name = g_strdup_printf("%s_%s", priv->nickname, priv->server);
+ g_strcanon(unique_name, allowed_chars, '_');
+
+ priv->bus_name = g_strdup_printf(BUS_NAME ".%s.%s", safe_proto, unique_name);
+ priv->object_path = g_strdup_printf(OBJECT_PATH "/%s/%s", safe_proto, unique_name);
+
+ g_free(safe_proto);
+ g_free(unique_name);
+
+ g_assert(error != NULL);
+
+ if (!dbus_g_proxy_call(bus_proxy, "RequestName", &request_error,
+ G_TYPE_STRING, priv->bus_name,
+ G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &request_name_result,
+ G_TYPE_INVALID))
+ {
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, request_error->message);
+ return FALSE;
+ }
+
+ if (request_name_result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ {
+ gchar *msg;
+
+ switch (request_name_result)
+ {
+ case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
+ {
+ msg = "Request has been queued, though we request non-queuing.";
+ }
+ break;
+ case DBUS_REQUEST_NAME_REPLY_EXISTS:
+ {
+ msg = "A connection manager already has this busname in use.";
+ }
+ break;
+ case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+ {
+ msg = "The connection manager already has this connection active.";
+ }
+ break;
+ default:
+ {
+ msg = "Unknown error return from RequestName";
+ }
+ break;
+ }
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "Error acquiring bus name %s, %s", priv->bus_name, msg);
+ return FALSE;
+ }
+
+ g_debug("%s: bus_name %s", G_STRFUNC, priv->bus_name);
+
+ dbus_g_connection_register_g_object(bus, priv->object_path, G_OBJECT(conn));
+
+ g_debug("%s: object path %s", G_STRFUNC, priv->object_path);
+
+ g_assert(bus_name != NULL);
+ g_assert(object_path != NULL);
+
+ *bus_name = g_strdup(priv->bus_name);
+ *object_path = g_strdup(priv->object_path);
+
+ return TRUE;
+}
+
+IdleHandleStorage *_idle_connection_get_handles(IdleConnection *conn)
+{
+ IdleConnectionPrivate *priv;
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ return priv->handles;
+}
+
+/*static void *socket_conn_thread_fn(void* conn);*/
+/*static gboolean socket_conn_open(IdleConnection *conn, GError **error);*/
+static void irc_handshakes(IdleConnection *conn);
+static IdleIMChannel *new_im_channel(IdleConnection *conn, IdleHandle handle, gboolean suppress_handler);
+static IdleMUCChannel *new_muc_channel(IdleConnection *conn, IdleHandle handle, gboolean suppress_handler);
+static void connection_connect_cb(IdleConnection *conn, gboolean success);
+static void connection_disconnect(IdleConnection *conn, TpConnectionStatusReason reason);
+static void connection_disconnect_cb(IdleConnection *conn, TpConnectionStatusReason reason);
+static void update_presence(IdleConnection *self, IdleHandle contact_handle, IdlePresenceState presence_id, const gchar *status_message);
+static void update_presence_full(IdleConnection *self, IdleHandle contact_handle, IdlePresenceState presence_id, const gchar *status_message, guint last_activity);
+static void connection_status_change(IdleConnection *conn, TpConnectionStatus status, TpConnectionStatusReason reason);
+static void connection_message_cb(IdleConnection *conn, const gchar *msg);
+static void emit_presence_update(IdleConnection *self, const IdleHandle *contact_handles);
+static void send_irc_cmd(IdleConnection *conn, const gchar *msg);
+static void handle_err_erroneusnickname(IdleConnection *conn);
+static void handle_err_nicknameinuse(IdleConnection *conn);
+static void priv_rename(IdleConnection *conn, guint old, guint new);
+
+static void handle_err_nicknameinuse(IdleConnection *conn)
+{
+/* IdleConnectionPrivate *priv;
+ gchar *newnick;
+ IdleHandle handle;
+ gchar cmd[IRC_MSG_MAXLEN+2];
+ GError *error;
+ gchar *tmp;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ */
+ connection_status_change(conn, TP_CONN_STATUS_DISCONNECTED, TP_CONN_STATUS_REASON_NAME_IN_USE);
+ /* CHANGE: do not handle it ourselves anymore. Instead fail and signal UI about the situation. */
+#if 0
+ g_assert(priv->nickname != NULL);
+
+ newnick = g_strdup_printf("%s_", priv->nickname);
+
+ g_free(priv->nickname);
+
+ priv->nickname = newnick;
+
+ if (!idle_connection_hton(conn, newnick, &tmp, &error))
+ {
+ g_debug("%s: failed charset conversion, sending unconverted...: %s", G_STRFUNC, error->message);
+ tmp = newnick;
+ }
+
+ g_snprintf(cmd, IRC_MSG_MAXLEN+2, "NICK %s", tmp);
+
+ send_irc_cmd(conn, cmd);
+
+ if (tmp != newnick)
+ {
+ g_free(tmp);
+ }
+
+ g_debug("%s: new nickname is %s", G_STRFUNC, priv->nickname);
+
+ handle = idle_handle_for_contact(priv->handles, newnick);
+
+ g_assert(handle != 0);
+
+ priv_rename(conn, priv->self_handle, handle);
+#endif
+}
+
+static void handle_err_erroneusnickname(IdleConnection *conn)
+{
+ connection_status_change(conn, TP_CONN_STATUS_DISCONNECTED, TP_CONN_STATUS_REASON_AUTHENTICATION_FAILED);
+}
+
+typedef struct
+{
+ guint old, new;
+} MUCChannelRenameData;
+
+static void muc_channel_rename_foreach(gpointer key, gpointer value, gpointer data)
+{
+ IdleMUCChannel *chan = IDLE_MUC_CHANNEL(value);
+
+ MUCChannelRenameData *rename_data = (MUCChannelRenameData *)(data);
+
+ _idle_muc_channel_rename(chan, rename_data->old, rename_data->new);
+}
+
+static gboolean msg_queue_timeout_cb(gpointer user_data);
+
+static gboolean sconn_timeout_cb(gpointer user_data)
+{
+ IdleConnection *conn = IDLE_CONNECTION(user_data);
+
+ connection_connect_cb(conn, FALSE);
+ connection_disconnect_cb(conn, TP_CONN_STATUS_REASON_NETWORK_ERROR);
+
+ return FALSE;
+}
+
+static void sconn_status_changed_cb(IdleServerConnectionIface *sconn, IdleServerConnectionState state, IdleServerConnectionStateReason reason, IdleConnection *conn)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ g_debug("%s: called with state %u", G_STRFUNC, state);
+
+ TpConnectionStatusReason tp_reason;
+
+ switch (reason)
+ {
+ case SERVER_CONNECTION_STATE_REASON_ERROR:
+ {
+ tp_reason = TP_CONN_STATUS_REASON_NETWORK_ERROR;
+ }
+ break;
+ case SERVER_CONNECTION_STATE_REASON_REQUESTED:
+ {
+ tp_reason = TP_CONN_STATUS_REASON_REQUESTED;
+ }
+ break;
+ default:
+ {
+ g_assert_not_reached();
+ }
+ break;
+ }
+
+ if (priv->sconn_timeout)
+ {
+ g_source_remove(priv->sconn_timeout);
+ priv->sconn_timeout = 0;
+ }
+
+ switch (state)
+ {
+ case SERVER_CONNECTION_STATE_NOT_CONNECTED:
+ {
+ if (priv->status == TP_CONN_STATUS_CONNECTING)
+ {
+ connection_connect_cb(conn, FALSE);
+ connection_disconnect_cb(conn, tp_reason);
+ }
+ else
+ {
+ connection_disconnect_cb(conn, reason);
+ }
+ }
+ break;
+ case SERVER_CONNECTION_STATE_CONNECTING:
+ {
+ priv->sconn_timeout = g_timeout_add(CONNECTION_TIMEOUT, sconn_timeout_cb, conn);
+ }
+ break;
+ case SERVER_CONNECTION_STATE_CONNECTED:
+ {
+ if ((priv->msg_queue_timeout == 0) && (g_queue_get_length(priv->msg_queue) > 0))
+ {
+ g_debug("%s: we had messages in queue, start unloading them now", G_STRFUNC);
+
+ priv->msg_queue_timeout = g_timeout_add(MSG_QUEUE_TIMEOUT, msg_queue_timeout_cb, conn);
+ }
+ }
+ break;
+ default:
+ {
+ g_assert_not_reached();
+ }
+ break;
+ }
+
+ priv->sconn_status = state;
+}
+
+static void split_message_cb(const gchar *msg, gpointer user_data)
+{
+ IdleConnection *conn = IDLE_CONNECTION(user_data);
+ gchar *converted;
+ GError *err;
+
+ if (!idle_connection_ntoh(conn, msg, &converted, &err))
+ {
+ g_debug("%s: ntoh: %s", G_STRFUNC, err->message);
+ g_error_free(err);
+ converted = (gchar *)(msg);
+ }
+
+ connection_message_cb(conn, converted);
+
+ if (converted != msg)
+ {
+ g_free(converted);
+ }
+}
+
+static void sconn_received_cb(IdleServerConnectionIface *sconn, gchar *raw_msg, IdleConnection *conn)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ msg_split(raw_msg, priv->splitbuf, IRC_MSG_MAXLEN+3, split_message_cb, conn);
+}
+
+static void priv_rename(IdleConnection *conn, guint old, guint new)
+{
+ IdleConnectionPrivate *priv;
+ MUCChannelRenameData data = {old, new};
+ gpointer chan;
+ IdleContactPresence *cp;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ if (old == new)
+ {
+ g_debug("%s: tried to rename with old == new? (%u, %u)", G_STRFUNC, old, new);
+
+ return;
+ }
+
+ cp = idle_handle_get_presence(priv->handles, old);
+
+ idle_handle_set_presence(priv->handles, new, cp);
+ idle_handle_set_presence(priv->handles, old, NULL);
+
+ if (old == priv->self_handle)
+ {
+ g_debug("%s: renaming self_handle (%u, %u)", G_STRFUNC, old, new);
+
+ idle_handle_unref(priv->handles, TP_HANDLE_TYPE_CONTACT, old);
+
+ priv->self_handle = new;
+
+ idle_handle_ref(priv->handles, TP_HANDLE_TYPE_CONTACT, new);
+ }
+
+ chan = g_hash_table_lookup(priv->im_channels, GINT_TO_POINTER(old));
+
+ if (chan != NULL)
+ {
+ g_hash_table_steal(priv->im_channels, GINT_TO_POINTER(old));
+
+ g_hash_table_insert(priv->im_channels, GINT_TO_POINTER(new), chan);
+
+ _idle_im_channel_rename((IdleIMChannel *)(chan), new);
+ }
+
+ g_hash_table_foreach(priv->muc_channels, muc_channel_rename_foreach, &data);
+
+ g_signal_emit(conn, signals[RENAMED], 0, old, new);
+}
+
+gboolean _idle_connection_connect(IdleConnection *conn, GError **error)
+{
+ IdleConnectionPrivate *priv;
+
+ g_assert(conn != NULL);
+ g_assert(error != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ g_assert(priv->nickname != NULL);
+ g_assert(priv->server != NULL);
+ g_assert(priv->port > 0 && priv->port <= G_MAXUINT16);
+
+
+ if (priv->conn == NULL)
+ {
+ gboolean valid;
+ GError *conn_error = NULL;
+ IdleServerConnectionIface *sconn;
+ GType connection_type = (priv->use_ssl)
+ ? IDLE_TYPE_SSL_SERVER_CONNECTION
+ : IDLE_TYPE_SERVER_CONNECTION;
+
+ if ((priv->self_handle = idle_handle_for_contact(priv->handles, priv->nickname)) == 0)
+ {
+ g_debug("%s: invalid nickname %s", G_STRFUNC, priv->nickname);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "invalid nickname %s", priv->nickname);
+
+ return FALSE;
+ }
+
+ valid = idle_handle_ref(priv->handles, TP_HANDLE_TYPE_CONTACT, priv->self_handle);
+
+ g_assert(valid == TRUE);
+
+ sconn = IDLE_SERVER_CONNECTION_IFACE(g_object_new(connection_type,
+ "host", priv->server,
+ "port", priv->port,
+ NULL));
+
+ g_signal_connect(sconn, "status-changed", (GCallback)(sconn_status_changed_cb), conn);
+
+ if (!idle_server_connection_iface_connect(sconn, &conn_error))
+ {
+ g_debug("%s: server connection failed to connect: %s", G_STRFUNC, conn_error->message);
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "failed to open low-level network connection: %s", conn_error->message);
+
+ g_error_free(conn_error);
+ g_object_unref(sconn);
+
+ return FALSE;
+ }
+
+ priv->conn = sconn;
+
+ g_signal_connect(sconn, "received", (GCallback)(sconn_received_cb), conn);
+
+ irc_handshakes(conn);
+
+ update_presence(conn, priv->self_handle, IDLE_PRESENCE_AVAILABLE, NULL);
+ }
+ else
+ {
+ g_debug("%s: conn already open!", G_STRFUNC);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "connection already open!");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean _idle_connection_send(IdleConnection *conn, const gchar *msg)
+{
+ send_irc_cmd(conn, msg);
+
+ return TRUE;
+}
+
+static gboolean msg_queue_timeout_cb(gpointer user_data)
+{
+ IdleConnection *conn = IDLE_CONNECTION(user_data);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ int i, j;
+ IdleOutputPendingMsg *output_msg;
+ gchar msg[IRC_MSG_MAXLEN+3];
+ GError *error;
+
+ g_debug("%s: called", G_STRFUNC);
+
+ if (priv->sconn_status != SERVER_CONNECTION_STATE_CONNECTED)
+ {
+ g_debug("%s: connection was not connected!", G_STRFUNC);
+
+ priv->msg_queue_timeout = 0;
+
+ return FALSE;
+ }
+
+ output_msg = g_queue_peek_head(priv->msg_queue);
+
+ if (output_msg == NULL)
+ {
+ priv->msg_queue_timeout = 0;
+
+ return FALSE;
+ }
+
+ g_strlcpy(msg, output_msg->message, IRC_MSG_MAXLEN+3);
+
+ for (i=1; i < MSG_QUEUE_UNLOAD_AT_A_TIME; i++)
+ {
+ output_msg = g_queue_peek_nth(priv->msg_queue, i);
+
+ if ((output_msg != NULL) && ((strlen(msg) + strlen(output_msg->message)) < IRC_MSG_MAXLEN+2))
+ {
+ strcat(msg, output_msg->message);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (idle_server_connection_iface_send(priv->conn, msg, &error))
+ {
+ for (j=0; j<i; j++)
+ {
+ output_msg = g_queue_pop_head(priv->msg_queue);
+
+ idle_output_pending_msg_free(output_msg);
+ }
+
+ priv->last_msg_sent = time(NULL);
+ }
+ else
+ {
+ g_debug("%s: low-level network connection failed to send: %s", G_STRFUNC, error->message);
+
+ g_error_free(error);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Queue a IRC command for sending, clipping it to IRC_MSG_MAXLEN bytes and appending the required <CR><LF> to it
+ */
+static void send_irc_cmd_full(IdleConnection *conn, const gchar *msg, guint priority)
+{
+ gchar cmd[IRC_MSG_MAXLEN+3];
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ int len;
+ GError *error;
+ gchar *converted;
+ GError *convert_error;
+ time_t curr_time = time(NULL);
+ IdleOutputPendingMsg *output_msg;
+
+ g_assert(msg != NULL);
+
+ /* Clip the message */
+
+ g_strlcpy(cmd, msg, IRC_MSG_MAXLEN+1);
+
+ /* Append <CR><LF> */
+
+ len = strlen(cmd);
+ cmd[len++] = '\r';
+ cmd[len++] = '\n';
+
+ cmd[len] = '\0';
+
+ if (!idle_connection_hton(conn, cmd, &converted, &convert_error))
+ {
+ g_debug("%s: hton: %s", G_STRFUNC, convert_error->message);
+ g_error_free(convert_error);
+ converted = g_strdup(cmd);
+ }
+
+ if ((priority == SERVER_CMD_MAX_PRIORITY)
+ || ((priv->status == TP_CONN_STATUS_CONNECTED)
+ && (priv->msg_queue_timeout == 0)
+ && (curr_time - priv->last_msg_sent > MSG_QUEUE_TIMEOUT)))
+ {
+ priv->last_msg_sent = curr_time;
+
+ if (!idle_server_connection_iface_send(priv->conn, converted, &error))
+ {
+ g_debug("%s: server connection failed to send: %s", G_STRFUNC, error->message);
+ g_error_free(error);
+ }
+ else
+ {
+ g_free(converted);
+ return;
+ }
+ }
+
+ output_msg = idle_output_pending_msg_new();
+ output_msg->message = converted;
+ output_msg->priority = priority;
+
+ g_queue_insert_sorted(priv->msg_queue, output_msg, pending_msg_compare, NULL);
+
+ if (priv->msg_queue_timeout == 0)
+ {
+ priv->msg_queue_timeout = g_timeout_add(MSG_QUEUE_TIMEOUT*1024, msg_queue_timeout_cb, conn);
+ }
+}
+
+static void send_irc_cmd(IdleConnection *conn, const gchar *msg)
+{
+ return send_irc_cmd_full(conn, msg, SERVER_CMD_NORMAL_PRIORITY);
+}
+
+static void cmd_parse(IdleConnection *conn, const gchar *msg);
+static gchar *prefix_cmd_parse(IdleConnection *conn, const gchar *msg);
+static gchar *prefix_numeric_parse(IdleConnection *conn, const gchar *msg);
+
+static void connection_message_cb(IdleConnection *conn, const gchar *msg)
+{
+ gchar scanf_fool[IRC_MSG_MAXLEN+2];
+ int scanf_numeric;
+ int cmdcount, numericcount;
+ gchar *reply = NULL;
+
+ IdleConnectionPrivate *priv;
+
+ /*g_return_if_fail(msg != NULL);
+ g_return_if_fail(msg[0] != '\0');*/
+
+ if (msg == NULL || msg[0] == '\0')
+ {
+ return;
+ }
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ /*priv->conn.queue_wait = FALSE;*/
+
+ if (msg[0] != ':')
+ {
+ cmd_parse(conn, msg);
+ }
+ else if ((numericcount = sscanf(msg, ":%s %i %s", scanf_fool, &scanf_numeric, scanf_fool)) == 3)
+ {
+ reply = prefix_numeric_parse(conn, msg+1);
+ }
+ else if ((cmdcount = sscanf(msg, ":%s %s %s", scanf_fool, scanf_fool, scanf_fool)) == 3)
+ {
+ reply = prefix_cmd_parse(conn, msg+1);
+ }
+ else
+ {
+ g_debug("%s: unrecognized message format from server (%i cmd, %i numeric) (%s)", G_STRFUNC, cmdcount, numericcount, msg);
+ return;
+ }
+
+ if (reply)
+ {
+ send_irc_cmd (conn, reply);
+ g_free (reply);
+ }
+}
+
+static void cmd_parse(IdleConnection *conn, const gchar *msg)
+{
+ if ((g_strncasecmp(msg, "PING ", 5) == 0) && (msg[5] != '\0'))
+ {
+ /* PING command, reply ... */
+ gchar *reply = g_strdup_printf("PONG %s", msg+5);
+ send_irc_cmd_full (conn, reply, SERVER_CMD_MAX_PRIORITY);
+ g_free (reply);
+ }
+ else
+ {
+ g_debug("%s: ignored unparsed message from server (%s)", G_STRFUNC, msg);
+ }
+}
+
+static void muc_channel_handle_quit_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ IdleMUCChannel *chan = value;
+ IdleHandle handle = GPOINTER_TO_INT(user_data);
+
+ g_debug("%s: for %p %p %p", G_STRFUNC, key, value, user_data);
+
+ _idle_muc_channel_handle_quit(chan, handle, TRUE, handle, TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE);
+}
+
+static gchar *prefix_cmd_parse(IdleConnection *conn, const gchar *msg)
+{
+ IdleConnectionPrivate *priv;
+ guint tokenc;
+ gchar *reply = NULL;
+
+ gchar **tokens;
+ gchar *sender = NULL, *cmd = NULL, *recipient = NULL;
+ gchar *temp = NULL;
+ gchar *from = NULL;
+ gunichar ucs4char;
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ tokens = g_strsplit_set(msg, " ", -1);
+ for (tokenc = 0; tokens[tokenc] != NULL; tokenc++) {}
+
+ sender = tokens[0];
+ cmd = tokens[1];
+ recipient = tokens[2];
+
+ if (!sender || !cmd || !recipient)
+ {
+ g_debug("%s: failed to parse message (%s) into sender, cmd and recipient", G_STRFUNC, msg);
+
+ goto cleanupl;
+ }
+
+ if ((temp = strchr(sender, '!')) > sender)
+ {
+ from = g_strndup(sender, temp-sender);
+ }
+ else
+ {
+ if ((temp = strchr(sender, '@')) > sender)
+ {
+ from = g_strndup(sender, temp-sender);
+ }
+ else
+ {
+ from = sender;
+ }
+ }
+
+ if ((g_strncasecmp(cmd, "NOTICE", 6) == 0) || (g_strncasecmp(cmd, "PRIVMSG", 7) == 0))
+ {
+ IdleHandle handle;
+ gchar *body;
+ TpChannelTextMessageType msgtype;
+
+ if ((body = strstr(msg, " :")+1) != NULL)
+ {
+ body++;
+ }
+ else
+ {
+ g_debug("%s got NOTICE/PRIVMSG with missing body identifier \" :\" (%s)", G_STRFUNC, msg);
+ goto cleanupl;
+ }
+
+ if (cmd[0] == 'N')
+ {
+ msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
+ }
+ else
+ {
+ if (body[0] == '\001')
+ {
+ char *suffix;
+
+ body++;
+
+ suffix = strrchr(body, '\001');
+
+ g_assert(suffix != NULL);
+
+ *suffix = '\0';
+
+ if (!g_strncasecmp(body, "ACTION ", 7))
+ {
+ g_debug("%s: detected CTCP ACTION message", G_STRFUNC);
+
+ body += 7;
+
+ msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
+ }
+ else if (!g_strncasecmp(body, "VERSION", 7))
+ {
+ g_debug("%s: detected CTCP VERSION message", G_STRFUNC);
+
+ reply = g_strdup_printf("NOTICE %s :\001VERSION %s\001", from, priv->ctcp_version_string);
+
+ goto cleanupl;
+ }
+ else
+ {
+ g_debug("%s: ignored unimplemented (non-ACTION/VERSION) CTCP (%s)", G_STRFUNC, body);
+ goto cleanupl;
+ }
+ }
+ else
+ {
+ msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
+ }
+ }
+
+ ucs4char = g_utf8_get_char_validated(recipient, -1);
+
+ if (g_unichar_isalpha(ucs4char))
+ {
+ IdleIMChannel *chan;
+
+ if ((handle = idle_handle_for_contact(priv->handles, from)) == 0)
+ {
+ g_debug("%s: got NOTICE/PRIVMSG with malformed sender %s (%s)", G_STRFUNC, from, msg);
+ goto cleanupl;
+ }
+
+ g_debug("%s: receiving msg with type %u from %s (handle %u): %s", G_STRFUNC, msgtype, from, handle, body);
+
+ if ((chan = g_hash_table_lookup(priv->im_channels, GINT_TO_POINTER(handle))) == NULL)
+ {
+ chan = new_im_channel(conn, handle, FALSE);
+
+ g_debug("%s: spawning new IdleIMChannel (address %p handle %u)", G_STRFUNC, chan, handle);
+ }
+ else
+ {
+ g_debug("%s: receiving thru existing IdleIMChannel %p", G_STRFUNC, chan);
+ }
+
+ _idle_im_channel_receive(chan, msgtype, handle, body);
+ }
+ else if ((recipient[0] == '#') || (recipient[0] == '&') || (recipient[0] == '!') || (recipient[0] == '+'))
+ {
+ IdleMUCChannel *chan;
+ IdleHandle sender_handle;
+
+ if ((handle = idle_handle_for_room(priv->handles, recipient)) == 0)
+ {
+ g_debug("%s: got NOTICE/PRIVMSG with malformed IRC channel recipient %s (%s)", G_STRFUNC, from, msg);
+ goto cleanupl;
+ }
+
+ if ((sender_handle = idle_handle_for_contact(priv->handles, from)) == 0)
+ {
+ g_debug("%s: got NOTICE/PRIVMSG with malformed sender %s (%s)", G_STRFUNC, from, msg);
+ goto cleanupl;
+ }
+
+ g_debug("%s: receiving NOTICE/PRIVMSG from %s (handle %u) to MUCChannel %s (handle %u): %s", G_STRFUNC, from, sender_handle, recipient, handle, body);
+
+ if ((chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle))) == NULL)
+ {
+ g_debug("%s: spawning new IdleMUCChannel", G_STRFUNC);
+
+ chan = new_muc_channel(conn, handle, FALSE);
+ }
+
+ _idle_muc_channel_receive(chan, msgtype, sender_handle, body);
+ }
+ else
+ {
+ g_debug("%s: ignored NOTICE/PRIVMSG from invalid sender (%s)", G_STRFUNC, from);
+ }
+ }
+ else if (g_strncasecmp(cmd, "JOIN", 4) == 0)
+ {
+ IdleHandle handle;
+ char *channel = recipient;
+ IdleMUCChannel *chan;
+
+ /* I see at least irc.paivola.fi (UnrealIRCD 3.2.4) sending the channel name with a : prefix although it doesn't say that in the RFC */
+ if (channel[0] == ':')
+ {
+ channel++;
+ }
+
+ handle = idle_handle_for_room(priv->handles, channel);
+
+ if (handle == 0)
+ {
+ g_debug("%s: received JOIN with malformed channel (%s)", G_STRFUNC, channel);
+ g_free(channel);
+ goto cleanupl;
+ }
+
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+
+ if (chan == NULL)
+ {
+ g_debug("%s: got JOIN message for channel we don't have in muc_channels? (%s)", G_STRFUNC, channel);
+ g_free(channel);
+ goto cleanupl;
+ }
+
+ g_debug("%s: got JOIN for IRC channel %s (handle %u)", G_STRFUNC, channel, handle);
+
+ _idle_muc_channel_join(chan, from);
+ }
+ else if (g_strncasecmp(cmd, "NICK", 4) == 0)
+ {
+ IdleHandle old_handle, new_handle;
+ gchar *old_down, *new_down;
+
+ old_down = from;
+ old_handle = idle_handle_for_contact(priv->handles, old_down);
+
+ new_down = recipient+1;
+ new_handle = idle_handle_for_contact(priv->handles, new_down);
+
+ g_debug("%s: got NICK (%s) -> (%s), %u -> %u", G_STRFUNC, old_down, new_down, old_handle, new_handle);
+
+ priv_rename(conn, old_handle, new_handle);
+ }
+ else if (g_strncasecmp(cmd, "MODE", 4) == 0)
+ {
+ IdleHandle handle;
+ IdleMUCChannel *chan;
+ gchar *tmp;
+ ucs4char = g_utf8_get_char_validated(recipient, -1);
+
+ if (g_unichar_isalpha(ucs4char))
+ {
+ g_debug("%s: got user MODE message, ignoring...", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ handle = idle_handle_for_room(priv->handles, recipient);
+
+ if (handle == 0)
+ {
+ g_debug("%s: could not get handle for (%s) in MODE", G_STRFUNC, recipient);
+ goto cleanupl;
+ }
+
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+
+ if (chan == NULL)
+ {
+ g_debug("%s got MODE message for channel we don't have in muc_channels (%s)", G_STRFUNC, recipient);
+ goto cleanupl;
+ }
+
+ tmp = strstr(msg, recipient);
+ g_assert(tmp != NULL);
+
+ tmp = strchr(tmp+1, ' ');
+ g_assert(tmp != NULL);
+
+ tmp++;
+
+ g_debug("%s: got MODE for (%s) (%s)", G_STRFUNC, recipient, tmp);
+
+ _idle_muc_channel_mode(chan, tmp);
+ }
+ else if (g_strncasecmp(cmd, "PART", 4) == 0)
+ {
+ IdleHandle handle;
+ IdleMUCChannel *chan;
+ gchar *chan_down;
+
+ chan_down = recipient;
+
+ handle = idle_handle_for_room(priv->handles, chan_down);
+
+ if (handle == 0)
+ {
+ g_debug("%s: could not get handle for (%s) in PART", G_STRFUNC, chan_down);
+ goto cleanupl;
+ }
+
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+
+ if (chan == NULL)
+ {
+ g_debug("%s: got PART message for channel we don't have in muc_channels? (%s)", G_STRFUNC, chan_down);
+ goto cleanupl;
+ }
+
+ g_debug("%s: got PART for (%s) in (%s)", G_STRFUNC, from, chan_down);
+
+ _idle_muc_channel_part(chan, from);
+ }
+ else if (g_strncasecmp(cmd, "KICK", 4) == 0)
+ {
+ IdleHandle handle;
+ IdleMUCChannel *chan;
+ gchar *chan_down;
+ gchar *nick;
+ gchar *tmp;
+ int i;
+
+ chan_down = recipient;
+
+ handle = idle_handle_for_room(priv->handles, chan_down);
+
+ if (handle == 0)
+ {
+ g_debug("%s: could not get handle for (%s) in KICK/PART", G_STRFUNC, chan_down);
+ goto cleanupl;
+ }
+
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+
+ if (chan == NULL)
+ {
+ g_debug("%s: got KICK/PART message for channel we don't have in muc_channels? (%s)", G_STRFUNC, chan_down);
+ goto cleanupl;
+ }
+
+ nick = strchr(msg, ' ');
+
+ for (i=0; i<2; i++)
+ {
+ if (nick == NULL)
+ {
+ g_debug("%s: failed to find nick in KICK message (%s)", G_STRFUNC, msg);
+ goto cleanupl;
+ }
+
+ nick = strchr(nick+1, ' ');
+ }
+
+ if (nick == NULL)
+ {
+ g_debug("%s: 1failed to find nick in KICK message (%s)", G_STRFUNC, msg);
+ goto cleanupl;
+ }
+
+ nick++;
+
+ tmp = strchr(nick, ' ');
+
+ if (tmp == NULL)
+ {
+ g_debug("%s: 2failed to find nick in KICK message (%s)", G_STRFUNC, msg);
+ goto cleanupl;
+ }
+
+ *tmp = '\0';
+
+ tmp = strchr(sender, '!');
+
+ if (tmp)
+ {
+ *tmp = '\0';
+ }
+
+ g_debug("%s: got KICK for (%s) by (%s) in (%s)", G_STRFUNC, nick, sender, chan_down);
+
+ _idle_muc_channel_kick(chan, nick, sender, TP_CHANNEL_GROUP_CHANGE_REASON_KICKED);
+ }
+ else if (g_strncasecmp(cmd, "INVITE", 6) == 0)
+ {
+ IdleHandle handle;
+ IdleMUCChannel *chan;
+ gboolean worked_around = FALSE;
+ gchar *tmp;
+ IdleHandle inviter_handle;
+
+ if (tokenc != 4)
+ {
+ g_debug("%s: got INVITE with tokenc != 4, ignoring...", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ /* workaround for non-conformant servers... */
+ if (tokens[3][0] == ':')
+ {
+ tokens[3]++;
+ worked_around = TRUE;
+ }
+
+ handle = idle_handle_for_room(priv->handles, tokens[3]);
+
+ if (worked_around)
+ {
+ tokens[3]--;
+ }
+
+ if (handle == 0)
+ {
+ g_debug("%s: failed to get handle for (%s) in INVITE", G_STRFUNC, tokens[3]);
+ goto cleanupl;
+ }
+
+ tmp = strchr(tokens[0], '!');
+
+ if (tmp)
+ {
+ *tmp = '\0';
+ }
+
+ inviter_handle = idle_handle_for_contact(priv->handles, tokens[0]);
+
+ if (!inviter_handle)
+ {
+ g_debug("%s: failed to get handle for inviter (%s) in INVITE", G_STRFUNC, tokens[0]);
+ }
+
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+
+ if (chan != NULL)
+ {
+ g_debug("%s: got INVITE for a channel we are already on, ignoring...", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ chan = new_muc_channel(conn, handle, FALSE);
+
+ g_assert(chan != NULL);
+
+ _idle_muc_channel_invited(chan, inviter_handle);
+
+ g_debug("%s: got INVITEd to channel (%s) (handle %u)", G_STRFUNC, tokens[3], handle);
+ }
+ else if (g_strncasecmp(cmd, "QUIT", 4) == 0)
+ {
+ IdleHandle handle;
+ IdleIMChannel *chan;
+
+ handle = idle_handle_for_contact(priv->handles, from);
+
+ if (handle == 0)
+ {
+ g_debug("%s: could not get handle for (%s) in QUIT", G_STRFUNC, from);
+ goto cleanupl;
+ }
+
+ chan = g_hash_table_lookup(priv->im_channels, GINT_TO_POINTER(handle));
+
+ if (chan != NULL)
+ {
+ GError *error;
+
+ g_debug("%s: contact QUIT, closing IMChannel...", G_STRFUNC);
+
+ if (!idle_im_channel_close(chan, &error))
+ {
+ g_debug("%s: idle_im_channel_close failed: %s", G_STRFUNC, error->message);
+ g_error_free(error);
+ }
+ }
+
+ g_hash_table_foreach(priv->muc_channels, muc_channel_handle_quit_foreach, GINT_TO_POINTER(handle));
+ }
+ else if (g_strncasecmp(cmd, "TOPIC", 5) == 0)
+ {
+ IdleHandle handle, setter_handle;
+ IdleMUCChannel *chan;
+ char *tmp;
+
+ handle = idle_handle_for_room(priv->handles, recipient);
+
+ if (handle == 0)
+ {
+ g_debug("%s: could not get handle for (%s) in TOPIC", G_STRFUNC, recipient);
+ goto cleanupl;
+ }
+
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+
+ if (chan == NULL)
+ {
+ g_debug("%s: got NULL MUCChannel for (%s) (handle %u)", G_STRFUNC, recipient, handle);
+ goto cleanupl;
+ }
+
+ tmp = strstr(msg, " :")+1;
+
+ if (tmp == NULL)
+ {
+ g_debug("%s: could not find body identifier in TOPIC message (%s)", G_STRFUNC, msg);
+ goto cleanupl;
+ }
+
+ setter_handle = idle_handle_for_contact(priv->handles, from);
+
+ if (setter_handle == 0)
+ {
+ g_debug("%s: could not get handle for (%s) in TOPIC", G_STRFUNC, from);
+ goto cleanupl;
+ }
+
+ if (tmp+1 != '\0')
+ {
+ _idle_muc_channel_topic_full(chan, setter_handle, time(NULL), tmp+1);
+ }
+ else
+ {
+ _idle_muc_channel_topic_unset(chan);
+ }
+
+ g_debug("%s: got TOPIC for (%s)", G_STRFUNC, recipient);
+ }
+ else
+ {
+ g_debug("%s: ignored unparsed message from server (%s) = (%s, %s, %s)", G_STRFUNC, msg, sender, cmd, recipient);
+ }
+
+cleanupl:
+
+ if (from != sender)
+ {
+ g_free(from);
+ }
+
+ g_strfreev(tokens);
+
+ return reply;
+}
+
+#define IRC_RPL_WELCOME 001
+#define IRC_RPL_AWAY 301
+#define IRC_RPL_UNAWAY 305
+#define IRC_RPL_NOWAWAY 306
+#define IRC_RPL_WHOISIDLE 317
+#define IRC_RPL_ENDOFWHOIS 318
+#define IRC_RPL_MODEREPLY 324
+#define IRC_RPL_TOPIC 332
+#define IRC_RPL_TOPIC_STAMP 333
+#define IRC_RPL_NAMEREPLY 353
+#define IRC_ERR_NOSUCHNICK 401
+#define IRC_ERR_CANNOTSENDTOCHAN 404
+#define IRC_ERR_ERRONEOUSNICKNAME 432
+#define IRC_ERR_NICKNAMEINUSE 433
+#define IRC_ERR_CHANNELISFULL 471
+#define IRC_ERR_INVITEONLYCHAN 473
+#define IRC_ERR_BANNEDFROMCHAN 474
+#define IRC_ERR_BADCHANNELKEY 475
+
+static gint strcasecmp_helper(gconstpointer a, gconstpointer b)
+{
+ return strcasecmp(a, b);
+}
+
+static gchar *prefix_numeric_parse(IdleConnection *conn, const gchar *msg)
+{
+ IdleConnectionPrivate *priv;
+
+ gchar *reply = NULL;
+
+ gchar **tokens;
+ guint numeric;
+ gchar *sender, *recipient;
+ int tokenc;
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ tokens = g_strsplit_set(msg, " ", -1);
+
+ for (tokenc = 0; tokens[tokenc] != NULL; tokenc++) {}
+
+ if (tokenc < 3)
+ {
+ g_debug("%s: got message with less than 3 tokens (%s), ignoring...", G_STRFUNC, msg);
+
+ goto cleanupl;
+ }
+
+ sender = tokens[0];
+ numeric = atoi(tokens[1]);
+ recipient = tokens[2];
+
+ if (!sender || !recipient)
+ {
+ goto cleanupl;
+ }
+
+ if (numeric == IRC_RPL_NAMEREPLY)
+ {
+ char *identifier;
+ char *channeltmp;
+ char *channel;
+ IdleHandle handle;
+ GArray *names_array;
+ guint i, lasti;
+ guint length;
+ IdleMUCChannel *chan;
+
+ if ((identifier = strchr(msg, '=')) != NULL)
+ {
+ g_debug("%s: found identifier for PUBLIC channel at %p", G_STRFUNC, identifier);
+ }
+ else if ((identifier = strchr(msg, '*')) != NULL)
+ {
+ g_debug("%s: found identifier for PRIVATE channel at %p", G_STRFUNC, identifier);
+ }
+ else if ((identifier = strchr(msg, '@')) != NULL)
+ {
+ g_debug("%s: found identifier for SECRET channel at %p", G_STRFUNC, identifier);
+ }
+ else
+ {
+ g_debug("%s: did not find channel type identifier in NAMES msg, ignoring", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ channeltmp = identifier+1;
+
+ identifier = strstr(identifier, " :")+1;
+
+ if (!identifier)
+ {
+ g_debug("%s: did not find body identifier in NAMES msg, ignoring", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ *identifier = '\0';
+
+ channeltmp = g_strdup(channeltmp);
+
+ channel = g_strstrip(channeltmp);
+
+ handle = idle_handle_for_room(priv->handles, channel);
+
+ if (handle == 0)
+ {
+ g_debug("%s: got invalid channel (%s) in NAMES, ignoring", G_STRFUNC, channel);
+
+ g_free(channeltmp);
+
+ goto cleanupl;
+ }
+
+ names_array = g_array_new(FALSE, FALSE, sizeof(gchar *));
+
+ identifier++;
+
+ length = strlen(identifier);
+ lasti = 0;
+
+ for (i=0; i < length; i++)
+ {
+ if (identifier[i] == ' ')
+ {
+ if (i>lasti)
+ {
+ gchar *tmp;
+
+ identifier[i] = '\0';
+
+ tmp = g_strdup(identifier+lasti);
+
+ g_array_append_val(names_array, tmp);
+ }
+
+ lasti = i+1;
+ }
+ }
+
+ chan = (IdleMUCChannel *)(g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle)));
+
+ if (chan == NULL)
+ {
+ g_debug("%s: got NAMES for channel (%s) we're not on but have a handle (%u) for?", G_STRFUNC, channel, handle);
+
+ g_free(channeltmp);
+
+ goto cleanupl;
+ }
+
+ _idle_muc_channel_names(chan, names_array);
+
+ g_debug("%s: parsed %u names for channel %s (handle %u)", G_STRFUNC, names_array->len, channel, handle);
+
+ g_free(channeltmp);
+
+ for (i=0; i<names_array->len; i++)
+ {
+ g_free(g_array_index(names_array, gchar *, i));
+ }
+ g_array_free(names_array, TRUE);
+ }
+ else if (numeric == IRC_ERR_BADCHANNELKEY)
+ {
+ gchar *channel;
+ gchar *tmp;
+ IdleHandle handle;
+ IdleMUCChannel *chan;
+
+ if (((channel = strchr(msg, '#')) == NULL) &&
+ ((channel = strchr(msg, '!')) == NULL) &&
+ ((channel = strchr(msg, '&')) == NULL) &&
+ ((channel = strchr(msg, '+')) == NULL))
+ {
+ g_debug("%s: didn't find valid channel name in ERR_BADCHANNELKEY (%s), ignoring", G_STRFUNC, msg);
+ goto cleanupl;
+ }
+
+ if ((tmp = strstr(channel, " :")+1) == NULL)
+ {
+ g_debug("%s: failed to find : separator in ERR_BADCHANNELKEY (%s), ignoring", G_STRFUNC, msg);
+ goto cleanupl;
+ }
+
+ *tmp = '\0';
+
+ channel = g_strstrip(channel);
+
+ handle = idle_handle_for_room(priv->handles, channel);
+
+ if (handle == 0)
+ {
+ g_debug("%s: failed to get handle for channel %s in ERR_BADCHANNELKEY (%s)", G_STRFUNC, channel, msg);
+ goto cleanupl;
+ }
+
+ chan = (IdleMUCChannel *)(g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle)));
+
+ if (chan == NULL)
+ {
+ g_debug("%s: got ERR_BADCHANNELKEY for channel %s we are not on yet have a handle (%u) for?", G_STRFUNC, channel, handle);
+ goto cleanupl;
+ }
+
+ _idle_muc_channel_badchannelkey(chan);
+
+ g_debug("%s: got ERR_BADCHANNELKEY for channel %s (handle %u)", G_STRFUNC, channel, handle);
+ }
+ else if (numeric == IRC_ERR_ERRONEOUSNICKNAME)
+ {
+ g_debug("%s: got ERR_ERROUNEUSNICKNAME", G_STRFUNC);
+
+ handle_err_erroneusnickname(conn);
+ }
+ else if (numeric == IRC_ERR_NICKNAMEINUSE)
+ {
+ g_debug("%s: got ERR_NICKNAMEINUSE", G_STRFUNC);
+
+ handle_err_nicknameinuse(conn);
+ }
+ else if (numeric == IRC_RPL_WELCOME)
+ {
+ g_debug("%s: got RPL_WELCOME", G_STRFUNC);
+
+ connection_connect_cb(conn, TRUE);
+ }
+ else if (numeric == IRC_RPL_TOPIC)
+ {
+ IdleHandle handle;
+ IdleMUCChannel *chan;
+ gchar *tmp;
+
+ if (tokenc < 5)
+ {
+ g_debug("%s: not enough tokens for RPL_TOPIC in (%s), ignoring...", G_STRFUNC, msg);
+ goto cleanupl;
+ }
+
+ handle = idle_handle_for_room(priv->handles, tokens[3]);
+
+ if (handle == 0)
+ {
+ g_debug("%s: failed to get handle for (%s) in RPL_TOPIC", G_STRFUNC, tokens[3]);
+ goto cleanupl;
+ }
+
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+
+ if (chan == NULL)
+ {
+ g_debug("%s: failed to find channel (%s) (handle %u) in RPL_TOPIC", G_STRFUNC, tokens[3], handle);
+ goto cleanupl;
+ }
+
+ tmp = strstr(msg, " :")+1;
+
+ if (tmp == NULL)
+ {
+ g_debug("%s: failed to find body separator in (%s)", G_STRFUNC, msg);
+
+ goto cleanupl;
+ }
+
+ _idle_muc_channel_topic(chan, tmp+1);
+
+ g_debug("%s: got RPL_TOPIC for (%s)", G_STRFUNC, tokens[3]);
+ }
+ else if (numeric == IRC_RPL_TOPIC_STAMP)
+ {
+ IdleHandle handle, toucher_handle;
+ IdleMUCChannel *chan;
+ guint timestamp;
+
+ if (tokenc != 6)
+ {
+ g_debug("%s: wrong amount of tokens (%u) for RPL_TOPICSTAMP in (%s), ignoring...", G_STRFUNC, tokenc, msg);
+ goto cleanupl;
+ }
+
+ handle = idle_handle_for_room(priv->handles, tokens[3]);
+
+ if (handle == 0)
+ {
+ g_debug("%s: failed to get handle for (%s) in RPL_TOPICSTAMP", G_STRFUNC, tokens[3]);
+ goto cleanupl;
+ }
+
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+
+ if (chan == NULL)
+ {
+ g_debug("%s: failed to find channel (%s) (handle %u) in RPL_TOPICSTAMP", G_STRFUNC, tokens[3], handle);
+ goto cleanupl;
+ }
+
+ toucher_handle = idle_handle_for_contact(priv->handles, tokens[4]);
+
+ if (toucher_handle == 0)
+ {
+ g_debug("%s: failed to get handle for toucher (%s) in RPL_TOPICSTAMP", G_STRFUNC, tokens[4]);
+ goto cleanupl;
+ }
+
+ if (sscanf(tokens[5], "%u", &timestamp) != 1)
+ {
+ g_debug("%s: failed to parse (%s) to uint in RPL_TOPICSTAMP", G_STRFUNC, tokens[5]);
+ goto cleanupl;
+ }
+
+ _idle_muc_channel_topic_touch(chan, toucher_handle, timestamp);
+
+ g_debug("%s: got RPL_TOPICSTAMP for (%s)", G_STRFUNC, tokens[3]);
+ }
+ else if (numeric == IRC_RPL_MODEREPLY)
+ {
+ IdleHandle handle;
+ IdleMUCChannel *chan;
+ gchar *tmp;
+
+ if (tokenc < 5)
+ {
+ g_debug("%s: got IRC_RPL_MODEREPLY with less than 5 tokens, ignoring... (%s)", G_STRFUNC, msg);
+ goto cleanupl;
+ }
+
+ handle = idle_handle_for_room(priv->handles, tokens[3]);
+
+ if (handle == 0)
+ {
+ g_debug("%s: could not get handle for (%s) in MODE", G_STRFUNC, tokens[3]);
+ goto cleanupl;
+ }
+
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+
+ if (chan == NULL)
+ {
+ g_debug("%s got MODE message for channel we don't have in muc_channels (%s)", G_STRFUNC, tokens[3]);
+ goto cleanupl;
+ }
+
+ tmp = strstr(msg, tokens[3]);
+ g_assert(tmp != NULL);
+
+ tmp = strchr(tmp+1, ' ');
+ g_assert(tmp != NULL);
+
+ tmp++;
+
+ g_debug("%s: got RPL_MODEREPLY for (%s) (%s)", G_STRFUNC, tokens[3], tmp);
+
+ _idle_muc_channel_mode(chan, tmp);
+ }
+ else if (numeric == IRC_ERR_NOSUCHNICK)
+ {
+ IdleHandle handle;
+ IdleIMChannel *chan;
+ GList *link;
+
+ if (tokenc < 4)
+ {
+ g_debug("%s: got ERR_NOSUCHNICK with tokenc < 4, ignoring...", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ if ((link = g_list_find_custom(priv->presence_reply_list, tokens[3], strcasecmp_helper)) != NULL)
+ {
+ g_debug("%s: one of the contacts we asked presence for was offline, removing query...", G_STRFUNC);
+ g_free(link->data);
+ priv->presence_reply_list = g_list_delete_link(priv->presence_reply_list, link);
+ }
+
+ handle = idle_handle_for_contact(priv->handles, tokens[3]);
+
+ if (handle == 0)
+ {
+ g_debug("%s: could not get handle for (%s) in ERR_NOSUCHNICK", G_STRFUNC, tokens[3]);
+ goto cleanupl;
+ }
+
+ update_presence(conn, handle, IDLE_PRESENCE_OFFLINE, NULL);
+
+ chan = g_hash_table_lookup(priv->im_channels, GINT_TO_POINTER(handle));
+
+ if (chan == NULL)
+ {
+ g_debug("%s: could not get IMChannel for (%s) (handle %u) in ERR_NOSUCHNICK", G_STRFUNC, tokens[3], handle);
+ goto cleanupl;
+ }
+
+ g_debug("%s: got ERR_NOSUCHNICK for (%s) (handle %u)", G_STRFUNC, tokens[3], handle);
+
+ _idle_im_channel_nosuchnick(chan);
+ }
+ else if (numeric == IRC_ERR_BANNEDFROMCHAN
+ || numeric == IRC_ERR_CHANNELISFULL
+ || numeric == IRC_ERR_INVITEONLYCHAN)
+ {
+ IdleHandle handle;
+ IdleMUCChannel *chan;
+ guint err;
+
+ handle = idle_handle_for_room(priv->handles, tokens[3]);
+
+ if (handle == 0)
+ {
+ g_debug("%s: failed to get handle for (%s) in join errors", G_STRFUNC, tokens[2]);
+ goto cleanupl;
+ }
+
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+
+ if (chan == NULL)
+ {
+ g_debug("%s: failed to find channel with handle %u in join errors", G_STRFUNC, handle);
+ goto cleanupl;
+ }
+
+ switch (numeric)
+ {
+ case IRC_ERR_BANNEDFROMCHAN:
+ {
+ err = MUC_CHANNEL_JOIN_ERROR_BANNED;
+ }
+ break;
+ case IRC_ERR_CHANNELISFULL:
+ {
+ err = MUC_CHANNEL_JOIN_ERROR_FULL;
+ }
+ break;
+ case IRC_ERR_INVITEONLYCHAN:
+ {
+ err = MUC_CHANNEL_JOIN_ERROR_INVITE_ONLY;
+ }
+ break;
+ default:
+ {
+ g_assert_not_reached();
+ }
+ break;
+ }
+
+ _idle_muc_channel_join_error(chan, err);
+ }
+ else if (numeric == IRC_RPL_NOWAWAY
+ || numeric == IRC_RPL_UNAWAY)
+ {
+ update_presence(conn, priv->self_handle,
+ (numeric == IRC_RPL_UNAWAY) ? IDLE_PRESENCE_AVAILABLE : IDLE_PRESENCE_AWAY, NULL);
+
+ g_debug("%s: got away state change", G_STRFUNC);
+ }
+ else if (numeric == IRC_RPL_AWAY)
+ {
+ IdleHandle handle;
+ char *tmp;
+ GList *link;
+
+ if (tokenc < 5)
+ {
+ g_debug("%s: got RPL_AWAY with tokenc < 3, ignoring...", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ handle = idle_handle_for_contact(priv->handles, tokens[3]);
+
+ if (handle == 0)
+ {
+ g_debug("%s: could not get handle for (%s) in IRC_RPL_AWAY...", G_STRFUNC, tokens[3]);
+ goto cleanupl;
+ }
+
+ tmp = strstr(msg, " :")+1;
+
+ if (tmp == NULL)
+ {
+ g_debug("%s: failed to find body separator in RPL_AWAY, ignoring...", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ g_debug("%s: got RPL_AWAY for %u", G_STRFUNC, handle);
+
+ if ((link = g_list_find_custom(priv->presence_reply_list, tokens[3], strcasecmp_helper)) != NULL)
+ {
+ g_debug("%s: got RPL_AWAY for someone we had queried presence for...", G_STRFUNC);
+ g_free(link->data);
+ priv->presence_reply_list = g_list_remove_link(priv->presence_reply_list, link);
+ }
+
+ update_presence(conn, handle, IDLE_PRESENCE_AWAY, tmp+1);
+ }
+ else if (numeric == IRC_RPL_ENDOFWHOIS)
+ {
+ GList *link;
+ IdleHandle handle;
+
+ if (tokenc < 4)
+ {
+ g_debug("%s: got IRC_RPL_ENDOFWHOIS with <4 tokens, ignoring...", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ if ((link = g_list_find_custom(priv->presence_reply_list, tokens[3], strcasecmp_helper)) != NULL)
+ {
+ g_debug("%s: got end of whois for someone we have queried presence for but haven't got away/offline yet -> available", G_STRFUNC);
+
+ g_free(link->data);
+ priv->presence_reply_list = g_list_remove_link(priv->presence_reply_list, link);
+
+ handle = idle_handle_for_contact(priv->handles, tokens[3]);
+
+ if (handle == 0)
+ {
+ g_debug("%s: could not get handle for (%s) in IRC_RPL_ENDOFWHOIS...", G_STRFUNC, tokens[3]);
+ goto cleanupl;
+ }
+
+ update_presence(conn, handle, IDLE_PRESENCE_AVAILABLE, NULL);
+ }
+ }
+ else if (numeric == IRC_RPL_WHOISIDLE)
+ {
+ IdleHandle handle;
+ IdleContactPresence *cp;
+ guint last_activity;
+
+ if (tokenc < 5)
+ {
+ g_debug("%s: got IRC_RPL_WHOISIDLE with tokenc < 5, ignoring...", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ handle = idle_handle_for_contact(priv->handles, tokens[2]);
+
+ if (handle == 0)
+ {
+ g_debug("%s: failed to get handle for (%s) in RPL_WHOISIDLE, ignoring...", G_STRFUNC, tokens[2]);
+ goto cleanupl;
+ }
+
+ cp = idle_handle_get_presence(priv->handles, handle);
+
+ if (cp == NULL)
+ {
+ g_debug("%s: failed to get cp in RPL_WHOISIDLE, ignoring...", G_STRFUNC);
+ goto cleanupl;
+ }
+
+ last_activity = time(NULL) - atoi(tokens[3]);
+
+ update_presence_full(conn, handle, cp->presence_state, NULL, last_activity);
+
+ g_debug("%s: got RPL_WHOISIDLE for (%s) (handle %u)", G_STRFUNC, tokens[2], handle);
+ }
+ else
+ {
+ g_debug("%s: ignored unparsed message from server (%s)", G_STRFUNC, msg);
+ }
+
+cleanupl:
+
+ g_strfreev(tokens);
+
+ return reply;
+}
+
+static void irc_handshakes(IdleConnection *conn)
+{
+ IdleConnectionPrivate *priv;
+ gchar msg[IRC_MSG_MAXLEN+1];
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ if (priv->password != NULL)
+ {
+ g_snprintf(msg, IRC_MSG_MAXLEN+1, "PASS %s", priv->password);
+
+ send_irc_cmd(conn, msg);
+ }
+
+ g_snprintf(msg, IRC_MSG_MAXLEN+1, "NICK %s", priv->nickname);
+ send_irc_cmd(conn, msg);
+
+ g_snprintf(msg, IRC_MSG_MAXLEN+1, "USER %s %u * :%s", priv->nickname, 8, priv->realname);
+ send_irc_cmd(conn, msg);
+}
+
+static void connection_connect_cb(IdleConnection *conn, gboolean success)
+{
+ g_assert(conn != NULL);
+
+ if (success)
+ {
+ connection_status_change(conn, TP_CONN_STATUS_CONNECTED, TP_CONN_STATUS_REASON_REQUESTED);
+ }
+ else
+ {
+ connection_status_change(conn, TP_CONN_STATUS_DISCONNECTED, TP_CONN_STATUS_REASON_NETWORK_ERROR);
+ }
+}
+
+/* 2 minutes */
+#define PRESENCE_POLLING_TIMEOUT 2*60*1000
+
+static void presence_request_push(IdleConnection *obj, const gchar *nick);
+
+static void polling_foreach_func(guint i, gpointer user_data)
+{
+ IdleConnection *conn = IDLE_CONNECTION(user_data);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleHandle handle = (IdleHandle)(i);
+
+ if (idle_handle_is_valid(priv->handles, TP_HANDLE_TYPE_CONTACT, handle))
+ {
+ const gchar *nick = idle_handle_inspect(priv->handles, TP_HANDLE_TYPE_CONTACT, handle);
+
+ presence_request_push(conn, nick);
+ }
+ else
+ {
+ g_intset_remove(priv->polled_presences, handle);
+ }
+}
+
+static gboolean presence_polling_cb(gpointer user_data)
+{
+ IdleConnection *conn = IDLE_CONNECTION(user_data);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(user_data);
+
+ g_intset_foreach(priv->polled_presences, polling_foreach_func, conn);
+
+ if (!g_intset_size(priv->polled_presences))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void polled_presence_add(IdleConnection *conn, IdleHandle handle)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ g_intset_add(priv->polled_presences, handle);
+
+ if (!priv->presence_polling_timer_id)
+ {
+ presence_polling_cb(conn);
+ priv->presence_polling_timer_id = g_timeout_add(PRESENCE_POLLING_TIMEOUT, presence_polling_cb, conn);
+ }
+}
+
+static void polled_presence_remove(IdleConnection *conn, IdleHandle handle)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ g_intset_remove(priv->polled_presences, handle);
+
+ if (priv->presence_polling_timer_id && !g_intset_size(priv->polled_presences))
+ {
+ g_source_remove(priv->presence_polling_timer_id);
+ priv->presence_polling_timer_id = 0;
+ }
+}
+
+static void update_presence(IdleConnection *self, IdleHandle contact_handle, IdlePresenceState presence_state, const gchar *status_message)
+{
+ return update_presence_full(self, contact_handle, presence_state, status_message, 0);
+}
+
+static void update_presence_full(IdleConnection *self, IdleHandle contact_handle, IdlePresenceState presence_state, const gchar *status_message, guint last_activity)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
+ IdleContactPresence *cp = idle_handle_get_presence(priv->handles, contact_handle);
+ IdleHandle handles[2] = {contact_handle, 0};
+
+ if (cp)
+ {
+ if (cp->presence_state == presence_state &&
+ ((cp->status_message == NULL && status_message == NULL) ||
+ (cp->status_message != NULL && status_message != NULL &&
+ strcmp(cp->status_message, status_message) == 0)))
+ {
+ return;
+ }
+
+ if (presence_state == IDLE_PRESENCE_AVAILABLE)
+ {
+ polled_presence_remove(self, contact_handle);
+ idle_contact_presence_free(cp);
+ idle_handle_set_presence(priv->handles, contact_handle, NULL);
+ }
+ }
+ else if (presence_state != IDLE_PRESENCE_AVAILABLE)
+ {
+ cp = idle_contact_presence_new0();
+ g_assert(idle_handle_set_presence(priv->handles, contact_handle, cp));
+
+ polled_presence_add(self, contact_handle);
+ }
+ else
+ {
+ goto emit;
+ }
+
+ cp->presence_state = presence_state;
+
+ g_free(cp->status_message);
+
+ if (status_message && status_message[0] != '\0')
+ {
+ cp->status_message = g_strdup(status_message);
+ }
+ else
+ {
+ cp->status_message = NULL;
+ }
+
+ if (last_activity != 0)
+ {
+ cp->last_activity = last_activity;
+ }
+
+emit:
+ emit_presence_update(self, handles);
+}
+
+
+#if 0
+static gboolean socket_conn_open(IdleConnection *conn, GError **error)
+{
+ IdleConnectionPrivate *priv;
+
+ g_assert(conn != NULL);
+ g_assert(error != NULL);
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ if (priv->conn.fd)
+ {
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "Connection already open!");
+ return FALSE;
+ }
+ else
+ {
+ int fd;
+ pthread_t thread;
+ int rc;
+ struct sockaddr_in sin = {0};
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(INADDR_ANY);
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ g_debug("%s: socket() failed: %s", G_STRFUNC, strerror(errno));
+
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "socket() failed: %s", strerror(errno));
+
+ return FALSE;
+ }
+
+ if (bind(fd, (struct sockaddr *)(&sin), sizeof(sin)) == -1)
+ {
+ g_debug("%s: bind() failed: %s", G_STRFUNC, strerror(errno));
+
+ close(fd);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "bind() failed: %s", strerror(errno));
+
+ return FALSE;
+ }
+
+ priv->conn.fd = fd;
+ priv->conn.out_queue = g_async_queue_new();
+ priv->conn.exit = FALSE;
+ priv->conn.crashed = FALSE;
+ priv->conn.queue_wait = TRUE;
+
+ if ((rc = pthread_create(&thread, NULL, socket_conn_thread_fn, conn)) != 0)
+ {
+ priv->conn.fd = 0;
+ g_async_queue_unref(priv->conn.out_queue);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "Failed to create a thread for the socket connection: %s", strerror(rc));
+
+ return FALSE;
+ }
+
+ priv->conn.thread = thread;
+
+ g_debug("%s: connection (likely) has been opened", G_STRFUNC);
+
+ return TRUE;
+ }
+}
+
+/**
+ * Write everything in a queue to a fd
+ * Returns TRUE on success and FALSE on failure (network error, connection closed etc)
+ */
+
+static gboolean send_all_queued(int fd, GAsyncQueue *queue);
+
+/**
+ * Read a line (terminated by <CR><LF>) from <stream> and make it available to the caller via <msg>
+ * A NULL *<msg> signals the last line in a batch
+ * Returns TRUE on success and FALSE on network error (connection closed etc)
+ *
+ * Performs a poll() on <pollfd> with timeout == 0.5 sec, so if no messages are incoming, it also serves as a sleep point.
+ */
+
+static gboolean receive_line(int fd, gchar **msg, int poll_fd);
+
+static gboolean threaded_connection_open(IdleConnection *conn, GError **error);
+
+static gboolean threaded_connection_open(IdleConnection *conn, GError **error)
+{
+ IdleConnectionPrivate *priv;
+ struct sockaddr_in sin = {0};
+ struct hostent *dst_host;
+/* FILE *stream;*/
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ if ((dst_host = gethostbyname(priv->server)) == NULL)
+ {
+ close(priv->conn.fd);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "Failed to resolve server %s", priv->server);
+
+ return FALSE;
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(priv->port);
+ sin.sin_addr = *((struct in_addr *)(dst_host->h_addr));
+
+ if (connect(priv->conn.fd, (struct sockaddr *)(&sin), sizeof(sin)) == -1)
+ {
+ close(priv->conn.fd);
+
+ g_debug("%s: connect() failed: %s", G_STRFUNC, strerror(errno));
+
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "connect() failed: %s", strerror(errno));
+
+ return FALSE;
+ }
+/*
+ if ((stream = fdopen(priv->conn.fd, "r+")) == NULL)
+ {
+ close(priv->conn.fd);
+
+ g_debug("%s: fdopen() failed: %s", G_STRFUNC, strerror(errno));
+
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "fdopen() failed: %s", strerror(errno));
+
+ return FALSE;
+ }
+
+ priv->conn.stream = stream;
+*/
+ g_debug("%s: success?", G_STRFUNC);
+
+ return TRUE;
+}
+
+static void msg_split(IdleConnection *conn, gchar *msg, socket_conn_message_cb_t cb);
+
+static void *socket_conn_thread_fn(void *conn_closure)
+{
+ IdleConnection *conn = (IdleConnection *)(conn_closure);
+ IdleConnectionPrivate *priv;
+ GAsyncQueue *sendq = NULL;
+ TpConnectionStatusReason disconnect_reason = TP_CONN_STATUS_REASON_REQUESTED;
+ GError *error;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ sendq = g_async_queue_ref(priv->conn.out_queue);
+
+ g_assert(sendq != NULL);
+
+ if (!threaded_connection_open(conn, &error))
+ {
+ g_debug("%s: threaded_connection_open failed: %s", G_STRFUNC, error->message);
+
+ g_error_free(error);
+
+ disconnect_reason = TP_CONN_STATUS_REASON_NETWORK_ERROR;
+
+ goto exitl;
+ }
+
+ while (!(priv->conn.exit))
+ {
+ gchar *msg;
+ gboolean recv_ret;
+
+ while ((recv_ret = receive_line(priv->conn.fd, &msg, priv->conn.fd)) && (msg != NULL))
+ {
+ msg_split(conn, msg, priv->conn.message_cb);
+ free(msg);
+ }
+
+ if (recv_ret == FALSE)
+ {
+ g_debug("%s: network error in receive_line", G_STRFUNC);
+ disconnect_reason = TP_CONN_STATUS_REASON_NETWORK_ERROR;
+ goto exitl;
+ }
+
+ if (!priv->conn.queue_wait)
+ {
+ if (!send_all_queued(priv->conn.fd, priv->conn.out_queue))
+ {
+ g_debug("%s: network error in send_all_queued", G_STRFUNC);
+ disconnect_reason = TP_CONN_STATUS_REASON_NETWORK_ERROR;
+ goto exitl;
+ }
+ }
+ else
+ {
+ g_debug("%s: queue holding", G_STRFUNC);
+ }
+ }
+
+exitl:
+
+ g_async_queue_unref(sendq);
+ sendq = NULL;
+
+/* priv->conn.disconnected_cb(conn, disconnect_reason);*/
+
+ return NULL;
+}
+
+static void msg_split(IdleConnection *conn, gchar *msg, socket_conn_message_cb_t cb)
+{
+ int i;
+ int lasti = 0;
+ gchar *tmp;
+ gboolean line_ends = FALSE;
+ IdleConnectionPrivate *priv;
+ guint len;
+
+ g_assert(conn != NULL);
+ g_assert(msg != NULL);
+ g_assert(cb != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ len = strnlen(msg, IRC_MSG_MAXLEN+2);
+
+ for (i = 0; i < len; i++)
+ {
+ if ((msg[i] == '\n' || msg[i] == '\r'))
+ {
+ msg[i] = '\0';
+
+ if (i>lasti)
+ {
+ if ((lasti == 0) && (priv->conn.msg_store != NULL))
+ {
+ tmp = g_strconcat(priv->conn.msg_store, msg, NULL);
+
+ g_free(priv->conn.msg_store);
+ priv->conn.msg_store = NULL;
+ }
+ else
+ {
+ tmp = g_strdup(msg+lasti);
+ }
+
+ cb(conn, tmp);
+
+ g_free(tmp);
+ }
+
+ lasti = i+1;
+
+ line_ends = TRUE;
+ }
+ else
+ {
+ line_ends = FALSE;
+ }
+ }
+
+ if (!line_ends)
+ {
+ priv->conn.msg_store = g_strndup(msg+lasti, IRC_MSG_MAXLEN-lasti+2);
+ }
+}
+
+static gboolean send_all_queued(int fd, GAsyncQueue *queue)
+{
+ gpointer data;
+ size_t length;
+ int i = 0;
+
+ g_assert(fd != 0);
+ g_assert(queue != NULL);
+
+ while ((data = g_async_queue_try_pop(queue)))
+ {
+ size_t written;
+
+ i++;
+
+ length = strnlen(data, IRC_MSG_MAXLEN+2);
+
+ if ((written = send(fd, data, length, 0)) != length)
+ {
+ g_free(data);
+
+ g_debug("%s: failed to send whole message (%u bytes written)", G_STRFUNC, written);
+
+ return FALSE;
+ }
+
+ g_free(data);
+ }
+
+ return TRUE;
+}
+
+static gboolean receive_line(int fd, gchar **msg, int poll_fd)
+{
+ gchar *ret_msg = NULL;
+ struct pollfd pfd = {poll_fd, POLLIN|POLLPRI|POLLERR|POLLHUP|POLLNVAL, 0};
+ int poll_ret;
+
+ g_assert(fd >= 0);
+ g_assert(msg != NULL);
+ g_assert(poll_fd >= 0);
+
+ *msg = NULL;
+
+ if ((poll_ret = poll(&pfd, 1, 500)) != 0)
+ {
+ if (poll_ret == -1)
+ {
+ g_debug("%s: poll() failed: %s", G_STRFUNC, strerror(errno));
+
+ return FALSE;
+ }
+
+ if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL))
+ {
+ g_debug("%s: pfd.revents & (POLLERR|POLLHUP|POLLNVAL) != 0", G_STRFUNC);
+
+ return FALSE;
+ }
+
+ if (pfd.revents & (POLLIN|POLLPRI))
+ {
+ size_t n = IRC_MSG_MAXLEN+2;
+
+ ret_msg = g_malloc0(n);
+
+ if ((n = recv(fd, ret_msg, n, 0)) <= 0)
+ {
+ g_debug("%s: recv() failed: %s", G_STRFUNC, strerror(errno));
+
+ g_free(ret_msg);
+
+ return FALSE;
+ }
+ else
+ {
+ ret_msg[n] = '\0';
+ }
+ }
+ }
+ else
+ {
+ *msg = NULL;
+ }
+
+ *msg = ret_msg;
+
+ return TRUE;
+}
+#endif
+
+static void connection_status_change(IdleConnection *conn, TpConnectionStatus status, TpConnectionStatusReason reason)
+{
+ IdleConnectionPrivate *priv;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ g_debug("%s: status %u reason %u", G_STRFUNC, status, reason);
+
+ if (priv->status != status)
+ {
+ priv->status = status;
+
+ g_debug("%s: emitting status-changed with status %u reason %u", G_STRFUNC, status, reason);
+ g_signal_emit(conn, signals[STATUS_CHANGED], 0, status, reason);
+ }
+}
+
+static void im_channel_closed_cb(IdleIMChannel *chan, gpointer user_data);
+static void muc_channel_closed_cb(IdleMUCChannel *chan, gpointer user_data);
+
+gboolean hash_foreach_close_im_channel(gpointer key, gpointer value, gpointer user_data)
+{
+ IdleIMChannel *chan = IDLE_IM_CHANNEL(value);
+ GError *error = NULL;
+
+ g_signal_handlers_disconnect_by_func(chan, (GCallback)(im_channel_closed_cb), user_data);
+ g_debug("%s: calling idle_im_channel_close on %p", G_STRFUNC, chan);
+ idle_im_channel_close(chan, &error);
+ g_debug("%s: removing channel %p", G_STRFUNC, chan);
+ return TRUE;
+}
+
+gboolean hash_foreach_close_muc_channel(gpointer key, gpointer value, gpointer user_data)
+{
+ IdleMUCChannel *chan = IDLE_MUC_CHANNEL(value);
+ GError *error = NULL;
+
+ g_signal_handlers_disconnect_by_func(chan, (GCallback)(muc_channel_closed_cb), user_data);
+ g_debug("%s: calling idle_muc_channel_close on %p", G_STRFUNC, chan);
+ idle_muc_channel_close(chan, &error);
+ g_debug("%s: removing channel %p", G_STRFUNC, chan);
+ return TRUE;
+}
+
+static void close_all_channels(IdleConnection *conn)
+{
+ IdleConnectionPrivate *priv;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ if (priv->im_channels)
+ {
+ GHashTable *tmp = priv->im_channels;
+ priv->im_channels = NULL;
+ g_hash_table_destroy(tmp);
+ }
+
+ if (priv->muc_channels)
+ {
+ GHashTable *tmp = priv->muc_channels;
+ priv->muc_channels = NULL;
+ g_hash_table_destroy(tmp);
+ }
+}
+
+static void send_quit_request(IdleConnection *conn)
+{
+ IdleConnectionPrivate *priv;
+ gchar cmd[IRC_MSG_MAXLEN+1];
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ g_snprintf(cmd, IRC_MSG_MAXLEN+1, "QUIT :%s", priv->quit_message);
+
+ send_irc_cmd(conn, cmd);
+}
+
+static void connection_disconnect(IdleConnection *conn, TpConnectionStatusReason reason)
+{
+ IdleConnectionPrivate *priv;
+ GError *error;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ priv->disconnect_reason = reason;
+
+ send_quit_request(conn);
+
+ connection_status_change(conn, TP_CONN_STATUS_DISCONNECTED, reason);
+
+ if (priv->conn != NULL)
+ {
+ if (!idle_server_connection_iface_disconnect(priv->conn, &error))
+ {
+ g_debug("%s: server connection failed to disconnect: %s", G_STRFUNC, error->message);
+ }
+ }
+}
+
+static void connection_disconnect_cb(IdleConnection *conn, TpConnectionStatusReason reason)
+{
+ IdleConnectionPrivate *priv;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ g_debug("%s: called with reason %u", G_STRFUNC, reason);
+
+ close_all_channels(conn);
+ connection_status_change(conn, TP_CONN_STATUS_DISCONNECTED, reason);
+
+ if (priv->conn)
+ {
+ g_signal_handlers_disconnect_by_func(priv->conn, (GCallback)(sconn_status_changed_cb), conn);
+ g_object_unref(priv->conn);
+ priv->conn = NULL;
+ }
+
+ if (priv->msg_queue_timeout)
+ {
+ g_source_remove(priv->msg_queue_timeout);
+ priv->msg_queue_timeout = 0;
+ }
+
+ if (!priv->disconnected)
+ {
+ priv->disconnected = TRUE;
+ g_debug("%s: emitting DISCONNECTED", G_STRFUNC);
+ g_signal_emit(conn, signals[DISCONNECTED], 0);
+ }
+}
+
+struct member_check_data
+{
+ IdleHandle handle;
+ gboolean is_present;
+};
+
+static void member_check_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ IdleMUCChannel *chan = IDLE_MUC_CHANNEL(value);
+ struct member_check_data *data = (struct member_check_data *)(user_data);
+
+ if (!data->is_present && _idle_muc_channel_has_current_member(chan, data->handle))
+ {
+ data->is_present = TRUE;
+ }
+}
+
+const IdleContactPresence *get_contact_presence(IdleConnection *conn, IdleHandle handle)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ const IdleContactPresence *ret;
+
+ ret = idle_handle_get_presence(priv->handles, handle);
+
+ if (!ret)
+ {
+ const static IdleContactPresence available_presence = {IDLE_PRESENCE_AVAILABLE, NULL, 0};
+ const static IdleContactPresence offline_presence = {IDLE_PRESENCE_OFFLINE, NULL, 0};
+
+ if (g_hash_table_lookup(priv->im_channels, GUINT_TO_POINTER(handle)) != NULL)
+ {
+ ret = &available_presence;
+ }
+ else
+ {
+ struct member_check_data data = {handle, FALSE};
+
+ g_hash_table_foreach(priv->muc_channels, member_check_foreach, &data);
+
+ if (data.is_present)
+ {
+ ret = &available_presence;
+ }
+ else
+ {
+ ret = &offline_presence;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void
+bastard_destroyer_from_collabora(GValue *value)
+{
+ g_value_unset(value);
+ g_free(value);
+}
+
+static void emit_presence_update(IdleConnection *self, const IdleHandle *contact_handles)
+{
+ IdleConnectionPrivate *priv;
+ const IdleContactPresence *cp;
+ GHashTable *presence;
+ GValueArray *vals;
+ GHashTable *contact_status, *parameters;
+ int i;
+
+ g_assert(self != NULL);
+ g_assert(IDLE_IS_CONNECTION(self));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(self);
+
+ presence = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)(g_value_array_free));
+
+ for (i = 0; contact_handles[i] != 0; i++)
+ {
+ GValue *message;
+
+ cp = get_contact_presence(self, contact_handles[i]);
+
+ if (cp == NULL)
+ {
+ g_debug("%s: did not find presence for %u!!", G_STRFUNC, contact_handles[i]);
+ continue;
+ }
+
+ message = g_new0(GValue, 1);
+ g_value_init(message, G_TYPE_STRING);
+
+ g_value_set_string(message, cp->status_message);
+
+ parameters = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)(bastard_destroyer_from_collabora));
+
+ g_hash_table_insert(parameters, "message", message);
+
+ contact_status = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)(g_hash_table_destroy));
+
+ g_hash_table_insert(contact_status, (gpointer)(idle_statuses[cp->presence_state].name), parameters);
+
+ vals = g_value_array_new(2);
+
+ g_value_array_append(vals, NULL);
+ g_value_init(g_value_array_get_nth(vals, 0), G_TYPE_UINT);
+ g_value_set_uint(g_value_array_get_nth(vals, 0), cp->last_activity);
+
+ g_value_array_append(vals, NULL);
+ g_value_init(g_value_array_get_nth(vals, 1),
+ dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
+ dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)));
+ g_value_take_boxed(g_value_array_get_nth(vals, 1), contact_status);
+
+ g_hash_table_insert(presence, GINT_TO_POINTER(contact_handles[i]), vals);
+ }
+
+#if 0
+ for (i = 0; contact_handles[i] != 0; i++)
+ {
+ GValue *message;
+
+ cp = idle_handle_get_qdata(priv->handles, TP_HANDLE_TYPE_CONTACT, contact_handles[i], data_key);
+
+ if (!cp)
+ {
+ continue;
+ }
+
+ message = g_new0(GValue, 1);
+ g_value_init(message, G_TYPE_STRING);
+ g_value_set_static_string(message, cp->status_message);
+
+ parameters = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)(g_hash_table_destroy));
+
+ g_hash_table_insert(parameters, "message", message);
+
+ contact_status = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)(g_hash_table_destroy));
+
+ g_hash_table_insert(contact_status, (gpointer)(idle_statuses[cp->presence_state].name), parameters);
+
+ vals = g_value_array_new(2);
+
+ g_value_array_append(vals, NULL);
+ g_value_init(g_value_array_get_nth(vals, 0), G_TYPE_UINT);
+ g_value_set_uint(g_value_array_get_nth(vals, 0), timestamp);
+
+ g_value_array_append(vals, NULL);
+ g_value_init(g_value_array_get_nth(vals, 1), dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
+ dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)));
+ g_value_take_boxed(g_value_array_get_nth(vals, 1), contact_status);
+
+ g_hash_table_insert(presence, GINT_TO_POINTER(contact_handles[i]), vals);
+ }
+#endif
+ g_debug("%s: emitting PRESENCE_UPDATE with %u presences", G_STRFUNC, g_hash_table_size(presence));
+ g_signal_emit(self, signals[PRESENCE_UPDATE], 0, presence);
+ g_hash_table_destroy(presence);
+}
+
+static gboolean signal_own_presence (IdleConnection *self, GError **error)
+{
+ IdleConnectionPrivate *priv;
+ gchar msg[IRC_MSG_MAXLEN+1];
+ IdleContactPresence *cp;
+
+ g_assert(self != NULL);
+ g_assert(error != NULL);
+ g_assert(IDLE_IS_CONNECTION(self));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(self);
+
+ cp = idle_handle_get_presence(priv->handles, priv->self_handle);
+
+ g_assert(cp != NULL);
+
+ switch (cp->presence_state)
+ {
+ case IDLE_PRESENCE_AVAILABLE:
+ {
+ strcpy(msg, "AWAY");
+ }
+ break;
+ default:
+ {
+ gchar *awaymsg;
+
+ /* we can't post a zero-length away message so let's use "away" instead */
+ if ((cp->status_message == NULL) || (strlen(cp->status_message) == 0))
+ {
+ awaymsg = g_strdup("away");
+ }
+ else
+ {
+ awaymsg = g_strndup(cp->status_message, IRC_MSG_MAXLEN+1-strlen("AWAY :"));
+ }
+
+ sprintf(msg, "AWAY :%s", awaymsg);
+
+ g_free(awaymsg);
+ }
+ break;
+ }
+
+ send_irc_cmd(self, msg);
+
+ return TRUE;
+}
+
+static IdleIMChannel *new_im_channel(IdleConnection *conn, IdleHandle handle, gboolean suppress_handler)
+{
+ IdleConnectionPrivate *priv;
+ IdleIMChannel *chan;
+ gchar *object_path;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ object_path = g_strdup_printf("%s/ImChannel%u", priv->object_path, handle);
+
+ chan = g_object_new(IDLE_TYPE_IM_CHANNEL, "connection", conn,
+ "object-path", object_path,
+ "handle", handle,
+ NULL);
+
+ g_debug("%s: object path %s", G_STRFUNC, object_path);
+
+ g_signal_connect(chan, "closed", (GCallback)(im_channel_closed_cb), conn);
+
+ g_hash_table_insert(priv->im_channels, GINT_TO_POINTER(handle), chan);
+
+ g_signal_emit(conn, signals[NEW_CHANNEL], 0, object_path, TP_IFACE_CHANNEL_TYPE_TEXT,
+ TP_HANDLE_TYPE_CONTACT, handle, suppress_handler);
+
+ g_free(object_path);
+
+ return chan;
+}
+
+static IdleMUCChannel *new_muc_channel(IdleConnection *conn, IdleHandle handle, gboolean suppress_handler)
+{
+ IdleConnectionPrivate *priv;
+ IdleMUCChannel *chan;
+ gchar *object_path;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ object_path = g_strdup_printf("%s/MucChannel%u", priv->object_path, handle);
+
+ chan = g_object_new(IDLE_TYPE_MUC_CHANNEL, "connection", conn,
+ "object-path", object_path,
+ "handle", handle,
+ NULL);
+
+ g_debug("%s: object path %s", G_STRFUNC, object_path);
+
+ g_signal_connect(chan, "closed", (GCallback)(muc_channel_closed_cb), conn);
+
+ g_hash_table_insert(priv->muc_channels, GINT_TO_POINTER(handle), chan);
+
+ g_signal_emit(conn, signals[NEW_CHANNEL], 0, object_path, TP_IFACE_CHANNEL_TYPE_TEXT,
+ TP_HANDLE_TYPE_ROOM, handle, suppress_handler);
+
+ g_free(object_path);
+
+ return chan;
+}
+
+static void muc_channel_join_ready_cb(IdleMUCChannel *chan, guint err, gpointer user_data)
+{
+ IdleConnection *conn = IDLE_CONNECTION(user_data);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ DBusGMethodInvocation *ctx;
+ gboolean suppress;
+ GError *error;
+ gchar *obj_path;
+ guint handle;
+ chan_req_data *data = g_hash_table_lookup(priv->chan_req_ctxs, chan);
+
+ g_assert(data != NULL);
+
+ ctx = data->ctx;
+ suppress = data->suppress_handler;
+
+ g_object_get(chan, "handle", &handle, NULL);
+ g_object_get(chan, "object-path", &obj_path, NULL);
+
+ switch (err)
+ {
+ case MUC_CHANNEL_JOIN_ERROR_NONE:
+ {
+ dbus_g_method_return(ctx, obj_path);
+ g_signal_emit(conn, signals[NEW_CHANNEL], 0, obj_path, TP_IFACE_CHANNEL_TYPE_TEXT,
+ TP_HANDLE_TYPE_ROOM, handle, suppress);
+ }
+ break;
+ case MUC_CHANNEL_JOIN_ERROR_BANNED:
+ {
+ error = g_error_new(TELEPATHY_ERRORS, ChannelBanned, "banned from room");
+ dbus_g_method_return_error(ctx, error);
+ g_error_free(error);
+ }
+ break;
+ case MUC_CHANNEL_JOIN_ERROR_FULL:
+ {
+ error = g_error_new(TELEPATHY_ERRORS, ChannelFull, "room full");
+ dbus_g_method_return_error(ctx, error);
+ g_error_free(error);
+ }
+ break;
+ case MUC_CHANNEL_JOIN_ERROR_INVITE_ONLY:
+ {
+ error = g_error_new(TELEPATHY_ERRORS, ChannelInviteOnly, "room invite only");
+ dbus_g_method_return_error(ctx, error);
+ g_error_free(error);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (err != MUC_CHANNEL_JOIN_ERROR_NONE)
+ {
+ g_hash_table_remove(priv->muc_channels, GINT_TO_POINTER(handle));
+ }
+
+ g_hash_table_remove(priv->chan_req_ctxs, chan);
+ g_free(obj_path);
+}
+
+static IdleMUCChannel *new_muc_channel_async_req(IdleConnection *conn, IdleHandle handle, gboolean suppress_handler, DBusGMethodInvocation *ctx)
+{
+ IdleConnectionPrivate *priv;
+ IdleMUCChannel *chan;
+ gchar *object_path;
+ chan_req_data *req_data;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ object_path = g_strdup_printf("%s/MucChannel%u", priv->object_path, handle);
+
+ chan = g_object_new(IDLE_TYPE_MUC_CHANNEL, "connection", conn,
+ "object-path", object_path,
+ "handle", handle,
+ NULL);
+
+ g_debug("%s: object path %s", G_STRFUNC, object_path);
+
+ g_signal_connect(chan, "closed", (GCallback)(muc_channel_closed_cb), conn);
+ g_signal_connect(chan, "join-ready", (GCallback)(muc_channel_join_ready_cb), conn);
+
+ g_hash_table_insert(priv->muc_channels, GINT_TO_POINTER(handle), chan);
+
+ req_data = g_new0(chan_req_data, 1);
+
+ req_data->ctx = ctx;
+ req_data->suppress_handler = suppress_handler;
+
+ g_hash_table_insert(priv->chan_req_ctxs, chan, req_data);
+
+ _idle_muc_channel_join_attempt(chan);
+
+ g_free(object_path);
+
+ return chan;
+}
+
+static void im_channel_closed_cb(IdleIMChannel *chan, gpointer user_data)
+{
+ IdleConnection *conn = IDLE_CONNECTION(user_data);
+ IdleConnectionPrivate *priv;
+ IdleHandle contact_handle;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ if (priv->im_channels)
+ {
+ g_object_get(chan, "handle", &contact_handle, NULL);
+
+ g_debug("%s: removing channel with handle %u", G_STRFUNC, contact_handle);
+ g_hash_table_remove(priv->im_channels, GINT_TO_POINTER(contact_handle));
+ g_debug("%s: removed channel with handle %u", G_STRFUNC, contact_handle);
+ }
+}
+
+static void muc_channel_closed_cb(IdleMUCChannel *chan, gpointer user_data)
+{
+ IdleConnection *conn = IDLE_CONNECTION(user_data);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleHandle room_handle;
+
+ if (priv->muc_channels)
+ {
+ g_object_get(chan, "handle", &room_handle, NULL);
+
+ g_debug("%s: removing channel with handle %u", G_STRFUNC, room_handle);
+ g_hash_table_remove(priv->muc_channels, GINT_TO_POINTER(room_handle));
+ g_debug("%s: removed channel with handle %u", G_STRFUNC, room_handle);
+ }
+}
+
+static void destroy_handle_sets(gpointer set)
+{
+ IdleHandleSet *handle_set;
+
+ g_assert(set != NULL);
+ handle_set = (IdleHandleSet *)(set);
+
+ idle_handle_set_destroy(handle_set);
+}
+
+void _idle_connection_client_hold_handle(IdleConnection *conn, gchar *client_name,
+ IdleHandle handle, TpHandleType type)
+{
+ IdleConnectionPrivate *priv;
+ IdleHandleSet *handle_set;
+ GData **handle_set_list;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ switch (type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ handle_set_list = &(priv->cl_contact_handle_sets);
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ handle_set_list = &(priv->cl_room_handle_sets);
+ }
+ break;
+ default:
+ {
+ g_critical("%s: invalid handle type %u", G_STRFUNC, type);
+ return;
+ }
+ }
+
+ handle_set = (IdleHandleSet *)(g_datalist_get_data(handle_set_list, client_name));
+
+ if (handle_set == NULL)
+ {
+ handle_set = idle_handle_set_new(priv->handles, type);
+ g_datalist_set_data_full(handle_set_list, client_name, handle_set, destroy_handle_sets);
+ }
+
+ idle_handle_set_add(handle_set, handle);
+}
+
+gboolean _idle_connection_client_release_handle(IdleConnection *conn, gchar *client_name,
+ IdleHandle handle, TpHandleType type)
+{
+ IdleConnectionPrivate *priv;
+ IdleHandleSet *handle_set;
+ GData **handle_set_list;
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ switch (type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ handle_set_list = &(priv->cl_contact_handle_sets);
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ handle_set_list = &(priv->cl_room_handle_sets);
+ }
+ break;
+ default:
+ {
+ g_critical("%s: invalid handle type %u", G_STRFUNC, type);
+ return FALSE;
+ }
+ }
+
+ handle_set = (IdleHandleSet *)(g_datalist_get_data(handle_set_list, client_name));
+
+ if (handle_set)
+ {
+ return idle_handle_set_remove(handle_set, handle);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static GHashTable *
+get_statuses_arguments()
+{
+ static GHashTable *arguments = NULL;
+
+ if (arguments == NULL)
+ {
+ arguments = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_insert (arguments, "message", "s");
+ }
+
+ return arguments;
+}
+
+#if 0
+/* slashcommand handlers */
+
+static void _idle_connection_slashquery(IdleConnection *conn, const gchar *nicks);
+static void _idle_connection_slashmsg(IdleConnection *conn, const gchar *nicknmsg, guint type);
+
+gboolean _idle_connection_slash_cmd(IdleConnection *conn, const gchar *cmd)
+{
+ IdleConnectionPrivate *priv;
+
+ g_assert(conn != NULL);
+ g_assert(cmd != NULL);
+
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ if (cmd[0] != '/')
+ {
+ return FALSE;
+ }
+
+ if ((g_strncasecmp(cmd+1, "QUERY ", 6) == 0) && (cmd[7] != '\0'))
+ {
+ _idle_connection_slashquery(conn, cmd+7);
+
+ return TRUE;
+ }
+ else if (g_strncasecmp(cmd+1, "MSG ", 4))
+ {
+ _idle_connection_slashmsg(conn, cmd+5, TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL);
+
+ return TRUE;
+ }
+ else if (g_strncasecmp(cmd+1, "NOTICE ", 7))
+ {
+ _idle_connection_slashmsg(conn, cmd+8, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE);
+
+ return TRUE;
+ }
+ else if (g_strncasecmp(cmd+1, "ACTION ", 7))
+ {
+ _idle_connection_slashmsg(conn, cmd+8, TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION);
+
+ return TRUE;
+ }
+ else
+ {
+ g_debug("%s: unimplemented slash command (%s)", G_STRFUNC, cmd);
+
+ return FALSE;
+ }
+}
+
+void _idle_connection_slashquery(IdleConnection *conn, const gchar *nicks)
+{
+ int i;
+ int lasti = 0;
+ gchar *nicktmp;
+ IdleConnectionPrivate *priv;
+ IdleIMChannel *chan;
+ IdleHandle handle;
+ size_t len = strlen(nicks);
+
+ g_assert(conn != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ for (i=0; i < len; i++)
+ {
+ if (isblank(nicks[i]))
+ {
+ if (i > lasti)
+ {
+ nicktmp = g_malloc(i-lasti+1);
+ memcpy(nicktmp, nicks+lasti, i-lasti);
+ nicktmp[i-lasti+1] = '\0';
+
+ if ((handle = idle_handle_for_contact(priv->handles, nicktmp)) == 0)
+ {
+ g_debug("%s: invalid nick %s passed", G_STRFUNC, nicktmp);
+ }
+ else
+ {
+ chan = (IdleIMChannel *)(g_hash_table_lookup(priv->im_channels, GINT_TO_POINTER(handle)));
+
+ if (chan != NULL)
+ {
+ g_debug("%s: query already open for %u", G_STRFUNC, handle);
+ }
+ else
+ {
+ g_debug("%s: spawning new IdleIMChannel for %u", G_STRFUNC, handle);
+
+ chan = new_im_channel(conn, handle, FALSE);
+ }
+ }
+
+ g_free(nicktmp);
+ }
+
+ lasti = i+1;
+ }
+ }
+}
+
+void _idle_connection_slashmsg(IdleConnection *conn, const gchar *nicknmsg, guint type)
+{
+ IdleConnectionPrivate *priv;
+ const gchar *nicktmp, *msgtmp;
+ gchar *nickcopy;
+
+ g_assert(conn != NULL);
+ g_assert(nicknmsg != NULL);
+ g_assert(IDLE_IS_CONNECTION(conn));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+
+ for (nicktmp = nicknmsg; isblank(*nicktmp); nicktmp++){}
+
+ for (msgtmp = nicktmp+1; (!isblank(*msgtmp)) && (*msgtmp != '\0'); msgtmp++){}
+
+ nickcopy = g_strndup(nicktmp, msgtmp-nicktmp-1);
+
+ for (msgtmp++; isblank(*msgtmp); msgtmp++){}
+
+ if (isalpha(nicktmp[0]))
+ {
+ IdleHandle handle;
+ IdleIMChannel *chan;
+
+ if ((handle = idle_handle_for_contact(priv->handles, nickcopy)) == 0)
+ {
+ g_debug("%s: invalid nick %s", G_STRFUNC, nicktmp);
+ }
+ else
+ {
+ chan = (IdleIMChannel *)(g_hash_table_lookup(priv->im_channels, GINT_TO_POINTER(handle)));
+
+ if (chan == NULL)
+ {
+ char *msg;
+
+ g_debug("%s: sending to %s straight without a channel", G_STRFUNC, nickcopy);
+
+ switch (type)
+ {
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
+ {
+ msg = g_strdup_printf("PRIVMSG %s :%s", nickcopy, msgtmp);
+ }
+ break;
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
+ {
+ msg = g_strdup_printf("NOTICE %s :%s", nickcopy, msgtmp);
+ }
+ break;
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
+ {
+ msg = g_strdup_printf("PRIVMSG %s :\001ACTION %s\001", nickcopy, msgtmp);
+ }
+ break;
+ default:
+ {
+ g_assert_not_reached();
+ }
+ break;
+ }
+
+ send_irc_cmd(conn, msg);
+
+ g_free(msg);
+ }
+ else
+ {
+ GError *send_error = NULL;
+
+ idle_im_channel_send(chan, type, msgtmp, &send_error);
+
+ if (send_error)
+ {
+ g_debug("%s: error in idle_im_channel_send: %s", G_STRFUNC, send_error->message);
+
+ g_error_free(send_error);
+ }
+ }
+ }
+ }
+ else
+ {
+ switch (nicktmp[0])
+ {
+ case '&':
+ case '#':
+ case '+':
+ case '!':
+ {
+ IdleHandle handle;
+
+ if ((handle = idle_handle_for_room(priv->handles, nickcopy)) == 0)
+ {
+ g_debug("%s: failed to get handle for channel %s", G_STRFUNC, nickcopy);
+ }
+ else
+ {
+ IdleMUCChannel *chan;
+ GError *send_error = NULL;
+
+ chan = (IdleMUCChannel *)(g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle)));
+
+ if (chan == NULL)
+ {
+ gchar *msg;
+
+ g_debug("%s: sending straight to %s without a channel", G_STRFUNC, nickcopy);
+
+ switch (type)
+ {
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
+ {
+ msg = g_strdup_printf("PRIVMSG %s :%s", nickcopy, msgtmp);
+ }
+ break;
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
+ {
+ msg = g_strdup_printf("NOTICE %s :%s", nickcopy, msgtmp);
+ }
+ break;
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
+ {
+ msg = g_strdup_printf("PRIVMSG %s :\001ACTION %s\001", nickcopy, msgtmp);
+ }
+ break;
+ default:
+ {
+ g_assert_not_reached();
+ }
+ break;
+ }
+
+ send_irc_cmd(conn, msg);
+
+ g_free(msg);
+ }
+ else
+ {
+ idle_muc_channel_send(chan, type, msgtmp, &send_error);
+
+ if (send_error != NULL)
+ {
+ g_debug("%s: idle_muc_channel_send failed: %s", G_STRFUNC, send_error->message);
+
+ g_error_free(send_error);
+ }
+ }
+ }
+ }
+ break;
+ default:
+ {
+ g_debug("%s: ignored invalid target %s for /msg, /action or /notice", G_STRFUNC, nickcopy);
+ }
+ }
+ }
+
+ g_free(nickcopy);
+}
+#endif
+/* D-BUS-exported methods */
+
+/**
+ * idle_connection_add_status
+ *
+ * Implements DBus method AddStatus
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_add_status (IdleConnection *obj, const gchar * status, GHashTable * parms, GError **error)
+{
+ IdleConnectionPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED(obj, priv, *error);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotImplemented,
+ "Only one status is possible at a time with this protocol");
+
+ return FALSE;
+}
+
+
+#if 0
+/**
+ * idle_connection_advertise_capabilities
+ *
+ * Implements DBus method AdvertiseCapabilities
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_advertise_capabilities (IdleConnection *obj, const gchar ** add, const gchar ** remove, GError **error)
+{
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ ERROR_IF_NOT_CONNECTED(obj, IDLE_CONNECTION_GET_PRIVATE(obj), *error);
+
+ /* collabora doesn't implement these -> I don't implement these . . . . */
+
+ return TRUE;
+}
+#endif
+
+
+/**
+ * idle_connection_clear_status
+ *
+ * Implements DBus method ClearStatus
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_clear_status (IdleConnection *obj, GError **error)
+{
+ IdleConnectionPrivate *priv;
+ IdleContactPresence *cp;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED(obj, priv, *error);
+
+ cp = idle_handle_get_presence(priv->handles, priv->self_handle);
+
+ if (!cp)
+ {
+ return TRUE;
+ }
+
+ cp->presence_state = IDLE_PRESENCE_AVAILABLE;
+ g_free(cp->status_message);
+ cp->status_message = NULL;
+ cp->last_activity = 0;
+
+ return signal_own_presence(obj, error);
+}
+
+
+/**
+ * idle_connection_connect
+ *
+ * Implements DBus method Connect
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_connect (IdleConnection *obj, GError **error)
+{
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ return _idle_connection_connect(obj, error);
+}
+
+
+/**
+ * idle_connection_disconnect
+ *
+ * Implements DBus method Disconnect
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_disconnect (IdleConnection *obj, GError **error)
+{
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ connection_disconnect(obj, TP_CONN_STATUS_REASON_REQUESTED);
+
+ return TRUE;
+}
+
+
+/* bah, capabilites is no good atm */
+#if 0
+/**
+ * idle_connection_get_capabilities
+ *
+ * Implements DBus method GetCapabilities
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_capabilities (IdleConnection *obj, GArray *handles, GPtrArray ** ret, GError **error)
+{
+ int i;
+ IdleConnectionPrivate *priv;
+ IdleHandle handle;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED(obj, priv, *error);
+
+ *ret = g_ptr_array_sized_new(handles->len);
+
+ for (i = 0; i<handles->len; i++)
+ {
+ GValue vals = {0.};
+
+ handle = g_array_index(handles, guint, i);
+
+ g_value_init(&vals, TP_CAPABILITY_PAIR_TYPE);
+ g_value_set_static_boxed(&vals, dbus_g_type_specialized_construct(TP_CAPABILITY_PAIR_TYPE));
+
+ if (handle == 0)
+ {
+ dbus_g_type_struct_set(&vals,
+ 0, handle,
+ }
+ else if (!idle_handle_is_valid(priv->handles, TP_HANDLE_TYPE_CONTACT, handle) && !idle_handle_is_valid(priv->handles, TP_HANDLE_TYPE_ROOM, handle))
+ {
+ g_debug("%s: invalid handle %u", G_STRFUNC, handle);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "invalid handle %u", handle);
+
+ return FALSE;
+ }
+
+ *ret = g_ptr_array_sized_new(2);
+
+ g_value_init(&vals, TP_CAPABILITY_PAIR_TYPE);
+ g_value_set_static_boxed(&vals, dbus_g_type_specialized_construct(TP_CAPABILITY_PAIR_TYPE));
+
+ dbus_g_type_struct_set(&vals, 0, TP_IFACE_CHANNEL_TYPE_TEXT, 1, TP_CONN_CAPABILITY_TYPE_CREATE, G_MAXUINT);
+
+ g_ptr_array_add(*ret, g_value_get_boxed(&vals));
+
+ dbus_g_type_struct_set(&vals, 0, TP_IFACE_CHANNEL_TYPE_TEXT, 1, TP_CONN_CAPABILITY_TYPE_CREATE, G_MAXUINT);
+
+ return TRUE;
+}
+#endif
+
+/**
+ * idle_connection_get_interfaces
+ *
+ * Implements DBus method GetInterfaces
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_interfaces (IdleConnection *obj, gchar *** ret, GError **error)
+{
+ const char *interfaces[] = {TP_IFACE_CONN_INTERFACE_PRESENCE,
+ /*TP_IFACE_CONN_INTERFACE_CAPABILITIES,*/
+ TP_IFACE_CONN_INTERFACE_RENAMING,
+ NULL};
+ IdleConnectionPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED(obj, priv, *error);
+
+ *ret = g_strdupv((gchar**)(interfaces));
+
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_get_protocol
+ *
+ * Implements DBus method GetProtocol
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_protocol (IdleConnection *obj, gchar ** ret, GError **error)
+{
+ IdleConnectionPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ *ret = g_strdup(priv->protocol);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_get_self_handle
+ *
+ * Implements DBus method GetSelfHandle
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_self_handle (IdleConnection *obj, guint* ret, GError **error)
+{
+ IdleConnectionPrivate *priv;
+
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED(obj, priv, *error);
+
+ *ret = priv->self_handle;
+
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_get_status
+ *
+ * Implements DBus method GetStatus
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_status (IdleConnection *obj, guint* ret, GError **error)
+{
+ IdleConnectionPrivate *priv;
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ *ret = priv->status;
+
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_get_statuses
+ *
+ * Implements DBus method GetStatuses
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_get_statuses (IdleConnection *obj, GHashTable ** ret, GError **error)
+{
+ IdleConnectionPrivate *priv;
+ GValueArray *status;
+ int i;
+
+ g_assert (IDLE_IS_CONNECTION (obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE (obj);
+
+ ERROR_IF_NOT_CONNECTED (obj, priv, *error)
+
+ g_debug ("%s called.", G_STRFUNC);
+
+ *ret = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) g_value_array_free);
+
+ for (i=0; i < LAST_IDLE_PRESENCE_ENUM; i++)
+ {
+ status = g_value_array_new (5);
+
+ g_value_array_append (status, NULL);
+ g_value_init (g_value_array_get_nth (status, 0), G_TYPE_UINT);
+ g_value_set_uint (g_value_array_get_nth (status, 0),
+ idle_statuses[i].presence_type);
+
+ g_value_array_append (status, NULL);
+ g_value_init (g_value_array_get_nth(status, 1), G_TYPE_BOOLEAN);
+ g_value_set_boolean(g_value_array_get_nth(status, 1),
+ idle_statuses[i].self);
+
+ g_value_array_append (status, NULL);
+ g_value_init(g_value_array_get_nth (status, 2), G_TYPE_BOOLEAN);
+ g_value_set_boolean(g_value_array_get_nth (status, 2),
+ idle_statuses[i].exclusive);
+
+ g_value_array_append (status, NULL);
+ g_value_init (g_value_array_get_nth (status, 3),
+ DBUS_TYPE_G_STRING_STRING_HASHTABLE);
+ g_value_set_static_boxed(g_value_array_get_nth (status, 3),
+ get_statuses_arguments());
+
+ g_hash_table_insert (*ret, (gchar*)(idle_statuses[i].name), status);
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_hold_handles
+ *
+ * Implements DBus method HoldHandles
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @context: The DBUS invocation context to use to return values
+ * or throw an error.
+ */
+gboolean idle_connection_hold_handles (IdleConnection *obj,
+ guint handle_type,
+ const GArray *handles,
+ DBusGMethodInvocation *context)
+{
+ IdleConnectionPrivate *priv;
+ GError *error = NULL;
+ gchar *sender;
+ int i;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED_ASYNC(obj, priv, error, context);
+
+ if (!idle_handle_type_is_valid(handle_type))
+ {
+ g_debug("%s: invalid handle type %u", G_STRFUNC, handle_type);
+
+ error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "invalid handle type %u", handle_type);
+ dbus_g_method_return_error(context, error);
+ g_error_free(error);
+
+ return FALSE;
+ }
+
+ sender = dbus_g_method_get_sender(context);
+
+ for (i=0; i<handles->len; i++)
+ {
+ IdleHandle handle;
+ gboolean valid;
+
+ handle = g_array_index(handles, guint, i);
+ valid = idle_handle_is_valid(priv->handles, handle_type, handle);
+
+ if (!valid)
+ {
+ g_debug("%s: invalid handle %u", G_STRFUNC, handle);
+
+ error = g_error_new(TELEPATHY_ERRORS, InvalidHandle, "invalid handle %u", handle);
+
+ dbus_g_method_return_error(context, error);
+
+ g_error_free(error);
+
+ return FALSE;
+ }
+
+ _idle_connection_client_hold_handle(obj, sender, handle, handle_type);
+ }
+
+ dbus_g_method_return(context);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_inspect_handle
+ *
+ * Implements DBus method InspectHandle
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_inspect_handle (IdleConnection *obj, guint handle_type, guint handle, gchar ** ret, GError **_error)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+ const gchar *tmp;
+
+ ERROR_IF_NOT_CONNECTED(obj, priv, *_error);
+
+ if (!idle_handle_type_is_valid(handle_type))
+ {
+ g_debug("%s: invalid handle type %u", G_STRFUNC, handle_type);
+
+ *_error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "invalid handle type %u", handle_type);
+
+ return FALSE;
+ }
+
+ tmp = idle_handle_inspect(priv->handles, handle_type, handle);
+
+ if (tmp == NULL)
+ {
+ g_debug("%s: invalid handle %u (type %u)", G_STRFUNC, handle, handle_type);
+
+ *_error = g_error_new(TELEPATHY_ERRORS, InvalidHandle, "invalid handle %u", handle);
+
+ return FALSE;
+ }
+
+ *ret = g_strdup(tmp);
+
+ return TRUE;
+}
+
+gboolean idle_connection_inspect_handles (IdleConnection *conn, guint handle_type, const GArray *handles, DBusGMethodInvocation *ctx)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ const char *tmp;
+ const gchar **ret;
+ int i;
+ GError *error;
+
+ ERROR_IF_NOT_CONNECTED_ASYNC(obj, priv, error, ctx);
+
+ if (!idle_handle_type_is_valid(handle_type))
+ {
+ g_debug("%s: invalid handle type %u", G_STRFUNC, handle_type);
+
+ error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "invalid handle type %u", handle_type);
+
+ dbus_g_method_return_error(ctx, error);
+
+ return FALSE;
+ }
+
+ ret = g_new(const gchar *, handles->len+1);
+
+ for (i=0; i<handles->len; i++)
+ {
+ IdleHandle handle = g_array_index(handles, guint, i);
+
+ tmp = idle_handle_inspect(priv->handles, handle_type, handle);
+
+ if (tmp == NULL)
+ {
+ g_debug("%s: invalid handle %u (type %u)", G_STRFUNC, handle, handle_type);
+
+ error = g_error_new(TELEPATHY_ERRORS, InvalidHandle, "invalid handle %u", handle);
+
+ g_free(ret);
+
+ dbus_g_method_return_error(ctx, error);
+
+ return FALSE;
+ }
+
+ ret[i] = tmp;
+ }
+
+ ret[i] = NULL;
+
+ dbus_g_method_return(ctx, ret);
+
+ g_free(ret);
+
+ return TRUE;
+}
+
+/**
+ * list_channel_hash_foreach:
+ * @key: iterated key
+ * @value: iterated value
+ * @data: data attached to this key/value pair
+ *
+ * Called by the exported ListChannels function, this should iterate over
+ * the handle/channel pairs in a hash, and to the GPtrArray in the
+ * ListChannelInfo struct, add a GValueArray containing the following:
+ * a D-Bus object path for the channel object on this service
+ * a D-Bus interface name representing the channel type
+ * an integer representing the handle type this channel communicates with, or zero
+ * an integer handle representing the contact, room or list this channel communicates with, or zero
+ */
+static void
+list_channel_hash_foreach (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ GObject *channel = G_OBJECT (value);
+ GPtrArray *channels = (GPtrArray *) data;
+ char *path, *type;
+ guint handle_type, handle;
+ GValueArray *vals;
+
+ g_object_get (channel, "object-path", &path,
+ "channel-type", &type,
+ "handle-type", &handle_type,
+ "handle", &handle, NULL);
+
+ g_debug ("list_channels_foreach_hash: adding path %s, type %s, "
+ "handle type %u, handle %u", path, type, handle_type, handle);
+
+ vals = g_value_array_new (4);
+
+ g_value_array_append (vals, NULL);
+ g_value_init (g_value_array_get_nth (vals, 0), DBUS_TYPE_G_OBJECT_PATH);
+ g_value_set_boxed (g_value_array_get_nth (vals, 0), path);
+ g_free (path);
+
+ g_value_array_append (vals, NULL);
+ g_value_init (g_value_array_get_nth (vals, 1), G_TYPE_STRING);
+ g_value_set_string (g_value_array_get_nth (vals, 1), type);
+ g_free (type);
+
+ g_value_array_append (vals, NULL);
+ g_value_init (g_value_array_get_nth (vals, 2), G_TYPE_UINT);
+ g_value_set_uint (g_value_array_get_nth (vals, 2), handle_type);
+
+ g_value_array_append (vals, NULL);
+ g_value_init (g_value_array_get_nth (vals, 3), G_TYPE_UINT);
+ g_value_set_uint (g_value_array_get_nth (vals, 3), handle);
+
+ g_ptr_array_add (channels, vals);
+}
+
+/**
+ * idle_connection_list_channels
+ *
+ * Implements DBus method ListChannels
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_list_channels (IdleConnection *obj, GPtrArray ** ret, GError **error)
+{
+ IdleConnectionPrivate *priv;
+ guint count;
+ GPtrArray *channels;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED(obj, priv, *error);
+
+ count = g_hash_table_size(priv->im_channels);
+ count += g_hash_table_size(priv->muc_channels);
+ channels = g_ptr_array_sized_new(count);
+
+ g_hash_table_foreach(priv->im_channels, list_channel_hash_foreach, channels);
+ g_hash_table_foreach(priv->muc_channels, list_channel_hash_foreach, channels);
+
+ *ret = channels;
+
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_release_handle
+ *
+ * Implements DBus method ReleaseHandle
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @context: The DBUS invocation context to use to return values
+ * or throw an error.
+ */
+gboolean idle_connection_release_handles (IdleConnection *obj,
+ guint handle_type,
+ const GArray *handles,
+ DBusGMethodInvocation *context)
+{
+ IdleConnectionPrivate *priv;
+ char *sender;
+ GError *error = NULL;
+ int i;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED_ASYNC(obj, priv, error, context);
+
+ if (!idle_handle_type_is_valid(handle_type))
+ {
+ g_debug("%s: invalid handle type %u", G_STRFUNC, handle_type);
+
+ error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "invalid handle type %u", handle_type);
+
+ dbus_g_method_return_error(context, error);
+
+ g_error_free(error);
+
+ return FALSE;
+ }
+
+ sender = dbus_g_method_get_sender(context);
+
+ for (i=0; i<handles->len; i++)
+ {
+ IdleHandle handle;
+ gboolean valid;
+
+ handle = g_array_index(handles, guint, i);
+ valid = idle_handle_is_valid(priv->handles, handle_type, handle);
+
+ if (!valid)
+ {
+ g_debug("%s: invalid handle %u", G_STRFUNC, handle);
+
+ error = g_error_new(TELEPATHY_ERRORS, InvalidHandle, "unknown handle %u", handle);
+
+ dbus_g_method_return_error(context, error);
+
+ g_error_free(error);
+
+ return FALSE;
+ }
+
+ _idle_connection_client_release_handle(obj, sender, handle, handle_type);
+ }
+
+ dbus_g_method_return(context);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_remove_status
+ *
+ * Implements DBus method RemoveStatus
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_remove_status (IdleConnection *obj, const gchar * status, GError **error)
+{
+ IdleConnectionPrivate *priv;
+ IdleContactPresence *cp;
+
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED(obj, priv, *error);
+
+ cp = idle_handle_get_presence(priv->handles, priv->self_handle);
+
+ if ((cp != NULL) && !strcmp(status, idle_statuses[cp->presence_state].name))
+ {
+ cp->presence_state = IDLE_PRESENCE_AVAILABLE;
+ g_free(cp->status_message);
+ cp->status_message = NULL;
+ cp->last_activity = 0;
+ return signal_own_presence(obj, error);
+ }
+ else
+ {
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "Attempting to remove non-existant presence.");
+ return FALSE;
+ }
+}
+
+/**
+ * idle_connection_request_channel
+ *
+ * Implements DBus method RequestChannel
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_request_channel (IdleConnection *obj, const gchar * type, guint handle_type, guint handle, gboolean suppress_handler, DBusGMethodInvocation *ctx)
+{
+ IdleConnectionPrivate *priv;
+ GError *error = NULL;
+ gboolean queued = FALSE;
+ gchar *ret;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED_ASYNC(obj, priv, error, ctx);
+
+ if (!idle_handle_is_valid(priv->handles, handle_type, handle))
+ {
+ goto INVALID_HANDLE;
+ }
+
+ if (!strcmp(type, TP_IFACE_CHANNEL_TYPE_TEXT))
+ {
+ gpointer chan;
+
+ switch (handle_type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ chan = g_hash_table_lookup(priv->im_channels, GINT_TO_POINTER(handle));
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ chan = g_hash_table_lookup(priv->muc_channels, GINT_TO_POINTER(handle));
+ }
+ break;
+ default:
+ {
+ goto NOT_AVAILABLE;
+ }
+ break;
+ }
+
+ if (chan == NULL)
+ {
+ switch (handle_type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ chan = new_im_channel(obj, handle, suppress_handler);
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ queued = TRUE;
+ chan = new_muc_channel_async_req(obj, handle, suppress_handler, ctx);
+ }
+ break;
+ default:
+ {
+ goto NOT_AVAILABLE;
+ }
+ break;
+ }
+ }
+
+ g_object_get(chan, "object-path", &ret, NULL);
+ }
+ else
+ {
+ goto NOT_IMPLEMENTED;
+ }
+
+ if (!queued)
+ {
+ dbus_g_method_return(ctx, ret);
+ }
+
+ g_free(ret);
+
+ return TRUE;
+
+NOT_AVAILABLE:
+ g_debug ("request_channel: requested channel is unavailable with "
+ "handle type %u", handle_type);
+
+ error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
+ "requested channel is not available with "
+ "handle type %u", handle_type);
+ dbus_g_method_return_error(ctx, error);
+ g_free(error);
+
+ return FALSE;
+
+INVALID_HANDLE:
+ g_debug ("request_channel: handle %u (type %u) not valid", handle, handle_type);
+
+ error = g_error_new (TELEPATHY_ERRORS, InvalidHandle,
+ "handle %u (type %u) not valid", handle, handle_type);
+ dbus_g_method_return_error(ctx, error);
+ g_free(error);
+
+ return FALSE;
+
+NOT_IMPLEMENTED:
+ g_debug ("request_channel: unsupported channel type %s", type);
+
+ error = g_error_new (TELEPATHY_ERRORS, NotImplemented,
+ "unsupported channel type %s", type);
+ dbus_g_method_return_error(ctx, error);
+ g_free(error);
+
+ return FALSE;
+}
+
+static IdleHandle _idle_connection_request_handle(IdleConnection *obj,
+ guint handle_type,
+ const gchar *name,
+ GError **error);
+
+/**
+ * idle_connection_request_handles
+ *
+ * Implements DBus method RequestHandles
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @context: The DBUS invocation context to use to return values
+ * or throw an error.
+ */
+gboolean idle_connection_request_handles (IdleConnection *obj,
+ guint handle_type,
+ const gchar **names,
+ DBusGMethodInvocation *context)
+{
+ IdleConnectionPrivate *priv;
+ gchar *sender;
+ GError *error = NULL;
+ GArray *handles;
+ const gchar **name;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED_ASYNC(obj, priv, error, context);
+
+ if (!idle_handle_type_is_valid(handle_type))
+ {
+ g_debug("%s: invalid handle type %u", G_STRFUNC, handle_type);
+
+ error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "invalid handle type %u", handle_type);
+ dbus_g_method_return_error(context, error);
+ g_error_free(error);
+
+ return FALSE;
+ }
+
+ sender = dbus_g_method_get_sender(context);
+ handles = g_array_new(FALSE, FALSE, sizeof(IdleHandle));
+
+ for (name = names; *name != NULL; name++)
+ {
+ IdleHandle handle;
+
+ handle = _idle_connection_request_handle(obj, handle_type, *name, &error);
+
+ if (!handle)
+ {
+ g_debug("%s: failed to request handle: %s", G_STRFUNC, error->message);
+ g_array_free(handles, TRUE);
+
+ dbus_g_method_return_error(context, error);
+ g_error_free(error);
+
+ return FALSE;
+ }
+
+ _idle_connection_client_hold_handle(obj, sender, handle, handle_type);
+
+ g_array_append_val(handles, handle);
+ }
+
+ dbus_g_method_return(context, handles);
+ g_array_free(handles, TRUE);
+
+ return TRUE;
+}
+
+static IdleHandle _idle_connection_request_handle(IdleConnection *obj,
+ guint handle_type,
+ const gchar *name,
+ GError **error)
+{
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+ IdleHandle handle;
+ gchar *final_name;
+
+ switch (handle_type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ handle = idle_handle_for_contact(priv->handles, name);
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ switch (name[0])
+ {
+ case '#':
+ case '&':
+ case '!':
+ case '+':
+ {
+ final_name = (gchar *)(name);
+ }
+ break;
+ default:
+ {
+ g_debug("%s: assuming user wanted #-channel (%s)", G_STRFUNC, name);
+ final_name = g_strdup_printf("#%s", name);
+ }
+ break;
+ }
+
+ handle = idle_handle_for_room(priv->handles, final_name);
+
+ if (final_name != name)
+ {
+ g_free(final_name);
+ }
+ }
+ break;
+ default:
+ {
+ g_debug("%s: unimplemented handle type %u", G_STRFUNC, handle_type);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "unimplemented handle type %u", handle_type);
+ return 0;
+ }
+ break;
+ }
+
+ if (handle == 0)
+ {
+ g_debug("%s: requested name %s was invalid", G_STRFUNC, name);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "requested name %s was invalid", name);
+
+ return 0;
+ }
+
+ return handle;
+}
+
+static gboolean presence_timer_cb(gpointer data)
+{
+ IdleConnection *conn = IDLE_CONNECTION(data);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ gchar *nick;
+ gchar cmd[IRC_MSG_MAXLEN+2];
+
+ nick = g_queue_pop_head(priv->presence_queue);
+
+ if (nick == NULL)
+ {
+ priv->presence_unload_timer_id = 0;
+
+ return FALSE;
+ }
+
+ g_snprintf(cmd, IRC_MSG_MAXLEN+2, "WHOIS %s", nick);
+
+ send_irc_cmd_full(conn, cmd, SERVER_CMD_MIN_PRIORITY);
+
+ priv->presence_reply_list = g_list_append(priv->presence_reply_list, nick);
+
+ return TRUE;
+}
+
+#define PRESENCE_TIMER_TIMEOUT 3300
+
+static void presence_request_push(IdleConnection *obj, const gchar *nick)
+{
+ IdleConnectionPrivate *priv;
+ GList *node;
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ node = g_queue_find_custom(priv->presence_queue, nick, (GCompareFunc)(strcmp));
+
+ if (!node)
+ {
+ g_queue_push_tail(priv->presence_queue, g_strdup(nick));
+ }
+
+ if (priv->presence_unload_timer_id == 0)
+ {
+ priv->presence_unload_timer_id = g_timeout_add(PRESENCE_TIMER_TIMEOUT, presence_timer_cb, obj);
+ }
+}
+
+/**
+ * idle_connection_request_presence
+ *
+ * Implements DBus method RequestPresence
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_request_presence (IdleConnection *obj, const GArray * contacts, GError **error)
+{
+ IdleConnectionPrivate *priv;
+ int i;
+ IdleHandle *handles = g_new0(IdleHandle, contacts->len+1);
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED(obj, priv, *error);
+
+ for (i=0; i<contacts->len; i++)
+ {
+ IdleHandle handle;
+
+ handle = g_array_index(contacts, guint, i);
+
+ if (!idle_handle_is_valid(priv->handles, TP_HANDLE_TYPE_CONTACT, handle))
+ {
+ g_debug("%s: invalid handle %u", G_STRFUNC, handle);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidHandle, "invalid handle %u", handle);
+
+ return FALSE;
+ }
+
+ handles[i] = handle;
+ }
+
+ handles[i] = 0;
+
+ g_debug("%s: got presence request for %u handles", G_STRFUNC, contacts->len);
+
+ if (contacts->len)
+ {
+ emit_presence_update(obj, handles);
+ }
+
+ g_free(handles);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_connection_set_last_activity_time
+ *
+ * Implements DBus method SetLastActivityTime
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_set_last_activity_time (IdleConnection *obj, guint time, GError **error)
+{
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ ERROR_IF_NOT_CONNECTED(obj, IDLE_CONNECTION_GET_PRIVATE(obj), *error);
+
+ /* this is not really possible but let's pretend it succeeded */
+
+ return TRUE;
+}
+
+struct idle_conn_hashtable_foreach_data
+{
+ IdleConnection *conn;
+ GError **error;
+ gboolean retval;
+};
+
+static void setstatuses_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ struct idle_conn_hashtable_foreach_data *data = (struct idle_conn_hashtable_foreach_data *)(user_data);
+ IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(data->conn);
+ int i;
+
+ for (i = 0; i < LAST_IDLE_PRESENCE_ENUM; i++)
+ {
+ if (!strcmp(idle_statuses[i].name, (const gchar *)(key)))
+ {
+ break;
+ }
+ }
+
+ if (i < LAST_IDLE_PRESENCE_ENUM)
+ {
+ GHashTable *args = (GHashTable *)(value);
+ GValue *message = g_hash_table_lookup(args, "message");
+ const gchar *status = NULL;
+ IdleContactPresence *cp;
+
+ if (message)
+ {
+ if (!G_VALUE_HOLDS_STRING(message))
+ {
+ g_debug("%s: got a status message which was not a string", G_STRFUNC);
+
+ if (*(data->error))
+ {
+ g_error_free(*(data->error));
+ *(data->error) = NULL;
+ }
+
+ *(data->error) = g_error_new(TELEPATHY_ERRORS, InvalidArgument,
+ "Status argument 'message' requires a string");
+ data->retval = FALSE;
+ return;
+ }
+
+ status = g_value_get_string(message);
+ }
+
+ cp = idle_handle_get_presence(priv->handles, priv->self_handle);
+
+ if (!cp)
+ {
+ cp = idle_contact_presence_new();
+ g_assert(idle_handle_set_presence(priv->handles, priv->self_handle, cp));
+ }
+
+ cp->presence_state = i;
+ cp->status_message = g_strdup(status);
+ cp->last_activity = 0;
+
+ data->retval = signal_own_presence(data->conn, data->error);
+ }
+ else
+ {
+ g_debug("%s: got unknown status identifier %s", G_STRFUNC, (const gchar *)(key));
+
+ if (*(data->error))
+ {
+ g_error_free(*(data->error));
+ *(data->error) = NULL;
+ }
+
+ *(data->error) = g_error_new(TELEPATHY_ERRORS, InvalidArgument,
+ "unknown status identifier received: %s",
+ (const gchar *)(key));
+ data->retval = FALSE;
+ }
+}
+
+/**
+ * idle_connection_set_status
+ *
+ * Implements DBus method SetStatus
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_connection_set_status (IdleConnection *obj, GHashTable * statuses, GError **error)
+{
+ IdleConnectionPrivate *priv;
+ int size;
+ struct idle_conn_hashtable_foreach_data data = {obj, error, TRUE};
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ ERROR_IF_NOT_CONNECTED(obj, priv, *error);
+
+ if ((size = g_hash_table_size(statuses)) != 1)
+ {
+ g_debug("%s: got %i statuses instead of 1", G_STRFUNC, size);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "Got %i statuses instead of 1", size);
+
+ return FALSE;
+ }
+
+ g_hash_table_foreach(statuses, setstatuses_foreach, &data);
+
+ if (*(data.error) != NULL)
+ {
+ g_debug("%s: error: %s", G_STRFUNC, (*data.error)->message);
+ g_error_free(*(data.error));
+ }
+
+ return data.retval;
+}
+
+gboolean idle_connection_request_rename(IdleConnection *obj, const gchar *nick, GError **error)
+{
+ IdleConnectionPrivate *priv;
+ IdleHandle handle;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_CONNECTION(obj));
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ handle = idle_handle_for_contact(priv->handles, nick);
+
+ if (handle == 0)
+ {
+ g_debug("%s: failed to get handle for (%s)", G_STRFUNC, nick);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "Failed to get handle for (%s)", nick);
+
+ return FALSE;
+ }
+
+ g_free(priv->nickname);
+
+ priv->nickname = g_strdup(nick);
+
+ priv_rename(obj, priv->self_handle, handle);
+
+ return TRUE;
+}
+
+gboolean idle_connection_hton(IdleConnection *obj, const gchar *input, gchar **output, GError **_error)
+{
+ IdleConnectionPrivate *priv;
+ GError *error = NULL;
+ gsize bytes_written;
+ gchar *ret;
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ if (input == NULL)
+ {
+ *output = NULL;
+ return TRUE;
+ }
+
+ ret = g_convert(input, -1, priv->charset, "UTF-8", NULL, &bytes_written, &error);
+
+ if (ret == NULL)
+ {
+ g_debug("%s: g_convert failed: %s", G_STRFUNC, error->message);
+ *_error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "character set conversion failed: %s", error->message);
+ g_error_free(error);
+ *output = NULL;
+ return FALSE;
+ }
+
+ *output = ret;
+ return TRUE;
+}
+
+gboolean idle_connection_ntoh(IdleConnection *obj, const gchar *input, gchar **output, GError **_error)
+{
+ IdleConnectionPrivate *priv;
+ GError *error = NULL;
+ gsize bytes_written;
+ gchar *ret;
+ gchar *p;
+
+ priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+
+ if (input == NULL)
+ {
+ *output = NULL;
+ return TRUE;
+ }
+
+ ret = g_convert(input, -1, "UTF-8", priv->charset, NULL, &bytes_written, &error);
+
+#if 0
+ if (ret == NULL)
+ {
+ g_debug("%s: g_convert failed: %s", G_STRFUNC, error->message);
+ *_error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "character set conversion failed: %s", error->message);
+ g_error_free(error);
+ *output = NULL;
+ return FALSE;
+ }
+#endif
+
+ if (ret == NULL)
+ {
+ g_debug("%s: charset conversion failed, falling back to US-ASCII: %s", G_STRFUNC, error->message);
+ g_error_free(error);
+
+ ret = g_strdup(input);
+
+ for (p=ret; *p != '\0'; p++)
+ {
+ if (*p & (1<<7))
+ {
+ *p = '?';
+ }
+ }
+ }
+
+ *output = ret;
+ return TRUE;
+}
diff --git a/src/idle-connection.h b/src/idle-connection.h
new file mode 100644
index 0000000..820b77c
--- /dev/null
+++ b/src/idle-connection.h
@@ -0,0 +1,106 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_CONNECTION_H__
+#define __IDLE_CONNECTION_H__
+
+#include <glib-object.h>
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+typedef struct _IdleConnection IdleConnection;
+typedef struct _IdleConnectionClass IdleConnectionClass;
+typedef struct _IdleContactPresence IdleContactPresence;
+
+#include "idle-handles.h"
+#include "telepathy-constants.h"
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ IDLE_PRESENCE_AVAILABLE,
+ IDLE_PRESENCE_AWAY,
+ IDLE_PRESENCE_OFFLINE,
+ LAST_IDLE_PRESENCE_ENUM,
+} IdlePresenceState;
+
+struct _IdleConnectionClass
+{
+ GObjectClass parent_class;
+};
+
+struct _IdleConnection
+{
+ GObject parent;
+};
+
+void idle_contact_presence_free(IdleContactPresence *cp);
+
+GType idle_connection_get_type(void);
+
+/* TYPE MACROS */
+#define IDLE_TYPE_CONNECTION \
+ (idle_connection_get_type())
+#define IDLE_CONNECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_CONNECTION, IdleConnection))
+#define IDLE_CONNECTION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_CONNECTION, IdleConnectionClass))
+#define IDLE_IS_CONNECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_CONNECTION))
+#define IDLE_IS_CONNECTION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_CONNECTION))
+#define IDLE_CONNECTION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_CONNECTION, IdleConnectionClass))
+
+gboolean _idle_connection_register(IdleConnection *conn, char **bus_name, char **object_path, GError **error);
+gboolean _idle_connection_send(IdleConnection *conn, const gchar *msg);
+IdleHandleStorage *_idle_connection_get_handles(IdleConnection *conn);
+void _idle_connection_client_hold_handle(IdleConnection *conn, gchar *client_name, IdleHandle handle, TpHandleType type);
+gboolean _idle_connection_client_release_handle(IdleConnection *conn, gchar *client_name, IdleHandle handle, TpHandleType type);
+
+gboolean idle_connection_add_status (IdleConnection *obj, const gchar *status, GHashTable *parms, GError **error);
+gboolean idle_connection_clear_status (IdleConnection *obj, GError **error);
+gboolean idle_connection_connect (IdleConnection *obj, GError **error);
+gboolean idle_connection_disconnect (IdleConnection *obj, GError **error);
+gboolean idle_connection_get_interfaces (IdleConnection *obj, gchar ***ret, GError **error);
+gboolean idle_connection_get_protocol (IdleConnection *obj, gchar **ret, GError **error);
+gboolean idle_connection_get_self_handle (IdleConnection *obj, guint*ret, GError **error);
+gboolean idle_connection_get_status (IdleConnection *obj, guint*ret, GError **error);
+gboolean idle_connection_get_statuses (IdleConnection *obj, GHashTable **ret, GError **error);
+gboolean idle_connection_hold_handles (IdleConnection *obj, guint handle_type, const GArray *handles, DBusGMethodInvocation *context);
+gboolean idle_connection_inspect_handles (IdleConnection *obj, guint handle_type, const GArray *handles, DBusGMethodInvocation *context);
+gboolean idle_connection_list_channels (IdleConnection *obj, GPtrArray **ret, GError **error);
+gboolean idle_connection_release_handles (IdleConnection *obj, guint handle_type, const GArray *handles, DBusGMethodInvocation *context);
+gboolean idle_connection_remove_status (IdleConnection *obj, const gchar *status, GError **error);
+gboolean idle_connection_request_channel (IdleConnection *obj, const gchar *type, guint handle_type, guint handle, gboolean suppress_handler, DBusGMethodInvocation *ctx);
+gboolean idle_connection_request_handles (IdleConnection *obj, guint handle_type, const gchar **names, DBusGMethodInvocation *context);
+gboolean idle_connection_request_presence (IdleConnection *obj, const GArray *contacts, GError **error);
+gboolean idle_connection_set_last_activity_time (IdleConnection *obj, guint time, GError **error);
+gboolean idle_connection_set_status (IdleConnection *obj, GHashTable *statuses, GError **error);
+gboolean idle_connection_request_rename (IdleConnection *obj, const char *name, GError **error);
+
+gboolean idle_connection_hton(IdleConnection *obj, const gchar *input, gchar **output, GError **error);
+gboolean idle_connection_ntoh(IdleConnection *obj, const gchar *input, gchar **output, GError **error);
+
+G_END_DECLS
+
+#endif /* #ifndef __IDLE_CONNECTION_H__*/
diff --git a/src/idle-dns-resolver.c b/src/idle-dns-resolver.c
new file mode 100644
index 0000000..76f46a1
--- /dev/null
+++ b/src/idle-dns-resolver.c
@@ -0,0 +1,566 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 "idle-dns-resolver.h"
+
+#define idle_dns_result_new() \
+ (g_slice_new(IdleDNSResult))
+#define idle_dns_result_new0() \
+ (g_slice_new0(IdleDNSResult))
+
+void idle_dns_result_destroy(IdleDNSResult *result)
+{
+ if (result->ai_next)
+ {
+ idle_dns_result_destroy(result->ai_next);
+ }
+
+ g_slice_free(IdleDNSResult, result);
+}
+
+/* don't use crappy sofia */
+#if 0
+#include "gintset.h"
+
+#define SU_ROOT_MAGIC_T IdleDNSResolver
+#include <sofia-sip/su_source.h>
+#include <sofia-sip/su_wait.h>
+#define HAVE_SU_WAIT_H 1
+#include <sofia-sip/sresolv.h>
+
+#include <glib.h>
+
+#include <string.h>
+
+struct _IdleDNSResolver
+{
+ sres_resolver_t *resolver;
+ su_root_t *root;
+
+ GHashTable *queries;
+
+ guint source_id;
+
+ guint serial;
+};
+
+typedef struct _IdleDNSQueryData IdleDNSQueryData;
+
+struct _IdleDNSQueryData
+{
+ IdleDNSResultCallback callback;
+ guint port;
+ guint query_id;
+ guint refcount;
+ GIntSet *pending_families;
+ IdleDNSResult *results;
+ gpointer user_data;
+};
+
+typedef struct _IdleDNSQueryInstance IdleDNSQueryInstance;
+
+struct _IdleDNSQueryInstance
+{
+ IdleDNSQueryData *data;
+ uint16_t type;
+};
+
+#define idle_dns_query_data_new() \
+ (g_slice_new(IdleDNSQueryData))
+
+#define idle_dns_query_data_new0() \
+ (g_slice_new0(IdleDNSQueryData))
+
+static IdleDNSQueryData *idle_dns_query_data_ref(IdleDNSQueryData *data)
+{
+ data->refcount++;
+ return data;
+}
+
+static void idle_dns_query_data_unref(IdleDNSQueryData *data)
+{
+ if (!--data->refcount)
+ {
+ g_intset_destroy(data->pending_families);
+ g_slice_free(IdleDNSQueryData, data);
+ }
+}
+
+#define idle_dns_query_instance_new() \
+ (g_slice_new(IdleDNSQueryInstance))
+
+#define idle_dns_query_instance_new0() \
+ (g_slice_new0(IdleDNSQueryInstance))
+
+static void idle_dns_query_instance_destroy(IdleDNSQueryInstance *instance)
+{
+ if (instance->data)
+ {
+ idle_dns_query_data_unref(instance->data);
+ instance->data = NULL;
+ }
+}
+
+IdleDNSResolver *idle_dns_resolver_new()
+{
+ IdleDNSResolver *ret = g_slice_new(IdleDNSResolver);
+ GSource *source;
+
+ ret->root = su_root_source_create(ret);
+ g_assert(ret->root);
+ su_root_threading(ret->root, FALSE);
+
+ source = su_root_gsource(ret->root);
+ g_assert(source != NULL);
+ ret->source_id = g_source_attach(source, NULL);
+
+ ret->resolver = sres_resolver_create(ret->root, NULL, TAG_END());
+ ret->queries = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)(idle_dns_query_instance_destroy));
+
+ return ret;
+}
+
+void idle_dns_resolver_destroy(IdleDNSResolver *res)
+{
+ if (res->resolver)
+ {
+ sres_resolver_unref(res->resolver);
+ res->resolver = NULL;
+ }
+
+ if (res->root)
+ {
+ su_root_destroy(res->root);
+ res->root = NULL;
+ }
+
+ if (res->source_id)
+ {
+ g_source_remove(res->source_id);
+ res->source_id = 0;
+ }
+
+ if (res->queries)
+ {
+ g_hash_table_destroy(res->queries);
+ res->queries = NULL;
+ }
+
+ g_slice_free(IdleDNSResolver, res);
+}
+
+struct _call_callback_helper
+{
+ guint id;
+ IdleDNSResult *results;
+ IdleDNSResultCallback callback;
+ gpointer user_data;
+};
+
+static gboolean call_callback_idle(gpointer user_data)
+{
+ struct _call_callback_helper *helper = user_data;
+
+ helper->callback(helper->id, helper->results, helper->user_data);
+
+ g_free(user_data);
+ return FALSE;
+}
+
+static void sres_answer_callback(sres_context_t *ctx, sres_query_t *query, sres_record_t **answers)
+{
+ IdleDNSResolver *resolver = (IdleDNSResolver *)(ctx);
+ IdleDNSQueryInstance *instance;
+ IdleDNSQueryData *data;
+ IdleDNSResult *results;
+ IdleDNSResult *tail;
+ int i;
+ IdleDNSResultCallback callback;
+ gpointer user_data;
+ struct _call_callback_helper *helper = g_new0(struct _call_callback_helper, 1);
+ guint id;
+ guint port;
+
+ instance = g_hash_table_lookup(resolver->queries, query);
+
+ if (!instance)
+ {
+ g_debug("%s: invalid or cancelled context %p, ignoring", G_STRFUNC, ctx);
+ return;
+ }
+
+ data = instance->data;
+
+ g_intset_remove(data->pending_families, instance->type);
+
+ callback = data->callback;
+ user_data = data->user_data;
+ port = data->port;
+ id = data->query_id;
+ results = data->results;
+
+ for (tail = results; tail && tail->ai_next; tail = tail->ai_next);
+
+ sres_sort_answers(resolver->resolver, answers);
+
+ for (i = 0; answers && answers[i] != NULL; i++)
+ {
+ IdleDNSResult *result;
+ int ai_family;
+ int ai_socktype = SOCK_STREAM;
+ int ai_protocol = 0;
+ struct sockaddr *ai_addr;
+ socklen_t ai_addrlen;
+
+ switch (answers[i]->sr_record->r_type)
+ {
+ case sres_type_a:
+ {
+ struct sockaddr_in *sin;
+
+ sin = g_new(struct sockaddr_in, 1);
+
+ sin->sin_family = ai_family = AF_INET;
+ sin->sin_port = port;
+ sin->sin_addr = answers[i]->sr_a->a_addr;
+
+ ai_addrlen = sizeof(struct sockaddr_in);
+
+ ai_addr = (struct sockaddr *)(sin);
+ };
+ break;
+ case sres_type_aaaa:
+ {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = g_new0(struct sockaddr_in6, 1);
+
+ sin6->sin6_family = ai_family = AF_INET6;
+ sin6->sin6_port = port;
+ memcpy(sin6->sin6_addr.s6_addr, answers[i]->sr_aaaa->aaaa_addr.u6_addr, 16);
+
+ /* FIXME find out about those flow and scope fields, they are currently just set to zero */
+
+ ai_addrlen = sizeof(struct sockaddr_in6);
+
+ ai_addr = (struct sockaddr *)(sin6);
+ };
+ break;
+ case sres_type_a6:
+ {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = g_new0(struct sockaddr_in6, 1);
+
+ sin6->sin6_family = ai_family = AF_INET6;
+ sin6->sin6_port = port;
+ memcpy(sin6->sin6_addr.s6_addr, answers[i]->sr_a6->a6_suffix.u6_addr, 16);
+
+ ai_addrlen = sizeof(struct sockaddr_in6);
+
+ ai_addr = (struct sockaddr *)(sin6);
+ }
+ break;
+ default:
+ {
+ g_debug("%s: unsupported address family %u encountered, ignoring", G_STRFUNC, answers[i]->sr_record->r_type);
+ continue;
+ }
+ break;
+ }
+
+ result = idle_dns_result_new0();
+
+ result->ai_family = ai_family;
+ result->ai_socktype = ai_socktype;
+ result->ai_protocol = ai_protocol;
+
+ result->ai_addr = ai_addr;
+ result->ai_addrlen = ai_addrlen;
+
+ if (tail)
+ {
+ tail->ai_next = result;
+ }
+
+ if (!results)
+ {
+ results = result;
+ }
+
+ tail = result;
+ }
+
+ sres_free_answers(resolver->resolver, answers);
+
+ data->results = results;
+
+ /*
+ * FIXME this sucks. We have to return from this function before we can destroy the resolver (which calling of callback can lead to) so we need to trampoline it with g_idle_add
+ */
+
+ helper->callback = callback;
+ helper->id = id;
+ helper->results = results;
+ helper->user_data = user_data;
+
+ if (!g_intset_size(data->pending_families))
+ {
+ g_idle_add(call_callback_idle, helper);
+ }
+
+ g_hash_table_remove(resolver->queries, query);
+}
+
+guint idle_dns_resolver_query(IdleDNSResolver *resolver, const gchar *name, guint port, IdleDNSResultCallback callback, gpointer user_data)
+{
+ IdleDNSQueryData *data;
+ guint query_id = resolver->serial++;
+ sres_query_t *sofia_query;
+ static const uint16_t types[] = {sres_type_a, sres_type_aaaa, sres_type_a6};
+ int i;
+
+ g_debug("%s: resolving %s:%u", G_STRFUNC, name, port);
+
+ data = idle_dns_query_data_new();
+
+ data->callback = callback;
+ data->user_data = user_data;
+ data->query_id = query_id;
+ data->port = htons((unsigned short)(port));
+ data->results = NULL;
+ data->refcount = 0;
+ data->pending_families = g_intset_new();
+
+ for (i = 0; i < (sizeof(types) / sizeof(uint16_t)); i++)
+ {
+ IdleDNSQueryInstance *instance = idle_dns_query_instance_new();
+ instance->data = idle_dns_query_data_ref(data);
+ instance->type = types[i];
+
+ sofia_query = sres_query(resolver->resolver,
+ sres_answer_callback,
+ (sres_context_t *)(resolver),
+ types[i],
+ name);
+
+ g_intset_add(data->pending_families, types[i]);
+
+ g_hash_table_insert(resolver->queries, sofia_query, instance);
+ }
+
+ return query_id;
+}
+
+static gboolean queries_remove_foreach_func(gpointer key, gpointer value, gpointer user_data)
+{
+ IdleDNSQueryData *data = (IdleDNSQueryData *)(value);
+
+ return (data->query_id = GPOINTER_TO_UINT(user_data));
+}
+
+void idle_dns_resolver_cancel_query(IdleDNSResolver *resolver, guint query_id)
+{
+ g_hash_table_foreach_remove(resolver->queries, queries_remove_foreach_func, GUINT_TO_POINTER(query_id));
+}
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+typedef struct _IdleDNSQueryData IdleDNSQueryData;
+
+struct _IdleDNSQueryData
+{
+ gchar *name;
+ guint port;
+ IdleDNSResultCallback cb;
+ gpointer user_data;
+ guint source_id;
+};
+
+#define idle_dns_query_data_new() \
+ (g_slice_new(IdleDNSQueryData))
+#define idle_dns_query_data_new0() \
+ (g_slice_new0(IdleDNSQueryData))
+
+static void idle_dns_query_data_destroy(IdleDNSQueryData *data)
+{
+ g_free(data->name);
+ g_slice_free(IdleDNSQueryData, data);
+}
+
+struct _IdleDNSResolver
+{
+ GHashTable *queries;
+ guint serial;
+};
+
+IdleDNSResolver *
+idle_dns_resolver_new()
+{
+ IdleDNSResolver *ret = g_slice_new(IdleDNSResolver);
+
+ ret->queries = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)(idle_dns_query_data_destroy));
+ ret->serial = 0;
+
+ return ret;
+}
+
+void
+idle_dns_resolver_destroy(IdleDNSResolver *res)
+{
+ g_hash_table_destroy(res->queries);
+ g_slice_free(IdleDNSResolver, res);
+}
+
+struct _idle_helper
+{
+ IdleDNSResolver *res;
+ guint serial;
+};
+
+#define _idle_helper_new() \
+ (g_slice_new(struct _idle_helper))
+#define _idle_helper_new0() \
+ (g_slice_new0(struct _idle_helper))
+
+static void
+_idle_helper_destroy(struct _idle_helper *helper)
+{
+ g_slice_free(struct _idle_helper, helper);
+}
+
+static gboolean
+_resolve_idle_func(struct _idle_helper *helper)
+{
+ IdleDNSQueryData *data = g_hash_table_lookup(helper->res->queries,
+ GUINT_TO_POINTER(helper->serial));
+ struct addrinfo *info = NULL;
+ struct addrinfo *cur;
+ int rc;
+ IdleDNSResult *results = NULL, *tail = NULL;
+ IdleDNSResultCallback cb;
+ gpointer user_data;
+ gchar *service = g_strdup_printf("%u", data->port);
+
+ cb = data->cb;
+ user_data = data->user_data;
+
+ rc = getaddrinfo(data->name, service, NULL, &info);
+
+ if (rc)
+ {
+ g_debug("%s: getaddrinfo(): %s", G_STRFUNC, gai_strerror(rc));
+ return FALSE;
+ }
+
+ for (cur = info; cur != NULL; cur = cur->ai_next)
+ {
+ IdleDNSResult *result = idle_dns_result_new();
+ g_debug("%s: got result with family %u", G_STRFUNC, cur->ai_family);
+
+ result->ai_family = cur->ai_family;
+ result->ai_socktype = cur->ai_socktype;
+ result->ai_protocol = cur->ai_protocol;
+ result->ai_addr = cur->ai_addr;
+ result->ai_addrlen = cur->ai_addrlen;
+ result->ai_next = NULL;
+
+ if (tail)
+ {
+ tail->ai_next = result;
+ }
+
+ if (!results)
+ {
+ results = result;
+ }
+
+ tail = result;
+ }
+
+ g_hash_table_remove(helper->res->queries, GUINT_TO_POINTER(helper->serial));
+ cb(helper->serial, results, user_data);
+ freeaddrinfo(info);
+ g_free(service);
+
+ return FALSE;
+}
+
+guint
+idle_dns_resolver_query(IdleDNSResolver *res, const gchar *name, guint port, IdleDNSResultCallback cb, gpointer user_data)
+{
+ IdleDNSQueryData *data;
+ struct _idle_helper *helper;
+ guint ret;
+
+ g_assert (res != NULL);
+ g_assert (name != NULL);
+ g_assert (port != 0);
+ g_assert (cb != NULL);
+
+ ret = res->serial++;
+
+ helper = _idle_helper_new();
+ helper->res = res;
+ helper->serial = ret;
+
+ data = idle_dns_query_data_new();
+
+ data->name = g_strdup(name);
+ data->port = port;
+ data->cb = cb;
+ data->user_data = user_data;
+ data->source_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
+ (GSourceFunc)(_resolve_idle_func),
+ helper,
+ (GDestroyNotify)(_idle_helper_destroy));
+
+ g_hash_table_insert(res->queries, GUINT_TO_POINTER(ret), data);
+
+ return ret;
+}
+
+void
+idle_dns_resolver_cancel_query(IdleDNSResolver *res, guint id)
+{
+ IdleDNSQueryData *data;
+
+ g_assert(res);
+
+ data = g_hash_table_lookup(res->queries, GUINT_TO_POINTER(id));
+
+ if (!data)
+ {
+ g_debug("%s query %u not found!", G_STRFUNC, id);
+ return;
+ }
+
+ g_source_remove(data->source_id);
+ g_hash_table_remove(res->queries, GUINT_TO_POINTER(id));
+}
+
diff --git a/src/idle-dns-resolver.h b/src/idle-dns-resolver.h
new file mode 100644
index 0000000..f456181
--- /dev/null
+++ b/src/idle-dns-resolver.h
@@ -0,0 +1,65 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 <sys/types.h>
+#include <netdb.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdleDNSResolver IdleDNSResolver;
+typedef struct _IdleDNSResult IdleDNSResult;
+
+struct _IdleDNSResult
+{
+ /* as passed to socket() */
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+
+ /* as passed to connect() */
+ struct sockaddr *ai_addr;
+ socklen_t ai_addrlen;
+
+ /* pointer to the next list member or NULL if this is the last one */
+ struct _IdleDNSResult *ai_next;
+};
+
+void idle_dns_result_destroy(IdleDNSResult *result);
+
+/*
+ * id: the query identifier as returned by query()
+ * results: a linked list of _IdleDNSResult structs which should be freed with idle_dns_result_destroy
+ * user_data: the user_data pointer as passed to query()
+ */
+
+typedef void (*IdleDNSResultCallback)(guint id, IdleDNSResult *results, gpointer user_data);
+
+IdleDNSResolver *idle_dns_resolver_new();
+void idle_dns_resolver_destroy(IdleDNSResolver *);
+
+/*
+ * returns: the ID of the query, which can be passed to cancel_query()
+ */
+
+guint idle_dns_resolver_query(IdleDNSResolver *, const gchar *name, guint port, IdleDNSResultCallback callback, gpointer user_data);
+void idle_dns_resolver_cancel_query(IdleDNSResolver *, guint id);
+
+G_END_DECLS
diff --git a/src/idle-handle-set.c b/src/idle-handle-set.c
new file mode 100644
index 0000000..0ef8606
--- /dev/null
+++ b/src/idle-handle-set.c
@@ -0,0 +1,195 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 "gintset.h"
+
+#include "idle-handles.h"
+#include "idle-handles-private.h"
+
+#include "idle-handle-set.h"
+
+struct _IdleHandleSet
+{
+ IdleHandleStorage *storage;
+ GIntSet *intset;
+ TpHandleType type;
+};
+
+IdleHandleSet *idle_handle_set_new(IdleHandleStorage *storage, TpHandleType type)
+{
+ IdleHandleSet *set;
+
+ set = g_new(IdleHandleSet, 1);
+ set->storage = storage;
+ set->intset = g_intset_new();
+ set->type = type;
+
+ return set;
+}
+
+static void freer(IdleHandleSet *set, IdleHandle handle, gpointer userdata)
+{
+ idle_handle_set_remove(set, handle);
+}
+
+void idle_handle_set_destroy(IdleHandleSet *set)
+{
+ idle_handle_set_foreach(set, freer, NULL);
+ g_intset_destroy(set->intset);
+ g_free(set);
+}
+
+void idle_handle_set_add(IdleHandleSet *set, IdleHandle handle)
+{
+ g_return_if_fail(set != NULL);
+ g_return_if_fail(handle != 0);
+
+ if (!g_intset_is_member(set->intset, handle))
+ {
+ if (!idle_handle_ref(set->storage, set->type, handle))
+ {
+ return;
+ }
+
+ g_intset_add(set->intset, handle);
+ }
+}
+
+gboolean idle_handle_set_remove(IdleHandleSet *set, IdleHandle handle)
+{
+ if ((set == NULL) || (handle == 0))
+ {
+ return FALSE;
+ }
+
+ if (g_intset_is_member(set->intset, handle))
+ {
+ if (idle_handle_unref(set->storage, set->type, handle))
+ {
+ g_intset_remove(set->intset, handle);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+gboolean idle_handle_set_contains(IdleHandleSet *set, IdleHandle handle)
+{
+ return g_intset_is_member(set->intset, handle);
+}
+
+typedef struct __idle_handle_set_foreach_data
+{
+ IdleHandleSet *set;
+ IdleHandleFunc func;
+ gpointer userdata;
+} _idle_handle_set_foreach_data;
+
+static void foreach_helper(guint i, gpointer userdata)
+{
+ _idle_handle_set_foreach_data *data;
+
+ data = (_idle_handle_set_foreach_data *)(userdata);
+
+ data->func(data->set, i, data->userdata);
+}
+
+void idle_handle_set_foreach(IdleHandleSet *set, IdleHandleFunc func, gpointer userdata)
+{
+ _idle_handle_set_foreach_data data = {set, func, userdata};
+ g_intset_foreach(set->intset, foreach_helper, &data);
+}
+
+gint idle_handle_set_size(IdleHandleSet *set)
+{
+ return (set != NULL) ? g_intset_size(set->intset) : 0;
+}
+
+GArray *idle_handle_set_to_array(IdleHandleSet *set)
+{
+ if (set != NULL)
+ {
+ return g_intset_to_array(set->intset);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+static void _idle_handle_set_ref_one(guint handle, gpointer data)
+{
+ IdleHandleSet *set;
+
+ set = (IdleHandleSet *)(data);
+ idle_handle_ref(set->storage, set->type, handle);
+}
+
+GIntSet *idle_handle_set_update(IdleHandleSet *set, const GIntSet *add)
+{
+ GIntSet *ret, *tmp;
+
+ if ((set == NULL) || (add == NULL))
+ {
+ return NULL;
+ }
+
+ ret = g_intset_difference(add, set->intset);
+ g_intset_foreach(ret, _idle_handle_set_ref_one, set);
+
+ tmp = g_intset_union(add, set->intset);
+ g_intset_destroy(set->intset);
+ set->intset = tmp;
+
+ return ret;
+}
+
+static void _idle_handle_set_unref_one(guint handle, gpointer data)
+{
+ IdleHandleSet *set = (IdleHandleSet *)(data);
+ idle_handle_unref(set->storage, set->type, handle);
+}
+
+GIntSet *idle_handle_set_difference_update(IdleHandleSet *set, const GIntSet *remove)
+{
+ GIntSet *ret, *tmp;
+
+ if ((set == NULL) || (remove == NULL))
+ {
+ return NULL;
+ }
+
+ ret = g_intset_intersection(remove, set->intset);
+ g_intset_foreach(ret, _idle_handle_set_unref_one, set);
+
+ tmp = g_intset_difference(set->intset, remove);
+ g_intset_destroy(set->intset);
+ set->intset = tmp;
+
+ return ret;
+}
diff --git a/src/idle-handle-set.h b/src/idle-handle-set.h
new file mode 100644
index 0000000..fdb0d7c
--- /dev/null
+++ b/src/idle-handle-set.h
@@ -0,0 +1,50 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_HANDLE_SET_H__
+#define __IDLE_HANDLE_SET_H__
+
+#include <glib.h>
+
+#include "gintset.h"
+#include "idle-handles.h"
+#include "telepathy-constants.h"
+
+G_BEGIN_DECLS
+
+typedef struct _IdleHandleSet IdleHandleSet;
+typedef void (*IdleHandleFunc)(IdleHandleSet *set, IdleHandle handle, gpointer userdata);
+
+IdleHandleSet *idle_handle_set_new(IdleHandleStorage *storage, TpHandleType type);
+void idle_handle_set_destroy(IdleHandleSet *set);
+
+void idle_handle_set_add(IdleHandleSet *set, IdleHandle handle);
+gboolean idle_handle_set_remove(IdleHandleSet *set, IdleHandle handle);
+gboolean idle_handle_set_contains(IdleHandleSet *set, IdleHandle handle);
+
+void idle_handle_set_foreach(IdleHandleSet *set, IdleHandleFunc func, gpointer userdata);
+
+gint idle_handle_set_size(IdleHandleSet *set);
+GArray *idle_handle_set_to_array(IdleHandleSet *set);
+
+GIntSet *idle_handle_set_update(IdleHandleSet *set, const GIntSet *add);
+GIntSet *idle_handle_set_difference_update(IdleHandleSet *set, const GIntSet *remove);
+
+#endif /* __IDLE_HANDLE_SET_H__ */
diff --git a/src/idle-handles-private.h b/src/idle-handles-private.h
new file mode 100644
index 0000000..d094ae4
--- /dev/null
+++ b/src/idle-handles-private.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_HANDLES_PRIVATE_H__
+#define __IDLE_HANDLES_PRIVATE_H__
+
+#include <glib.h>
+
+#include "idle-connection.h"
+#include "gheap.h"
+
+typedef struct _IdleHandlePriv IdleHandlePriv;
+
+struct _IdleHandlePriv
+{
+ guint refcount;
+ gchar *string;
+ IdleContactPresence *cp;
+};
+
+struct _IdleHandleStorage
+{
+ GHashTable *contact_handles;
+ GHashTable *room_handles;
+
+ GHashTable *contact_strings;
+ GHashTable *room_strings;
+
+ guint contact_serial;
+ guint room_serial;
+
+ GHeap *contact_unused;
+ GHeap *room_unused;
+};
+
+#endif
diff --git a/src/idle-handles.c b/src/idle-handles.c
new file mode 100644
index 0000000..b7efb8c
--- /dev/null
+++ b/src/idle-handles.c
@@ -0,0 +1,589 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 <string.h>
+#include <ctype.h>
+
+#include "idle-handles.h"
+#include "idle-handles-private.h"
+
+#include "idle-connection.h"
+
+#include "gintset.h"
+
+#define idle_handle_priv_new() (g_slice_new0(IdleHandlePriv))
+
+gboolean idle_nickname_is_valid(const gchar *nickname)
+{
+ gsize len;
+ gunichar ucs4char;
+ const gchar *char_pos = nickname;
+
+ len = g_utf8_strlen(nickname, -1);
+
+ if (!len)
+ {
+ return FALSE;
+ }
+
+ while (char_pos != NULL)
+ {
+ ucs4char = g_utf8_get_char_validated(char_pos, -1);
+
+ switch (*char_pos)
+ {
+ case '[':
+ case ']':
+ case '\\':
+ case '`':
+ case '_':
+ case '^':
+ case '{':
+ case '|':
+ case '}':
+ case '-':
+ break;
+ case '\0':
+ {
+ return TRUE;
+ }
+ break;
+ default:
+ {
+ if (!(g_unichar_isalpha(ucs4char) || ((char_pos != nickname) && g_unichar_isdigit(ucs4char))))
+ {
+ return FALSE;
+ }
+ }
+ break;
+ }
+
+ char_pos = g_utf8_find_next_char(char_pos, NULL);
+ }
+
+ return TRUE;
+}
+
+gboolean idle_channelname_is_valid(const gchar *channel)
+{
+ const static gchar not_allowed_chars[6] = {' ', '\007', ',', '\r', '\n', ':'};
+ const gchar *tmp, *tmp2;
+ int i;
+ gsize len;
+
+ if ((channel[0] != '#') && (channel[0] != '!') && (channel[0] != '&') && (channel[0] != '+'))
+ {
+ return FALSE;
+ }
+
+ len = strlen(channel);
+
+ if ((len < 2) || (len > 50))
+ {
+ return FALSE;
+ }
+
+ if (channel[0] == '!')
+ {
+ for (i=0; i<5; i++)
+ {
+ if (!g_ascii_isupper(channel[i+1]) && !isdigit(channel[i+1]))
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ tmp = strchr(channel+1, ':');
+
+ if (tmp != NULL)
+ {
+ for (tmp2 = channel+1; tmp2 != tmp; tmp2++)
+ {
+ for (i=0; i<6; i++)
+ {
+ if (*tmp2 == not_allowed_chars[i])
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ for (tmp2 = tmp+1; tmp2 != channel+len; tmp2++)
+ {
+ for (i=0; i<6; i++)
+ {
+ if (*tmp2 == not_allowed_chars[i])
+ {
+ return FALSE;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (tmp2 = channel+1; tmp2 != channel+len; tmp2++)
+ {
+ for (i=0; i<6; i++)
+ {
+ if (*tmp2 == not_allowed_chars[i])
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ }
+
+ return TRUE;
+}
+
+static void handle_priv_free(IdleHandlePriv *priv)
+{
+ g_assert(priv != NULL);
+ g_free(priv->string);
+
+ if (priv->cp != NULL)
+ {
+ idle_contact_presence_free(priv->cp);
+ }
+
+ g_slice_free(IdleHandlePriv, priv);
+}
+
+static IdleHandle idle_handle_alloc(IdleHandleStorage *storage, TpHandleType type)
+{
+ IdleHandle ret;
+
+ g_assert(storage != NULL);
+
+ switch (type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ ret = GPOINTER_TO_INT(g_heap_extract_first(storage->contact_unused));
+
+ if (ret == 0)
+ {
+ ret = storage->contact_serial++;
+ }
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ ret = GPOINTER_TO_INT(g_heap_extract_first(storage->room_unused));
+
+ if (ret == 0)
+ {
+ ret = storage->room_serial++;
+ }
+ }
+ break;
+ case TP_HANDLE_TYPE_LIST:
+ default:
+ {
+ g_debug("%s: unsupported handle type %u", G_STRFUNC, type);
+ ret = 0;
+ }
+ break;
+ }
+
+/* g_debug("%s: returning %u (type %u)", G_STRFUNC, ret, type);*/
+
+ return ret;
+}
+
+IdleHandlePriv *handle_priv_lookup(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle)
+{
+ IdleHandlePriv *priv;
+
+ g_assert(storage != NULL);
+
+ if (handle == 0)
+ {
+ return NULL;
+ }
+
+ switch (type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ priv = g_hash_table_lookup(storage->contact_handles, GINT_TO_POINTER(handle));
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ priv = g_hash_table_lookup(storage->room_handles, GINT_TO_POINTER(handle));
+ }
+ break;
+ case TP_HANDLE_TYPE_LIST:
+ default:
+ {
+ g_critical("%s: Only TP_HANDLE_TYPE_CONTACT and TP_HANDLE_TYPE_ROOM supported!", G_STRFUNC);
+ return NULL;
+ }
+ break;
+ }
+
+ return priv;
+}
+
+void handle_priv_remove(IdleHandleStorage *storage, TpHandleType type, IdleHandlePriv *priv, IdleHandle handle)
+{
+ g_assert(storage != NULL);
+
+ switch (type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ {
+ g_hash_table_remove(storage->contact_strings, priv->string);
+ g_hash_table_remove(storage->contact_handles, GINT_TO_POINTER(handle));
+
+ if (handle == storage->contact_serial-1)
+ {
+ /* take advantage of good luck ;) */
+ storage->contact_serial--;
+ }
+ else
+ {
+ g_heap_add(storage->contact_unused, GINT_TO_POINTER(handle));
+ }
+ }
+ break;
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ g_hash_table_remove(storage->room_strings, priv->string);
+ g_hash_table_remove(storage->room_handles, GINT_TO_POINTER(handle));
+
+ if (handle == storage->room_serial-1)
+ {
+ storage->room_serial--;
+ }
+ else
+ {
+ g_heap_add(storage->room_unused, GINT_TO_POINTER(handle));
+ }
+ }
+ break;
+ case TP_HANDLE_TYPE_LIST:
+ default:
+ {
+ g_critical("%s: Only TP_HANDLE_TYPE_CONTACT and TP_HANDLE_TYPE_ROOM supported!", G_STRFUNC);
+ return;
+ }
+ break;
+ }
+}
+
+gboolean idle_handle_type_is_valid(TpHandleType type)
+{
+ switch (type)
+ {
+ case TP_HANDLE_TYPE_CONTACT:
+ case TP_HANDLE_TYPE_ROOM:
+ {
+ return TRUE;
+ }
+ break;
+ case TP_HANDLE_TYPE_LIST:
+ default:
+ {
+ return FALSE;
+ }
+ break;
+ }
+}
+
+static guint g_strncase_hash(gconstpointer key)
+{
+ guint ret;
+ gchar *tmp;
+
+ tmp = g_utf8_strdown(key, 32);
+ ret = g_str_hash(tmp);
+
+ g_free(tmp);
+
+ return ret;
+}
+
+static gboolean g_strncase_equal(gconstpointer a, gconstpointer b)
+{
+ gchar *s1, *s2;
+ gboolean ret;
+
+ s1 = g_utf8_casefold(a, -1);
+ s2 = g_utf8_casefold(b, -1);
+
+ ret = (strcmp(s1, s2) == 0);
+
+ g_free(s1);
+ g_free(s2);
+
+ return ret;
+}
+
+static gint idle_handle_compare(gconstpointer a, gconstpointer b)
+{
+ return (a < b) ? -1 : (a == b) ? 0 : 1;
+}
+
+IdleHandleStorage *idle_handle_storage_new()
+{
+ IdleHandleStorage *ret;
+
+ ret = g_slice_new0(IdleHandleStorage);
+
+ ret->contact_handles = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)(handle_priv_free));
+ ret->room_handles = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)(handle_priv_free));
+
+ ret->contact_strings = g_hash_table_new_full(g_strncase_hash, g_strncase_equal, NULL, NULL);
+ ret->room_strings = g_hash_table_new_full(g_strncase_hash, g_strncase_equal, NULL, NULL);
+
+ ret->contact_unused = g_heap_new(idle_handle_compare);
+ ret->room_unused = g_heap_new(idle_handle_compare);
+
+ ret->contact_serial = 1;
+ ret->room_serial = 1;
+
+ return ret;
+}
+
+void idle_handle_storage_destroy(IdleHandleStorage *storage)
+{
+ g_assert(storage != NULL);
+ g_assert(storage->contact_handles != NULL);
+ g_assert(storage->room_handles != NULL);
+
+ g_hash_table_destroy(storage->contact_handles);
+ g_hash_table_destroy(storage->room_handles);
+
+ g_hash_table_destroy(storage->contact_strings);
+ g_hash_table_destroy(storage->room_strings);
+
+ g_heap_destroy(storage->contact_unused);
+ g_heap_destroy(storage->room_unused);
+
+ g_slice_free(IdleHandleStorage, storage);
+}
+
+gboolean idle_handle_is_valid(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle)
+{
+ return (handle_priv_lookup(storage, type, handle) != NULL);
+}
+
+gboolean idle_handle_ref(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle)
+{
+ IdleHandlePriv *priv;
+
+ priv = handle_priv_lookup(storage, type, handle);
+
+ if (priv == NULL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ priv->refcount++;
+ return TRUE;
+ }
+}
+
+gboolean idle_handle_unref(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle)
+{
+ IdleHandlePriv *priv;
+
+ priv = handle_priv_lookup(storage, type, handle);
+
+ if (priv == NULL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ g_assert(priv->refcount > 0);
+
+ priv->refcount--;
+
+ if (priv->refcount == 0)
+ {
+ handle_priv_remove(storage, type, priv, handle);
+ }
+
+ return TRUE;
+ }
+}
+
+const char *idle_handle_inspect(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle)
+{
+ IdleHandlePriv *priv;
+
+ priv = handle_priv_lookup(storage, type, handle);
+
+ if (priv == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ return priv->string;
+ }
+}
+
+IdleHandle idle_handle_for_contact(IdleHandleStorage *storage, const char *nickname)
+{
+ IdleHandle handle;
+
+ g_assert(storage != NULL);
+
+ if ((!nickname) || (nickname[0] == '\0'))
+ {
+ g_debug("%s: handle for invalid nickname requested", G_STRFUNC);
+ return 0;
+ }
+
+ if (!idle_nickname_is_valid(nickname))
+ {
+ g_debug("%s: nickname (%s) isn't valid!", G_STRFUNC, nickname);
+ return 0;
+ }
+
+ handle = GPOINTER_TO_INT(g_hash_table_lookup(storage->contact_strings, nickname));
+
+ if (handle == 0)
+ {
+ handle = idle_handle_alloc(storage, TP_HANDLE_TYPE_CONTACT);
+ }
+
+ if (handle_priv_lookup(storage, TP_HANDLE_TYPE_CONTACT, handle) == NULL)
+ {
+ IdleHandlePriv *priv;
+ priv = idle_handle_priv_new();
+ priv->string = g_strdup(nickname);
+
+ g_hash_table_insert(storage->contact_handles, GINT_TO_POINTER(handle), priv);
+ g_hash_table_insert(storage->contact_strings, priv->string, GINT_TO_POINTER(handle));
+ }
+
+ return handle;
+}
+
+gboolean idle_handle_for_room_exists(IdleHandleStorage *storage, const char *channel_up)
+{
+ IdleHandle handle;
+ gchar *channel;
+
+ g_assert(storage != NULL);
+
+ channel = g_ascii_strdown(channel_up, -1);
+
+ if ((channel == NULL) || (channel[0] == '\0'))
+ {
+ return FALSE;
+ }
+ else
+ {
+ handle = GPOINTER_TO_INT(g_hash_table_lookup(storage->room_strings, channel));
+ }
+
+ g_free(channel);
+
+ return handle_priv_lookup(storage, TP_HANDLE_TYPE_ROOM, handle) != NULL;
+}
+
+IdleHandle idle_handle_for_room(IdleHandleStorage *storage, const char *channel)
+{
+ IdleHandle handle;
+
+ g_assert(storage != NULL);
+
+ if ((channel == NULL) || (channel[0] == '\0'))
+ {
+ g_debug("%s: handle for a invalid channel requested", G_STRFUNC);
+ return 0;
+ }
+
+ if (!idle_channelname_is_valid(channel))
+ {
+ g_debug("%s: channel name (%s) not valid!", G_STRFUNC, channel);
+ return 0;
+ }
+
+ handle = GPOINTER_TO_INT(g_hash_table_lookup(storage->room_strings, channel));
+
+ if (handle == 0)
+ {
+ handle = idle_handle_alloc(storage, TP_HANDLE_TYPE_ROOM);
+ }
+
+ if (handle_priv_lookup(storage, TP_HANDLE_TYPE_ROOM, handle) == NULL)
+ {
+ IdleHandlePriv *priv;
+ priv = idle_handle_priv_new();
+ priv->string = g_strdup(channel);
+
+ g_hash_table_insert(storage->room_handles, GINT_TO_POINTER(handle), priv);
+ g_hash_table_insert(storage->room_strings, priv->string, GINT_TO_POINTER(handle));
+ }
+
+ return handle;
+}
+
+gboolean idle_handle_set_presence(IdleHandleStorage *storage, IdleHandle handle, IdleContactPresence *cp)
+{
+ IdleHandlePriv *priv;
+
+ g_assert(storage != NULL);
+ g_assert(handle != 0);
+
+ priv = handle_priv_lookup(storage, TP_HANDLE_TYPE_CONTACT, handle);
+
+ if (priv == NULL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ priv->cp = cp;
+ }
+
+ return TRUE;
+}
+
+IdleContactPresence *idle_handle_get_presence(IdleHandleStorage *storage, IdleHandle handle)
+{
+ IdleHandlePriv *priv;
+
+ g_assert(storage != NULL);
+ g_assert(handle != 0);
+
+ priv = handle_priv_lookup(storage, TP_HANDLE_TYPE_CONTACT, handle);
+
+ if (priv == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ return priv->cp;
+ }
+}
+
diff --git a/src/idle-handles.h b/src/idle-handles.h
new file mode 100644
index 0000000..2eb5365
--- /dev/null
+++ b/src/idle-handles.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_HANDLES_H__
+#define __IDLE_HANDLES_H__
+
+#include <glib.h>
+
+typedef struct _IdleHandleStorage IdleHandleStorage;
+typedef guint IdleHandle;
+
+#include "idle-connection.h"
+#include "telepathy-constants.h"
+
+G_BEGIN_DECLS
+
+gboolean idle_handle_type_is_valid(TpHandleType type);
+
+IdleHandleStorage *idle_handle_storage_new();
+void idle_handle_storage_destroy(IdleHandleStorage *storage);
+
+gboolean idle_nickname_is_valid(const gchar *nickname);
+gboolean idle_channelname_is_valid(const gchar *channelname);
+
+gboolean idle_handle_is_valid(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle);
+gboolean idle_handle_ref(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle);
+gboolean idle_handle_unref(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle);
+const char *idle_handle_inspect(IdleHandleStorage *storage, TpHandleType type, IdleHandle handle);
+
+IdleHandle idle_handle_for_contact(IdleHandleStorage *storage, const char *nickname);
+gboolean idle_handle_for_room_exists(IdleHandleStorage *storage, const char *channel);
+IdleHandle idle_handle_for_room(IdleHandleStorage *storage, const char *channel);
+
+gboolean idle_handle_set_presence(IdleHandleStorage *storage, IdleHandle contact_handle, IdleContactPresence *presence);
+IdleContactPresence *idle_handle_get_presence(IdleHandleStorage *storage, IdleHandle contact_handle);
+
+G_END_DECLS
+
+#endif /* __IDLE_HANDLES_H__ */
diff --git a/src/idle-im-channel-signals-marshal.list b/src/idle-im-channel-signals-marshal.list
new file mode 100644
index 0000000..d4b1325
--- /dev/null
+++ b/src/idle-im-channel-signals-marshal.list
@@ -0,0 +1,4 @@
+VOID:VOID
+VOID:INT,INT,INT,INT,INT,STRING
+VOID:INT,INT,INT,STRING
+VOID:INT,INT,STRING
diff --git a/src/idle-im-channel.c b/src/idle-im-channel.c
new file mode 100644
index 0000000..8982c79
--- /dev/null
+++ b/src/idle-im-channel.c
@@ -0,0 +1,814 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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>
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <time.h>
+
+#include "idle-connection.h"
+#include "idle-handles.h"
+#include "telepathy-constants.h"
+#include "telepathy-errors.h"
+#include "telepathy-helpers.h"
+#include "telepathy-interfaces.h"
+
+#include "idle-im-channel.h"
+#include "idle-im-channel-glue.h"
+#include "idle-im-channel-signals-marshal.h"
+
+#define IRC_MSG_MAXLEN 510
+
+G_DEFINE_TYPE(IdleIMChannel, idle_im_channel, G_TYPE_OBJECT)
+
+/* signal enum */
+enum
+{
+ CLOSED,
+ RECEIVED,
+ SENT,
+ SEND_ERROR,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/* property enum */
+enum
+{
+ PROP_CONNECTION = 1,
+ PROP_OBJECT_PATH,
+ PROP_CHANNEL_TYPE,
+ PROP_HANDLE_TYPE,
+ PROP_HANDLE,
+ LAST_PROPERTY_ENUM
+};
+
+typedef struct _IdleIMPendingMessage IdleIMPendingMessage;
+
+struct _IdleIMPendingMessage
+{
+ guint id;
+
+ time_t timestamp;
+ IdleHandle sender;
+
+ TpChannelTextMessageType type;
+
+ gchar *text;
+};
+
+/* private structure */
+typedef struct _IdleIMChannelPrivate IdleIMChannelPrivate;
+
+struct _IdleIMChannelPrivate
+{
+ IdleConnection *connection;
+ gchar *object_path;
+ IdleHandle handle;
+
+ guint recv_id;
+ GQueue *pending_messages;
+
+ IdleIMPendingMessage *last_msg;
+
+ gboolean closed;
+
+ gboolean dispose_has_run;
+};
+
+#define _idle_im_pending_new() \
+ (g_slice_new(IdleIMPendingMessage))
+#define _idle_im_pending_new0() \
+ (g_slice_new0(IdleIMPendingMessage))
+
+static void _idle_im_pending_free(IdleIMPendingMessage *msg)
+{
+ if (msg->text)
+ {
+ g_free(msg->text);
+ }
+
+ g_slice_free(IdleIMPendingMessage, msg);
+}
+
+#define IDLE_IM_CHANNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_IM_CHANNEL, IdleIMChannelPrivate))
+
+static void
+idle_im_channel_init (IdleIMChannel *obj)
+{
+ IdleIMChannelPrivate *priv = IDLE_IM_CHANNEL_GET_PRIVATE (obj);
+
+ priv->pending_messages = g_queue_new();
+
+ priv->last_msg = _idle_im_pending_new0();
+}
+
+static void idle_im_channel_dispose (GObject *object);
+static void idle_im_channel_finalize (GObject *object);
+
+static GObject *idle_im_channel_constructor(GType type, guint n_props, GObjectConstructParam *props)
+{
+ GObject *obj;
+ IdleIMChannelPrivate *priv;
+ DBusGConnection *bus;
+ IdleHandleStorage *handles;
+ gboolean valid;
+
+ obj = G_OBJECT_CLASS(idle_im_channel_parent_class)->constructor(type, n_props, props);
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE(IDLE_IM_CHANNEL(obj));
+
+ handles = _idle_connection_get_handles(priv->connection);
+ valid = idle_handle_ref(handles, TP_HANDLE_TYPE_CONTACT, priv->handle);
+ g_assert(valid);
+
+ bus = tp_get_bus();
+ dbus_g_connection_register_g_object(bus, priv->object_path, obj);
+
+ return obj;
+}
+
+static void idle_im_channel_get_property(GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ IdleIMChannel *chan;
+ IdleIMChannelPrivate *priv;
+
+ g_assert(object != NULL);
+ g_assert(IDLE_IS_IM_CHANNEL(object));
+
+ chan = IDLE_IM_CHANNEL(object);
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE(chan);
+
+ switch (property_id)
+ {
+ case PROP_CONNECTION:
+ {
+ g_value_set_object(value, priv->connection);
+ }
+ break;
+ case PROP_OBJECT_PATH:
+ {
+ g_value_set_string(value, priv->object_path);
+ }
+ break;
+ case PROP_CHANNEL_TYPE:
+ {
+ g_value_set_string(value, TP_IFACE_CHANNEL_TYPE_TEXT);
+ }
+ break;
+ case PROP_HANDLE_TYPE:
+ {
+ g_value_set_uint(value, TP_HANDLE_TYPE_CONTACT);
+ }
+ break;
+ case PROP_HANDLE:
+ {
+ g_value_set_uint(value, priv->handle);
+ }
+ break;
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+ break;
+ }
+}
+
+static void idle_im_channel_set_property(GObject *object, guint property_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ IdleIMChannel *chan = IDLE_IM_CHANNEL(object);
+ IdleIMChannelPrivate *priv;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_IM_CHANNEL(chan));
+
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE(chan);
+
+ switch (property_id)
+ {
+ case PROP_CONNECTION:
+ {
+ priv->connection = g_value_get_object(value);
+ }
+ break;
+ case PROP_OBJECT_PATH:
+ {
+ if (priv->object_path)
+ {
+ g_free(priv->object_path);
+ }
+
+ priv->object_path = g_value_dup_string(value);
+ }
+ break;
+ case PROP_HANDLE:
+ {
+ priv->handle = g_value_get_uint(value);
+ }
+ break;
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+ break;
+ }
+}
+
+static void
+idle_im_channel_class_init (IdleIMChannelClass *idle_im_channel_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (idle_im_channel_class);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (idle_im_channel_class, sizeof (IdleIMChannelPrivate));
+
+ object_class->constructor = idle_im_channel_constructor;
+
+ object_class->get_property = idle_im_channel_get_property;
+ object_class->set_property = idle_im_channel_set_property;
+
+ object_class->dispose = idle_im_channel_dispose;
+ object_class->finalize = idle_im_channel_finalize;
+
+ param_spec = g_param_spec_object ("connection", "IdleConnection object",
+ "The IdleConnection object that owns this "
+ "IMChannel object.",
+ IDLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_string ("object-path", "D-Bus object path",
+ "The D-Bus object path used for this "
+ "object on the bus.",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec);
+
+ param_spec = g_param_spec_string ("channel-type", "Telepathy channel type",
+ "The D-Bus interface representing the "
+ "type of this channel.",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CHANNEL_TYPE, param_spec);
+
+ param_spec = g_param_spec_uint ("handle-type", "Contact handle type",
+ "The TpHandleType representing a "
+ "contact handle.",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_HANDLE_TYPE, param_spec);
+
+ param_spec = g_param_spec_uint ("handle", "Contact handle",
+ "The IdleHandle representing the contact "
+ "with whom this channel communicates.",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_HANDLE, param_spec);
+
+ signals[CLOSED] =
+ g_signal_new ("closed",
+ G_OBJECT_CLASS_TYPE (idle_im_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_im_channel_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[RECEIVED] =
+ g_signal_new ("received",
+ G_OBJECT_CLASS_TYPE (idle_im_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_im_channel_marshal_VOID__INT_INT_INT_INT_INT_STRING,
+ G_TYPE_NONE, 6, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ signals[SENT] =
+ g_signal_new ("sent",
+ G_OBJECT_CLASS_TYPE (idle_im_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_im_channel_marshal_VOID__INT_INT_STRING,
+ G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ signals[SEND_ERROR] =
+ g_signal_new("send-error",
+ G_OBJECT_CLASS_TYPE(idle_im_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_im_channel_marshal_VOID__INT_INT_INT_STRING,
+ G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (idle_im_channel_class), &dbus_glib_idle_im_channel_object_info);
+}
+
+void
+idle_im_channel_dispose (GObject *object)
+{
+ IdleIMChannel *self = IDLE_IM_CHANNEL (object);
+ IdleIMChannelPrivate *priv = IDLE_IM_CHANNEL_GET_PRIVATE (self);
+
+ g_assert(object != NULL);
+
+ if (priv->dispose_has_run)
+ return;
+
+ priv->dispose_has_run = TRUE;
+
+ if (!priv->closed)
+ {
+ g_signal_emit(self, signals[CLOSED], 0);
+ priv->closed = TRUE;
+ }
+
+ if (G_OBJECT_CLASS (idle_im_channel_parent_class)->dispose)
+ G_OBJECT_CLASS (idle_im_channel_parent_class)->dispose (object);
+}
+
+void
+idle_im_channel_finalize (GObject *object)
+{
+ IdleIMChannel *self = IDLE_IM_CHANNEL (object);
+ IdleIMChannelPrivate *priv = IDLE_IM_CHANNEL_GET_PRIVATE (self);
+ IdleHandleStorage *handles;
+ IdleIMPendingMessage *msg;
+
+ handles = _idle_connection_get_handles(priv->connection);
+ idle_handle_unref(handles, TP_HANDLE_TYPE_CONTACT, priv->handle);
+
+ if (priv->object_path)
+ {
+ g_free(priv->object_path);
+ }
+
+ while ((msg = g_queue_pop_head(priv->pending_messages)) != NULL)
+ {
+ _idle_im_pending_free(msg);
+ }
+
+ g_queue_free(priv->pending_messages);
+
+ _idle_im_pending_free(priv->last_msg);
+
+ G_OBJECT_CLASS (idle_im_channel_parent_class)->finalize (object);
+}
+
+gboolean _idle_im_channel_receive(IdleIMChannel *chan, TpChannelTextMessageType type, IdleHandle sender, const gchar *text)
+{
+ IdleIMChannelPrivate *priv;
+ IdleIMPendingMessage *msg;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_IM_CHANNEL(chan));
+
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE(chan);
+
+ msg = _idle_im_pending_new();
+
+ msg->id = priv->recv_id++;
+ msg->timestamp = time(NULL);
+ msg->sender = sender;
+ msg->type = type;
+ msg->text = g_strdup(text);
+
+ g_queue_push_tail(priv->pending_messages, msg);
+
+ g_signal_emit(chan, signals[RECEIVED], 0,
+ msg->id,
+ msg->timestamp,
+ msg->sender,
+ msg->type,
+ 0,
+ msg->text);
+
+ g_debug("%s: queued message %u", G_STRFUNC, msg->id);
+
+ return FALSE;
+}
+
+void _idle_im_channel_rename(IdleIMChannel *chan, IdleHandle new)
+{
+ IdleIMChannelPrivate *priv;
+ IdleHandleStorage *handles;
+ gboolean valid;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_IM_CHANNEL(chan));
+
+ g_assert(new != 0);
+
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE(chan);
+ handles = _idle_connection_get_handles(priv->connection);
+
+ idle_handle_unref(handles, TP_HANDLE_TYPE_CONTACT, priv->handle);
+ priv->handle = new;
+ valid = idle_handle_ref(handles, TP_HANDLE_TYPE_CONTACT, priv->handle);
+ g_assert(valid);
+
+ g_debug("%s: changed to handle %u", G_STRFUNC, new);
+}
+
+void _idle_im_channel_nosuchnick(IdleIMChannel *chan)
+{
+ IdleIMChannelPrivate *priv;
+ IdleIMPendingMessage *last_msg;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_IM_CHANNEL(chan));
+
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE(chan);
+
+ g_assert(priv->last_msg != NULL);
+ last_msg = priv->last_msg;
+
+ /* TODO this is so incorrect, we are assuming it was always the most recent message etc... */
+
+ g_signal_emit(chan, signals[SEND_ERROR], 0, TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE, last_msg->timestamp, last_msg->type, last_msg->text);
+}
+
+static gint idle_pending_message_compare(gconstpointer msg, gconstpointer id)
+{
+ IdleIMPendingMessage *message = (IdleIMPendingMessage *)(msg);
+
+ return (message->id != GPOINTER_TO_INT(id));
+}
+
+/**
+ * idle_im_channel_acknowledge_pending_messages
+ *
+ * Implements DBus method AcknowledgePendingMessages
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_acknowledge_pending_messages (IdleIMChannel *obj, const GArray *ids, GError **error)
+{
+ IdleIMChannelPrivate *priv;
+ int i;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_IM_CHANNEL(obj));
+
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE(obj);
+
+ for (i=0; i < ids->len; i++)
+ {
+ GList *node;
+ IdleIMPendingMessage *msg;
+ guint id = g_array_index(ids, guint, i);
+
+ node = g_queue_find_custom(priv->pending_messages,
+ GINT_TO_POINTER(id),
+ idle_pending_message_compare);
+
+ if (!node)
+ {
+ g_debug("%s: message id %u not found", G_STRFUNC, id);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "message id %u not found", id);
+
+ return FALSE;
+ }
+
+ msg = (IdleIMPendingMessage *)(node->data);
+
+ g_debug("%s: acknowledging message id %u", G_STRFUNC, id);
+
+ g_queue_delete_link(priv->pending_messages, node);
+
+ _idle_im_pending_free(msg);
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_close
+ *
+ * Implements DBus method Close
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_close (IdleIMChannel *obj, GError **error)
+{
+ IdleIMChannelPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_IM_CHANNEL(obj));
+
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE(obj);
+ priv->closed = TRUE;
+
+ g_debug("%s called on %p", G_STRFUNC, obj);
+ g_signal_emit(obj, signals[CLOSED], 0);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_get_channel_type
+ *
+ * Implements DBus method GetChannelType
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_get_channel_type (IdleIMChannel *obj, gchar ** ret, GError **error)
+{
+ *ret = g_strdup(TP_IFACE_CHANNEL_TYPE_TEXT);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_get_handle
+ *
+ * Implements DBus method GetHandle
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_get_handle (IdleIMChannel *obj, guint* ret, guint* ret1, GError **error)
+{
+ IdleIMChannelPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_IM_CHANNEL(obj));
+
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE(obj);
+
+ *ret = TP_HANDLE_TYPE_CONTACT;
+ *ret1 = priv->handle;
+
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_get_interfaces
+ *
+ * Implements DBus method GetInterfaces
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_get_interfaces (IdleIMChannel *obj, gchar *** ret, GError **error)
+{
+ const gchar *interfaces[] = {NULL};
+
+ *ret = g_strdupv((gchar **)(interfaces));
+
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_list_pending_messages
+ *
+ * Implements DBus method ListPendingMessages
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_list_pending_messages (IdleIMChannel *obj,
+ gboolean clear,
+ GPtrArray ** ret,
+ GError **error)
+{
+ IdleIMChannelPrivate *priv;
+ guint count;
+ GPtrArray *messages;
+ GList *cur;
+
+ g_assert (IDLE_IS_IM_CHANNEL (obj));
+
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE (obj);
+
+ count = g_queue_get_length (priv->pending_messages);
+ messages = g_ptr_array_sized_new (count);
+
+ for (cur = g_queue_peek_head_link(priv->pending_messages);
+ cur != NULL;
+ cur = cur->next)
+ {
+ IdleIMPendingMessage *msg = (IdleIMPendingMessage *)(cur->data);
+ GValueArray *vals;
+
+ vals = g_value_array_new (6);
+
+ g_value_array_append (vals, NULL);
+ g_value_init (g_value_array_get_nth (vals, 0), G_TYPE_UINT);
+ g_value_set_uint (g_value_array_get_nth (vals, 0), msg->id);
+
+ g_value_array_append (vals, NULL);
+ g_value_init (g_value_array_get_nth (vals, 1), G_TYPE_UINT);
+ g_value_set_uint (g_value_array_get_nth (vals, 1), msg->timestamp);
+
+ g_value_array_append (vals, NULL);
+ g_value_init (g_value_array_get_nth (vals, 2), G_TYPE_UINT);
+ g_value_set_uint (g_value_array_get_nth (vals, 2), msg->sender);
+
+ g_value_array_append (vals, NULL);
+ g_value_init (g_value_array_get_nth (vals, 3), G_TYPE_UINT);
+ g_value_set_uint (g_value_array_get_nth (vals, 3), msg->type);
+
+ g_value_array_append (vals, NULL);
+ g_value_init (g_value_array_get_nth (vals, 4), G_TYPE_UINT);
+ g_value_set_uint (g_value_array_get_nth (vals, 4), 0);
+
+ g_value_array_append (vals, NULL);
+ g_value_init (g_value_array_get_nth (vals, 5), G_TYPE_STRING);
+ g_value_set_string (g_value_array_get_nth (vals, 5), msg->text);
+
+ g_ptr_array_add (messages, vals);
+ }
+
+ if (clear)
+ {
+ IdleIMPendingMessage *msg;
+
+ while ((msg = g_queue_pop_head(priv->pending_messages)) != NULL)
+ {
+ _idle_im_pending_free(msg);
+ }
+ }
+
+ *ret = messages;
+
+ return TRUE;
+}
+
+
+/**
+ * idle_im_channel_send
+ *
+ * Implements DBus method Send
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_im_channel_send (IdleIMChannel *obj, guint type, const gchar * text, GError **error)
+{
+ IdleIMChannelPrivate *priv;
+ gchar msg[IRC_MSG_MAXLEN+1];
+ const char *recipient;
+ time_t timestamp;
+ const gchar *final_text = text;
+ gsize len;
+ gchar *part;
+ gsize headerlen;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_IM_CHANNEL(obj));
+
+ priv = IDLE_IM_CHANNEL_GET_PRIVATE(obj);
+
+ recipient = idle_handle_inspect(_idle_connection_get_handles(priv->connection), TP_HANDLE_TYPE_CONTACT,
+ priv->handle);
+
+ if ((recipient == NULL) || (strlen(recipient) == 0))
+ {
+ g_debug("%s: invalid recipient", G_STRFUNC);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "invalid recipient");
+
+ return FALSE;
+ }
+
+ len = strlen(final_text);
+ part = (gchar*)final_text;
+
+ if (type == TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL)
+ {
+ g_snprintf(msg, IRC_MSG_MAXLEN+1, "PRIVMSG %s :", recipient);
+ }
+ else if (type == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
+ {
+ g_snprintf(msg, IRC_MSG_MAXLEN+1, "PRIVMSG %s :\001ACTION ", recipient);
+ }
+ else if (type == TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE)
+ {
+ g_snprintf(msg, IRC_MSG_MAXLEN+1, "NOTICE %s :", recipient);
+ }
+ else
+ {
+ g_debug("%s: invalid message type %u", G_STRFUNC, type);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "invalid message type %u", type);
+
+ return FALSE;
+ }
+
+ headerlen = strlen(msg);
+
+ while (part < final_text+len)
+ {
+ char *br = strchr (part, '\n');
+ size_t len = IRC_MSG_MAXLEN-headerlen;
+ if (br)
+ {
+ len = (len < br - part) ? len : br - part;
+ }
+
+ if (type == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
+ {
+ g_snprintf(msg+headerlen, len + 1, "%s\001", part);
+ len -= 1;
+ }
+ else
+ {
+ g_strlcpy(msg+headerlen, part, len + 1);
+ }
+ part += len;
+ if (br)
+ {
+ part++;
+ }
+
+ _idle_connection_send(priv->connection, msg);
+ }
+
+ timestamp = time(NULL);
+
+ g_signal_emit(obj, signals[SENT], 0, timestamp, type, text);
+
+ if (priv->last_msg->text)
+ {
+ g_free(priv->last_msg->text);
+ }
+
+ priv->last_msg->timestamp = timestamp;
+ priv->last_msg->type = type;
+ priv->last_msg->text = g_strdup(text);
+
+ return TRUE;
+}
+
diff --git a/src/idle-im-channel.h b/src/idle-im-channel.h
new file mode 100644
index 0000000..ebc308f
--- /dev/null
+++ b/src/idle-im-channel.h
@@ -0,0 +1,69 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_IM_CHANNEL_H__
+#define __IDLE_IM_CHANNEL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdleIMChannel IdleIMChannel;
+typedef struct _IdleIMChannelClass IdleIMChannelClass;
+
+struct _IdleIMChannelClass {
+ GObjectClass parent_class;
+};
+
+struct _IdleIMChannel {
+ GObject parent;
+};
+
+GType idle_im_channel_get_type(void);
+
+/* TYPE MACROS */
+#define IDLE_TYPE_IM_CHANNEL \
+ (idle_im_channel_get_type())
+#define IDLE_IM_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_IM_CHANNEL, IdleIMChannel))
+#define IDLE_IM_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_IM_CHANNEL, IdleIMChannelClass))
+#define IDLE_IS_IM_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_IM_CHANNEL))
+#define IDLE_IS_IM_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_IM_CHANNEL))
+#define IDLE_IM_CHANNEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_IM_CHANNEL, IdleIMChannelClass))
+
+gboolean _idle_im_channel_receive(IdleIMChannel *chan, TpChannelTextMessageType type, IdleHandle sender, const gchar *msg);
+void _idle_im_channel_rename(IdleIMChannel *chan, IdleHandle new_handle);
+void _idle_im_channel_nosuchnick(IdleIMChannel *chan);
+
+gboolean idle_im_channel_acknowledge_pending_messages (IdleIMChannel *obj, const GArray *ids, GError **error);
+gboolean idle_im_channel_close (IdleIMChannel *obj, GError **error);
+gboolean idle_im_channel_get_channel_type (IdleIMChannel *obj, gchar ** ret, GError **error);
+gboolean idle_im_channel_get_handle (IdleIMChannel *obj, guint* ret, guint* ret1, GError **error);
+gboolean idle_im_channel_get_interfaces (IdleIMChannel *obj, gchar *** ret, GError **error);
+gboolean idle_im_channel_list_pending_messages (IdleIMChannel *obj, gboolean clear, GPtrArray ** ret, GError **error);
+gboolean idle_im_channel_send (IdleIMChannel *obj, guint type, const gchar * text, GError **error);
+
+G_END_DECLS
+
+#endif /* #ifndef __IDLE_IM_CHANNEL_H__*/
diff --git a/src/idle-muc-channel-signals-marshal.list b/src/idle-muc-channel-signals-marshal.list
new file mode 100644
index 0000000..5863b3c
--- /dev/null
+++ b/src/idle-muc-channel-signals-marshal.list
@@ -0,0 +1,11 @@
+VOID:VOID
+VOID:INT,INT
+VOID:INT
+VOID:VOID
+VOID:STRING,BOXED,BOXED,BOXED,BOXED,INT,INT
+VOID:INT,INT
+VOID:BOXED
+VOID:BOXED
+VOID:INT,INT,INT,INT,INT,STRING
+VOID:INT,INT,INT,STRING
+VOID:INT,INT,STRING
diff --git a/src/idle-muc-channel.c b/src/idle-muc-channel.c
new file mode 100644
index 0000000..4d76053
--- /dev/null
+++ b/src/idle-muc-channel.c
@@ -0,0 +1,3474 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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>
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+
+#include "idle-muc-channel.h"
+#include "idle-muc-channel-signals-marshal.h"
+
+#include "idle-muc-channel-glue.h"
+
+#include "idle-connection.h"
+#include "idle-handles.h"
+#include "idle-handle-set.h"
+
+#include "telepathy-helpers.h"
+#include "telepathy-errors.h"
+#include "telepathy-constants.h"
+#include "telepathy-interfaces.h"
+
+#define IRC_MSG_MAXLEN 510
+
+#define DEBUGSPIKE {g_debug("at %s, line %u", G_STRFUNC, __LINE__);}
+
+G_DEFINE_TYPE(IdleMUCChannel, idle_muc_channel, G_TYPE_OBJECT)
+
+/* signal enum */
+enum
+{
+ CLOSED,
+ GROUP_FLAGS_CHANGED,
+ LOST_MESSAGE,
+ MEMBERS_CHANGED,
+ PASSWORD_FLAGS_CHANGED,
+ PROPERTIES_CHANGED,
+ PROPERTY_FLAGS_CHANGED,
+ RECEIVED,
+ SEND_ERROR,
+ SENT,
+ JOIN_READY,
+ LAST_SIGNAL
+};
+
+/* property enum */
+enum
+{
+ PROP_CONNECTION = 1,
+ PROP_OBJECT_PATH,
+ PROP_CHANNEL_TYPE,
+ PROP_HANDLE_TYPE,
+ PROP_HANDLE,
+ LAST_PROPERTY_ENUM
+};
+
+typedef enum
+{
+ MUC_STATE_CREATED = 0,
+ MUC_STATE_JOINING,
+ MUC_STATE_NEED_PASSWORD,
+ MUC_STATE_JOINED,
+ MUC_STATE_PARTED
+} IdleMUCState;
+
+#define LAST_MODE_FLAG_SHIFT (15)
+
+typedef enum
+{
+ MODE_FLAG_CREATOR = 1,
+ MODE_FLAG_OPERATOR_PRIVILEGE = 2,
+ MODE_FLAG_VOICE_PRIVILEGE = 4,
+
+ MODE_FLAG_ANONYMOUS = 8,
+ MODE_FLAG_INVITE_ONLY = 16,
+ MODE_FLAG_MODERATED= 32,
+ MODE_FLAG_NO_OUTSIDE_MESSAGES = 64,
+ MODE_FLAG_QUIET= 128,
+ MODE_FLAG_PRIVATE= 256,
+ MODE_FLAG_SECRET= 512,
+ MODE_FLAG_SERVER_REOP= 1024,
+ MODE_FLAG_TOPIC_ONLY_SETTABLE_BY_OPS = 2048,
+
+ MODE_FLAG_KEY= 4096,
+ MODE_FLAG_USER_LIMIT = 8192,
+
+ MODE_FLAG_HALFOP_PRIVILEGE = 16384,
+
+ LAST_MODE_FLAG_ENUM
+} IRCChannelModeFlags;
+
+
+typedef struct
+{
+ IRCChannelModeFlags flags;
+ guint limit;
+ gchar *topic;
+ gchar *key;
+ guint topic_touched;
+ guint topic_toucher;
+} IRCChannelModeState;
+
+#define TP_TYPE_PROPERTY_INFO_STRUCT (dbus_g_type_get_struct ("GValueArray", \
+ G_TYPE_UINT, \
+ G_TYPE_STRING, \
+ G_TYPE_STRING, \
+ G_TYPE_UINT, \
+ G_TYPE_INVALID))
+#define TP_TYPE_PROPERTY_INFO_LIST (dbus_g_type_get_collection ("GPtrArray", \
+ TP_TYPE_PROPERTY_INFO_STRUCT))
+
+#define TP_TYPE_PROPERTY_VALUE_STRUCT (dbus_g_type_get_struct ("GValueArray", \
+ G_TYPE_UINT, \
+ G_TYPE_VALUE, \
+ G_TYPE_INVALID))
+#define TP_TYPE_PROPERTY_VALUE_LIST (dbus_g_type_get_collection ("GPtrArray", \
+ TP_TYPE_PROPERTY_VALUE_STRUCT))
+
+#define TP_TYPE_PROPERTY_FLAGS_STRUCT (dbus_g_type_get_struct ("GValueArray", \
+ G_TYPE_UINT, \
+ G_TYPE_UINT, \
+ G_TYPE_INVALID))
+#define TP_TYPE_PROPERTY_FLAGS_LIST (dbus_g_type_get_collection ("GPtrArray", \
+ TP_TYPE_PROPERTY_FLAGS_STRUCT))
+
+typedef enum
+{
+ TP_PROPERTY_INVITE_ONLY = 0,
+ TP_PROPERTY_LIMIT,
+ TP_PROPERTY_LIMITED,
+ TP_PROPERTY_MODERATED,
+ TP_PROPERTY_PASSWORD,
+ TP_PROPERTY_PASSWORD_REQUIRED,
+ TP_PROPERTY_PRIVATE,
+ TP_PROPERTY_SUBJECT,
+ TP_PROPERTY_SUBJECT_TIMESTAMP,
+ TP_PROPERTY_SUBJECT_CONTACT,
+ LAST_TP_PROPERTY_ENUM
+} IdleMUCChannelTPProperty;
+
+typedef struct
+{
+ const gchar *name;
+ GType type;
+} TPPropertySignature;
+
+typedef struct
+{
+ GValue *value;
+ guint flags;
+} TPProperty;
+
+
+static const TPPropertySignature property_signatures[] =
+{
+ {"invite-only", G_TYPE_BOOLEAN},
+ {"limit", G_TYPE_UINT},
+ {"limited", G_TYPE_BOOLEAN},
+ {"moderated", G_TYPE_BOOLEAN},
+ {"password", G_TYPE_STRING},
+ {"password-required", G_TYPE_BOOLEAN},
+ {"private", G_TYPE_BOOLEAN},
+ {"subject", G_TYPE_STRING},
+ {"subject-timestamp", G_TYPE_UINT},
+ {"subject-contact", G_TYPE_UINT},
+ {NULL, G_TYPE_NONE}
+};
+
+static const gchar *ascii_muc_states[] =
+{
+ "MUC_STATE_CREATED",
+ "MUC_STATE_JOINING",
+ "MUC_STATE_NEED_PASSWORD",
+ "MUC_STATE_JOINED",
+ "MUC_STATE_PARTED"
+};
+
+static void muc_channel_tp_properties_init(IdleMUCChannel *chan);
+static void muc_channel_tp_properties_destroy(IdleMUCChannel *chan);
+static void change_tp_properties(IdleMUCChannel *chan, const GPtrArray *props);
+static void set_tp_property_flags(IdleMUCChannel *chan, const GArray *prop_ids, TpPropertyFlags add, TpPropertyFlags remove);
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/* private structure */
+typedef struct _IdleMUCChannelPrivate IdleMUCChannelPrivate;
+
+struct _IdleMUCChannelPrivate
+{
+ IdleConnection *connection;
+ gchar *object_path;
+ IdleHandle handle;
+ const gchar *channel_name;
+
+ IdleHandle own_handle;
+
+ guint recv_id;
+ GQueue *pending_messages;
+
+ IdleHandleSet *local_pending;
+ IdleHandleSet *remote_pending;
+ IdleHandleSet *current_members;
+
+ IdleMUCState state;
+
+ IRCChannelModeState mode_state;
+
+ guint group_flags;
+ guint password_flags;
+ TPProperty *properties;
+
+ DBusGMethodInvocation *passwd_ctx;
+
+ gboolean join_ready;
+ gboolean closed;
+
+ gboolean dispose_has_run;
+};
+
+typedef struct _IdleMUCPendingMessage IdleMUCPendingMessage;
+
+struct _IdleMUCPendingMessage
+{
+ guint id;
+
+ time_t timestamp;
+ IdleHandle sender;
+
+ TpChannelTextMessageType type;
+
+ gchar *text;
+};
+
+static void change_group_flags(IdleMUCChannel *chan, guint add, guint delete);
+static void change_password_flags(IdleMUCChannel *chan, guint flag, gboolean state);
+
+#define _idle_muc_pending_new() \
+ (g_slice_new(IdleMUCPendingMessage))
+#define _idle_muc_pending_new0() \
+ (g_slice_new0(IdleMUCPendingMessage))
+
+static void _idle_muc_pending_free(IdleMUCPendingMessage *msg)
+{
+ if (msg->text)
+ {
+ g_free(msg->text);
+ }
+
+ g_slice_free(IdleMUCPendingMessage, msg);
+}
+
+#define IDLE_MUC_CHANNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_MUC_CHANNEL, IdleMUCChannelPrivate))
+
+static void
+idle_muc_channel_init (IdleMUCChannel *obj)
+{
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE (obj);
+
+ priv->pending_messages = g_queue_new();
+
+ priv->group_flags = 0; /* |
+ TP_CHANNEL_GROUP_FLAG_CAN_REMOVE |
+ TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE;*/
+
+ priv->password_flags = 0;
+
+ priv->closed = FALSE;
+ priv->state = MUC_STATE_CREATED;
+
+ priv->mode_state.flags = 0;
+ priv->mode_state.topic = NULL;
+ priv->mode_state.key = NULL;
+
+ priv->properties = g_new0(TPProperty, LAST_TP_PROPERTY_ENUM);
+ muc_channel_tp_properties_init(obj);
+
+ priv->dispose_has_run = FALSE;
+}
+
+static void idle_muc_channel_dispose (GObject *object);
+static void idle_muc_channel_finalize (GObject *object);
+
+static GObject *idle_muc_channel_constructor(GType type, guint n_props, GObjectConstructParam *props)
+{
+ GObject *obj;
+ IdleMUCChannelPrivate *priv;
+ DBusGConnection *bus;
+ IdleHandleStorage *handles;
+ gboolean valid;
+
+ obj = G_OBJECT_CLASS(idle_muc_channel_parent_class)->constructor(type, n_props, props);
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(IDLE_MUC_CHANNEL(obj));
+
+ handles = _idle_connection_get_handles(priv->connection);
+ valid = idle_handle_ref(handles, TP_HANDLE_TYPE_ROOM, priv->handle);
+ g_assert(valid);
+ priv->channel_name = idle_handle_inspect(handles, TP_HANDLE_TYPE_ROOM, priv->handle);
+
+ idle_connection_get_self_handle(priv->connection, &(priv->own_handle), NULL);
+ valid = idle_handle_ref(handles, TP_HANDLE_TYPE_CONTACT, priv->own_handle);
+ g_assert(valid);
+
+ priv->local_pending = idle_handle_set_new(handles, TP_HANDLE_TYPE_CONTACT);
+ priv->remote_pending = idle_handle_set_new(handles, TP_HANDLE_TYPE_CONTACT);
+ priv->current_members = idle_handle_set_new(handles, TP_HANDLE_TYPE_CONTACT);
+
+ bus = tp_get_bus();
+ dbus_g_connection_register_g_object(bus, priv->object_path, obj);
+ g_assert(valid);
+
+ return obj;
+}
+
+static void idle_muc_channel_get_property(GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ IdleMUCChannel *chan;
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(object != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(object));
+
+ chan = IDLE_MUC_CHANNEL(object);
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+
+ switch (property_id)
+ {
+ case PROP_CONNECTION:
+ {
+ g_value_set_object(value, priv->connection);
+ }
+ break;
+ case PROP_OBJECT_PATH:
+ {
+ g_value_set_string(value, priv->object_path);
+ }
+ break;
+ case PROP_CHANNEL_TYPE:
+ {
+ g_value_set_string(value, TP_IFACE_CHANNEL_TYPE_TEXT);
+ }
+ break;
+ case PROP_HANDLE_TYPE:
+ {
+ g_value_set_uint(value, TP_HANDLE_TYPE_ROOM);
+ }
+ break;
+ case PROP_HANDLE:
+ {
+ g_value_set_uint(value, priv->handle);
+ }
+ break;
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+ break;
+ }
+}
+
+static void idle_muc_channel_set_property(GObject *object, guint property_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ IdleMUCChannel *chan = IDLE_MUC_CHANNEL(object);
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ switch (property_id)
+ {
+ case PROP_CONNECTION:
+ {
+ priv->connection = g_value_get_object(value);
+ }
+ break;
+ case PROP_OBJECT_PATH:
+ {
+ if (priv->object_path)
+ {
+ g_free(priv->object_path);
+ }
+
+ priv->object_path = g_value_dup_string(value);
+ }
+ break;
+ case PROP_HANDLE:
+ {
+ priv->handle = g_value_get_uint(value);
+ g_debug("%s: setting handle to %u", G_STRFUNC, priv->handle);
+ }
+ break;
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+ break;
+ }
+}
+
+static void
+idle_muc_channel_class_init (IdleMUCChannelClass *idle_muc_channel_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (idle_muc_channel_class);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (idle_muc_channel_class, sizeof (IdleMUCChannelPrivate));
+
+ object_class->constructor = idle_muc_channel_constructor;
+
+ object_class->get_property = idle_muc_channel_get_property;
+ object_class->set_property = idle_muc_channel_set_property;
+
+ object_class->dispose = idle_muc_channel_dispose;
+ object_class->finalize = idle_muc_channel_finalize;
+
+ param_spec = g_param_spec_object ("connection", "IdleConnection object",
+ "The IdleConnection object that owns this "
+ "MUCChannel object.",
+ IDLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_string ("object-path", "D-Bus object path",
+ "The D-Bus object path used for this "
+ "object on the bus.",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec);
+
+ param_spec = g_param_spec_string ("channel-type", "Telepathy channel type",
+ "The D-Bus interface representing the "
+ "type of this channel.",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CHANNEL_TYPE, param_spec);
+
+ param_spec = g_param_spec_uint ("handle-type", "Contact handle type",
+ "The TpHandleType representing a "
+ "room handle.",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_HANDLE_TYPE, param_spec);
+
+ param_spec = g_param_spec_uint ("handle", "Contact handle",
+ "The IdleHandle representing this room.",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_HANDLE, param_spec);
+
+ signals[CLOSED] =
+ g_signal_new ("closed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[GROUP_FLAGS_CHANGED] =
+ g_signal_new ("group-flags-changed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+ signals[LOST_MESSAGE] =
+ g_signal_new ("lost-message",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[MEMBERS_CHANGED] =
+ g_signal_new ("members-changed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__STRING_BOXED_BOXED_BOXED_BOXED_INT_INT,
+ G_TYPE_NONE, 7, G_TYPE_STRING, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, G_TYPE_UINT, G_TYPE_UINT);
+
+ signals[PASSWORD_FLAGS_CHANGED] =
+ g_signal_new ("password-flags-changed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+ signals[PROPERTIES_CHANGED] =
+ g_signal_new ("properties-changed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_VALUE, G_TYPE_INVALID)))));
+
+ signals[PROPERTY_FLAGS_CHANGED] =
+ g_signal_new ("property-flags-changed",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID)))));
+
+ signals[RECEIVED] =
+ g_signal_new ("received",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT_INT_INT_INT_INT_STRING,
+ G_TYPE_NONE, 6, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ signals[SEND_ERROR] =
+ g_signal_new ("send-error",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT_INT_INT_STRING,
+ G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ signals[SENT] =
+ g_signal_new ("sent",
+ G_OBJECT_CLASS_TYPE (idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT_INT_STRING,
+ G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
+
+ signals[JOIN_READY] =
+ g_signal_new("join-ready",
+ G_OBJECT_CLASS_TYPE(idle_muc_channel_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_muc_channel_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (idle_muc_channel_class), &dbus_glib_idle_muc_channel_object_info);
+}
+
+void
+idle_muc_channel_dispose (GObject *object)
+{
+ IdleMUCChannel *self = IDLE_MUC_CHANNEL (object);
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE (self);
+
+ if (priv->dispose_has_run)
+ return;
+
+ priv->dispose_has_run = TRUE;
+
+ if (!priv->closed)
+ {
+ g_signal_emit(self, signals[CLOSED], 0);
+ priv->closed = TRUE;
+ }
+
+ if (G_OBJECT_CLASS (idle_muc_channel_parent_class)->dispose)
+ G_OBJECT_CLASS (idle_muc_channel_parent_class)->dispose (object);
+}
+#if 0
+/* unref all handles in given GArray */
+static void unref_handle_array(IdleHandleStorage *storage, GArray *handles, TpHandleType type)
+{
+ int i;
+
+ g_assert(storage != NULL);
+ g_assert(handles != NULL);
+
+ for (i=0; i<handles->len; i++)
+ {
+ idle_handle_unref(storage, type, (IdleHandle)(g_array_index(handles, guint, i)));
+ }
+}
+#endif
+
+#if 0
+static void handle_unref_foreach(IdleHandleSet *set, IdleHandle handle, gpointer userdata)
+{
+ IdleHandleStorage *storage = IDLE_HANDLE_STORAGE(userdata);
+
+ idle_handle_unref(storage, TP_HANDLE_TYPE_CONTACT, handle);
+}
+#endif
+
+void
+idle_muc_channel_finalize (GObject *object)
+{
+ IdleMUCChannel *self = IDLE_MUC_CHANNEL (object);
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE (self);
+ IdleHandleStorage *handles;
+ IdleMUCPendingMessage *msg;
+
+ handles = _idle_connection_get_handles(priv->connection);
+ idle_handle_unref(handles, TP_HANDLE_TYPE_ROOM, priv->handle);
+
+ idle_handle_unref(handles, TP_HANDLE_TYPE_CONTACT, priv->own_handle);
+
+ if (priv->object_path)
+ {
+ g_free(priv->object_path);
+ }
+
+ if (priv->mode_state.topic)
+ {
+ g_free(priv->mode_state.topic);
+ }
+
+ if (priv->mode_state.key)
+ {
+ g_free(priv->mode_state.key);
+ }
+
+ while ((msg = g_queue_pop_head(priv->pending_messages)) != NULL)
+ {
+ _idle_muc_pending_free(msg);
+ }
+
+ g_queue_free(priv->pending_messages);
+
+ muc_channel_tp_properties_destroy(self);
+ g_free(priv->properties);
+
+ idle_handle_set_destroy(priv->current_members);
+ idle_handle_set_destroy(priv->local_pending);
+ idle_handle_set_destroy(priv->remote_pending);
+
+ G_OBJECT_CLASS (idle_muc_channel_parent_class)->finalize (object);
+}
+
+static void muc_channel_tp_properties_init(IdleMUCChannel *chan)
+{
+ IdleMUCChannelPrivate *priv;
+ TPProperty *props;
+ int i;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+ props = priv->properties;
+
+ for (i=0; i<LAST_TP_PROPERTY_ENUM; i++)
+ {
+ GValue *value;
+ props[i].value = value = g_new0(GValue, 1);
+
+ g_value_init(value, property_signatures[i].type);
+
+ props[i].flags = 0;
+ }
+}
+
+static void muc_channel_tp_properties_destroy(IdleMUCChannel *chan)
+{
+ IdleMUCChannelPrivate *priv;
+ TPProperty *props;
+ int i;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+ props = priv->properties;
+
+ for (i=0; i<LAST_TP_PROPERTY_ENUM; i++)
+ {
+ g_value_unset(props[i].value);
+ g_free(props[i].value);
+ }
+}
+
+static gboolean g_value_compare(const GValue *v1, const GValue *v2)
+{
+ GType t1, t2;
+
+ g_assert(v1 != NULL);
+ g_assert(v2 != NULL);
+
+ g_assert(G_IS_VALUE(v1));
+ g_assert(G_IS_VALUE(v2));
+
+ t1 = G_VALUE_TYPE(v1);
+ t2 = G_VALUE_TYPE(v2);
+
+ if (t1 != t2)
+ {
+ g_debug("%s: different types %s and %s compared!", G_STRFUNC, g_type_name(t1), g_type_name(t2));
+ return FALSE;
+ }
+
+ switch (t1)
+ {
+ case G_TYPE_BOOLEAN:
+ return g_value_get_boolean(v1) == g_value_get_boolean(v2);
+ case G_TYPE_UINT:
+ return g_value_get_uint(v1) == g_value_get_uint(v2);
+ case G_TYPE_STRING:
+ {
+ const gchar *s1, *s2;
+
+ s1 = g_value_get_string(v1);
+ s2 = g_value_get_string(v2);
+
+ if ((s1 == NULL) && (s2 == NULL))
+ {
+ return TRUE;
+ }
+ else if ((s1 == NULL) || (s2 == NULL))
+ {
+ return FALSE;
+ }
+
+ return (strcmp(s1, s2) == 0);
+ }
+ default:
+ g_debug("%s: unknown type %s in comparison", G_STRFUNC, g_type_name(t1));
+ return FALSE;
+ }
+}
+
+static void change_tp_properties(IdleMUCChannel *chan, const GPtrArray *props)
+{
+ IdleMUCChannelPrivate *priv;
+ int i;
+ GPtrArray *changed_props;
+ GArray *flags;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+ g_assert(props != NULL);
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ changed_props = g_ptr_array_new();
+ flags = g_array_new(FALSE, FALSE, sizeof(guint));
+
+ for (i=0; i<props->len; i++)
+ {
+ GValue *curr_val;
+ GValue prop = {0, };
+ GValue *new_val;
+ guint prop_id;
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_set_static_boxed(&prop, g_ptr_array_index(props, i));
+
+ dbus_g_type_struct_get(&prop,
+ 0, &prop_id,
+ 1, &new_val,
+ G_MAXUINT);
+
+ if (prop_id >= LAST_TP_PROPERTY_ENUM)
+ {
+ g_debug("%s: prop_id >= LAST_TP_PROPERTY_ENUM, corruption!11", G_STRFUNC);
+ continue;
+ }
+
+ curr_val = priv->properties[prop_id].value;
+
+ if (!g_value_compare(new_val, curr_val))
+ {
+ g_value_copy(new_val, curr_val);
+
+ g_ptr_array_add(changed_props, g_value_get_boxed(&prop));
+ g_array_append_val(flags, prop_id);
+
+ g_debug("%s: tp_property %u changed", G_STRFUNC, prop_id);
+ }
+
+ g_value_unset(&prop);
+ }
+
+ if (changed_props->len > 0)
+ {
+ g_debug("%s: emitting PROPERTIES_CHANGED with %u properties", G_STRFUNC, changed_props->len);
+ g_signal_emit(chan, signals[PROPERTIES_CHANGED], 0, changed_props);
+ }
+
+ if (flags->len > 0)
+ {
+ g_debug("%s: flagging properties as readable with %u props", G_STRFUNC, flags->len);
+ set_tp_property_flags(chan, flags, TP_PROPERTY_FLAG_READ, 0);
+ }
+
+ g_ptr_array_free(changed_props, TRUE);
+ g_array_free(flags, TRUE);
+}
+
+static void set_tp_property_flags(IdleMUCChannel *chan, const GArray *props, TpPropertyFlags add, TpPropertyFlags remove)
+{
+ IdleMUCChannelPrivate *priv;
+ int i;
+ GPtrArray *changed_props;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ changed_props = g_ptr_array_new();
+
+ if (props == NULL)
+ {
+ g_debug("%s: setting all flags with %u, %u", G_STRFUNC, add, remove);
+
+ for (i=0; i<LAST_TP_PROPERTY_ENUM; i++)
+ {
+ guint curr_flags = priv->properties[i].flags;
+ guint flags = (curr_flags | add) & (~remove);
+
+ if (curr_flags != flags)
+ {
+ GValue prop = {0, };
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_FLAGS_STRUCT);
+ g_value_take_boxed(&prop, dbus_g_type_specialized_construct(TP_TYPE_PROPERTY_FLAGS_STRUCT));
+
+ dbus_g_type_struct_set(&prop,
+ 0, i,
+ 1, flags,
+ G_MAXUINT);
+
+ priv->properties[i].flags = flags;
+
+ g_ptr_array_add(changed_props, g_value_get_boxed(&prop));
+ }
+ }
+ }
+ else
+ {
+ for (i=0; i<props->len; i++)
+ {
+ guint prop_id = g_array_index(props, guint, i);
+ guint curr_flags = priv->properties[prop_id].flags;
+ guint flags = (curr_flags | add) & (~remove);
+
+ if (curr_flags != flags)
+ {
+ GValue prop = {0, };
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_FLAGS_STRUCT);
+ g_value_take_boxed(&prop, dbus_g_type_specialized_construct(TP_TYPE_PROPERTY_FLAGS_STRUCT));
+
+ dbus_g_type_struct_set(&prop,
+ 0, prop_id,
+ 1, flags,
+ G_MAXUINT);
+
+ priv->properties[prop_id].flags = flags;
+
+ g_ptr_array_add(changed_props, g_value_get_boxed(&prop));
+ }
+ }
+ }
+
+ if (changed_props->len > 0)
+ {
+ g_debug("%s: emitting PROPERTY_FLAGS_CHANGED with %u properties", G_STRFUNC, changed_props->len);
+ g_signal_emit(chan, signals[PROPERTY_FLAGS_CHANGED], 0, changed_props);
+ }
+
+ g_ptr_array_free(changed_props, TRUE);
+}
+
+static void change_sets(IdleMUCChannel *obj, GIntSet *add_current, GIntSet *remove_current, GIntSet *add_local, GIntSet *remove_local, GIntSet *add_remote, GIntSet *remove_remote, IdleHandle actor, TpChannelGroupChangeReason reason);
+
+static void provide_password_reply(IdleMUCChannel *chan, gboolean success);
+
+static void provide_password_reply(IdleMUCChannel *chan, gboolean success)
+{
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ if (priv->passwd_ctx != NULL)
+ {
+ dbus_g_method_return(priv->passwd_ctx, success);
+ priv->passwd_ctx = NULL;
+ }
+ else
+ {
+ g_debug("%s: don't have a ProvidePassword context to return with! (channel handle %u)", G_STRFUNC, priv->handle);
+ }
+
+ if (success)
+ {
+ change_password_flags(chan, TP_CHANNEL_PASSWORD_FLAG_PROVIDE, 0);
+ }
+}
+
+static void change_state(IdleMUCChannel *obj, IdleMUCState state)
+{
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ if ((state > MUC_STATE_JOINING) && (!priv->join_ready))
+ {
+ g_signal_emit(obj, signals[JOIN_READY], 0, MUC_CHANNEL_JOIN_ERROR_NONE);
+ priv->join_ready = TRUE;
+ }
+
+ if (priv->state == MUC_STATE_NEED_PASSWORD && state == MUC_STATE_JOINED)
+ {
+ change_password_flags(obj, TP_CHANNEL_PASSWORD_FLAG_PROVIDE, FALSE);
+ provide_password_reply(obj, TRUE);
+ }
+
+ if (priv->state == MUC_STATE_NEED_PASSWORD && state == MUC_STATE_NEED_PASSWORD)
+ {
+ provide_password_reply(obj, FALSE);
+ }
+
+ if (priv->state < MUC_STATE_NEED_PASSWORD && state == MUC_STATE_NEED_PASSWORD)
+ {
+ change_password_flags(obj, TP_CHANNEL_PASSWORD_FLAG_PROVIDE, TRUE);
+ }
+
+ priv->state = state;
+
+ g_debug("%s: IdleMUCChannel %u changed to state %s", G_STRFUNC, priv->handle, ascii_muc_states[state]);
+}
+
+static void change_group_flags(IdleMUCChannel *obj, guint add, guint remove)
+{
+ IdleMUCChannelPrivate *priv;
+ guint _add = 0, _remove = 0;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ _add = (~priv->group_flags) & add;
+ _remove = priv->group_flags & remove;
+
+ priv->group_flags |= add;
+ priv->group_flags &= ~remove;
+
+ if (_add | _remove)
+ {
+ g_signal_emit(obj, signals[GROUP_FLAGS_CHANGED], 0, add, remove);
+ g_debug("%s: emitting GROUP_FLAGS_CHANGED with %u %u", G_STRFUNC, add, remove);
+ }
+}
+
+static IdleMUCChannelTPProperty to_prop_id(IRCChannelModeFlags flag)
+{
+ switch (flag)
+ {
+ case MODE_FLAG_INVITE_ONLY:
+ return TP_PROPERTY_INVITE_ONLY;
+ case MODE_FLAG_MODERATED:
+ return TP_PROPERTY_MODERATED;
+ case MODE_FLAG_PRIVATE:
+ case MODE_FLAG_SECRET:
+ return TP_PROPERTY_PRIVATE;
+ case MODE_FLAG_KEY:
+ return TP_PROPERTY_PASSWORD_REQUIRED;
+ case MODE_FLAG_USER_LIMIT:
+ return TP_PROPERTY_LIMITED;
+ default:
+ return LAST_TP_PROPERTY_ENUM;
+ }
+}
+
+static void change_mode_state(IdleMUCChannel *obj, guint add, guint remove)
+{
+ IdleMUCChannelPrivate *priv;
+ IRCChannelModeFlags flags;
+ guint group_add = 0, group_remove = 0;
+ GPtrArray *tp_props_to_change;
+ guint prop_flags = 0;
+ guint combined;
+ int i;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ remove &= ~add;
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+ flags = priv->mode_state.flags;
+
+ tp_props_to_change = g_ptr_array_new();
+
+ g_debug("%s: got %x, %x", G_STRFUNC, add, remove);
+
+ add &= ~flags;
+ remove &= flags;
+
+ g_debug("%s: operation %x, %x", G_STRFUNC, add, remove);
+
+ flags |= add;
+ flags &= ~remove;
+
+ combined = add|remove;
+
+ if (add & MODE_FLAG_INVITE_ONLY)
+ {
+ if (!(flags & (MODE_FLAG_OPERATOR_PRIVILEGE|MODE_FLAG_HALFOP_PRIVILEGE)))
+ {
+ group_remove |= TP_CHANNEL_GROUP_FLAG_CAN_ADD;
+ }
+ }
+ else if (remove & MODE_FLAG_INVITE_ONLY)
+ {
+ group_add |= TP_CHANNEL_GROUP_FLAG_CAN_ADD;
+ }
+
+ if (combined & (MODE_FLAG_OPERATOR_PRIVILEGE|MODE_FLAG_HALFOP_PRIVILEGE))
+ {
+ GArray *flags_to_change;
+
+ static const guint flags_helper[] =
+ {
+ TP_PROPERTY_INVITE_ONLY,
+ TP_PROPERTY_LIMIT,
+ TP_PROPERTY_LIMITED,
+ TP_PROPERTY_MODERATED,
+ TP_PROPERTY_PASSWORD,
+ TP_PROPERTY_PASSWORD_REQUIRED,
+ TP_PROPERTY_PRIVATE,
+ TP_PROPERTY_SUBJECT,
+ LAST_TP_PROPERTY_ENUM
+ };
+
+ flags_to_change = g_array_new(FALSE, FALSE, sizeof(guint));
+
+ for (i=0; flags_helper[i] != LAST_TP_PROPERTY_ENUM; i++)
+ {
+ guint prop_id = flags_helper[i];
+ g_array_append_val(flags_to_change, prop_id);
+ }
+
+ prop_flags = TP_PROPERTY_FLAG_WRITE;
+
+ if (add & (MODE_FLAG_OPERATOR_PRIVILEGE|MODE_FLAG_HALFOP_PRIVILEGE))
+ {
+ group_add |= TP_CHANNEL_GROUP_FLAG_CAN_ADD|TP_CHANNEL_GROUP_FLAG_CAN_REMOVE|TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE;
+
+ set_tp_property_flags(obj, flags_to_change, prop_flags, 0);
+ }
+ else if (remove & (MODE_FLAG_OPERATOR_PRIVILEGE|MODE_FLAG_HALFOP_PRIVILEGE))
+ {
+ group_remove |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE|TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE;
+
+ if (flags & MODE_FLAG_INVITE_ONLY)
+ {
+ group_remove |= TP_CHANNEL_GROUP_FLAG_CAN_ADD;
+ }
+
+ set_tp_property_flags(obj, flags_to_change, 0, prop_flags);
+ }
+ }
+
+ for (i = 1; i<LAST_MODE_FLAG_ENUM; i = (i << 1))
+ {
+ if (combined & i)
+ {
+ IdleMUCChannelTPProperty tp_prop_id;
+
+ tp_prop_id = to_prop_id(i);
+
+ if (tp_prop_id < LAST_TP_PROPERTY_ENUM)
+ {
+ GValue prop = {0, };
+ GValue val_auto_is_fine = {0, };
+ GValue *val = &val_auto_is_fine;
+ GType type = property_signatures[tp_prop_id].type;
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_take_boxed(&prop, dbus_g_type_specialized_construct(TP_TYPE_PROPERTY_VALUE_STRUCT));
+
+ g_value_init(val, type);
+
+ if (type != G_TYPE_BOOLEAN)
+ {
+ g_debug("%s: type != G_TYPE_BOOLEAN for %u (modeflag %u), ignoring", G_STRFUNC, tp_prop_id, i);
+ continue;
+ }
+
+ g_value_set_boolean(val, (add & i) ? TRUE : FALSE);
+
+ dbus_g_type_struct_set(&prop,
+ 0, tp_prop_id,
+ 1, val,
+ G_MAXUINT);
+
+ g_ptr_array_add(tp_props_to_change, g_value_get_boxed(&prop));
+
+ if (add & i)
+ {
+ GValue prop = {0, };
+ GValue val_auto_is_fine = {0, };
+ GValue *val = &val_auto_is_fine;
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_take_boxed(&prop, dbus_g_type_specialized_construct(TP_TYPE_PROPERTY_VALUE_STRUCT));
+
+ if (i == MODE_FLAG_USER_LIMIT)
+ {
+ g_value_init(val, G_TYPE_UINT);
+ g_value_set_uint(val, priv->mode_state.limit);
+ tp_prop_id = TP_PROPERTY_LIMIT;
+ }
+ else if (i == MODE_FLAG_KEY)
+ {
+ g_value_init(val, G_TYPE_STRING);
+ g_value_set_string(val, priv->mode_state.key);
+ tp_prop_id = TP_PROPERTY_PASSWORD;
+ }
+ else
+ {
+ continue;
+ }
+
+ dbus_g_type_struct_set(&prop,
+ 0, tp_prop_id,
+ 1, val,
+ G_MAXUINT);
+
+ g_ptr_array_add(tp_props_to_change, g_value_get_boxed(&prop));
+ }
+ }
+ }
+ }
+
+ change_group_flags(obj, group_add, group_remove);
+ change_tp_properties(obj, tp_props_to_change);
+
+ priv->mode_state.flags = flags;
+
+ g_debug("%s: changed to %x", G_STRFUNC, flags);
+}
+
+static void change_password_flags(IdleMUCChannel *obj, guint flag, gboolean state)
+{
+ IdleMUCChannelPrivate *priv;
+ guint add = 0, remove = 0;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ if (state)
+ {
+ add = (~(priv->password_flags)) & flag;
+ priv->password_flags |= flag;
+ }
+ else
+ {
+ remove = priv->password_flags & flag;
+ priv->password_flags &= ~flag;
+ }
+
+ if (add | remove)
+ {
+ g_debug("%s: emitting PASSWORD_FLAGS_CHANGED with %u %u", G_STRFUNC, add, remove);
+ g_signal_emit(obj, signals[PASSWORD_FLAGS_CHANGED], 0, add, remove);
+ }
+}
+
+static void change_sets(IdleMUCChannel *obj,
+ GIntSet *add_current,
+ GIntSet *remove_current,
+ GIntSet *add_local,
+ GIntSet *remove_local,
+ GIntSet *add_remote,
+ GIntSet *remove_remote,
+ IdleHandle actor,
+ TpChannelGroupChangeReason reason)
+{
+ IdleMUCChannelPrivate *priv;
+ GIntSet *add, *remove, *local_pending, *remote_pending, *tmp1, *tmp2;
+ GArray *vadd, *vremove, *vlocal, *vremote;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ add = g_intset_new();
+ remove = g_intset_new();
+ local_pending = g_intset_new();
+ remote_pending = g_intset_new();
+
+ if (add_current)
+ {
+ tmp1 = idle_handle_set_update(priv->current_members, add_current);
+ tmp2 = g_intset_union(add, tmp1);
+
+ g_intset_destroy(add);
+ add = tmp2;
+
+ g_intset_destroy(tmp1);
+ }
+
+ if (remove_current)
+ {
+ tmp1 = idle_handle_set_difference_update(priv->current_members, remove_current);
+ tmp2 = g_intset_union(remove, tmp1);
+
+ g_intset_destroy(remove);
+ remove = tmp2;
+
+ g_intset_destroy(tmp1);
+ }
+
+ if (add_local)
+ {
+ tmp1 = idle_handle_set_update(priv->local_pending, add_local);
+ tmp2 = g_intset_union(local_pending, tmp1);
+
+ g_intset_destroy(local_pending);
+ local_pending = tmp2;
+
+ g_intset_destroy(tmp1);
+ }
+
+ if (remove_local)
+ {
+ tmp1 = idle_handle_set_difference_update(priv->local_pending, remove_local);
+ tmp2 = g_intset_union(remove, tmp1);
+
+ g_intset_destroy(remove);
+ remove = tmp2;
+
+ g_intset_destroy(tmp1);
+ }
+
+ if (add_remote)
+ {
+ tmp1 = idle_handle_set_update(priv->remote_pending, add_remote);
+ tmp2 = g_intset_union(remote_pending, tmp1);
+
+ g_intset_destroy(remote_pending);
+ remote_pending = tmp2;
+
+ g_intset_destroy(tmp1);
+ }
+
+ if (remove_remote)
+ {
+ tmp1 = idle_handle_set_difference_update(priv->remote_pending, remove_remote);
+ tmp2 = g_intset_union(remove, tmp1);
+
+ g_intset_destroy(remove);
+ remove = tmp2;
+
+ g_intset_destroy(tmp1);
+ }
+
+ tmp1 = g_intset_difference(remove, add);
+ g_intset_destroy(remove);
+ remove = tmp1;
+
+ tmp1 = g_intset_difference(remove, local_pending);
+ g_intset_destroy(remove);
+ remove = tmp1;
+
+ tmp1 = g_intset_difference(remove, remote_pending);
+ g_intset_destroy(remove);
+ remove = tmp1;
+
+ vadd = g_intset_to_array(add);
+ vremove = g_intset_to_array(remove);
+ vlocal = g_intset_to_array(local_pending);
+ vremote = g_intset_to_array(remote_pending);
+
+ if ((vadd->len + vremove->len + vlocal->len + vremote->len) > 0)
+ {
+ g_debug("%s: emitting MEMBERS_CHANGED for channel with handle %u, amounts to (%u, %u, %u, %u)", G_STRFUNC, priv->handle, vadd->len, vremove->len, vlocal->len, vremote->len);
+ g_signal_emit(obj, signals[MEMBERS_CHANGED], 0, "", vadd, vremove, vlocal, vremote, actor, reason);
+ }
+
+ g_array_free(vadd, TRUE);
+ g_array_free(vremove, TRUE);
+ g_array_free(vlocal, TRUE);
+ g_array_free(vremote, TRUE);
+
+ g_intset_destroy(add);
+ g_intset_destroy(remove);
+ g_intset_destroy(local_pending);
+ g_intset_destroy(remote_pending);
+}
+
+gboolean _idle_muc_channel_receive(IdleMUCChannel *chan, TpChannelTextMessageType type, IdleHandle sender, const gchar *text)
+{
+ IdleMUCChannelPrivate *priv;
+ IdleMUCPendingMessage *msg;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ msg = _idle_muc_pending_new();
+
+ msg->id = priv->recv_id++;
+ msg->timestamp = time(NULL);
+ msg->sender = sender;
+ msg->type = type;
+ msg->text = g_strdup(text);
+
+ g_queue_push_tail(priv->pending_messages, msg);
+
+ g_signal_emit(chan, signals[RECEIVED], 0,
+ msg->id,
+ msg->timestamp,
+ msg->sender,
+ msg->type,
+ 0,
+ msg->text);
+
+ g_debug("%s: queued message %u", G_STRFUNC, msg->id);
+
+ return FALSE;
+}
+
+static void send_mode_query_request(IdleMUCChannel *chan)
+{
+ IdleMUCChannelPrivate *priv;
+ gchar cmd[IRC_MSG_MAXLEN+2];
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ g_snprintf(cmd, IRC_MSG_MAXLEN+2, "MODE %s", priv->channel_name);
+
+ _idle_connection_send(priv->connection, cmd);
+}
+
+void _idle_muc_channel_join(IdleMUCChannel *chan, const gchar *nick)
+{
+ IdleMUCChannelPrivate *priv;
+ IdleHandle handle;
+ GIntSet *set;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+ g_assert(nick != NULL);
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ set = g_intset_new();
+
+ handle = idle_handle_for_contact(_idle_connection_get_handles(priv->connection), nick);
+
+ if (handle == 0)
+ {
+ g_debug("%s: invalid nick (%s)", G_STRFUNC, nick);
+
+ return;
+ }
+
+ if (handle == priv->own_handle)
+ {
+ /* woot we managed to get into a channel, great */
+ change_state(chan, MUC_STATE_JOINED);
+ g_intset_add(set, handle);
+ change_sets(chan, set, NULL, NULL, set, NULL, set, handle, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
+ change_group_flags(chan, TP_CHANNEL_GROUP_FLAG_CAN_ADD, 0);
+
+ send_mode_query_request(chan);
+
+ if (priv->channel_name[0] == '+')
+ {
+ /* according to IRC specs, PLUS channels do not support channel modes and alway have only +t set, so we work with that. */
+ change_mode_state(chan, MODE_FLAG_TOPIC_ONLY_SETTABLE_BY_OPS, 0);
+ }
+ }
+ else
+ {
+ g_intset_add(set, handle);
+
+ change_sets(chan, set, NULL, NULL, NULL, NULL, set, handle, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
+ }
+
+ g_debug("%s: member joined with handle %u and nick %s", G_STRFUNC, handle, nick);
+
+ g_intset_destroy(set);
+}
+
+void _idle_muc_channel_part(IdleMUCChannel *chan, const gchar *nick)
+{
+ return _idle_muc_channel_kick(chan, nick, nick, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
+}
+
+void _idle_muc_channel_kick(IdleMUCChannel *chan, const gchar *nick, const gchar *kicker, TpChannelGroupChangeReason reason)
+{
+ IdleMUCChannelPrivate *priv;
+ IdleHandle handle;
+ IdleHandle kicker_handle;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ handle = idle_handle_for_contact(_idle_connection_get_handles(priv->connection), nick);
+
+ if (handle == 0)
+ {
+ g_debug("%s: failed to get handle for (%s)", G_STRFUNC, nick);
+
+ return;
+ }
+
+ kicker_handle = idle_handle_for_contact(_idle_connection_get_handles(priv->connection), kicker);
+
+ if (kicker_handle == 0)
+ {
+ g_debug("%s: failed to get handle for (%s)", G_STRFUNC, kicker);
+ }
+
+ return _idle_muc_channel_handle_quit(chan,
+ handle,
+ FALSE,
+ kicker_handle,
+ reason);
+}
+
+void _idle_muc_channel_handle_quit(IdleMUCChannel *chan,
+ IdleHandle handle,
+ gboolean suppress,
+ IdleHandle actor,
+ TpChannelGroupChangeReason reason)
+{
+ IdleMUCChannelPrivate *priv;
+ GIntSet *set;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ set = g_intset_new();
+
+ g_intset_add(set, handle);
+
+ change_sets(chan, NULL, set, NULL, set, NULL, set, actor, reason);
+
+ if (handle == priv->own_handle)
+ {
+ g_debug("%s: it was us!", G_STRFUNC);
+
+ change_state(chan, MUC_STATE_PARTED);
+
+ if (!suppress)
+ {
+ priv->closed = TRUE;
+
+ g_signal_emit(chan, signals[CLOSED], 0);
+ }
+ }
+
+ g_intset_destroy(set);
+}
+
+void _idle_muc_channel_invited(IdleMUCChannel *chan, IdleHandle inviter)
+{
+ IdleMUCChannelPrivate *priv;
+ GIntSet *handles_to_add;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ handles_to_add = g_intset_new();
+
+ g_intset_add(handles_to_add, priv->own_handle);
+
+ change_sets(chan, NULL, NULL, handles_to_add, NULL, NULL, NULL, inviter, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED);
+
+ g_intset_destroy(handles_to_add);
+}
+
+void _idle_muc_channel_names(IdleMUCChannel *chan, GArray *names)
+{
+ IdleMUCChannelPrivate *priv;
+ int i;
+ GIntSet *handles_to_add;
+ IdleHandleStorage *handles;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+ g_assert(names != NULL);
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ handles = _idle_connection_get_handles(priv->connection);
+
+ handles_to_add = g_intset_new();
+
+ for (i=0; i<names->len; i++)
+ {
+ IdleHandle handle;
+ gchar *nick = g_array_index(names, gchar *, i);
+ gunichar ucs4char;
+
+ gchar own_mode = '\0';
+
+ ucs4char = g_utf8_get_char_validated(nick, -1);
+
+ if (!g_unichar_isalpha(ucs4char))
+ {
+ own_mode = *nick;
+
+ nick++;
+ }
+
+ handle = idle_handle_for_contact(handles, nick);
+
+ if (handle == 0)
+ {
+ g_debug("%s: failed to get valid handle for nick %s, ignoring", G_STRFUNC, nick);
+ continue;
+ }
+
+ if (handle == priv->own_handle)
+ {
+ guint remove = MODE_FLAG_OPERATOR_PRIVILEGE|MODE_FLAG_VOICE_PRIVILEGE|MODE_FLAG_HALFOP_PRIVILEGE;
+ guint add = 0;
+
+ switch (own_mode)
+ {
+ case '@':
+ {
+ g_debug("%s: we are OP", G_STRFUNC);
+ add |= MODE_FLAG_OPERATOR_PRIVILEGE;
+ }
+ break;
+ case '&':
+ {
+ g_debug("%s: we are HALFOP", G_STRFUNC);
+ add |= MODE_FLAG_HALFOP_PRIVILEGE;
+ }
+ break;
+ case '+':
+ {
+ g_debug("%s: we are VOICED", G_STRFUNC);
+ add |= MODE_FLAG_VOICE_PRIVILEGE;
+ }
+ break;
+ default:
+ {
+ g_debug("%s: we are NORMAL", G_STRFUNC);
+ }
+ break;
+ }
+
+ remove &= ~add;
+
+ change_mode_state(chan, add, remove);
+ }
+
+ g_intset_add(handles_to_add, handle);
+
+ }
+
+ change_sets(chan, handles_to_add, NULL, NULL, handles_to_add, NULL, handles_to_add, 0, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
+}
+
+void _idle_muc_channel_mode(IdleMUCChannel *chan, const gchar *mode_str)
+{
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+ gchar **mode_argv;
+ gchar *operation;
+ gboolean remove;
+ guint mode_accum = 0;
+ guint limit = 0;
+ gchar *key = NULL;
+ const gchar *own_nick;
+ GArray *flags_to_change;
+ static const guint flags_helper[] =
+ {
+ TP_PROPERTY_INVITE_ONLY,
+ TP_PROPERTY_LIMITED,
+ TP_PROPERTY_MODERATED,
+ TP_PROPERTY_PASSWORD_REQUIRED,
+ TP_PROPERTY_PRIVATE,
+ LAST_TP_PROPERTY_ENUM
+ };
+
+ int i = 1;
+
+ mode_argv = g_strsplit_set(mode_str, " ", -1);
+
+ if (mode_argv[0] == NULL)
+ {
+ g_debug("%s: failed to parse (%s) to tokens", G_STRFUNC, mode_str);
+ goto cleanupl;
+ }
+
+ operation = mode_argv[0]+1;
+
+ if (mode_argv[0][0] == '+')
+ {
+ remove = FALSE;
+ }
+ else if (mode_argv[0][0] == '-')
+ {
+ remove = TRUE;
+ }
+ else
+ {
+ g_debug("%s: failed to decide whether to add or remove modes in (%s)", G_STRFUNC, mode_argv[0]);
+ goto cleanupl;
+ }
+
+ own_nick = idle_handle_inspect(_idle_connection_get_handles(priv->connection), TP_HANDLE_TYPE_CONTACT, priv->own_handle);
+
+ while (*operation != '\0')
+ {
+ switch (*operation)
+ {
+ case 'o':
+ {
+ if (g_strncasecmp(own_nick, mode_argv[i++], -1) == 0)
+ {
+ g_debug("%s: got MODE o concerning us", G_STRFUNC);
+ mode_accum |= MODE_FLAG_OPERATOR_PRIVILEGE;
+ }
+ }
+ break;
+ case 'h':
+ {
+ if (g_strncasecmp(own_nick, mode_argv[i++], -1) == 0)
+ {
+ g_debug("%s: got MODE h concerning us", G_STRFUNC);
+ mode_accum |= MODE_FLAG_HALFOP_PRIVILEGE;
+ }
+ }
+ break;
+ case 'v':
+ {
+ if (g_strncasecmp(own_nick, mode_argv[i++], -1) == 0)
+ {
+ g_debug("%s: got MODE v concerning us", G_STRFUNC);
+ mode_accum |= MODE_FLAG_VOICE_PRIVILEGE;
+ }
+ }
+ break;
+ case 'l':
+ {
+ limit = atoi(mode_argv[i++]);
+ g_debug("%s: got channel user limit %u", G_STRFUNC, limit);
+ mode_accum |= MODE_FLAG_USER_LIMIT;
+ }
+ break;
+ case 'k':
+ {
+ key = g_strdup(mode_argv[i++]);
+ g_debug("%s: got channel key %s", G_STRFUNC, key);
+ mode_accum |= MODE_FLAG_KEY;
+ }
+ break;
+ case 'a':
+ {
+ mode_accum |= MODE_FLAG_ANONYMOUS;
+ }
+ break;
+ case 'i':
+ {
+ mode_accum |= MODE_FLAG_INVITE_ONLY;
+ }
+ break;
+ case 'm':
+ {
+ mode_accum |= MODE_FLAG_MODERATED;
+ }
+ break;
+ case 'n':
+ {
+ mode_accum |= MODE_FLAG_NO_OUTSIDE_MESSAGES;
+ }
+ break;
+ case 'q':
+ {
+ mode_accum |= MODE_FLAG_QUIET;
+ }
+ break;
+ case 'p':
+ {
+ mode_accum |= MODE_FLAG_PRIVATE;
+ }
+ break;
+ case 's':
+ {
+ mode_accum |= MODE_FLAG_SECRET;
+ }
+ break;
+ case 'r':
+ {
+ mode_accum |= MODE_FLAG_SERVER_REOP;
+ }
+ break;
+ case 't':
+ {
+ mode_accum |= MODE_FLAG_TOPIC_ONLY_SETTABLE_BY_OPS;
+ }
+ break;
+ default:
+ {
+ g_debug("%s: did not understand mode identifier %c", G_STRFUNC, *operation);
+ }
+ break;
+ }
+ operation++;
+ }
+
+ if (mode_accum & MODE_FLAG_KEY)
+ {
+ priv->mode_state.key = key;
+ }
+ if (mode_accum & MODE_FLAG_USER_LIMIT)
+ {
+ priv->mode_state.limit = limit;
+ }
+
+ flags_to_change = g_array_new(FALSE, FALSE, sizeof(guint));
+
+ for (i=0; flags_helper[i] != LAST_TP_PROPERTY_ENUM; i++)
+ {
+ guint prop_id = flags_helper[i];
+ g_array_append_val(flags_to_change, prop_id);
+ }
+
+ set_tp_property_flags(chan, flags_to_change, TP_PROPERTY_FLAG_READ, 0);
+
+ if (!remove)
+ {
+ change_mode_state(chan, mode_accum, 0);
+ }
+ else
+ {
+ change_mode_state(chan, 0, mode_accum);
+ }
+
+cleanupl:
+
+ g_strfreev(mode_argv);
+}
+
+void _idle_muc_channel_topic(IdleMUCChannel *chan, const char *topic)
+{
+ GValue prop = {0, };
+ GValue val = {0, };
+ GPtrArray *arr;
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(chan != NULL);
+ g_assert(topic != NULL);
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_take_boxed(&prop, dbus_g_type_specialized_construct(TP_TYPE_PROPERTY_VALUE_STRUCT));
+
+ g_value_init(&val, G_TYPE_STRING);
+ g_value_set_string(&val, topic);
+
+ dbus_g_type_struct_set(&prop,
+ 0, TP_PROPERTY_SUBJECT,
+ 1, &val,
+ G_MAXUINT);
+
+ arr = g_ptr_array_new();
+
+ g_ptr_array_add(arr, g_value_get_boxed(&prop));
+
+ change_tp_properties(chan, arr);
+
+ g_ptr_array_free(arr, TRUE);
+}
+
+void _idle_muc_channel_topic_touch(IdleMUCChannel *chan, const IdleHandle toucher, const guint timestamp)
+{
+ GValue prop = {0, };
+ GValue val = {0, };
+ GPtrArray *arr;
+
+ arr = g_ptr_array_new();
+
+ g_assert(chan != NULL);
+ g_assert(toucher != 0);
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_take_boxed(&prop, dbus_g_type_specialized_construct(TP_TYPE_PROPERTY_VALUE_STRUCT));
+
+ g_value_init(&val, G_TYPE_UINT);
+ g_value_set_uint(&val, toucher);
+
+ dbus_g_type_struct_set(&prop,
+ 0, TP_PROPERTY_SUBJECT_CONTACT,
+ 1, &val,
+ G_MAXUINT);
+
+ g_ptr_array_add(arr, g_value_get_boxed(&prop));
+
+ g_value_set_uint(&val, timestamp);
+
+ dbus_g_type_struct_set(&prop,
+ 0, TP_PROPERTY_SUBJECT_TIMESTAMP,
+ 1, &val,
+ G_MAXUINT);
+
+ g_ptr_array_add(arr, g_value_get_boxed(&prop));
+
+ change_tp_properties(chan, arr);
+
+ g_ptr_array_free(arr, TRUE);
+}
+
+void _idle_muc_channel_topic_full(IdleMUCChannel *chan, const IdleHandle toucher, const guint timestamp, const gchar *topic)
+{
+ GValue prop = {0, };
+ GValue val = {0, };
+ GPtrArray *arr;
+
+ arr = g_ptr_array_new();
+
+ g_assert(chan != NULL);
+ g_assert(toucher != 0);
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_take_boxed(&prop, dbus_g_type_specialized_construct(TP_TYPE_PROPERTY_VALUE_STRUCT));
+
+ g_value_init(&val, G_TYPE_UINT);
+ g_value_set_uint(&val, toucher);
+
+ dbus_g_type_struct_set(&prop,
+ 0, TP_PROPERTY_SUBJECT_CONTACT,
+ 1, &val,
+ G_MAXUINT);
+
+ g_ptr_array_add(arr, g_value_get_boxed(&prop));
+
+ g_value_set_uint(&val, timestamp);
+
+ dbus_g_type_struct_set(&prop,
+ 0, TP_PROPERTY_SUBJECT_TIMESTAMP,
+ 1, &val,
+ G_MAXUINT);
+
+ g_ptr_array_add(arr, g_value_get_boxed(&prop));
+
+ g_value_unset(&val);
+ g_value_init(&val, G_TYPE_STRING);
+ g_value_set_string(&val, topic);
+
+ dbus_g_type_struct_set(&prop,
+ 0, TP_PROPERTY_SUBJECT,
+ 1, &val,
+ G_MAXUINT);
+
+ change_tp_properties(chan, arr);
+
+ g_ptr_array_free(arr, TRUE);
+}
+
+void _idle_muc_channel_topic_unset(IdleMUCChannel *chan)
+{
+ GArray *arr = g_array_new(FALSE, FALSE, sizeof(guint));
+ guint not_even_g_array_append_can_take_address_of_enumeration_constants_in_c;
+ guint *tmp = &not_even_g_array_append_can_take_address_of_enumeration_constants_in_c;
+
+ *tmp = TP_PROPERTY_SUBJECT;
+ g_array_append_val(arr, *tmp);
+ *tmp = TP_PROPERTY_SUBJECT_TIMESTAMP;
+ g_array_append_val(arr, *tmp);
+ *tmp = TP_PROPERTY_SUBJECT_CONTACT;
+ g_array_append_val(arr, *tmp);
+
+ set_tp_property_flags(chan, arr, 0, TP_PROPERTY_FLAG_READ);
+
+ g_array_free(arr, TRUE);
+}
+
+void _idle_muc_channel_badchannelkey(IdleMUCChannel *chan)
+{
+ change_state(chan, MUC_STATE_NEED_PASSWORD);
+}
+
+void _idle_muc_channel_join_error(IdleMUCChannel *chan, IdleMUCChannelJoinError err)
+{
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ if (!priv->join_ready)
+ {
+ priv->join_ready = TRUE;
+
+ g_signal_emit(chan, signals[JOIN_READY], 0, err);
+ }
+ else
+ {
+ g_debug("%s: already emitted JOIN_READY! (current err %u)", G_STRFUNC, err);
+ }
+}
+
+void _idle_muc_channel_rename(IdleMUCChannel *chan, IdleHandle old, IdleHandle new)
+{
+ IdleMUCChannelPrivate *priv;
+ GIntSet *cadd, *cremove, *ladd, *lremove, *radd, *rremove;
+
+ g_assert(chan != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(chan));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ cadd = g_intset_new();
+ cremove = g_intset_new();
+ ladd = g_intset_new();
+ lremove = g_intset_new();
+ radd = g_intset_new();
+ rremove = g_intset_new();
+
+ if (priv->own_handle == old)
+ {
+ gboolean valid;
+ IdleHandleStorage *handles;
+
+ handles = _idle_connection_get_handles(priv->connection);
+
+ valid = idle_handle_unref(handles, TP_HANDLE_TYPE_CONTACT, old);
+ g_assert(valid);
+
+ priv->own_handle = new;
+
+ valid = idle_handle_ref(handles, TP_HANDLE_TYPE_CONTACT, new);
+ g_assert(valid);
+
+ g_debug("%s: changed own_handle to %u", G_STRFUNC, new);
+ }
+
+ if (idle_handle_set_contains(priv->current_members, old))
+ {
+ g_intset_add(cadd, new);
+ g_intset_add(cremove, old);
+ }
+ else if (idle_handle_set_contains(priv->local_pending, old))
+ {
+ g_intset_add(ladd, new);
+ g_intset_add(lremove, old);
+ }
+ else if (idle_handle_set_contains(priv->remote_pending, old))
+ {
+ g_intset_add(radd, new);
+ g_intset_add(rremove, old);
+ }
+
+ change_sets(chan, cadd, cremove, ladd, lremove, radd, rremove, new, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
+
+ g_intset_destroy(cadd);
+ g_intset_destroy(cremove);
+ g_intset_destroy(ladd);
+ g_intset_destroy(lremove);
+ g_intset_destroy(radd);
+ g_intset_destroy(rremove);
+}
+
+static void send_join_request(IdleMUCChannel *obj, const gchar *password)
+{
+ IdleMUCChannelPrivate *priv;
+ gchar cmd[IRC_MSG_MAXLEN+1];
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ if (password)
+ {
+ g_snprintf(cmd, IRC_MSG_MAXLEN+1, "JOIN %s %s", priv->channel_name, password);
+ }
+ else
+ {
+ g_snprintf(cmd, IRC_MSG_MAXLEN+1, "JOIN %s", priv->channel_name);
+ }
+
+ _idle_connection_send(priv->connection, cmd);
+}
+
+void _idle_muc_channel_join_attempt(IdleMUCChannel *obj)
+{
+ return send_join_request(obj, NULL);
+}
+
+gboolean _idle_muc_channel_has_current_member(IdleMUCChannel *chan, IdleHandle handle)
+{
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE(chan);
+
+ return idle_handle_set_contains(priv->current_members, handle);
+}
+
+static gboolean send_invite_request(IdleMUCChannel *obj, IdleHandle handle, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ gchar cmd[IRC_MSG_MAXLEN+1];
+ const gchar *nick;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ nick = idle_handle_inspect(_idle_connection_get_handles(priv->connection), TP_HANDLE_TYPE_CONTACT, handle);
+
+ if ((nick == NULL) || (nick[0] == '\0'))
+ {
+ g_debug("%s: invalid handle %u passed", G_STRFUNC, handle);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidHandle, "invalid handle %u passed", handle);
+
+ return FALSE;
+ }
+
+ g_snprintf(cmd, IRC_MSG_MAXLEN+1, "INVITE %s %s", nick, priv->channel_name);
+
+ _idle_connection_send(priv->connection, cmd);
+
+ return TRUE;
+}
+
+static gboolean send_kick_request(IdleMUCChannel *obj, IdleHandle handle, const gchar *msg, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ gchar cmd[IRC_MSG_MAXLEN+1];
+ const gchar *nick;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ nick = idle_handle_inspect(_idle_connection_get_handles(priv->connection), TP_HANDLE_TYPE_CONTACT, handle);
+
+ if ((nick == NULL) || (nick[0] == '\0'))
+ {
+ g_debug("%s: invalid handle %u passed", G_STRFUNC, handle);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidHandle, "invalid handle %u passed", handle);
+
+ return FALSE;
+ }
+
+ if (msg != NULL)
+ {
+ g_snprintf(cmd, IRC_MSG_MAXLEN+1, "KICK %s %s %s", priv->channel_name, nick, msg);
+ }
+ else
+ {
+ g_snprintf(cmd, IRC_MSG_MAXLEN+1, "KICK %s %s", priv->channel_name, nick);
+ }
+
+ _idle_connection_send(priv->connection, cmd);
+
+ return TRUE;
+}
+
+static gboolean add_member(IdleMUCChannel *obj, IdleHandle handle, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ IdleHandleStorage *handles;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+ handles = _idle_connection_get_handles(priv->connection);
+
+ if (handle == priv->own_handle)
+ {
+ if (idle_handle_set_contains(priv->current_members, handle) || idle_handle_set_contains(priv->remote_pending, handle))
+ {
+ g_debug("%s: we are already a member of or trying to join the channel with handle %u", G_STRFUNC, priv->handle);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "we are already a member of or trying to join the channel with handle %u", priv->handle);
+
+ return FALSE;
+ }
+ else
+ {
+ GIntSet *add_set = g_intset_new();
+
+ send_join_request(obj, NULL);
+
+ change_state(obj, MUC_STATE_JOINING);
+
+ g_intset_add(add_set, handle);
+
+ change_sets(obj, NULL, NULL, NULL, NULL, add_set, NULL, 0, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
+ }
+ }
+ else
+ {
+ if (idle_handle_set_contains(priv->current_members, handle) || idle_handle_set_contains(priv->remote_pending, handle))
+ {
+ g_debug("%s: the requested contact (handle %u) to be added to the room (handle %u) is already a member of or has already been invited to join the room", G_STRFUNC, handle, priv->handle);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "the requested contact (handle %u) to be added to the room (handle %u) is already a member of or has already been invited to join the room", handle, priv->handle);
+
+ return FALSE;
+ }
+ else
+ {
+ GError *invite_error;
+ GIntSet *add_set = g_intset_new();
+
+ if (!send_invite_request(obj, handle, &invite_error))
+ {
+ *error = invite_error;
+
+ return FALSE;
+ }
+
+ g_intset_add(add_set, handle);
+
+ change_sets(obj,
+ NULL, NULL,
+ NULL, NULL,
+ add_set, NULL,
+ priv->own_handle,
+ TP_CHANNEL_GROUP_CHANGE_REASON_INVITED);
+ }
+ }
+
+ return TRUE;
+}
+
+static gint idle_pending_message_compare(gconstpointer msg, gconstpointer id)
+{
+ IdleMUCPendingMessage *message = (IdleMUCPendingMessage *)(msg);
+
+ return (message->id != GPOINTER_TO_INT(id));
+}
+
+/**
+ * idle_muc_channel_acknowledge_pending_messages
+ *
+ * Implements DBus method AcknowledgePendingMessages
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_acknowledge_pending_messages (IdleMUCChannel *obj,
+ const GArray *ids,
+ GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ int i;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ for (i=0; i<ids->len; i++)
+ {
+ GList *node;
+ IdleMUCPendingMessage *msg;
+ guint id = g_array_index(ids, guint, i);
+
+ node = g_queue_find_custom(priv->pending_messages,
+ GINT_TO_POINTER(id),
+ idle_pending_message_compare);
+
+ if (node == NULL)
+ {
+ g_debug("%s: message %u not found", G_STRFUNC, id);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "message id %u not found", id);
+
+ return FALSE;
+ }
+
+ msg = (IdleMUCPendingMessage *)(node->data);
+
+ g_debug("%s: acknowledging pending message with id %u", G_STRFUNC, id);
+
+ g_queue_delete_link(priv->pending_messages, node);
+
+ _idle_muc_pending_free(msg);
+ }
+
+ return TRUE;
+}
+
+/**
+ * idle_muc_channel_add_members
+ *
+ * Implements DBus method AddMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_add_members (IdleMUCChannel *obj, const GArray * contacts, const gchar * message, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ int i;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ for (i=0; i < contacts->len; i++)
+ {
+ IdleHandle handle;
+ GError *add_error;
+
+ handle = (IdleHandle)(g_array_index(contacts, guint, i));
+
+ if (!add_member(obj, handle, &add_error))
+ {
+ *error = add_error;
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void part_from_channel(IdleMUCChannel *obj, const gchar *msg)
+{
+ IdleMUCChannelPrivate *priv;
+ gchar cmd[IRC_MSG_MAXLEN+1];
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ if (msg != NULL)
+ {
+ g_snprintf(cmd, IRC_MSG_MAXLEN+1, "PART %s %s", priv->channel_name, msg);
+ }
+ else
+ {
+ g_snprintf(cmd, IRC_MSG_MAXLEN+1, "PART %s", priv->channel_name);
+ }
+
+ _idle_connection_send(priv->connection, cmd);
+}
+
+
+/**
+ * idle_muc_channel_close
+ *
+ * Implements DBus method Close
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_close (IdleMUCChannel *obj, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ if (priv->state == MUC_STATE_JOINED)
+ {
+ part_from_channel(obj, NULL);
+ }
+
+ if (priv->state < MUC_STATE_JOINED)
+ {
+ if (!priv->closed)
+ {
+ g_signal_emit(obj, signals[CLOSED], 0);
+ priv->closed = TRUE;
+ }
+ }
+
+ g_debug("%s: called on %p", G_STRFUNC, obj);
+
+ return TRUE;
+}
+
+/**
+ * idle_muc_channel_get_all_members
+ *
+ * Implements DBus method GetAllMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_all_members (IdleMUCChannel *obj, GArray ** ret, GArray ** ret1, GArray ** ret2, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ *ret = idle_handle_set_to_array(priv->current_members);
+ *ret1 = idle_handle_set_to_array(priv->local_pending);
+ *ret2 = idle_handle_set_to_array(priv->remote_pending);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_channel_type
+ *
+ * Implements DBus method GetChannelType
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_channel_type (IdleMUCChannel *obj, gchar ** ret, GError **error)
+{
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ *ret = g_strdup(TP_IFACE_CHANNEL_TYPE_TEXT);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_group_flags
+ *
+ * Implements DBus method GetGroupFlags
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_group_flags (IdleMUCChannel *obj, guint* ret, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ *ret = priv->group_flags;
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_handle
+ *
+ * Implements DBus method GetHandle
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_handle (IdleMUCChannel *obj, guint* ret, guint* ret1, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ *ret = TP_HANDLE_TYPE_ROOM;
+ *ret1 = priv->handle;
+
+ g_debug("%s: returning handle %u", G_STRFUNC, *ret1);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_handle_owners
+ *
+ * Implements DBus method GetHandleOwners
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_handle_owners (IdleMUCChannel *obj, const GArray * handles, GArray ** ret, GError **error)
+{
+ int i;
+
+ *ret = g_array_sized_new(FALSE, FALSE, sizeof(guint), handles->len);
+
+ for (i=0; i<handles->len; i++)
+ {
+ g_array_index(*ret, guint, i) = g_array_index(handles, guint, i);
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_interfaces
+ *
+ * Implements DBus method GetInterfaces
+ * on interface org.freedesktop.Telepathy.Channel
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_interfaces (IdleMUCChannel *obj, gchar *** ret, GError **error)
+{
+ /* TODO implement Properties */
+ const gchar *interfaces[] = {TP_IFACE_CHANNEL_INTERFACE_PASSWORD,
+ TP_IFACE_CHANNEL_INTERFACE_GROUP,
+ TP_IFACE_PROPERTIES, NULL};
+
+ *ret = g_strdupv((gchar **)(interfaces));
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_local_pending_members
+ *
+ * Implements DBus method GetLocalPendingMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_local_pending_members (IdleMUCChannel *obj, GArray ** ret, GError **error)
+{
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ *ret = idle_handle_set_to_array(priv->local_pending);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_members
+ *
+ * Implements DBus method GetMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_members (IdleMUCChannel *obj, GArray ** ret, GError **error)
+{
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ *ret = idle_handle_set_to_array(priv->current_members);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_message_types
+ *
+ * Implements DBus method GetMessageTypes
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_message_types (IdleMUCChannel *obj, GArray ** ret, GError **error)
+{
+ int i;
+
+ *ret = g_array_sized_new(FALSE, FALSE, sizeof(guint), 3);
+
+ for (i=0; i<3; i++)
+ {
+ g_array_index(*ret, guint, i) = i;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_password_flags
+ *
+ * Implements DBus method GetPasswordFlags
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Password
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_password_flags (IdleMUCChannel *obj, guint* ret, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ *ret = priv->password_flags;
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_properties
+ *
+ * Implements DBus method GetProperties
+ * on interface org.freedesktop.Telepathy.Properties
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_properties (IdleMUCChannel *obj, const GArray * properties, GPtrArray ** ret, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ int i;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ for (i=0; i<properties->len; i++)
+ {
+ IdleMUCChannelTPProperty prop = g_array_index(properties, guint, i);
+
+ if (prop >= LAST_TP_PROPERTY_ENUM)
+ {
+ g_debug("%s: invalid property id %u", G_STRFUNC, prop);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "invalid property id %u", prop);
+
+ return FALSE;
+ }
+
+ if (!(priv->properties[prop].flags & TP_PROPERTY_FLAG_READ))
+ {
+ g_debug("%s: not allowed to read property %u", G_STRFUNC, prop);
+
+ *error = g_error_new(TELEPATHY_ERRORS, PermissionDenied, "not allowed to read property %u", prop);
+
+ return FALSE;;
+ }
+ }
+
+ *ret = g_ptr_array_sized_new(properties->len);
+
+ for (i=0; i<properties->len; i++)
+ {
+ IdleMUCChannelTPProperty prop = g_array_index(properties, guint, i);
+ GValue prop_val = {0, };
+
+ g_value_init(&prop_val, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_take_boxed(&prop_val,
+ dbus_g_type_specialized_construct(TP_TYPE_PROPERTY_VALUE_STRUCT));
+
+ dbus_g_type_struct_set(&prop_val,
+ 0, prop,
+ 1, priv->properties[prop].value,
+ G_MAXUINT);
+
+ g_ptr_array_add(*ret, g_value_get_boxed(&prop_val));
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_remote_pending_members
+ *
+ * Implements DBus method GetRemotePendingMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_remote_pending_members (IdleMUCChannel *obj, GArray ** ret, GError **error)
+{
+ IdleMUCChannelPrivate *priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ *ret = idle_handle_set_to_array(priv->remote_pending);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_get_self_handle
+ *
+ * Implements DBus method GetSelfHandle
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_get_self_handle (IdleMUCChannel *obj, guint* ret, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ g_debug("%s: returning handle %u", G_STRFUNC, priv->own_handle);
+
+ *ret = priv->own_handle;
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_list_pending_messages
+ *
+ * Implements DBus method ListPendingMessages
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_list_pending_messages (IdleMUCChannel *obj,
+ gboolean clear,
+ GPtrArray ** ret,
+ GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ guint count;
+ GPtrArray *messages;
+ GList *cur;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ count = g_queue_get_length(priv->pending_messages);
+
+ messages = g_ptr_array_sized_new(count);
+
+ for (cur = g_queue_peek_head_link(priv->pending_messages); cur != NULL; cur = cur->next)
+ {
+ IdleMUCPendingMessage *msg = (IdleMUCPendingMessage *)(cur->data);
+ GValueArray *vals;
+
+ vals = g_value_array_new(6);
+
+ g_value_array_append(vals, NULL);
+ g_value_init(g_value_array_get_nth(vals, 0), G_TYPE_UINT);
+ g_value_set_uint(g_value_array_get_nth(vals, 0), msg->id);
+
+ g_value_array_append(vals, NULL);
+ g_value_init(g_value_array_get_nth(vals, 1), G_TYPE_UINT);
+ g_value_set_uint(g_value_array_get_nth(vals, 1), msg->timestamp);
+
+ g_value_array_append(vals, NULL);
+ g_value_init(g_value_array_get_nth(vals, 2), G_TYPE_UINT);
+ g_value_set_uint(g_value_array_get_nth(vals, 2), msg->sender);
+
+ g_value_array_append(vals, NULL);
+ g_value_init(g_value_array_get_nth(vals, 3), G_TYPE_UINT);
+ g_value_set_uint(g_value_array_get_nth(vals, 3), msg->type);
+
+ g_value_array_append(vals, NULL);
+ g_value_init(g_value_array_get_nth(vals, 4), G_TYPE_UINT);
+ g_value_set_uint(g_value_array_get_nth(vals, 4), 0);
+
+ g_value_array_append(vals, NULL);
+ g_value_init(g_value_array_get_nth(vals, 5), G_TYPE_STRING);
+ g_value_set_string(g_value_array_get_nth(vals, 5), msg->text);
+
+ g_ptr_array_add(messages, vals);
+ }
+
+ *ret = messages;
+
+ if (clear)
+ {
+ IdleMUCPendingMessage *msg;
+
+ while ((msg = g_queue_pop_head(priv->pending_messages)) != NULL)
+ {
+ _idle_muc_pending_free(msg);
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_list_properties
+ *
+ * Implements DBus method ListProperties
+ * on interface org.freedesktop.Telepathy.Properties
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_list_properties (IdleMUCChannel *obj, GPtrArray ** ret, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ guint i;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ *ret = g_ptr_array_sized_new(LAST_TP_PROPERTY_ENUM);
+
+ for (i=0; i<LAST_TP_PROPERTY_ENUM; i++)
+ {
+ GValue prop = {0, };
+ const gchar *dbus_sig;
+ const gchar *name;
+ guint flags;
+
+ switch (property_signatures[i].type)
+ {
+ case G_TYPE_BOOLEAN:
+ {
+ dbus_sig = "b";
+ }
+ break;
+ case G_TYPE_UINT:
+ {
+ dbus_sig = "u";
+ }
+ break;
+ case G_TYPE_STRING:
+ {
+ dbus_sig = "s";
+ }
+ break;
+ default:
+ {
+ g_debug("%s: encountered unknown type %s", G_STRFUNC, g_type_name(property_signatures[i].type));
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "internal error in %s", G_STRFUNC);
+
+ return FALSE;
+ }
+ break;
+ }
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_INFO_STRUCT);
+ g_value_take_boxed(&prop,
+ dbus_g_type_specialized_construct(TP_TYPE_PROPERTY_INFO_STRUCT));
+
+ name = property_signatures[i].name;
+ flags = priv->properties[i].flags;
+
+ dbus_g_type_struct_set(&prop,
+ 0, i,
+ 1, property_signatures[i].name,
+ 2, dbus_sig,
+ 3, priv->properties[i].flags,
+ G_MAXUINT);
+
+ g_ptr_array_add(*ret, g_value_get_boxed(&prop));
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_provide_password
+ *
+ * Implements DBus method ProvidePassword
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Password
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_provide_password (IdleMUCChannel *obj, const gchar * password, DBusGMethodInvocation *context)
+{
+ IdleMUCChannelPrivate *priv;
+ GError *error;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ if (!(priv->password_flags & TP_CHANNEL_PASSWORD_FLAG_PROVIDE) || (priv->passwd_ctx != NULL))
+ {
+ g_debug("%s: don't need a password now or authentication already in process (handle %u)", G_STRFUNC, priv->handle);
+
+ error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "don't need a password now or authentication already in process (handle %u)", priv->handle);
+
+ dbus_g_method_return_error(context, error);
+ g_error_free(error);
+
+ return FALSE;
+ }
+
+ priv->passwd_ctx = context;
+
+ send_join_request(obj, password);
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_remove_members
+ *
+ * Implements DBus method RemoveMembers
+ * on interface org.freedesktop.Telepathy.Channel.Interface.Group
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_remove_members (IdleMUCChannel *obj, const GArray * contacts, const gchar * message, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ int i;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ for (i=0; i<contacts->len; i++)
+ {
+ GError *kick_error;
+ IdleHandle handle = g_array_index(contacts, guint, i);
+
+ if (handle == priv->own_handle)
+ {
+ part_from_channel(obj, message);
+
+ return TRUE;
+ }
+
+ if (!idle_handle_set_contains(priv->current_members, handle))
+ {
+ g_debug("%s: handle %u not a current member!", G_STRFUNC, handle);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "handle %u is not a current member of the channel", handle);
+
+ return FALSE;
+ }
+
+ if (!send_kick_request(obj, handle, message, &kick_error))
+ {
+ g_debug("%s: send_kick_request failed: %s", G_STRFUNC, kick_error->message);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, kick_error->message);
+
+ g_error_free(kick_error);
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * idle_muc_channel_send
+ *
+ * Implements DBus method Send
+ * on interface org.freedesktop.Telepathy.Channel.Type.Text
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_send (IdleMUCChannel *obj, guint type, const gchar * text, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ gchar msg[IRC_MSG_MAXLEN+2];
+ const char *recipient;
+ time_t timestamp;
+ const gchar *final_text = text;
+ gsize len;
+ gchar *part;
+ gsize headerlen;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ recipient = idle_handle_inspect(_idle_connection_get_handles(priv->connection), TP_HANDLE_TYPE_ROOM, priv->handle);
+
+ if ((recipient == NULL) || (recipient[0] == '\0'))
+ {
+ g_debug("%s: invalid recipient (handle %u)", G_STRFUNC, priv->handle);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "invalid recipient");
+
+ return FALSE;
+ }
+
+ len = strlen(final_text);
+ part = (gchar*)final_text;
+
+ switch (type)
+ {
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
+ {
+ g_snprintf(msg, IRC_MSG_MAXLEN, "PRIVMSG %s :", recipient);
+ }
+ break;
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
+ {
+ g_snprintf(msg, IRC_MSG_MAXLEN, "PRIVMSG %s :\001ACTION ", recipient);
+ }
+ break;
+ case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
+ {
+ g_snprintf(msg, IRC_MSG_MAXLEN, "NOTICE %s :", recipient);
+ }
+ break;
+ default:
+ {
+ g_debug("%s: invalid message type %u", G_STRFUNC, type);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "invalid message type %u", type);
+
+ return FALSE;
+ }
+ break;
+ }
+
+ headerlen = strlen(msg);
+
+ while (part < final_text+len)
+ {
+ char *br = strchr (part, '\n');
+ size_t len = IRC_MSG_MAXLEN-headerlen;
+ if (br)
+ {
+ len = (len < br - part) ? len : br - part;
+ }
+
+ if (type == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
+ {
+ g_snprintf(msg+headerlen, len + 1, "%s\001", part);
+ len -= 1;
+ }
+ else
+ {
+ g_strlcpy(msg+headerlen, part, len + 1);
+ }
+ part += len;
+ if (br)
+ {
+ part++;
+ }
+
+ _idle_connection_send(priv->connection, msg);
+ }
+
+ timestamp = time(NULL);
+
+ if ((priv->mode_state.flags & MODE_FLAG_MODERATED) && !(priv->mode_state.flags & (MODE_FLAG_OPERATOR_PRIVILEGE|MODE_FLAG_HALFOP_PRIVILEGE|MODE_FLAG_VOICE_PRIVILEGE)))
+ {
+ g_debug("%s: emitting SEND_ERROR with (%u, %llu, %u, %s)", G_STRFUNC, TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED, (guint64)(timestamp), type, text);
+ g_signal_emit(obj, signals[SEND_ERROR], 0, TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED, timestamp, type, text);
+ }
+ else
+ {
+ g_debug("%s: emitting SENT with (%llu, %u, %s)", G_STRFUNC, (guint64)(timestamp), type, text);
+ g_signal_emit(obj, signals[SENT], 0, timestamp, type, text);
+ }
+
+ return TRUE;
+}
+
+static char to_irc_mode(IdleMUCChannelTPProperty prop_id)
+{
+ switch (prop_id)
+ {
+ case TP_PROPERTY_INVITE_ONLY:
+ return 'i';
+ case TP_PROPERTY_MODERATED:
+ return 'm';
+ case TP_PROPERTY_PRIVATE:
+ return 's';
+ default:
+ return '\0';
+ }
+}
+
+static int prop_arr_find(const GPtrArray *props, IdleMUCChannelTPProperty needle)
+{
+ int i;
+
+ for (i=0; i<props->len; i++)
+ {
+ GValue prop = {0, };
+ guint prop_id;
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_set_static_boxed(&prop, g_ptr_array_index(props, i));
+
+ dbus_g_type_struct_get(&prop,
+ 0, &prop_id,
+ G_MAXUINT);
+
+ if (prop_id == needle)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static void send_properties_request(IdleMUCChannel *obj, const GPtrArray *properties)
+{
+ IdleMUCChannelPrivate *priv;
+ int i;
+ GPtrArray *waiting;
+ gchar cmd[IRC_MSG_MAXLEN+2];
+ size_t len;
+ gchar *body;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+ g_assert(properties != NULL);
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ waiting = g_ptr_array_new();
+
+ g_snprintf(cmd, IRC_MSG_MAXLEN+2, "MODE %s ", priv->channel_name);
+ len = strlen(cmd);
+ body = cmd+len;
+
+ for (i=0; i<properties->len; i++)
+ {
+ GValue prop = {0, };
+ IdleMUCChannelTPProperty prop_id;
+ GValue *prop_val;
+ char irc_mode;
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_set_static_boxed(&prop, g_ptr_array_index(properties, i));
+
+ dbus_g_type_struct_get(&prop,
+ 0, &prop_id,
+ 1, &prop_val,
+ G_MAXUINT);
+
+ irc_mode = to_irc_mode(prop_id);
+
+ if (irc_mode != '\0')
+ {
+ g_assert(G_VALUE_TYPE(prop_val) == G_TYPE_BOOLEAN);
+
+ gboolean state = g_value_get_boolean(prop_val);
+
+ size_t seq = 0;
+
+ if (state)
+ {
+ body[seq++] = '+';
+ }
+ else
+ {
+ body[seq++] = '-';
+ }
+
+ body[seq++] = irc_mode;
+ body[seq++] = '\0';
+
+ _idle_connection_send(priv->connection, cmd);
+ }
+ else
+ {
+ if (prop_id == TP_PROPERTY_SUBJECT)
+ {
+ const gchar *subject = g_value_get_string(prop_val);
+ gchar cmd[IRC_MSG_MAXLEN+2];
+
+ g_snprintf(cmd, IRC_MSG_MAXLEN+2, "TOPIC %s :%s", priv->channel_name, subject);
+
+ _idle_connection_send(priv->connection, cmd);
+ }
+ else
+ {
+ g_ptr_array_add(waiting, g_value_get_boxed(&prop));
+ }
+ }
+ }
+
+ if (waiting->len)
+ {
+ int i, j;
+ gpointer tmp;
+
+ i = prop_arr_find(waiting, TP_PROPERTY_LIMITED);
+ j = prop_arr_find(waiting, TP_PROPERTY_LIMIT);
+
+ if ((i != -1) && (j != -1) && (i < j))
+ {
+ g_debug("%s: swapping order of TP_PROPERTY_LIMIT and TP_PROPERTY_LIMITED", G_STRFUNC);
+
+ tmp = g_ptr_array_index(waiting, i);
+ g_ptr_array_index(waiting, i) = g_ptr_array_index(waiting, j);
+ g_ptr_array_index(waiting, j) = tmp;
+ }
+
+ i = prop_arr_find(waiting, TP_PROPERTY_PASSWORD_REQUIRED);
+ j = prop_arr_find(waiting, TP_PROPERTY_PASSWORD);
+
+ if ((i != -1) && (j != -1) && (i < j))
+ {
+ g_debug("%s: swapping order of TP_PROPERTY_PASSWORD and TP_PROPERTY_PASSWORD_REQUIRED", G_STRFUNC);
+
+ tmp = g_ptr_array_index(waiting, i);
+ g_ptr_array_index(waiting, i) = g_ptr_array_index(waiting, j);
+ g_ptr_array_index(waiting, j) = tmp;
+ }
+ }
+
+ /* okay now the data is ALWAYS before the boolean */
+
+ for (i=0; i<waiting->len; i++)
+ {
+ GValue prop = {0, };
+ IdleMUCChannelTPProperty prop_id;
+ GValue *prop_val;
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_set_static_boxed(&prop, g_ptr_array_index(waiting, i));
+
+ dbus_g_type_struct_get(&prop,
+ 0, &prop_id,
+ 1, &prop_val,
+ G_MAXUINT);
+
+ g_assert(prop_id < LAST_TP_PROPERTY_ENUM);
+
+ if (prop_id == TP_PROPERTY_LIMIT || prop_id == TP_PROPERTY_PASSWORD)
+ {
+ int j;
+
+ g_value_copy(prop_val, priv->properties[prop_id].value);
+
+ j = prop_arr_find(waiting, prop_id+1);
+
+ if (j == -1)
+ {
+ if (prop_id == TP_PROPERTY_LIMIT && priv->mode_state.flags & MODE_FLAG_USER_LIMIT)
+ {
+ g_snprintf(body, IRC_MSG_MAXLEN-len, "+l %u", g_value_get_uint(prop_val));
+ }
+ else if (prop_id == TP_PROPERTY_PASSWORD && priv->mode_state.flags & MODE_FLAG_KEY)
+ {
+ g_snprintf(body, IRC_MSG_MAXLEN-len, "+k %s", g_value_get_string(prop_val));
+ }
+ else
+ {
+ g_debug("%s: %u", G_STRFUNC, __LINE__);
+ }
+ }
+ else
+ {
+ g_debug("%s: %u", G_STRFUNC, __LINE__);
+ }
+ }
+ else if (prop_id == TP_PROPERTY_LIMITED)
+ {
+ guint limit = g_value_get_uint(priv->properties[TP_PROPERTY_LIMIT].value);
+
+ if (g_value_get_boolean(prop_val))
+ {
+ if (limit != 0)
+ {
+ g_snprintf(body, IRC_MSG_MAXLEN-len, "+l %u", limit);
+ }
+ else
+ {
+ g_debug("%s: %u", G_STRFUNC, __LINE__);
+ }
+ }
+ else
+ {
+ g_snprintf(body, IRC_MSG_MAXLEN-len, "-l");
+ }
+ }
+ else if (prop_id == TP_PROPERTY_PASSWORD_REQUIRED)
+ {
+ const gchar *key = g_value_get_string(priv->properties[TP_PROPERTY_PASSWORD].value);
+
+ if (g_value_get_boolean(prop_val))
+ {
+ if (key != NULL)
+ {
+ g_snprintf(body, IRC_MSG_MAXLEN-len, "+k %s", key);
+ }
+ else
+ {
+ g_debug("%s: %u", G_STRFUNC, __LINE__);
+ }
+ }
+ else
+ {
+ g_snprintf(body, IRC_MSG_MAXLEN-len, "-k");
+ }
+ }
+ else
+ {
+ g_debug("%s: %u", G_STRFUNC, __LINE__);
+ }
+
+ _idle_connection_send(priv->connection, cmd);
+
+#if 0
+ if (prop_id == TP_PROPERTY_LIMIT
+ && (prop_arr_has_set(waiting, TP_PROPERTY_LIMITED)
+ || (priv->mode_state.flags & MODE_FLAG_USER_LIMIT)))
+ {
+ guint limit = g_value_get_uint(prop_val);
+
+ g_snprintf(body, IRC_MSG_MAXLEN-len, "+l %u", limit);
+ }
+ else if (prop_id == TP_PROPERTY_PASSWORD
+ && (prop_arr_has_set(waiting, TP_PROPERTY_PASSWORD_REQUIRED)
+ || priv->mode_state.flags & MODE_FLAG_KEY))
+ {
+ const gchar *key = g_value_get_string(prop_val);
+
+ g_snprintf(body, IRC_MSG_MAXLEN-len, "+k %s", key);
+ }
+ else
+ {
+ if (prop_id == TP_PROPERTY_LIMITED && !g_value_get_boolean(prop_val))
+ {
+ g_snprintf(body, IRC_MSG_MAXLEN-len, "-l");
+ }
+ else if (prop_id == TP_PROPERTY_PASSWORD_REQUIRED && !g_value_get_boolean(prop_val))
+ {
+ g_snprintf(body, IRC_MSG_MAXLEN-len, "-k");
+ }
+ else
+ {
+ g_debug("%s: did not do anything with %u", G_STRFUNC, prop_id);
+ continue;
+ }
+ }
+#endif
+ }
+
+ g_ptr_array_free(waiting, TRUE);
+}
+
+/**
+ * idle_muc_channel_set_properties
+ *
+ * Implements DBus method SetProperties
+ * on interface org.freedesktop.Telepathy.Properties
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occured, DBus will throw the error only if this
+ * function returns false.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+gboolean idle_muc_channel_set_properties (IdleMUCChannel *obj, const GPtrArray * properties, GError **error)
+{
+ IdleMUCChannelPrivate *priv;
+ GPtrArray *to_change;
+ int i;
+
+ g_assert(obj != NULL);
+ g_assert(IDLE_IS_MUC_CHANNEL(obj));
+
+ priv = IDLE_MUC_CHANNEL_GET_PRIVATE(obj);
+
+ to_change = g_ptr_array_new();
+
+ for (i=0; i<properties->len; i++)
+ {
+ GValue prop = {0, };
+ IdleMUCChannelTPProperty prop_id;
+ GValue *prop_val;
+
+ g_value_init(&prop, TP_TYPE_PROPERTY_VALUE_STRUCT);
+ g_value_set_static_boxed(&prop, g_ptr_array_index(properties, i));
+
+ dbus_g_type_struct_get(&prop,
+ 0, &prop_id,
+ 1, &prop_val,
+ G_MAXUINT);
+
+ if (prop_id >= LAST_TP_PROPERTY_ENUM)
+ {
+ g_debug("%s: invalid property id %u", G_STRFUNC, prop_id);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "invalid property id %u", prop_id);
+
+ return FALSE;
+ }
+
+ if ((priv->properties[prop_id].flags & TP_PROPERTY_FLAG_WRITE) == 0)
+ {
+ g_debug("%s: not allowed to set property with id %u", G_STRFUNC, prop_id);
+
+ *error = g_error_new(TELEPATHY_ERRORS, PermissionDenied, "not allowed to set property with id %u", prop_id);
+
+ return FALSE;
+ }
+
+ if (!g_value_type_compatible(G_VALUE_TYPE(prop_val), property_signatures[prop_id].type))
+ {
+ g_debug("%s: incompatible value type %s for prop_id %u", G_STRFUNC, g_type_name(G_VALUE_TYPE(prop_val)), prop_id);
+
+ *error = g_error_new(TELEPATHY_ERRORS, InvalidArgument, "incompatible value type %s for prop_id %u", g_type_name(G_VALUE_TYPE(prop_val)), prop_id);
+
+ return FALSE;
+ }
+
+ if (!g_value_compare(prop_val, priv->properties[prop_id].value))
+ {
+ g_ptr_array_add(to_change, g_value_get_boxed(&prop));
+ }
+ }
+
+ send_properties_request(obj, to_change);
+
+ g_ptr_array_free(to_change, TRUE);
+
+ return TRUE;
+}
+
diff --git a/src/idle-muc-channel.h b/src/idle-muc-channel.h
new file mode 100644
index 0000000..b106d08
--- /dev/null
+++ b/src/idle-muc-channel.h
@@ -0,0 +1,114 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_MUC_CHANNEL_H__
+#define __IDLE_MUC_CHANNEL_H__
+
+#include <glib-object.h>
+
+#include "idle-handles.h"
+#include "telepathy-constants.h"
+
+G_BEGIN_DECLS
+
+typedef struct _IdleMUCChannel IdleMUCChannel;
+typedef struct _IdleMUCChannelClass IdleMUCChannelClass;
+
+struct _IdleMUCChannelClass {
+ GObjectClass parent_class;
+};
+
+struct _IdleMUCChannel {
+ GObject parent;
+};
+
+typedef enum
+{
+ MUC_CHANNEL_JOIN_ERROR_NONE,
+ MUC_CHANNEL_JOIN_ERROR_BANNED,
+ MUC_CHANNEL_JOIN_ERROR_INVITE_ONLY,
+ MUC_CHANNEL_JOIN_ERROR_FULL
+} IdleMUCChannelJoinError;
+
+GType idle_muc_channel_get_type(void);
+
+/* TYPE MACROS */
+#define IDLE_TYPE_MUC_CHANNEL \
+ (idle_muc_channel_get_type())
+#define IDLE_MUC_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_MUC_CHANNEL, IdleMUCChannel))
+#define IDLE_MUC_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_MUC_CHANNEL, IdleMUCChannelClass))
+#define IDLE_IS_MUC_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_MUC_CHANNEL))
+#define IDLE_IS_MUC_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_MUC_CHANNEL))
+#define IDLE_MUC_CHANNEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_MUC_CHANNEL, IdleMUCChannelClass))
+
+gboolean idle_muc_channel_acknowledge_pending_messages (IdleMUCChannel *obj, const GArray *ids, GError **error);
+gboolean idle_muc_channel_add_members (IdleMUCChannel *obj, const GArray * contacts, const gchar * message, GError **error);
+gboolean idle_muc_channel_close (IdleMUCChannel *obj, GError **error);
+gboolean idle_muc_channel_get_all_members (IdleMUCChannel *obj, GArray ** ret, GArray ** ret1, GArray ** ret2, GError **error);
+gboolean idle_muc_channel_get_channel_type (IdleMUCChannel *obj, gchar ** ret, GError **error);
+gboolean idle_muc_channel_get_group_flags (IdleMUCChannel *obj, guint* ret, GError **error);
+gboolean idle_muc_channel_get_handle (IdleMUCChannel *obj, guint* ret, guint* ret1, GError **error);
+gboolean idle_muc_channel_get_handle_owners (IdleMUCChannel *obj, const GArray * handles, GArray ** ret, GError **error);
+gboolean idle_muc_channel_get_interfaces (IdleMUCChannel *obj, gchar *** ret, GError **error);
+gboolean idle_muc_channel_get_local_pending_members (IdleMUCChannel *obj, GArray ** ret, GError **error);
+gboolean idle_muc_channel_get_members (IdleMUCChannel *obj, GArray ** ret, GError **error);
+gboolean idle_muc_channel_get_message_types (IdleMUCChannel *obj, GArray ** ret, GError **error);
+gboolean idle_muc_channel_get_password_flags (IdleMUCChannel *obj, guint* ret, GError **error);
+gboolean idle_muc_channel_get_properties (IdleMUCChannel *obj, const GArray * properties, GPtrArray ** ret, GError **error);
+gboolean idle_muc_channel_get_remote_pending_members (IdleMUCChannel *obj, GArray ** ret, GError **error);
+gboolean idle_muc_channel_get_self_handle (IdleMUCChannel *obj, guint* ret, GError **error);
+gboolean idle_muc_channel_list_pending_messages (IdleMUCChannel *obj, gboolean clear, GPtrArray ** ret, GError **error);
+gboolean idle_muc_channel_list_properties (IdleMUCChannel *obj, GPtrArray ** ret, GError **error);
+gboolean idle_muc_channel_provide_password (IdleMUCChannel *obj, const gchar * password, DBusGMethodInvocation *ctx);
+gboolean idle_muc_channel_remove_members (IdleMUCChannel *obj, const GArray * contacts, const gchar * message, GError **error);
+gboolean idle_muc_channel_send (IdleMUCChannel *obj, guint type, const gchar * text, GError **error);
+gboolean idle_muc_channel_set_properties (IdleMUCChannel *obj, const GPtrArray * properties, GError **error);
+
+void _idle_muc_channel_join_attempt(IdleMUCChannel *chan);
+
+gboolean _idle_muc_channel_receive(IdleMUCChannel *chan, TpChannelTextMessageType type, IdleHandle sender, const gchar *msg);
+void _idle_muc_channel_rename(IdleMUCChannel *chan, guint old, guint _new);
+
+void _idle_muc_channel_join(IdleMUCChannel *chan, const gchar *nick);
+void _idle_muc_channel_part(IdleMUCChannel *chan, const gchar *nick);
+void _idle_muc_channel_kick(IdleMUCChannel *chan, const gchar *nick, const gchar *kicker, TpChannelGroupChangeReason reason);
+void _idle_muc_channel_handle_quit(IdleMUCChannel *chan, IdleHandle handle, gboolean suppress, IdleHandle actor, TpChannelGroupChangeReason reason);
+void _idle_muc_channel_invited(IdleMUCChannel *chan, IdleHandle inviter);
+
+void _idle_muc_channel_names(IdleMUCChannel *chan, GArray *nicks);
+void _idle_muc_channel_mode(IdleMUCChannel *chan, const gchar *params);
+void _idle_muc_channel_topic(IdleMUCChannel *chan, const gchar *topic);
+void _idle_muc_channel_topic_touch(IdleMUCChannel *chan, const IdleHandle handle, const guint timestamp);
+void _idle_muc_channel_topic_full(IdleMUCChannel *chan, const IdleHandle handle, const guint timestamp, const gchar *topic);
+void _idle_muc_channel_topic_unset(IdleMUCChannel *chan);
+
+void _idle_muc_channel_badchannelkey(IdleMUCChannel *chan);
+void _idle_muc_channel_join_error(IdleMUCChannel *chan, IdleMUCChannelJoinError err);
+
+gboolean _idle_muc_channel_has_current_member(IdleMUCChannel *chan, IdleHandle handle);
+
+G_END_DECLS
+
+#endif /* #ifndef __IDLE_MUC_CHANNEL_H__*/
diff --git a/src/idle-server-connection-iface-signals-marshal.list b/src/idle-server-connection-iface-signals-marshal.list
new file mode 100644
index 0000000..6cb7431
--- /dev/null
+++ b/src/idle-server-connection-iface-signals-marshal.list
@@ -0,0 +1 @@
+VOID:UINT,UINT
diff --git a/src/idle-server-connection-iface.c b/src/idle-server-connection-iface.c
new file mode 100644
index 0000000..8895b02
--- /dev/null
+++ b/src/idle-server-connection-iface.c
@@ -0,0 +1,98 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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-object.h>
+
+#include "idle-server-connection-iface.h"
+
+#include "idle-server-connection-iface-signals-marshal.h"
+
+static void idle_server_connection_iface_base_init(gpointer klass)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ initialized = TRUE;
+
+ g_signal_new("status-changed",
+ G_OBJECT_CLASS_TYPE(klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ idle_server_connection_iface_marshal_VOID__UINT_UINT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+ g_signal_new("received",
+ G_OBJECT_CLASS_TYPE(klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+ }
+}
+
+GType idle_server_connection_iface_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0)
+ {
+ static const GTypeInfo info =
+ {
+ sizeof (IdleServerConnectionIfaceClass),
+ idle_server_connection_iface_base_init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ NULL,
+ NULL
+ };
+
+ type = g_type_register_static(G_TYPE_INTERFACE, "IdleServerConnectionIface", &info, 0);
+ }
+
+ return type;
+}
+
+gboolean idle_server_connection_iface_connect(IdleServerConnectionIface *iface, GError **error)
+{
+ return IDLE_SERVER_CONNECTION_IFACE_GET_CLASS(iface)->connect(iface, error);
+}
+
+gboolean idle_server_connection_iface_disconnect(IdleServerConnectionIface *iface, GError **error)
+{
+ return IDLE_SERVER_CONNECTION_IFACE_GET_CLASS(iface)->disconnect(iface, error);
+}
+
+gboolean idle_server_connection_iface_send(IdleServerConnectionIface *iface, const gchar *cmd, GError **error)
+{
+ return IDLE_SERVER_CONNECTION_IFACE_GET_CLASS(iface)->send(iface, cmd, error);
+}
+
+IdleServerConnectionState idle_server_connection_get_state(IdleServerConnectionIface *iface)
+{
+ return IDLE_SERVER_CONNECTION_IFACE_GET_CLASS(iface)->get_state(iface);
+}
+
diff --git a/src/idle-server-connection-iface.h b/src/idle-server-connection-iface.h
new file mode 100644
index 0000000..c55a11f
--- /dev/null
+++ b/src/idle-server-connection-iface.h
@@ -0,0 +1,87 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_SERVER_CONNECTION_IFACE_H__
+#define __IDLE_SERVER_CONNECTION_IFACE_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDLE_TYPE_SERVER_CONNECTION_IFACE idle_server_connection_iface_get_type()
+
+#define IDLE_SERVER_CONNECTION_IFACE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+ IDLE_TYPE_SERVER_CONNECTION_IFACE, IdleServerConnectionIface))
+
+#define IDLE_SERVER_CONNECTION_IFACE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((obj), \
+ IDLE_TYPE_SERVER_CONNECTION_IFACE, IdleServerConnectionIfaceClass))
+
+#define IDLE_IS_SERVER_CONNECTION_IFACE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_SERVER_CONNECTION_IFACE))
+
+#define IDLE_IS_SERVER_CONNECTION_IFACE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_SERVER_CONNECTION_IFACE))
+
+#define IDLE_SERVER_CONNECTION_IFACE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_INTERFACE((obj), \
+ IDLE_TYPE_SERVER_CONNECTION_IFACE, IdleServerConnectionIfaceClass))
+
+typedef struct _IdleServerConnectionIface IdleServerConnectionIface;
+typedef struct _IdleServerConnectionIfaceClass IdleServerConnectionIfaceClass;
+
+typedef enum
+{
+ SERVER_CONNECTION_STATE_NOT_CONNECTED,
+ SERVER_CONNECTION_STATE_CONNECTING,
+ SERVER_CONNECTION_STATE_CONNECTED,
+} IdleServerConnectionState;
+
+typedef enum
+{
+ SERVER_CONNECTION_STATE_REASON_ERROR,
+ SERVER_CONNECTION_STATE_REASON_REQUESTED
+} IdleServerConnectionStateReason;
+
+struct _IdleServerConnectionIfaceClass
+{
+ GTypeInterface parent_class;
+
+ gboolean (*connect) (IdleServerConnectionIface *, GError **error);
+ gboolean (*disconnect) (IdleServerConnectionIface *, GError **error);
+
+ gboolean (*send) (IdleServerConnectionIface *, const gchar *cmd, GError **error);
+
+ IdleServerConnectionState (*get_state) (IdleServerConnectionIface *);
+};
+
+GType idle_server_connection_iface_get_type(void);
+
+gboolean idle_server_connection_iface_connect(IdleServerConnectionIface *, GError **error);
+gboolean idle_server_connection_iface_disconnect(IdleServerConnectionIface *, GError **error);
+
+gboolean idle_server_connection_iface_send(IdleServerConnectionIface *, const gchar *cmd, GError **error);
+
+IdleServerConnectionState idle_server_connection_iface_get_state(IdleServerConnectionIface *);
+
+G_END_DECLS
+
+#endif
diff --git a/src/idle-server-connection-util.c b/src/idle-server-connection-util.c
new file mode 100644
index 0000000..71b7b3c
--- /dev/null
+++ b/src/idle-server-connection-util.c
@@ -0,0 +1,104 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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>
+
+#define __USE_GNU
+#include <string.h>
+
+#include "idle-server-connection-util.h"
+
+void msg_split(gchar *msg, gchar *split_buf, gsize max_len, message_cb cb, gpointer user_data)
+{
+ int i;
+ int lasti = 0;
+ gchar *tmp;
+ gboolean line_ends = FALSE;
+ guint len;
+
+ g_assert(msg != NULL);
+ g_assert(split_buf != NULL);
+ g_assert(cb != NULL);
+
+ len = strnlen(msg, max_len);
+
+ for (i = 0; i < len; i++)
+ {
+ if ((msg[i] == '\n' || msg[i] == '\r'))
+ {
+ msg[i] = '\0';
+
+ if (i>lasti)
+ {
+ if ((lasti == 0) && (split_buf[0] != '\0'))
+ {
+ tmp = g_strconcat(split_buf, msg, NULL);
+ memset(split_buf, '\0', max_len);
+ }
+ else
+ {
+ tmp = g_strdup(msg+lasti);
+ }
+
+ cb(tmp, user_data);
+
+ g_free(tmp);
+ }
+
+ lasti = i+1;
+
+ line_ends = TRUE;
+ }
+ else
+ {
+ line_ends = FALSE;
+ }
+ }
+
+ if (!line_ends)
+ {
+ g_strlcpy(split_buf, msg+lasti, max_len-lasti);
+ }
+ else
+ {
+ memset(split_buf, '\0', max_len);
+ }
+}
+
+void idle_output_pending_msg_free(IdleOutputPendingMsg *msg)
+{
+ g_free(msg->message);
+ g_slice_free(IdleOutputPendingMsg, msg);
+}
+
+gint pending_msg_compare(gconstpointer a, gconstpointer b, gpointer unused)
+{
+ const IdleOutputPendingMsg *msg1 = a, *msg2 = b;
+
+ if (msg1->priority < msg2->priority)
+ {
+ return 1;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
diff --git a/src/idle-server-connection-util.h b/src/idle-server-connection-util.h
new file mode 100644
index 0000000..22930b9
--- /dev/null
+++ b/src/idle-server-connection-util.h
@@ -0,0 +1,62 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_SERVER_CONNECTION_UTIL_H__
+#define __IDLE_SERVER_CONNECTION_UTIL_H__
+
+typedef void (*message_cb)(const gchar *msg, gpointer user_data);
+
+void msg_split(gchar *msg, gchar *splitbuf, gsize max_len, message_cb cb, gpointer user_data);
+
+typedef struct _IdleOutputPendingMsg IdleOutputPendingMsg;
+
+struct _IdleOutputPendingMsg
+{
+ gchar *message;
+ guint priority;
+};
+
+#define idle_output_pending_msg_new() \
+ (g_slice_new(IdleOutputPendingMsg))
+#define idle_output_pending_msg_new0() \
+ (g_slice_new0(IdleOutputPendingMsg))
+
+void idle_output_pending_msg_free(IdleOutputPendingMsg *msg);
+
+#define SERVER_CMD_MIN_PRIORITY 0
+#define SERVER_CMD_NORMAL_PRIORITY G_MAXUINT/2
+#define SERVER_CMD_MAX_PRIORITY G_MAXUINT
+
+gint pending_msg_compare(gconstpointer a, gconstpointer b, gpointer unused);
+
+#define IRC_MSG_MAXLEN 510
+
+/* From RFC 2813 :
+ * This in essence means that the client may send one (1) message every
+ * two (2) seconds without being adversely affected. Services MAY also
+ * be subject to this mechanism.
+ */
+
+#define MSG_QUEUE_UNLOAD_AT_A_TIME 1
+#define MSG_QUEUE_TIMEOUT 2
+
+#define CONNECTION_TIMEOUT 15000
+
+#endif
diff --git a/src/idle-server-connection.c b/src/idle-server-connection.c
new file mode 100644
index 0000000..63fbcd9
--- /dev/null
+++ b/src/idle-server-connection.c
@@ -0,0 +1,797 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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-object.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <string.h>
+
+#include "idle-server-connection.h"
+#include "idle-server-connection-iface.h"
+#include "idle-server-connection-util.h"
+
+#include "telepathy-errors.h"
+
+#include "idle-dns-resolver.h"
+
+static void idle_server_connection_iface_init(gpointer g_iface, gpointer iface_data);
+typedef struct _IdleServerConnectionPrivate IdleServerConnectionPrivate;
+
+#define IDLE_SERVER_CONNECTION_GET_PRIVATE(conn) (G_TYPE_INSTANCE_GET_PRIVATE((conn), IDLE_TYPE_SERVER_CONNECTION, IdleServerConnectionPrivate))
+
+G_DEFINE_TYPE_WITH_CODE(IdleServerConnection, idle_server_connection, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE(IDLE_TYPE_SERVER_CONNECTION_IFACE, idle_server_connection_iface_init));
+
+enum
+{
+ PROP_HOST = 1,
+ PROP_PORT
+};
+
+struct _AsyncConnectData
+{
+ guint watch_id;
+
+ guint fd;
+ GIOChannel *io_chan;
+
+ IdleDNSResult *res;
+ IdleDNSResult *cur;
+};
+
+#define async_connect_data_new() \
+ (g_slice_new(struct _AsyncConnectData))
+#define async_connect_data_new0() \
+ (g_slice_new0(struct _AsyncConnectData))
+
+static void async_connect_data_destroy(struct _AsyncConnectData *data)
+{
+ if (data->watch_id)
+ {
+ g_source_remove(data->watch_id);
+ data->watch_id = 0;
+ }
+
+ if (data->fd)
+ {
+ close(data->fd);
+ data->fd = 0;
+ }
+
+ if (data->io_chan)
+ {
+ g_io_channel_shutdown(data->io_chan, FALSE, NULL);
+ g_io_channel_unref(data->io_chan);
+ data->io_chan = NULL;
+ }
+
+ if (data->res)
+ {
+ idle_dns_result_destroy(data->res);
+ data->res = NULL;
+ }
+
+ g_slice_free(struct _AsyncConnectData, data);
+}
+
+struct _IdleServerConnectionPrivate
+{
+ gchar *host;
+ guint port;
+
+ GIOChannel *io_chan;
+ IdleServerConnectionState state;
+
+ guint read_watch_id;
+
+ gboolean dispose_has_run;
+
+ IdleDNSResolver *resolver;
+ struct _AsyncConnectData *connect_data;
+};
+
+static GObject *idle_server_connection_constructor(GType type, guint n_props, GObjectConstructParam *props);
+
+static void idle_server_connection_init(IdleServerConnection *conn)
+{
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ priv->host = NULL;
+ priv->port = 0;
+
+ priv->io_chan = NULL;
+ priv->state = SERVER_CONNECTION_STATE_NOT_CONNECTED;
+ priv->read_watch_id = 0;
+ priv->connect_data = NULL;
+ priv->resolver = idle_dns_resolver_new();
+
+ priv->dispose_has_run = FALSE;
+}
+
+static GObject *idle_server_connection_constructor(GType type, guint n_props, GObjectConstructParam *props)
+{
+ GObject *ret;
+
+ ret = G_OBJECT_CLASS(idle_server_connection_parent_class)->constructor(type, n_props, props);
+
+ return ret;
+}
+
+static void idle_server_connection_dispose(GObject *obj)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(obj);
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ if (priv->dispose_has_run)
+ {
+ return;
+ }
+
+ g_debug("%s: dispose called", G_STRFUNC);
+ priv->dispose_has_run = TRUE;
+
+ if (priv->state == SERVER_CONNECTION_STATE_CONNECTED)
+ {
+ GError *error = NULL;
+ g_warning("%s: connection was open when the object was deleted, it'll probably crash now...", G_STRFUNC);
+ if (!idle_server_connection_iface_disconnect(IDLE_SERVER_CONNECTION_IFACE(obj), &error))
+ {
+ g_error_free(error);
+ }
+ }
+
+ if (priv->connect_data != NULL)
+ {
+ async_connect_data_destroy(priv->connect_data);
+ priv->connect_data = NULL;
+ }
+
+ if (priv->resolver != NULL)
+ {
+ idle_dns_resolver_destroy(priv->resolver);
+ priv->resolver = NULL;
+ }
+}
+
+static void idle_server_connection_finalize(GObject *obj)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(obj);
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ g_free(priv->host);
+}
+
+static void idle_server_connection_get_property(GObject *obj,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(obj);
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ switch (prop_id)
+ {
+ case PROP_HOST:
+ {
+ g_value_set_string(value, priv->host);
+ }
+ break;
+ case PROP_PORT:
+ {
+ g_value_set_uint(value, priv->port);
+ }
+ break;
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+ }
+ break;
+ }
+}
+
+static void idle_server_connection_set_property(GObject *obj,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(obj);
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ switch (prop_id)
+ {
+ case PROP_HOST:
+ {
+ g_free(priv->host);
+ priv->host = g_value_dup_string(value);
+ }
+ break;
+ case PROP_PORT:
+ {
+ priv->port = g_value_get_uint(value);
+ }
+ break;
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+ }
+ break;
+ }
+}
+
+static void idle_server_connection_class_init(IdleServerConnectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private(klass, sizeof(IdleServerConnectionPrivate));
+
+ object_class->constructor = idle_server_connection_constructor;
+ object_class->dispose = idle_server_connection_dispose;
+ object_class->finalize = idle_server_connection_finalize;
+
+ object_class->get_property = idle_server_connection_get_property;
+ object_class->set_property = idle_server_connection_set_property;
+
+ pspec = g_param_spec_string("host", "Remote host",
+ "Hostname of the remote service to connect to.",
+ NULL,
+ G_PARAM_READABLE|
+ G_PARAM_WRITABLE|
+ G_PARAM_STATIC_NICK|
+ G_PARAM_STATIC_BLURB);
+
+ g_object_class_install_property(object_class, PROP_HOST, pspec);
+
+ pspec = g_param_spec_uint("port", "Remote port",
+ "Port number of the remote service to connect to.",
+ 0, 0xffff, 0,
+ G_PARAM_READABLE|
+ G_PARAM_WRITABLE|
+ G_PARAM_STATIC_NICK|
+ G_PARAM_STATIC_BLURB);
+
+ g_object_class_install_property(object_class, PROP_PORT, pspec);
+}
+
+static void change_state(IdleServerConnection *conn, IdleServerConnectionState state, guint reason)
+{
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ if (state == priv->state)
+ {
+ return;
+ }
+
+ g_debug("%s: emitting status-changed, state %u, reason %u", G_STRFUNC, state, reason);
+
+ priv->state = state;
+ g_signal_emit_by_name(conn, "status-changed", state, reason);
+}
+
+static gboolean iface_disconnect_impl_full(IdleServerConnectionIface *iface, GError **error, guint reason);
+
+static gboolean io_err_cleanup_func(gpointer data)
+{
+ GError *error = NULL;
+
+ if (!iface_disconnect_impl_full(IDLE_SERVER_CONNECTION_IFACE(data), &error, SERVER_CONNECTION_STATE_REASON_ERROR))
+ {
+ g_debug("%s: disconnect: %s", G_STRFUNC, error->message);
+ g_error_free(error);
+ }
+
+ return FALSE;
+}
+
+static gboolean io_func(GIOChannel *src, GIOCondition cond, gpointer data)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(data);
+ gchar buf[IRC_MSG_MAXLEN+3];
+ GIOStatus status;
+ gsize len;
+ GError *error = NULL;
+
+ if (cond & (G_IO_ERR|G_IO_HUP))
+ {
+ g_debug("%s: got G_IO_ERR|G_IO_HUP", G_STRFUNC);
+ g_idle_add(io_err_cleanup_func, data);
+ return FALSE;
+ }
+
+ memset(buf, 0, IRC_MSG_MAXLEN+3);
+ status = g_io_channel_read_chars(src, buf, IRC_MSG_MAXLEN+2, &len, &error);
+
+ if ((status != G_IO_STATUS_NORMAL) && (status != G_IO_STATUS_AGAIN))
+ {
+ g_debug("%s: status: %u, error: %s", G_STRFUNC, status, (error != NULL) ? (error->message != NULL) ? error->message : "(null)" : "(null)");
+
+ if (error)
+ {
+ g_error_free(error);
+ }
+
+ g_idle_add(io_err_cleanup_func, data);
+ return FALSE;
+ }
+
+ g_signal_emit_by_name(conn, "received", buf);
+
+ return TRUE;
+}
+
+static gboolean do_connect(IdleServerConnection *conn);
+
+static gboolean iface_connect_impl(IdleServerConnectionIface *iface, GError **error)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(iface);
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ if (priv->state != SERVER_CONNECTION_STATE_NOT_CONNECTED)
+ {
+ g_debug("%s: already connecting or connected!", G_STRFUNC);
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "already connecting or connected!");
+ return FALSE;
+ }
+
+ if ((priv->host == NULL) || (priv->host[0] == '\0'))
+ {
+ g_debug("%s: host not set!", G_STRFUNC);
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "host not set!");
+ return FALSE;
+ }
+
+ if (priv->port == 0)
+ {
+ g_debug("%s: port not set!", G_STRFUNC);
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "port not set!");
+ return FALSE;
+ }
+
+ if (!do_connect(conn))
+ {
+ g_debug("%s: do_connect failed", G_STRFUNC);
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "failed to connect");
+ return FALSE;
+ }
+
+ change_state(conn, SERVER_CONNECTION_STATE_CONNECTING, SERVER_CONNECTION_STATE_REASON_REQUESTED);
+
+ return TRUE;
+}
+
+static gboolean connect_io_func(GIOChannel *src, GIOCondition cond, gpointer data)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(data);
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+ int rc;
+ int fd = -1;
+ int optval;
+ socklen_t optlen = sizeof(optval);
+ GIOChannel *io_chan;
+ struct _AsyncConnectData *connect_data = priv->connect_data;
+
+ IdleDNSResult *cur = connect_data->cur;
+ IdleDNSResult *next = cur->ai_next;
+
+ g_assert(getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == 0);
+
+ if (optval == 0)
+ {
+ int opt;
+
+ g_debug("%s: connected!", G_STRFUNC);
+
+ fd = connect_data->fd;
+
+ change_state(conn, SERVER_CONNECTION_STATE_CONNECTED, SERVER_CONNECTION_STATE_REASON_REQUESTED);
+
+ g_assert(priv->io_chan == NULL);
+ g_assert(priv->read_watch_id == 0);
+
+ priv->read_watch_id = g_io_add_watch(connect_data->io_chan, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP, io_func, data);
+ priv->io_chan = connect_data->io_chan;
+
+ connect_data->io_chan = NULL;
+ connect_data->fd = 0;
+
+ async_connect_data_destroy(priv->connect_data);
+ priv->connect_data = NULL;
+
+ opt = 1;
+
+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0)
+ {
+ g_warning("%s: failed to set TCP_NODELAY", G_STRFUNC);
+ }
+
+ opt = 1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt)) < 0)
+ {
+ g_warning("%s: failed to set SO_KEEPALIVE", G_STRFUNC);
+ }
+
+ return FALSE;
+ }
+
+ g_debug("%s: connection failed", G_STRFUNC);
+
+ if (next == NULL)
+ {
+ g_debug("%s: and this was the last address we can try, tough luck.", G_STRFUNC);
+ change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+
+ return FALSE;
+ }
+
+ if ((next->ai_family == cur->ai_family) &&
+ (next->ai_socktype == cur->ai_socktype) &&
+ (next->ai_protocol == cur->ai_protocol))
+ {
+ int i;
+
+ g_debug("%s: re-using existing socket for trying again", G_STRFUNC);
+
+ connect(connect_data->fd, next->ai_addr, next->ai_addrlen);
+
+ for (i=0; i<5 && errno == ECONNABORTED; i++)
+ {
+ g_debug("%s: got ECONNABORTED for (%i+1)th time", G_STRFUNC, i);
+ connect(connect_data->fd, next->ai_addr, next->ai_addrlen);
+ }
+
+ if (errno != EINPROGRESS)
+ {
+ g_debug("%s: connect() failed: %s", G_STRFUNC, g_strerror(errno));
+ change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+ return FALSE;
+ }
+
+ connect_data->cur = next;
+
+ return TRUE;
+ }
+
+ g_debug("%s: we'll have to create a new socket since the address family/socket type/protocol is different", G_STRFUNC);
+
+ for (cur = next; cur != NULL; cur = cur->ai_next)
+ {
+ fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
+
+ if (fd == -1)
+ {
+ if ((errno == EINVAL) || (errno == EAFNOSUPPORT))
+ {
+ continue;
+ }
+
+ g_debug("%s: socket() failed: %s", G_STRFUNC, g_strerror(errno));
+ return FALSE;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (fd == -1)
+ {
+ g_debug("%s: could not socket(): %s", G_STRFUNC, g_strerror(errno));
+ change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+ return FALSE;
+ }
+
+ io_chan = g_io_channel_unix_new(fd);
+ g_io_channel_set_encoding(io_chan, NULL, NULL);
+ g_io_channel_set_buffered(io_chan, FALSE);
+
+ rc = fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ if (rc != 0)
+ {
+ g_debug("%s: failed to set socket to non-blocking mode: %s", G_STRFUNC, g_strerror(errno));
+ g_io_channel_shutdown(io_chan, FALSE, NULL);
+ g_io_channel_unref(io_chan);
+ close(fd);
+ change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+ return FALSE;
+ }
+
+ rc = connect(fd, cur->ai_addr, cur->ai_addrlen);
+
+ g_assert(rc == -1);
+
+ if (errno != EINPROGRESS)
+ {
+ g_debug("%s: initial connect() failed: %s", G_STRFUNC, g_strerror(errno));
+ g_io_channel_shutdown(io_chan, FALSE, NULL);
+ g_io_channel_unref(io_chan);
+ close(fd);
+ change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+ return FALSE;
+ }
+
+ g_io_channel_shutdown(connect_data->io_chan, FALSE, NULL);
+ g_io_channel_unref(connect_data->io_chan);
+ connect_data->io_chan = io_chan;
+
+ close(connect_data->fd);
+ connect_data->fd = fd;
+
+ connect_data->watch_id = g_io_add_watch(io_chan, G_IO_OUT|G_IO_ERR, connect_io_func, conn);
+
+ return FALSE;
+}
+
+static void dns_result_callback(guint unused, IdleDNSResult *results, gpointer user_data)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(user_data);
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+ IdleDNSResult *cur;
+ int fd = -1;
+ int rc = -1;
+ GIOChannel *io_chan;
+
+ if (!results)
+ {
+ g_debug("%s: no DNS results received", G_STRFUNC);
+ change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+ return;
+ }
+
+ for (cur = results; cur != NULL; cur = cur->ai_next)
+ {
+ fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
+
+ if (fd == -1)
+ {
+ if ((errno == EINVAL) || (errno = EAFNOSUPPORT))
+ {
+ continue;
+ }
+
+ g_debug("%s: socket() failed: %s", G_STRFUNC, g_strerror(errno));
+ return;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (fd == -1)
+ {
+ g_debug("%s: failed: %s", G_STRFUNC, g_strerror(errno));
+ change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+ return;
+ }
+
+ rc = fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ if (rc != 0)
+ {
+ g_debug("%s: failed to set socket to non-blocking mode: %s", G_STRFUNC, g_strerror(errno));
+ close(fd);
+ change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+ return;
+ }
+
+ rc = connect(fd, cur->ai_addr, cur->ai_addrlen);
+
+ g_assert(rc == -1);
+
+ if (errno != EINPROGRESS)
+ {
+ g_debug("%s: initial connect() failed: %s", G_STRFUNC, g_strerror(errno));
+ close(fd);
+ change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+ return;
+ }
+
+ if (priv->connect_data != NULL)
+ {
+ async_connect_data_destroy(priv->connect_data);
+ }
+
+ priv->connect_data = async_connect_data_new();
+
+ io_chan = g_io_channel_unix_new(fd);
+ g_io_channel_set_encoding(io_chan, NULL, NULL);
+ g_io_channel_set_buffered(io_chan, FALSE);
+
+ priv->connect_data->fd = fd;
+ priv->connect_data->io_chan = io_chan;
+ priv->connect_data->res = results;
+ priv->connect_data->cur = cur;
+ priv->connect_data->watch_id = g_io_add_watch(io_chan, G_IO_OUT|G_IO_ERR, connect_io_func, conn);
+}
+
+static gboolean do_connect(IdleServerConnection *conn)
+{
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ idle_dns_resolver_query(priv->resolver, priv->host, priv->port, dns_result_callback, conn);
+
+ return TRUE;
+}
+
+static gboolean iface_disconnect_impl(IdleServerConnectionIface *iface, GError **error)
+{
+ return iface_disconnect_impl_full(iface, error, SERVER_CONNECTION_STATE_REASON_REQUESTED);
+}
+
+static gboolean iface_disconnect_impl_full(IdleServerConnectionIface *iface, GError **error, guint reason)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(iface);
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+ GError *io_error = NULL;
+ int fd;
+ GIOStatus status;
+
+ g_assert(priv != NULL);
+
+ if (priv->state != SERVER_CONNECTION_STATE_CONNECTED)
+ {
+ g_debug("%s: the connection was not open", G_STRFUNC);
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "the connection was not open");
+ return FALSE;
+ }
+
+ if (priv->read_watch_id != 0)
+ {
+ g_source_remove(priv->read_watch_id);
+ priv->read_watch_id = 0;
+ }
+
+ if (priv->io_chan != NULL)
+ {
+ fd = g_io_channel_unix_get_fd(priv->io_chan);
+ status = g_io_channel_shutdown(priv->io_chan, TRUE, &io_error);
+
+ if (status != G_IO_STATUS_NORMAL && io_error)
+ {
+ g_debug("%s: g_io_channel_shutdown failed: %s", G_STRFUNC, io_error->message);
+ g_error_free(io_error);
+ }
+
+ if (close(fd))
+ {
+ g_debug("%s: unable to close fd: %s", G_STRFUNC, g_strerror(errno));
+ }
+
+ g_io_channel_unref(priv->io_chan);
+ priv->io_chan = NULL;
+ }
+
+ change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, reason);
+
+ return TRUE;
+}
+
+static gboolean iface_send_impl(IdleServerConnectionIface *iface, const gchar *cmd, GError **error)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(iface);
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+ GIOStatus status;
+ gsize written;
+ GError *local_error = NULL;
+
+ if (priv->state != SERVER_CONNECTION_STATE_CONNECTED)
+ {
+ g_debug("%s: connection was not open!", G_STRFUNC);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "connection was not open!");
+
+ return FALSE;
+ }
+
+ status = g_io_channel_write_chars(priv->io_chan,
+ cmd,
+ -1,
+ &written,
+ &local_error);
+
+ if (local_error)
+ {
+ g_debug("%s: error: %s", G_STRFUNC, local_error->message);
+ g_error_free(local_error);
+ }
+
+ switch (status)
+ {
+ case G_IO_STATUS_ERROR:
+ {
+ g_debug("%s: got G_IO_STATUS_ERROR", G_STRFUNC);
+
+ if (iface_disconnect_impl_full(IDLE_SERVER_CONNECTION_IFACE(conn), &local_error, SERVER_CONNECTION_STATE_REASON_ERROR))
+ {
+ g_error_free(local_error);
+ }
+
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "got G_IO_STATUS_ERROR");
+
+ return FALSE;
+ }
+ break;
+ case G_IO_STATUS_NORMAL:
+ {
+ g_debug("%s: sent \"%s\"", G_STRFUNC, cmd);
+
+ return TRUE;
+ }
+ break;
+ case G_IO_STATUS_EOF:
+ {
+ g_debug("%s: got G_IO_STATUS_EOF", G_STRFUNC);
+
+ if (iface_disconnect_impl_full(IDLE_SERVER_CONNECTION_IFACE(conn), &local_error, SERVER_CONNECTION_STATE_REASON_ERROR))
+ {
+ g_error_free(local_error);
+ }
+
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "got G_IO_STATUS_EOF");
+
+ return FALSE;
+ }
+ break;
+ case G_IO_STATUS_AGAIN:
+ {
+ g_debug("%s: got G_IO_STATUS_AGAIN", G_STRFUNC);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "got G_IO_STATUS_AGAIN");
+
+ return FALSE;
+ }
+ break;
+ default:
+ {
+ g_assert_not_reached();
+ return FALSE;
+ }
+ }
+}
+
+static IdleServerConnectionState iface_get_state_impl(IdleServerConnectionIface *iface)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION(iface);
+ IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ return priv->state;
+}
+
+static void idle_server_connection_iface_init(gpointer g_iface, gpointer iface_data)
+{
+ IdleServerConnectionIfaceClass *klass = (IdleServerConnectionIfaceClass *)(g_iface);
+
+ klass->connect = iface_connect_impl;
+ klass->disconnect = iface_disconnect_impl;
+ klass->send = iface_send_impl;
+ klass->get_state = iface_get_state_impl;
+}
diff --git a/src/idle-server-connection.h b/src/idle-server-connection.h
new file mode 100644
index 0000000..b5786f7
--- /dev/null
+++ b/src/idle-server-connection.h
@@ -0,0 +1,63 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_SERVER_CONNECTION_H__
+#define __IDLE_SERVER_CONNECTION_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdleServerConnection IdleServerConnection;
+typedef struct _IdleServerConnectionClass IdleServerConnectionClass;
+
+struct _IdleServerConnection
+{
+ GObject parent;
+};
+
+struct _IdleServerConnectionClass
+{
+ GObjectClass parent;
+};
+
+GType idle_server_connection_get_type(void);
+
+#define IDLE_TYPE_SERVER_CONNECTION \
+ (idle_server_connection_get_type())
+
+#define IDLE_SERVER_CONNECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_SERVER_CONNECTION, IdleServerConnection))
+
+#define IDLE_SERVER_CONNECTION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_SERVER_CONNECTION, IdleServerConnection))
+
+#define IDLE_IS_SERVER_CONNECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_SERVER_CONNECTION))
+
+#define IDLE_IS_SERVER_CONNECTION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_SERVER_CONNECTION))
+
+#define IDLE_SERVER_CONNECTION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), IDLE_TYPE_SERVER_CONNECTION, IdleServerConnectionClass))
+
+G_END_DECLS
+
+#endif
diff --git a/src/idle-ssl-server-connection.c b/src/idle-ssl-server-connection.c
new file mode 100644
index 0000000..a368cc0
--- /dev/null
+++ b/src/idle-ssl-server-connection.c
@@ -0,0 +1,766 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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-object.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <string.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include "idle-ssl-server-connection.h"
+#include "idle-server-connection-iface.h"
+
+#include "idle-server-connection-util.h"
+
+#include "telepathy-errors.h"
+
+#include "idle-dns-resolver.h"
+
+/* From RFC 2813 :
+ * This in essence means that the client may send one (1) message every
+ * two (2) seconds without being adversely affected. Services MAY also
+ * be subject to this mechanism.
+ */
+
+#define SEND_QUEUE_UNLOAD_AT_A_TIME 1
+#define SEND_QUEUE_UNLOAD_DELAY 2
+
+static void idle_ssl_server_connection_iface_init(gpointer g_iface, gpointer iface_data);
+typedef struct _IdleSSLServerConnectionPrivate IdleSSLServerConnectionPrivate;
+
+#define IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((conn), IDLE_TYPE_SSL_SERVER_CONNECTION, \
+ IdleSSLServerConnectionPrivate))
+
+G_DEFINE_TYPE_WITH_CODE(IdleSSLServerConnection, idle_ssl_server_connection, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE(IDLE_TYPE_SERVER_CONNECTION_IFACE, idle_ssl_server_connection_iface_init));
+
+enum
+{
+ PROP_HOST = 1,
+ PROP_PORT,
+};
+
+typedef void (*async_connecting_finished_cb)(IdleSSLServerConnection *conn, gboolean success);
+
+typedef struct _AsyncConnectData AsyncConnectData;
+struct _AsyncConnectData
+{
+ IdleSSLServerConnection *conn;
+
+ guint watch_id;
+ GIOChannel *io_chan;
+ guint fd;
+
+ IdleDNSResult *res;
+ IdleDNSResult *cur;
+
+ async_connecting_finished_cb finished_cb;
+};
+
+#define async_connect_data_new() \
+ (g_slice_new(AsyncConnectData))
+#define async_connect_data_new0() \
+ (g_slice_new0(AsyncConnectData))
+
+static void async_connect_data_destroy(AsyncConnectData *data)
+{
+ if (data->watch_id)
+ {
+ g_source_remove(data->watch_id);
+ data->watch_id = 0;
+ }
+
+ if (data->io_chan)
+ {
+ g_io_channel_shutdown(data->io_chan, FALSE, NULL);
+ g_io_channel_unref(data->io_chan);
+ data->io_chan = NULL;
+ }
+
+ if (data->fd)
+ {
+ close(data->fd);
+ data->fd = 0;
+ }
+
+ if (data->res)
+ {
+ idle_dns_result_destroy(data->res);
+ data->res = NULL;
+ data->cur = NULL;
+ }
+
+ g_slice_free(AsyncConnectData, data);
+}
+
+struct _IdleSSLServerConnectionPrivate
+{
+ gchar *host;
+ guint port;
+
+ GIOChannel *io_chan;
+ int fd;
+ SSL *ssl;
+ BIO *bio;
+
+ guint read_watch_id;
+ guint last_message_sent;
+
+ IdleDNSResolver *resolver;
+ AsyncConnectData *connect_data;
+
+ IdleServerConnectionState state;
+
+ gboolean dispose_has_run;
+};
+
+static GObject *idle_ssl_server_connection_constructor(GType type, guint n_props, GObjectConstructParam *props);
+
+static void idle_ssl_server_connection_init(IdleSSLServerConnection *conn)
+{
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ priv->host = NULL;
+ priv->port = 0;
+
+ priv->io_chan = NULL;
+ priv->fd = 0;
+ priv->ssl = NULL;
+ priv->bio = NULL;
+
+ priv->read_watch_id = 0;
+ priv->last_message_sent = 0;
+
+ priv->resolver = idle_dns_resolver_new();
+ priv->connect_data = NULL;
+
+ priv->state = SERVER_CONNECTION_STATE_NOT_CONNECTED;
+
+ priv->dispose_has_run = FALSE;
+}
+
+static GObject *idle_ssl_server_connection_constructor(GType type, guint n_props, GObjectConstructParam *props)
+{
+ GObject *ret;
+
+ ret = G_OBJECT_CLASS(idle_ssl_server_connection_parent_class)->constructor(type, n_props, props);
+
+ return ret;
+}
+
+static void idle_ssl_server_connection_dispose(GObject *obj)
+{
+ IdleSSLServerConnection *conn = IDLE_SSL_SERVER_CONNECTION(obj);
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ if (priv->dispose_has_run)
+ {
+ return;
+ }
+
+ g_debug("%s: dispose called", G_STRFUNC);
+ priv->dispose_has_run = TRUE;
+
+ if (priv->state == SERVER_CONNECTION_STATE_CONNECTED)
+ {
+ GError *error = NULL;
+ g_warning("%s: connection was open when the object was deleted, it'll probably crash now..", G_STRFUNC);
+
+ if (!idle_server_connection_iface_disconnect(IDLE_SERVER_CONNECTION_IFACE(obj), &error))
+ {
+ g_error_free(error);
+ }
+ }
+
+ if (priv->ssl)
+ {
+ SSL_free(priv->ssl);
+ priv->ssl = NULL;
+ }
+
+ if (priv->bio)
+ {
+ BIO_free(priv->bio);
+ priv->bio = NULL;
+ }
+
+ if (priv->read_watch_id)
+ {
+ g_source_remove(priv->read_watch_id);
+ priv->read_watch_id = 0;
+ }
+}
+
+static void idle_ssl_server_connection_finalize(GObject *obj)
+{
+ IdleSSLServerConnection *conn = IDLE_SSL_SERVER_CONNECTION(obj);
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ g_free(priv->host);
+
+ idle_dns_resolver_destroy(priv->resolver);
+
+ if (priv->connect_data)
+ {
+ async_connect_data_destroy(priv->connect_data);
+ priv->connect_data = NULL;
+ }
+}
+
+static void idle_ssl_server_connection_get_property(GObject *obj,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdleSSLServerConnection *conn = IDLE_SSL_SERVER_CONNECTION(obj);
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ switch (prop_id)
+ {
+ case PROP_HOST:
+ {
+ g_value_set_string(value, priv->host);
+ }
+ break;
+ case PROP_PORT:
+ {
+ g_value_set_uint(value, priv->port);
+ }
+ break;
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+ }
+ break;
+ }
+}
+
+static void idle_ssl_server_connection_set_property(GObject *obj,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdleSSLServerConnection *conn = IDLE_SSL_SERVER_CONNECTION(obj);
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ switch (prop_id)
+ {
+ case PROP_HOST:
+ {
+ g_free(priv->host);
+ priv->host = g_value_dup_string(value);
+ }
+ break;
+ case PROP_PORT:
+ {
+ priv->port = g_value_get_uint(value);
+ }
+ break;
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+ }
+ break;
+ }
+}
+
+static void idle_ssl_server_connection_class_init(IdleSSLServerConnectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private(klass, sizeof(IdleSSLServerConnectionPrivate));
+
+ object_class->constructor = idle_ssl_server_connection_constructor;
+ object_class->dispose = idle_ssl_server_connection_dispose;
+ object_class->finalize = idle_ssl_server_connection_finalize;
+
+ object_class->get_property = idle_ssl_server_connection_get_property;
+ object_class->set_property = idle_ssl_server_connection_set_property;
+
+ pspec = g_param_spec_string("host", "Remote host",
+ "Hostname of the remote service to connect to.",
+ NULL,
+ G_PARAM_READABLE|
+ G_PARAM_WRITABLE|
+ G_PARAM_STATIC_NICK|
+ G_PARAM_STATIC_BLURB);
+
+ g_object_class_install_property(object_class, PROP_HOST, pspec);
+
+ pspec = g_param_spec_uint("port", "Remote port",
+ "Port number of the remote service to connect to.",
+ 0, 0xffff, 0,
+ G_PARAM_READABLE|
+ G_PARAM_WRITABLE|
+ G_PARAM_STATIC_NICK|
+ G_PARAM_STATIC_BLURB);
+
+ g_object_class_install_property(object_class, PROP_PORT, pspec);
+}
+
+static void ssl_conn_change_state(IdleSSLServerConnection *conn, guint state, guint reason)
+{
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ if (state == priv->state)
+ {
+ return;
+ }
+
+ priv->state = state;
+
+ g_signal_emit_by_name(conn, "status-changed", state, reason);
+}
+
+static SSL_CTX *ssl_conn_get_ctx()
+{
+ static SSL_CTX *ctx = NULL;
+
+ if (!ctx)
+ {
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(SSLv23_client_method());
+
+ if (!ctx)
+ {
+ g_debug("%s: OpenSSL initialization failed!", G_STRFUNC);
+ }
+ }
+
+ return ctx;
+}
+
+static gboolean iface_ssl_disconnect_impl_full(IdleServerConnectionIface *iface, guint reason, GError **error);
+
+static gboolean ssl_io_err_cleanup_func(gpointer user_data)
+{
+ IdleServerConnectionIface *iface = IDLE_SERVER_CONNECTION_IFACE(user_data);
+ GError *error;
+
+ if (!iface_ssl_disconnect_impl_full(iface, SERVER_CONNECTION_STATE_REASON_ERROR, &error))
+ {
+ g_debug("%s: disconnect: %s", G_STRFUNC, error->message);
+ g_error_free(error);
+ }
+
+ return FALSE;
+}
+
+static gboolean ssl_io_func(GIOChannel *src, GIOCondition cond, gpointer data)
+{
+ IdleSSLServerConnection *conn = IDLE_SSL_SERVER_CONNECTION(data);
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(data);
+ gchar buf[IRC_MSG_MAXLEN+3];
+ int err;
+
+ if ((cond == G_IO_ERR) || (cond == G_IO_HUP))
+ {
+ g_debug("%s: got G_IO_ERR || G_IO_HUP", G_STRFUNC);
+ ssl_conn_change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+
+ priv->read_watch_id = 0;
+ return FALSE;
+ }
+
+ memset(buf, 0, IRC_MSG_MAXLEN+3);
+
+ do
+ {
+ err = SSL_read(priv->ssl, buf, IRC_MSG_MAXLEN+2);
+ } while ((err <= 0) && (SSL_get_error(priv->ssl, err) == SSL_ERROR_WANT_READ));
+
+ if (err <= 0)
+ {
+ g_debug("%s: SSL_read failed with error %i", G_STRFUNC, SSL_get_error(priv->ssl, err));
+
+ g_idle_add(ssl_io_err_cleanup_func, conn);
+
+ priv->read_watch_id = 0;
+ return FALSE;
+ }
+
+ g_signal_emit_by_name(conn, "received", buf);
+
+ return TRUE;
+}
+
+static void ssl_async_connecting_finished_cb(IdleSSLServerConnection *conn, gboolean success)
+{
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+ SSL_CTX *ctx;
+ X509 *cert;
+ int status;
+ int opt;
+
+ if (!success)
+ {
+ return ssl_conn_change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, SERVER_CONNECTION_STATE_REASON_ERROR);
+ }
+
+ priv->fd = priv->connect_data->fd;
+ priv->connect_data->fd = 0;
+
+ priv->io_chan = priv->connect_data->io_chan;
+ priv->connect_data->io_chan = NULL;
+
+ if (priv->connect_data->watch_id)
+ {
+ g_source_remove(priv->connect_data->watch_id);
+ priv->connect_data->watch_id = 0;
+ }
+
+ priv->read_watch_id = g_io_add_watch(priv->io_chan, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP, ssl_io_func, conn);
+
+ if (fcntl(priv->fd, F_SETFL, 0))
+ {
+ g_debug("%s: failed to set socket back to blocking mode", G_STRFUNC);
+ }
+
+ opt = 1;
+ setsockopt(priv->fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
+
+ opt = 1;
+ setsockopt(priv->fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
+
+ ctx = ssl_conn_get_ctx();
+
+ if (!ctx)
+ {
+ g_debug("%s: failed to get SSL context object", G_STRFUNC);
+ return ssl_async_connecting_finished_cb(conn, FALSE);
+ }
+
+ priv->ssl = SSL_new(ctx);
+
+ if (!priv->ssl)
+ {
+ g_debug("%s: failed to create SSL object", G_STRFUNC);
+ return ssl_async_connecting_finished_cb(conn, FALSE);
+ }
+
+ status = SSL_set_fd(priv->ssl, priv->fd);
+
+ if (!status)
+ {
+ g_debug("%s: failed to set SSL socket", G_STRFUNC);
+ return ssl_async_connecting_finished_cb(conn, FALSE);
+ }
+
+ status = SSL_connect(priv->ssl);
+
+ if (status <= 0)
+ {
+ g_debug("%s: SSL_connect failed with status %i (error %i)", G_STRFUNC, status, SSL_get_error(priv->ssl, status));
+ return ssl_async_connecting_finished_cb(conn, FALSE);
+ }
+
+ cert = SSL_get_peer_certificate(priv->ssl);
+
+ if (!cert)
+ {
+ g_debug("%s: failed to get SSL peer certificate", G_STRFUNC);
+ return ssl_async_connecting_finished_cb(conn, FALSE);
+ }
+
+ /* TODO sometime in the future implement certificate verification */
+
+ X509_free(cert);
+
+ ssl_conn_change_state(conn, SERVER_CONNECTION_STATE_CONNECTED, SERVER_CONNECTION_STATE_REASON_REQUESTED);
+}
+
+static void ssl_do_connect(AsyncConnectData *data);
+
+static gboolean ssl_connect_io_func(GIOChannel *src, GIOCondition cond, gpointer user_data)
+{
+ AsyncConnectData *data = (AsyncConnectData *)(user_data);
+ int optval;
+ socklen_t optlen = sizeof(optval);
+
+ g_assert(getsockopt(data->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == 0);
+
+ if (optval == 0)
+ {
+ data->finished_cb(data->conn, TRUE);
+ }
+ else
+ {
+ g_source_remove(data->watch_id);
+ data->watch_id = 0;
+
+ g_io_channel_shutdown(data->io_chan, FALSE, NULL);
+ data->io_chan = NULL;
+
+ close(data->fd);
+ data->fd = 0;
+
+ ssl_do_connect(data);
+ }
+
+ return FALSE;
+}
+
+static void ssl_do_connect(AsyncConnectData *data)
+{
+ int fd = -1, rc = -1;
+ GIOChannel *io_chan;
+
+ for (; data->cur != NULL; data->cur = data->cur->ai_next)
+ {
+ fd = socket(data->cur->ai_family, data->cur->ai_socktype, data->cur->ai_protocol);
+
+ if (fd == -1)
+ {
+ g_debug("%s: socket() failed: %s", G_STRFUNC, g_strerror(errno));
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (fd == -1)
+ {
+ g_debug("%s: failed: %s", G_STRFUNC, g_strerror(errno));
+ return data->finished_cb(data->conn, FALSE);
+ }
+
+ rc = fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ if (rc != 0)
+ {
+ g_debug("%s: failed to set socket to non-blocking mode: %s", G_STRFUNC, g_strerror(errno));
+ close(fd);
+ return data->finished_cb(data->conn, FALSE);
+ }
+
+ rc = connect(fd, data->cur->ai_addr, data->cur->ai_addrlen);
+
+ g_assert(rc == -1);
+
+ if (errno != EINPROGRESS)
+ {
+ g_debug("%s: connect() failed: %s", G_STRFUNC, g_strerror(errno));
+ close(fd);
+ return data->finished_cb(data->conn, FALSE);
+ }
+
+ io_chan = g_io_channel_unix_new(fd);
+ g_io_channel_set_encoding(io_chan, NULL, NULL);
+ g_io_channel_set_buffered(io_chan, FALSE);
+
+ data->fd = fd;
+ data->io_chan = io_chan;
+ data->watch_id = g_io_add_watch(io_chan, G_IO_OUT|G_IO_ERR, ssl_connect_io_func, data);
+}
+
+static void ssl_dns_result_cb(guint unused, IdleDNSResult *results, gpointer user_data)
+{
+ IdleSSLServerConnection *conn = IDLE_SSL_SERVER_CONNECTION(user_data);
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+ AsyncConnectData *data;
+
+ if (priv->connect_data)
+ {
+ async_connect_data_destroy(priv->connect_data);
+ }
+
+ priv->connect_data = data = async_connect_data_new0();
+
+ data->conn = conn;
+ data->res = results;
+ data->cur = results;
+ data->finished_cb = ssl_async_connecting_finished_cb;
+
+ return ssl_do_connect(data);
+}
+
+static gboolean iface_ssl_connect_impl(IdleServerConnectionIface *iface, GError **error)
+{
+ IdleSSLServerConnection *conn = IDLE_SSL_SERVER_CONNECTION(iface);
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ if (priv->state != SERVER_CONNECTION_STATE_NOT_CONNECTED)
+ {
+ g_debug("%s: connection was not in state NOT_CONNECTED", G_STRFUNC);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "connection was in state NOT_CONNECTED");
+
+ return FALSE;
+ }
+
+ if (!priv->host && !priv->host[0])
+ {
+ g_debug("%s: no hostname provided", G_STRFUNC);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "no hostname provided");
+
+ return FALSE;
+ }
+
+ if (!priv->port)
+ {
+ g_debug("%s: no port provided", G_STRFUNC);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "no port provided");
+
+ return FALSE;
+ }
+
+ idle_dns_resolver_query(priv->resolver, priv->host, priv->port, ssl_dns_result_cb, conn);
+
+ ssl_conn_change_state(conn, SERVER_CONNECTION_STATE_CONNECTING, SERVER_CONNECTION_STATE_REASON_REQUESTED);
+
+ return TRUE;
+}
+
+static gboolean iface_ssl_disconnect_impl_full(IdleServerConnectionIface *iface, guint reason, GError **error)
+{
+ IdleSSLServerConnection *conn = IDLE_SSL_SERVER_CONNECTION(iface);
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ if (priv->state == SERVER_CONNECTION_STATE_NOT_CONNECTED)
+ {
+ g_debug("%s: not connected", G_STRFUNC);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "not connected");
+
+ return FALSE;
+ }
+
+ if (priv->read_watch_id)
+ {
+ g_source_remove(priv->read_watch_id);
+ priv->read_watch_id = 0;
+ }
+
+ if (priv->io_chan)
+ {
+ GError *io_error = NULL;
+
+ g_io_channel_shutdown(priv->io_chan, FALSE, &io_error);
+
+ if (io_error)
+ {
+ g_debug("%s: g_io_channel_shutdown failed: %s", G_STRFUNC, io_error->message);
+
+ g_error_free(io_error);
+ }
+
+ g_io_channel_unref(priv->io_chan);
+
+ priv->io_chan = NULL;
+ }
+
+ if (priv->fd)
+ {
+ close(priv->fd);
+ priv->fd = 0;
+ }
+
+ ssl_conn_change_state(conn, SERVER_CONNECTION_STATE_NOT_CONNECTED, reason);
+
+ return TRUE;
+}
+
+static gboolean iface_ssl_disconnect_impl(IdleServerConnectionIface *iface, GError **error)
+{
+ return iface_ssl_disconnect_impl_full(iface, SERVER_CONNECTION_STATE_REASON_REQUESTED, error);
+}
+
+static gboolean iface_ssl_send_impl(IdleServerConnectionIface *iface, const gchar *cmd, GError **error)
+{
+ IdleSSLServerConnection *conn = IDLE_SSL_SERVER_CONNECTION(iface);
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+ gsize len;
+ int rc;
+
+ g_assert(cmd != NULL);
+
+ if (priv->state != SERVER_CONNECTION_STATE_CONNECTED)
+ {
+ g_debug("%s: connection was not in state CONNECTED", G_STRFUNC);
+
+ *error = g_error_new(TELEPATHY_ERRORS, NotAvailable, "connection was not in state CONNECTED");
+
+ return FALSE;
+ }
+
+ len = strlen(cmd);
+
+ if (!len)
+ {
+ return TRUE;
+ }
+
+ rc = SSL_write(priv->ssl, cmd, len);
+
+ if (rc <= 0)
+ {
+ GError *local_error;
+
+ g_debug("%s: SSL_write failed with status %i (error %i)", G_STRFUNC, rc, SSL_get_error(priv->ssl, rc));
+
+ if (!iface_ssl_disconnect_impl_full(IDLE_SERVER_CONNECTION_IFACE(conn), SERVER_CONNECTION_STATE_REASON_ERROR, &local_error))
+ {
+ g_error_free(local_error);
+ }
+
+ *error = g_error_new(TELEPATHY_ERRORS, NetworkError, "SSL_write failed");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+IdleServerConnectionState iface_ssl_get_state_impl(IdleServerConnectionIface *iface)
+{
+ IdleSSLServerConnection *conn = IDLE_SSL_SERVER_CONNECTION(iface);
+ IdleSSLServerConnectionPrivate *priv = IDLE_SSL_SERVER_CONNECTION_GET_PRIVATE(conn);
+
+ return priv->state;
+}
+
+static void idle_ssl_server_connection_iface_init(gpointer g_iface, gpointer iface_data)
+{
+ IdleServerConnectionIfaceClass *klass = (IdleServerConnectionIfaceClass *)(g_iface);
+
+ klass->connect = iface_ssl_connect_impl;
+ klass->disconnect = iface_ssl_disconnect_impl;
+ klass->send = iface_ssl_send_impl;
+ klass->get_state = iface_ssl_get_state_impl;
+}
diff --git a/src/idle-ssl-server-connection.h b/src/idle-ssl-server-connection.h
new file mode 100644
index 0000000..6cfc41d
--- /dev/null
+++ b/src/idle-ssl-server-connection.h
@@ -0,0 +1,63 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_SSL_SERVER_CONNECTION_H__
+#define __IDLE_SSL_SERVER_CONNECTION_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdleSSLServerConnection IdleSSLServerConnection;
+typedef struct _IdleSSLServerConnectionClass IdleSSLServerConnectionClass;
+
+struct _IdleSSLServerConnection
+{
+ GObject parent;
+};
+
+struct _IdleSSLServerConnectionClass
+{
+ GObjectClass parent;
+};
+
+GType idle_ssl_server_connection_get_type(void);
+
+#define IDLE_TYPE_SSL_SERVER_CONNECTION \
+ (idle_ssl_server_connection_get_type())
+
+#define IDLE_SSL_SERVER_CONNECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_SSL_SERVER_CONNECTION, IdleSSLServerConnection))
+
+#define IDLE_SSL_SERVER_CONNECTION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_SSL_SERVER_CONNECTION, IdleSSLServerConnection))
+
+#define IDLE_IS_SSL_SERVER_CONNECTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_SSL_SERVER_CONNECTION))
+
+#define IDLE_IS_SSL_SERVER_CONNECTION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_SSL_SERVER_CONNECTION))
+
+#define IDLE_SSL_SERVER_CONNECTION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), IDLE_TYPE_SSL_SERVER_CONNECTION, IdleSSLServerConnectionClass))
+
+G_END_DECLS
+
+#endif
diff --git a/src/idle-version.h b/src/idle-version.h
new file mode 100644
index 0000000..75d00e7
--- /dev/null
+++ b/src/idle-version.h
@@ -0,0 +1,26 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 IDLE_VERSION_H
+#define IDLE_VERSION_H
+
+#include "config.h"
+
+#endif
diff --git a/src/idle.c b/src/idle.c
new file mode 100644
index 0000000..badabd1
--- /dev/null
+++ b/src/idle.c
@@ -0,0 +1,185 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 "idle.h"
+
+#include "idle-connection-manager.h"
+#include "telepathy-errors.h"
+#include "telepathy-errors-enumtypes.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <execinfo.h>
+
+GSource *timeout = NULL;
+GMainLoop *mainloop = NULL;
+IdleConnectionManager *manager = NULL;
+gboolean connections_exist = FALSE;
+guint timeout_id;
+
+#define DIE_TIME 5000
+
+static gboolean
+kill_connection_manager (gpointer data)
+{
+ if (!g_getenv ("IDLE_PERSIST") && !connections_exist)
+ {
+ g_debug("no connections, and timed out");
+ g_object_unref (manager);
+ g_main_loop_quit (mainloop);
+ }
+
+ return FALSE;
+}
+
+static void
+new_connection (IdleConnectionManager *conn, gchar *bus_name,
+ gchar *object_path, gchar *proto)
+{
+ g_debug("%s called with %s, %s, %s", G_STRFUNC, bus_name, object_path, proto);
+ connections_exist = TRUE;
+ g_source_remove (timeout_id);
+}
+
+static void
+no_more_connections (IdleConnectionManager *conn)
+{
+ if (g_main_context_find_source_by_id (g_main_loop_get_context (mainloop),
+ timeout_id))
+ {
+ g_source_remove (timeout_id);
+ }
+ connections_exist = FALSE;
+ timeout_id = g_timeout_add(DIE_TIME, kill_connection_manager, NULL);
+}
+
+#if 0
+static void gabble_critical_handler (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data)
+{
+ void *array[10];
+ size_t size;
+ char **strings;
+ size_t i;
+
+ fprintf (stderr, "%s: %s::%s, level %d\n", G_STRFUNC, log_domain, message, log_level);
+
+ fprintf(stderr, "\n########## Backtrace ##########\n");
+ size = backtrace (array, 10);
+ strings = backtrace_symbols (array, size);
+
+ fprintf (stderr, "Obtained %zd stack frames.\n", size);
+
+ for (i = 0; i < size; i++)
+ fprintf (stderr, "%s\n", strings[i]);
+
+ free (strings);
+ abort();
+}
+
+static void signal_handler_helper(int signum)
+{
+ void *array[10];
+ size_t size;
+ char **strings;
+ size_t i;
+
+ fprintf (stderr, "%s: %d", G_STRFUNC, signum);
+
+ fprintf(stderr, "\n########## Backtrace ##########\n");
+ size = backtrace (array, 10);
+ strings = backtrace_symbols (array, size);
+
+ fprintf (stderr, "Obtained %zd stack frames.\n", size);
+
+ for (i = 0; i < size; i++)
+ fprintf (stderr, "%s\n", strings[i]);
+
+ free (strings);
+
+ exit(1);
+}
+#endif
+
+#include <signal.h>
+
+int main(int argc, char **argv) {
+
+ g_type_init();
+/*
+ {*/
+ /*GLogLevelFlags fatal_mask;
+
+ fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+ fatal_mask |= G_LOG_LEVEL_CRITICAL;
+ g_log_set_always_fatal (fatal_mask);*/
+
+ /* useful backtrace handler from gabble */
+ /*
+ g_log_set_handler ("GLib-GObject", G_LOG_LEVEL_CRITICAL| G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL
+ | G_LOG_FLAG_RECURSION, gabble_critical_handler, NULL);
+ g_log_set_handler ("GLib", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL
+ | G_LOG_FLAG_RECURSION, gabble_critical_handler, NULL);
+ g_log_set_handler (NULL, G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL
+ | G_LOG_FLAG_RECURSION, gabble_critical_handler, NULL);
+ }
+*/
+ /*
+ signal(SIGHUP, signal_handler_helper);
+ signal(SIGSEGV, signal_handler_helper);
+ signal(SIGTERM, signal_handler_helper);
+ signal(SIGPIPE, signal_handler_helper);
+ signal(SIGABRT, signal_handler_helper);
+ signal(SIGINT, signal_handler_helper);
+*/
+
+ g_set_prgname("telepathy-idle");
+#if 0
+ if (!g_thread_supported())
+ {
+ g_thread_init(NULL);
+ }
+#endif
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ dbus_g_error_domain_register (TELEPATHY_ERRORS,
+ "org.freedesktop.Telepathy.Error", TELEPATHY_TYPE_ERRORS);
+
+ manager = g_object_new (IDLE_TYPE_CONNECTION_MANAGER, NULL);
+
+ g_signal_connect (manager, "new-connection",
+ (GCallback) new_connection, NULL);
+
+ g_signal_connect (manager, "no-more-connections",
+ (GCallback) no_more_connections, NULL);
+
+ _idle_connection_manager_register (manager);
+
+ g_debug ("started");
+
+ timeout_id = g_timeout_add(DIE_TIME, kill_connection_manager, NULL);
+
+ g_main_loop_run (mainloop);
+
+ return 0;
+}
diff --git a/src/idle.h b/src/idle.h
new file mode 100644
index 0000000..45e5d99
--- /dev/null
+++ b/src/idle.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __IDLE_H__
+#define __IDLE_H__
+
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+G_END_DECLS
+
+#endif /* __IDLE_H__ */
diff --git a/src/telepathy-constants.h b/src/telepathy-constants.h
new file mode 100644
index 0000000..db272de
--- /dev/null
+++ b/src/telepathy-constants.h
@@ -0,0 +1,117 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __TELEPATHY_CONSTANTS_H__
+#define __TELEPATHY_CONSTANTS_H__
+
+#include <glib.h>
+G_BEGIN_DECLS
+
+typedef enum {
+TP_CONN_MGR_PARAM_FLAG_REQUIRED = 1,
+TP_CONN_MGR_PARAM_FLAG_REGISTER = 2,
+TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT = 4
+} TpConnectionManagerParamFlag;
+
+typedef enum {
+TP_HANDLE_TYPE_NONE = 0,
+TP_HANDLE_TYPE_CONTACT = 1,
+TP_HANDLE_TYPE_ROOM = 2,
+TP_HANDLE_TYPE_LIST = 3
+} TpHandleType;
+
+typedef enum {
+TP_CONN_PRESENCE_TYPE_UNSET = 0,
+TP_CONN_PRESENCE_TYPE_OFFLINE = 1,
+TP_CONN_PRESENCE_TYPE_AVAILABLE = 2,
+TP_CONN_PRESENCE_TYPE_AWAY = 3,
+TP_CONN_PRESENCE_TYPE_EXTENDED_AWAY = 4,
+TP_CONN_PRESENCE_TYPE_HIDDEN = 5
+} TpConnectionPresenceType;
+
+typedef enum {
+TP_CONN_STATUS_CONNECTED = 0,
+TP_CONN_STATUS_CONNECTING = 1,
+TP_CONN_STATUS_DISCONNECTED = 2
+} TpConnectionStatus;
+
+typedef enum {
+TP_CONN_STATUS_REASON_NONE_SPECIFIED = 0,
+TP_CONN_STATUS_REASON_REQUESTED = 1,
+TP_CONN_STATUS_REASON_NETWORK_ERROR = 2,
+TP_CONN_STATUS_REASON_AUTHENTICATION_FAILED = 3,
+TP_CONN_STATUS_REASON_ENCRYPTION_ERROR = 4,
+TP_CONN_STATUS_REASON_NAME_IN_USE = 5
+} TpConnectionStatusReason;
+
+typedef enum {
+TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL = 0,
+TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION = 1,
+TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE = 2
+} TpChannelTextMessageType;
+
+typedef enum {
+TP_CHANNEL_GROUP_FLAG_CAN_ADD = 1,
+TP_CHANNEL_GROUP_FLAG_CAN_REMOVE = 2,
+TP_CHANNEL_GROUP_FLAG_CAN_RESCIND = 4,
+TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD = 8,
+TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE = 16,
+TP_CHANNEL_GROUP_FLAG_MESSAGE_ACCEPT = 32,
+TP_CHANNEL_GROUP_FLAG_MESSAGE_REJECT = 64,
+TP_CHANNEL_GROUP_FLAG_MESSAGE_RESCIND = 128
+} TpChannelGroupFlags;
+
+typedef enum {
+TP_CHANNEL_HOLD_STATE_NONE = 0,
+TP_CHANNEL_HOLD_STATE_SEND_ONLY = 1,
+TP_CHANNEL_HOLD_STATE_RECV_ONLY = 2,
+TP_CHANNEL_HOLD_STATE_BOTH = 3
+} TpChannelHoldState;
+
+typedef enum {
+TP_CHANNEL_PASSWORD_FLAG_PROVIDE = 8
+} TpChannelPasswordFlags;
+
+typedef enum {
+ TP_PROPERTY_FLAG_READ = 1,
+ TP_PROPERTY_FLAG_WRITE = 2
+} TpPropertyFlags;
+
+typedef enum {
+ TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN = 0,
+ TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE = 1,
+ TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT = 2,
+ TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED = 3,
+ TP_CHANNEL_TEXT_SEND_ERROR_TOO_LONG = 4
+} TpChannelTextSenderror;
+
+typedef enum {
+ TP_CHANNEL_GROUP_CHANGE_REASON_NONE = 0,
+ TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE = 1,
+ TP_CHANNEL_GROUP_CHANGE_REASON_KICKED = 2,
+ TP_CHANNEL_GROUP_CHANGE_REASON_BUSY = 3,
+ TP_CHANNEL_GROUP_CHANGE_REASON_INVITED = 4
+}
+TpChannelGroupChangeReason;
+
+G_END_DECLS
+
+
+#endif
diff --git a/src/telepathy-errors.c b/src/telepathy-errors.c
new file mode 100644
index 0000000..9f9b80c
--- /dev/null
+++ b/src/telepathy-errors.c
@@ -0,0 +1,31 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 "telepathy-errors.h"
+
+GQuark
+telepathy_errors_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("telepathy_errors");
+ return quark;
+}
diff --git a/src/telepathy-errors.h b/src/telepathy-errors.h
new file mode 100644
index 0000000..ede0742
--- /dev/null
+++ b/src/telepathy-errors.h
@@ -0,0 +1,60 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __TELEPATHY_ERRORS_H__
+#define __TELEPATHY_ERRORS_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ ChannelBanned,
+ ChannelFull,
+ ChannelInviteOnly,
+ InvalidHandle, /** The contact name specified is unknown on this channel
+ * or connection.
+ */
+ Disconnected, /** The connection is not currently connected and cannot be
+ * used.
+ */
+ InvalidArgument, /** Raised when one of the provided arguments is invalid.
+ */
+ NetworkError, /** Raised when there is an error reading from or writing
+ * to the network.
+ */
+ PermissionDenied, /** The user is not permitted to perform the requested
+ * operation.
+ */
+ NotAvailable, /** Raised when the requested functionality is temporarily
+ * unavailable.
+ */
+ NotImplemented, /** Raised when the requested method, channel, etc is not
+ * available on this connection.
+ */
+} TelepathyErrors;
+
+GQuark telepathy_errors_quark (void);
+#define TELEPATHY_ERRORS telepathy_errors_quark ()
+
+G_END_DECLS
+
+#endif /* #ifndef __TELEPATHY_ERRORS_H__*/
diff --git a/src/telepathy-helpers.c b/src/telepathy-helpers.c
new file mode 100644
index 0000000..5c8cf18
--- /dev/null
+++ b/src/telepathy-helpers.c
@@ -0,0 +1,63 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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-glib.h>
+#include "telepathy-helpers.h"
+
+DBusGConnection *
+tp_get_bus ()
+{
+ static DBusGConnection *bus = NULL;
+
+ if (bus == NULL)
+ {
+ GError *error = NULL;
+
+ bus = dbus_g_bus_get (DBUS_BUS_STARTER, &error);
+
+ if (bus == NULL)
+ g_error ("Failed to connect to starter bus: %s", error->message);
+ }
+
+ return bus;
+}
+
+DBusGProxy *
+tp_get_bus_proxy ()
+{
+ static DBusGProxy *bus_proxy = NULL;
+
+ if (bus_proxy == NULL)
+ {
+ DBusGConnection *bus = tp_get_bus ();
+
+ bus_proxy = dbus_g_proxy_new_for_name (bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus");
+
+ if (bus_proxy == NULL)
+ g_error ("Failed to get proxy object for bus.");
+ }
+
+ return bus_proxy;
+}
+
+
diff --git a/src/telepathy-helpers.h b/src/telepathy-helpers.h
new file mode 100644
index 0000000..234e898
--- /dev/null
+++ b/src/telepathy-helpers.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __TELEPATHY_HELPERS_H__
+#define __TELEPATHY_HELPERS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+DBusGConnection * tp_get_bus ();
+DBusGProxy * tp_get_bus_proxy ();
+
+G_END_DECLS
+
+#endif /* __TELEPATHY_HELPERS_H__ */
+
diff --git a/src/telepathy-interfaces.h b/src/telepathy-interfaces.h
new file mode 100644
index 0000000..c3e1563
--- /dev/null
+++ b/src/telepathy-interfaces.h
@@ -0,0 +1,65 @@
+/*
+ * This file is part of telepathy-idle
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ *
+ * 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 __TELEPATHY_INTERFACES_H__
+#define __TELEPATHY_INTERFACES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TP_IFACE_CHANNEL_INTERFACE \
+ "org.freedesktop.Telepathy.Channel"
+#define TP_IFACE_CHANNEL_INTERFACE_GROUP \
+ "org.freedesktop.Telepathy.Channel.Interface.Group"
+#define TP_IFACE_CHANNEL_INTERFACE_HOLD \
+ "org.freedesktop.Telepathy.Channel.Interface.Hold"
+#define TP_IFACE_CHANNEL_INTERFACE_PASSWORD \
+ "org.freedesktop.Telepathy.Channel.Interface.Password"
+#define TP_IFACE_CHANNEL_INTERFACE_TRANSFER \
+ "org.freedesktop.Telepathy.Channel.Interface.Transfer"
+#define TP_IFACE_CHANNEL_TYPE_ROOM_LIST \
+ "org.freedesktop.Telepathy.Channel.Type.RoomList"
+#define TP_IFACE_CHANNEL_TYPE_TEXT \
+ "org.freedesktop.Telepathy.Channel.Type.Text"
+#define TP_IFACE_CONN_INTERFACE \
+ "org.freedesktop.Telepathy.Connection"
+#define TP_IFACE_CONN_INTERFACE_ALIASING \
+ "org.freedesktop.Telepathy.Connection.Interface.Aliasing"
+#define TP_IFACE_CONN_INTERFACE_CAPABILITIES \
+ "org.freedesktop.Telepathy.Connection.Interface.Capabilities"
+#define TP_IFACE_CONN_INTERFACE_CONTACT_INFO \
+ "org.freedesktop.Telepathy.Connection.Interface.ContactInfo"
+#define TP_IFACE_CONN_INTERFACE_FORWARDING \
+ "org.freedesktop.Telepathy.Connection.Interface.Forwarding"
+#define TP_IFACE_CONN_INTERFACE_PRESENCE \
+ "org.freedesktop.Telepathy.Connection.Interface.Presence"
+#define TP_IFACE_CONN_INTERFACE_PRIVACY \
+ "org.freedesktop.Telepathy.Connection.Interface.Privacy"
+#define TP_IFACE_CONN_INTERFACE_RENAMING \
+ "org.freedesktop.Telepathy.Connection.Interface.Renaming"
+#define TP_IFACE_CONN_MGR_INTERFACE \
+ "org.freedesktop.Telepathy.ConnectionManager"
+#define TP_IFACE_PROPERTIES \
+ "org.freedesktop.Telepathy.Properties"
+
+G_END_DECLS
+
+#endif /* #ifndef __TELEPATHY_INTERFACES_H__*/