Repository: tieto/sipe Branch: launchpad-next Commit: ad0d885d8a52 Files: 339 Total size: 3.8 MB Directory structure: gitextract_3ldssbmk/ ├── .gitignore ├── .tx/ │ └── config ├── AUTHORS ├── COPYING ├── ChangeLog ├── Makefile.am ├── Makefile.mingw ├── NEWS ├── README ├── TODO ├── VERSION ├── autogen.sh ├── configure.ac ├── contrib/ │ ├── dbus/ │ │ ├── SipeHelper.pm │ │ ├── sipe-call-phone-number.pl │ │ ├── sipe-join-conference-with-organizer-and-id.pl │ │ ├── sipe-join-conference-with-uri.pl │ │ ├── sipe-republish-calendar.pl │ │ └── sipe-reset-status.pl │ ├── debian/ │ │ ├── changelog │ │ ├── compat │ │ ├── control │ │ ├── copyright │ │ ├── docs │ │ └── rules │ ├── debug/ │ │ ├── parse_log.pl │ │ └── parse_valgrind.pl │ ├── mingw-cross-compile/ │ │ ├── README.txt │ │ ├── fetch.sh │ │ └── local.mak │ ├── opensuse-build-service/ │ │ ├── PKGBUILD │ │ ├── generate_debian.sh │ │ ├── generate_nsi.pl │ │ ├── pidgin-sipe-freerdp2.dsc │ │ ├── pidgin-sipe-gstreamer1.dsc │ │ ├── pidgin-sipe-telepathy.dsc │ │ ├── pidgin-sipe.changes │ │ ├── pidgin-sipe.nsi.template │ │ └── pidgin-sipe.spec │ └── rpm/ │ └── pidgin-sipe.spec ├── git-build.sh ├── git-snapshot.sh ├── pidgin-sipe.nsi ├── pidgin-sipe.wxs ├── pixmaps/ │ ├── 16/ │ │ └── Makefile.am │ ├── 22/ │ │ └── Makefile.am │ ├── 24/ │ │ └── Makefile.am │ ├── 32/ │ │ └── Makefile.am │ ├── 48/ │ │ └── Makefile.am │ ├── Makefile.am │ ├── Makefile.common.am │ ├── Makefile.mingw │ └── scalable/ │ └── Makefile.am ├── po/ │ ├── LINGUAS │ ├── Makefile.mingw │ ├── POTFILES.in │ ├── ar.po │ ├── cs.po │ ├── da.po │ ├── de.po │ ├── el.po │ ├── es.po │ ├── fi.po │ ├── fr.po │ ├── fr_CA.po │ ├── hi.po │ ├── hu.po │ ├── it.po │ ├── ja.po │ ├── ko.po │ ├── lt.po │ ├── nb.po │ ├── nl.po │ ├── pidgin-sipe.pot │ ├── pl.po │ ├── pt.po │ ├── pt_BR.po │ ├── ro.po │ ├── ru.po │ ├── sv.po │ ├── ta.po │ ├── te.po │ ├── tr.po │ ├── transifex-pot-fixup.pl │ ├── zh_CN.po │ └── zh_TW.po ├── siplcs.vcxproj ├── siplcs.vcxproj.filters └── src/ ├── Makefile.am ├── Makefile.mingw ├── adium/ │ ├── DCPurpleSIPEJoinChatViewController.h │ ├── DCPurpleSIPEJoinChatViewController.m │ ├── ESPurpleSIPEAccount.h │ ├── ESPurpleSIPEAccount.m │ ├── ESSIPEAccountViewController.h │ ├── ESSIPEAccountViewController.m │ ├── ESSIPELibpurpleServicePlugin.h │ ├── ESSIPELibpurpleServicePlugin.m │ ├── ESSIPEService.h │ ├── ESSIPEService.m │ ├── English.lproj/ │ │ ├── DCPurpleSIPEJoinChatView.xib │ │ ├── ESSIPEAccountView.xib │ │ └── InfoPlist.strings │ ├── Info.plist │ ├── PurpleDefaultsSIPE.plist │ ├── README.Adium │ ├── SIPEAdiumPlugin.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ ├── SIPEAdiumPlugin.xcscheme │ │ └── pidgin-sipe.xcscheme │ ├── SIPEAdiumPlugin_Prefix.pch │ ├── check_release.sh │ ├── find_adium_build.sh │ ├── openssl/ │ │ ├── libcrypto.0.9.8.tbd │ │ └── opensslconf.h │ └── xcconfigs/ │ ├── Base.xcconfig │ ├── Debug-Release.xcconfig │ ├── Debug.xcconfig │ ├── Release.xcconfig │ ├── SIPEAdiumPlugin.xcconfig │ └── libpidgin-sipe.xcconfig ├── api/ │ ├── Makefile.am │ ├── sipe-backend.h │ ├── sipe-common.h │ ├── sipe-core.h │ ├── sipe-mime.h │ └── sipe-nls.h ├── core/ │ ├── Makefile.am │ ├── Makefile.mingw │ ├── libsiperc.rc.in │ ├── md4.c │ ├── md4.h │ ├── sdpmsg.c │ ├── sdpmsg.h │ ├── sip-csta.c │ ├── sip-csta.h │ ├── sip-sec-basic.c │ ├── sip-sec-basic.h │ ├── sip-sec-digest-tests.c │ ├── sip-sec-digest.c │ ├── sip-sec-digest.h │ ├── sip-sec-gssapi.c │ ├── sip-sec-gssapi.h │ ├── sip-sec-mech.h │ ├── sip-sec-negotiate.c │ ├── sip-sec-negotiate.h │ ├── sip-sec-ntlm-analyzer.c │ ├── sip-sec-ntlm-tests.c │ ├── sip-sec-ntlm.c │ ├── sip-sec-ntlm.h │ ├── sip-sec-sspi.c │ ├── sip-sec-sspi.h │ ├── sip-sec-tls-dsk.c │ ├── sip-sec-tls-dsk.h │ ├── sip-sec.c │ ├── sip-sec.h │ ├── sip-soap.c │ ├── sip-soap.h │ ├── sip-transport.c │ ├── sip-transport.h │ ├── sipe-appshare-client.h │ ├── sipe-appshare-remmina.c │ ├── sipe-appshare-xfreerdp.c │ ├── sipe-appshare.c │ ├── sipe-appshare.h │ ├── sipe-buddy.c │ ├── sipe-buddy.h │ ├── sipe-cal.c │ ├── sipe-cal.h │ ├── sipe-cert-crypto-nss.c │ ├── sipe-cert-crypto-openssl.c │ ├── sipe-cert-crypto.h │ ├── sipe-certificate.c │ ├── sipe-certificate.h │ ├── sipe-chat.c │ ├── sipe-chat.h │ ├── sipe-conf.c │ ├── sipe-conf.h │ ├── sipe-core-private.h │ ├── sipe-core.c │ ├── sipe-crypt-nss.c │ ├── sipe-crypt-openssl.c │ ├── sipe-crypt.h │ ├── sipe-dialog.c │ ├── sipe-dialog.h │ ├── sipe-digest-nss.c │ ├── sipe-digest-openssl.c │ ├── sipe-digest.h │ ├── sipe-domino.c │ ├── sipe-domino.h │ ├── sipe-ews-autodiscover.c │ ├── sipe-ews-autodiscover.h │ ├── sipe-ews.c │ ├── sipe-ews.h │ ├── sipe-ft-lync.c │ ├── sipe-ft-lync.h │ ├── sipe-ft-tftp.c │ ├── sipe-ft-tftp.h │ ├── sipe-ft.c │ ├── sipe-ft.h │ ├── sipe-generic-tests.c │ ├── sipe-group.c │ ├── sipe-group.h │ ├── sipe-groupchat.c │ ├── sipe-groupchat.h │ ├── sipe-http-request.c │ ├── sipe-http-request.h │ ├── sipe-http-transport.c │ ├── sipe-http-transport.h │ ├── sipe-http.c │ ├── sipe-http.h │ ├── sipe-im.c │ ├── sipe-im.h │ ├── sipe-incoming.c │ ├── sipe-incoming.h │ ├── sipe-lync-autodiscover.c │ ├── sipe-lync-autodiscover.h │ ├── sipe-media.c │ ├── sipe-media.h │ ├── sipe-mime-common.c │ ├── sipe-mime.c │ ├── sipe-msrtp.c │ ├── sipe-notify.c │ ├── sipe-notify.h │ ├── sipe-ocs2005.c │ ├── sipe-ocs2005.h │ ├── sipe-ocs2007.c │ ├── sipe-ocs2007.h │ ├── sipe-rtf-tests.c │ ├── sipe-rtf.h │ ├── sipe-rtf.l │ ├── sipe-schedule.c │ ├── sipe-schedule.h │ ├── sipe-session.c │ ├── sipe-session.h │ ├── sipe-sign.c │ ├── sipe-sign.h │ ├── sipe-sipmsg-tests.c │ ├── sipe-status.c │ ├── sipe-status.h │ ├── sipe-subscriptions.c │ ├── sipe-subscriptions.h │ ├── sipe-svc.c │ ├── sipe-svc.h │ ├── sipe-tls-analyzer.c │ ├── sipe-tls-tester.c │ ├── sipe-tls.c │ ├── sipe-tls.h │ ├── sipe-ucs.c │ ├── sipe-ucs.h │ ├── sipe-user.c │ ├── sipe-user.h │ ├── sipe-utils.c │ ├── sipe-utils.h │ ├── sipe-webticket.c │ ├── sipe-webticket.h │ ├── sipe-win32dep.c │ ├── sipe-win32dep.h │ ├── sipe-xml-tests.c │ ├── sipe-xml.c │ ├── sipe-xml.h │ ├── sipmsg.c │ ├── sipmsg.h │ ├── uuid.c │ └── uuid.h ├── miranda/ │ ├── INSTALL │ ├── miranda-buddy.c │ ├── miranda-chat.c │ ├── miranda-connection.c │ ├── miranda-debug.c │ ├── miranda-dnsquery.c │ ├── miranda-ft.c │ ├── miranda-groupchat.c │ ├── miranda-im.c │ ├── miranda-input.c │ ├── miranda-markup.c │ ├── miranda-media.c │ ├── miranda-network.c │ ├── miranda-notify.c │ ├── miranda-plugin.c │ ├── miranda-private.h │ ├── miranda-resource.h │ ├── miranda-schedule.c │ ├── miranda-search.c │ ├── miranda-setting.c │ ├── miranda-status.c │ ├── miranda-transport.c │ ├── miranda-user.c │ ├── miranda-utils.c │ ├── miranda-version.h │ ├── miranda.rc │ ├── sipe-miranda.c │ └── vlc.c ├── purple/ │ ├── Makefile.am │ ├── pidgin-sipe.metainfo.xml │ ├── purple-buddy.c │ ├── purple-chat.c │ ├── purple-connection.c │ ├── purple-dbus-bindings.c │ ├── purple-dbus.c │ ├── purple-dbus.h │ ├── purple-debug.c │ ├── purple-dnsquery.c │ ├── purple-ft.c │ ├── purple-groupchat.c │ ├── purple-im.c │ ├── purple-markup.c │ ├── purple-media.c │ ├── purple-mime.c │ ├── purple-network.c │ ├── purple-notify.c │ ├── purple-plugin-common.c │ ├── purple-plugin.c │ ├── purple-plugin3.c │ ├── purple-private.h │ ├── purple-schedule.c │ ├── purple-search.c │ ├── purple-setting.c │ ├── purple-status.c │ ├── purple-transport.c │ ├── purple-user.c │ ├── tests-load.c │ └── tests.c └── telepathy/ ├── Makefile.am ├── data/ │ ├── Makefile.am │ ├── org.freedesktop.Telepathy.ConnectionManager.sipe.service.in │ └── sipe.profile ├── telepathy-buddy.c ├── telepathy-connection.c ├── telepathy-debug.c ├── telepathy-dnsquery.c ├── telepathy-main.c ├── telepathy-private.h ├── telepathy-protocol.c ├── telepathy-schedule.c ├── telepathy-search.c ├── telepathy-status.c ├── telepathy-stubs.c ├── telepathy-tls.c └── telepathy-transport.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *~ *Makefile *Makefile.in *Makefile.in.in *.la *.lo *.log .deps .libs libtool config.* configure /ABOUT-NLS autom4te* *.swp aclocal.m4 compile depcomp GITVERSION install-sh intltool-extract* intltool-merge* intltool-update* *.bz2 *.gz *.xz ltmain.sh /m4/ missing mkinstalldirs /INSTALL pixmaps/*/im-sipe.* po/Makevars.template po/POTFILES po/Rules-quot po/stamp-it po/*.header po/*.sed po/*.sin *.gmo src/*/*.o src/adium/adium-frameworks src/adium/SIPEAdiumPlugin.xcodeproj/project.pbxproj-e src/adium/SIPEAdiumPlugin.xcodeproj/project.xcworkspace/ src/adium/SIPEAdiumPlugin.xcodeproj/xcuserdata/ src/core/sipe-rtf.c src/core/*_tests src/core/sipe_ntlm_analyzer src/core/sipe_tls_analyzer src/core/sipe_tls_tester src/purple/tests src/purple/tests_load src/telepathy/telepathy-sipe src/telepathy/data/org.freedesktop.Telepathy.ConnectionManager.sipe.service stamp-h1 siplcs.vcxproj.user test-driver ylwrap *.trs *.DS_Store adium/SIPEAdiumPlugin.xcodeproj/project.xcworkspace/* adium/SIPEAdiumPlugin.xcodeproj/xcuserdata/* ================================================ FILE: .tx/config ================================================ [main] host = https://www.transifex.com [pidgin-sipe.mob] file_filter = po/.po source_file = po/pidgin-sipe.pot source_lang = en ================================================ FILE: AUTHORS ================================================ Maintainers: Stefan Becker Jakub Adam Retired Maintainers: Jochen De Smet (Miranda port) Michael Lamb (Adium port) Anibal Avelar Tomáš Hrabčík pier11 Gabriel Burt Daniel Beichl Other Contributors: Jakub 'jimmac' Steiner (icon artwork) Harris Kauffman (Adium port) Matt Meissner (Adium port) Michael Steffens Carl Seutter Sean E. Millichamp Andrew Rechenberg Matthias Scharrer Garett Shulman Tyson Vinson Andrew Schenck Ivan Frade Jay (Rhyas) John Beranek David Woodhouse Edmondas Girkantas Peter Fales Kyle Hubert Andrey Vaynberger Michael Olbrich Based on the initial SIP/SIMPLE gaim protocol plugin by: Thomas Butter Translators: Please check the "Translators:" section in the po/*.po files. ================================================ FILE: COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: ChangeLog ================================================ version 1.25.1 "???" (????-??-??) - Fixed #359: Incorrect build due to false negative configure checks (Stefan Becker) - refactor --enable-quality-check (Stefan Becker) version 1.25.0 "Buddy Idle Time, RTF" (2019-10-12) - Feature #107: Provide idle start time for a buddy (Stefan Becker) - Feature #77: RTF support (incoming) (Stefan Becker) * the code only extracts plain text from incoming RTF - Fixed #358: FTBFS with glib-2.0 >= 2.62.0 (Stefan Becker) - Fixed #350: Inconsistent parsing of From:/To: headers (Stefan Becker) - raise BR telepathy-glib >= 0.24.0 (Stefan Becker) - switch from GPLv2+ to SPDX identifier GPL-2.0-or-later (Stefan Becker) version 1.24.0 "Application Sharing II" (2018-11-10) - Feature #104: Use user agent also for HTTP (Stefan Becker) - Feature #6: Application Sharing Server (Jakub Adam) * requires freerdp-shadow2 - Fixed #343: Build fails on FreeBSD - add timeout handling for media streams (Alaoui Youness) - update AppStream handling (Jakub Adam, Stefan Becker) - fix missing localisation in some code modules (Stefan Becker) - debug log improvements (Stefan Becker) version 1.23.3 "Bug Fixes III" (2018-08-20) - appshare: fix black screen with Remmina v1.2.0-rcgit.27 (Jakub Adam) - various minor fixes (Jakub Adam, Michael Olbrich) - fix compilation errors with libpurple 2.14.0 & GCC 8.0 (Jakub Adam, Stefan Becker) - mingw: update fetch script to Pidgin 2.13.0 (Stefan Becker) version 1.23.2 "Bug Fixes II" (2018-03-10) - fix some HTTP requests that were not sent (Jakub Adam, Stefan Becker) version 1.23.1 "Bug Fixes I" (2018-02-25) - Fixed #338: Incorrect port 0 for IPv6 socket on Windows (Stefan Becker) - Fixed #337: Duplicate candidates in SDP (Jakub Adam, Stefan Becker) - Fixed #336: Lync autodiscover does not follow user redirect (Stefan Becker) - media: unconditionally ignore multichannel codecs (Jakub Adam) - updated translations: Turkish (tr) version 1.23.0 "D-Bus, IPv6, OS X 10.11+" (2017-10-28) - Feature #101: Mac OS X 10.13 OpenSSL support (Stefan Becker) - Feature #100: Extend libpurple D-Bus interface (Stefan Becker) - Feature #99: IPv6 addresses in SIP & SDP messages (Stefan Becker) - Feature #96: Support for OS X 10.11+ SDK (Stefan Becker) - don't load buddy photos from unknown sites by default (Jakub Adam, Stefan Becker) * custom web URIs pose a security risk as they may be abused * users can override this behaviour in the account settings * Office365 accounts should not be affected by this change - add support for GMime 3.0 API (Stefan Becker) - raise BR glib-2.0 >= 2.18.0 (Stefan Becker) - raise BR purple >= 2.7.0 (Stefan Becker) - drop support for GMime 2.4 (Stefan Becker) - drop support for gstreamer-0.10 (Stefan Becker) version 1.22.1 "Bug Fixes I" (2017-06-11) - Fixed #320: Multiple client detection broken (Stefan Becker) - speed up Lync Autodiscover by using AccessLocation (Andrey Vaynberger) - adium: update build instructions for Xcode 7.x or newer (Stefan Becker) * the build environment used for releases 1.22.0 or older (Xcode 6.x on OS X 10.11) is no longer available to the project * releases starting with 1.22.1 will use Xcode 8.x on macOS 10.12 * build target continues to be OS X 10.9 - purple: avoid rare SSL read deadlock (Stefan Becker) - various minor fixes (Michael Olbrich) - crypto: make code compile with OpenSSL 1.1.0 (Stefan Becker) - drop references to Reuters Messaging (Stefan Becker) - updated translations: Lithuanian (lt), Russian (ru), Swedish (sv), Turkish (tr) version 1.22.0 "Application Sharing, Lync Autodiscover & Logging" (2017-02-01) - Feature #93: Support for Lync Autodiscover (Stefan Becker) - Feature #6: Application Sharing Viewer (Jakub Adam) * requires libpurple >= 2.12.0 * needs an external RDP client - remmina and xfreerdp are supported - Fixed #315: Crash when contact list is empty (Stefan Becker) - Fixed #314: sipe login problems with long pw (Stefan Becker) - separate logging and debugging output (Stefan Becker) * logging is always shown, e.g. in the Pidgin debug window * full message debugging now requires PURPLE_UNSAFE_DEBUG=1 - new translations: Greek (el), Lithuanian (lt) version 1.21.1 "Bug Fixes I" (2016-05-28) - various bug fixes in media support (Jakub Adam) - configure no longer ignores CFLAGS/LDFLAGS/LIBS (Stefan Becker) version 1.21.0 "Lync File Transfer" (2016-04-23) - Feature #91: Support embedded XML as buddy photo URL (Stefan Becker) - Feature #90: Add AppStream metadata file (Jiri Eischmann, Stefan Becker) - Feature #89: Improve "Join scheduled conference" dialog (Stefan Becker) - Feature #87: Support multiple HTTP cookies (Stefan Becker) - Feature #85: XML raw extract should ignore name space (Stefan Becker) - Fixed #311: Crash when SIP transport becomes invalid (Stefan Becker) - Fixed #293: Mandatory wsa:MessageID node missing (Stefan Becker) - add support for Lync File Transfer protocol (Jakub Adam) * requires libpurple >= 2.12.0 * Lync FT will be used for sending files when Lync 2013 is detected - add build options to "About SIPE plugin" message (Stefan Becker) version 1.20.1 "Bug Fixes I" (2015-10-24) - add support for another type of ADFS response (Stefan Becker) - improve configure check for back-ported features (Stefan Becker, Jakub Adam) - updated translations: French (fr), Russian (ru) version 1.20.0 "SRTP, Conference URL & TLS-DSK Improvements" (2015-08-29) - Feature #82: Parse HTML from Lync conference URL (Stefan Becker) - Feature #69: SRTP Support (Jakub Adam) * requires libpurple >= 3.0.0 - Fixed #285: Office365 rejects RC4 in TLS-DSK (Stefan Becker) * added support for AES-128/256-CBC version 1.19.1 "Bug Fixes I" (2015-04-04) - Fixed #278: 488 error after libnice upgrade (Jakub Adam) - fix SIP re-authentication timeout to be max. 8 hours (Stefan Becker) version 1.19.0 "Auto Authentication, MFA & Search Improvements" (2015-02-07) - Feature #80: Move parsing of login name (Stefan Becker) - Feature #79: support for Adium group chat bookmarks (David Matz, Stefan Becker) - Feature #78: Support searching for SIP ID (Stefan Becker) - Feature #76: ADFS can't always be used (Stefan Becker) * for accounts that have Multi-Factor Authentication (MFA) enabled - Feature #73: Support buddy photos from contactCard (Stefan Becker) - Feature #65: Fall back from Kerberos to NTLM (Stefan Becker) - Fixed #277: Raised contact names (Stefan Becker) - Fixed #240: Corrupted HTTP response crashes SIPE (Stefan Becker) - fix calendar state machine when EWS URL is set (Stefan Becker) - fall back to [MS-DLX] BasicSearch to improve search experience (Stefan Becker, various) - implement search functionality for UCS (Stefan Becker) - adium: add chat room list UI (David Matz) - adium: fix duplicate debug log messages (Stefan Becker) - support for libnice TCP mode (Youness Alaoul, Jakub Adam) - refactor CCCP request code (Jakub Adam) version 1.18.5 "Bug Fixes V" (2014-12-29) - Fixed #276: Redundant "const" breaks build with clang (Stefan Becker) - Fixed #269: purple idle-away converted to Away (Stefan Becker) - svc: use authuser for RealmInfo request (Stefan Becker) - adium: add release checking script (Stefan Becker) - mingw: update fetch script to Pidgin 2.10.11 (Stefan Becker) - updated translations: Italian (it), Swedish (sv) version 1.18.4 "Bug Fixes IV" (2014-10-18) - Fixed #263: ADFS fails when user and login name differ (Stefan Becker) - Fixed #262: Adium: SIPE doesn't auto- or re-connect (Stefan Becker) - fixed memory leaks (Stefan Becker) - fixed processing of presence publish event response (John Zhang, Stefan Becker) * fixes a longstanding issue that the Pidgin user status sometimes didn't switch back to "Available" after the end of a meeting version 1.18.3 "Bug Fixes III" (2014-08-16) - Fixed #259: HTML response to EWS autodiscover triggers libxml2 assert (Stefan Becker) - Fixed #258: V&V call gets rejected when IPv6 is enabled (Stefan Becker, Jakub Adam) - Fixed #257: Windows 7: SIPE crashes after a minute (Stefan Becker) - mingw: improve crash information reporting (Stefan Becker) version 1.18.2 "Bug Fixes II" (2014-06-07) - Fixed #255: Crash when PersistentChat sends BYE instead of response (Stefan Becker) - Fixed #248: Remove libpurple SSL configure check (Stefan Becker) - Fixed #245: "Unable to resolve DNS SRV record" error when joining conference (Stefan Becker) - Fixed #241: Adium filters ":" from "sip:" (Stefan Becker) - Fixed #210: Conference call ends with error message (for real this time, Jakub Adam) - ews: extract settings also from type EXPR (Stefan Becker) - ucs: honor user specified email URL (Stefan Becker) - adium: fix compilation on OS X 10.7 (Stefan Becker) - updated Transifex URLs (Stefan Becker) - updated translations: Hindi (hi), Telugu (te) version 1.18.1 "Bug Fixes I" (2014-04-12) - Fixed #238: False "not delivered" in conference (Stefan Becker) - Fixed #237: HTML escaping not removed from URL (Stefan Becker) - Fixed #210: Conference call ends with error message (Jakub Adam) - fix endless loop with failed HTTP Basic authentication (Stefan Becker) - fix crash when gstreamer nice plugin is missing (Stefan Becker, Jakub Adam) - fix EWS autodiscover for some Office 365 users (Stefan Becker) - purple: fix missing "Copy to" in buddy menu (Stefan Becker) - purple/adium: ignore empty search values (Stefan Becker) - adium: fix group chat UI (Stefan Becker) - adium: implement BEAST mitigations for 10.8.5 (Michael Lamb) - add indication when user is connected from a mobile device (Harris Kauffman) - updated translations: Chinese (zh_CN), Portuguese (pt) version 1.18.0 "Adium, GSS-NTLMSSP & OpenSSL" (2014-01-11) - Feature #71: Add support for EWS Autodiscover redirection (Stefan Becker) - Feature #69: Add UI support for (group) chats (Michael Lamb) * NOTE: Adium does not have an UI to fetch the room list - Feature #64: Add support for GSS-NTLMSSP (Stefan Becker) * sip-sec-krb5.c module has been renamed to sip-sec-gssapi.c * if gssapi/gssapi_ntlmssp.h is detected then sip-sec-ntlm.c will be disabled and NTLM will be handled by sip-sec-gssapi.c instead * NOTE: at the time of this writing the user has to set up GSS-NTLMSSP by hand on his system, i.e. /etc/gss/mech - Fixed #227: Adium client doesn't save email option settings (Harris P. Kauffman) - Fixed #216: SIPE stops working on Mavericks (Stefan Becker, Michael Lamb) * add an UI option to disable SSL BEAST mitigations * NOTE: requires Adium 1.5.10 - Fixed #197: Account stays in connecting stage (Harris P. Kauffman) - cleanup for sip-sec Kerberos & SSPI modules (Stefan Becker) * replace old TGT hack with gss_acquire_cred_with_password() * clean up Kerberos detection in configure * remove special case handling; code is more straight-forward now * thanks to David Woodhouse and Simo Sorce for the GSSAPI information - implement internal keepalive handling (Stefan Becker) - implement crypto backend based on OpenSSL (Stefan Becker) - adium: Xcode project files update (Michael Lamb) - adium: replace NSS crypto backend with OpenSSL (Stefan Becker) * NOTE: please make sure to read the updated build instructions! version 1.17.3 "Bug Fixes III" (2013-12-11) - Fixed #225: HTTP re-authentication with NTLM fails (Stefan Becker) - Fixed #222: SIPE crashes when groupchat session expires (Stefan Becker) - fix UCS Persona key extraction (Stefan Becker) version 1.17.2 "Bug Fixes II" (2013-11-30) - Fixed #214: Typing notification does not always work (Stefan Becker) * reverted one change which caused problems for some users - Fixed #222: SIPE crashes when groupchat session expires (Stefan Becker) - updated translations: Romanian (ro) version 1.17.1 "Bug Fixes I" (2013-11-16) - Fixed #215: Password not entity encoded in WSSE element (Stefan Becker) - Fixed #214: Typing notification does not always work (Stefan Becker) - accept alternatives for webticket timestamp/keydata (Stefan Becker) - adium: add "don't publish calendar" to account UI (Stefan Becker) - contrib: add SSL BEAST mitigation patch for Adium (Stefan Becker) - updated translations: French (fr) version 1.17.0 "Lync 2013" (2013-09-21) - Feature #62: Support for Lync 2013 Unified Contact Store (Stefan Becker) - Feature #59: Support for Lync 2013 Persistent Chats (Stefan Becker) - Fixed #211: Status "away" or "busy" incorrectly mapped to "Invisible" (Michael Lamb) - Fixed #209: group chat doesn't like HTML (Stefan Becker) - Fixed #200: OCS archiving system blocks audio/video connection (Jakub Adam) - Fixed #187: Duplicate messages in group chat (Stefan Becker) - Fixed #184: Duplicate users showing in Group Chat (Stefan Becker) - fix EWS autodiscover for Office 365 (Stefan Becker) - add support for group chat history (Stefan Becker) - add support for buddy photos on Lync 2013 (Stefan Becker) version 1.16.1 "Bug Fixes I" (2013-07-13) - Feature #66: Windows DLL version information (Stefan Becker) - fix call failure when host has multiple IP addresses (Jakub Adam) - fix buddy list handling after moving to Lync 2013 (Stefan Becker) * Lync 2013 migrates buddy list to Unified Contact Store (UCS) * NOTE: modifying the buddy list is *NOT* supported yet! - crash fixes for new HTTP stack (Stefan Becker) version 1.16.0 "HTTP Rewrite & Subscription Fixes" (2013-06-14) - Feature #58: Implement Digest authentication scheme for SIP Proxy Authentication (Stefan Becker) - Fixed #196: Useragent value not forwarded to core (Michael Lamb) - Fixed #193: Pidgin Status changes stop working (Stefan Becker) - Fixed #186: Users appear offline when they are not (Stefan Becker) - fix kinit-less use case with krb5 >= 1.11 (Stefan Becker) - rewritten HTTP stack from scratch (Stefan Becker) * cleaner, layered and hopefully less error-prone implementation * HTTP stack internals no longer exposed to user code * reduced network traffic and less SSL handshakes by utilizing HTTP/1.1 connection keep alive for multiple HTTP requests to the same host - switch purple backend to deferred destruction approach (Stefan Becker) * Pidgin should no longer crash at connection close, even in corner cases - add menu entry to make a call with a phone number (Jakub Adam) - some progress on telepathy backend (Stefan Becker) * add TLS certificate accept/reject user interaction * add "Single Sign-On" & "Don't Publish Calendar" account options version 1.15.1 "Bug Fixes I" (2013-04-07) - NOTE: SIPE SourceForge project got updated. Because of this all bug and feature request numbers have changed. - Fixed #190: SIP 407 response rejected with invalid message signature (Stefan Becker) - Fixed #189: Adium SIPE plugin vs. libpurple linking issues (Michael Lamb) - fixed free-after-use issue that caused crashes for some users (Stefan Becker) - fixed broken NTLM fallback in Negotiate (Stefan Becker) - fixed subscriptions expiration by subscribing again after re-authentication (Stefan Becker) - allow different user name and login for Office 365 authentication (Stefan Becker) - add SIPE version & git commit ID to debug log (Stefan Becker) - added valgrind log analyzer script (Stefan Becker) - added NTLM message anaylzer (Stefan Becker) - updated translations: Hungarion (hu), Romanian (ro) - updated Adium port (Michael Lamb, Harris P. Kauffman) version 1.15.0 "Authentication & Autodiscovery Update" (2013-03-09) - Feature #3578135: Support Kerberos for HTTP(S) authentication w/o SSPI (Stefan Becker) * effective for all platforms that support --with-krb5 * this triggered a series of cleanup & simplification changes and functionality & memory leak fixes in the sip-sec modules * special thanks to Jarek Polok for the logs and testing - Feature #3594094: Add HTTPS to autodiscover probe (Stefan Becker) - Feature #3607040: Simple button to disable calendar integration (Stefan Becker) - Fixed #3603228: Crash on 1.14.1 when connecting to server (Stefan Becker) - Fixed #3604671: sip uri with apostrophe is not valid (Stefan Becker) - fixed HTTP redirect crash (Stefan Becker) - unified Single Sign-On handling in all places (Stefan Becker) * if SSO is enabled then "Login" & "Password" settings are ignored * SSO is now off by default for new accounts * NOTE: if you do *NOT* use SSO, then be sure to disable it in the "Advanced" tab of the account settings after updating! - added implementation for HTTP "WWW-Authenticate: Negotiate" scheme (Stefan Becker) * effective for all platforms that support --with-krb5 * it will try Kerberos first, then fall back to NTLM * valid Kerberos Single Sign-On setup will be detected automatically * setup for a mixed Kerberos/NTLM HTTP environment: - login name: DOMAIN\account - password: domain password - authentication: Kerberos - Single Sign-On: OFF(!) (see above) - enabled TLS-DSK support in Windows SSPI version - TLS-DSK: don't ask for password if SSPI or Kerberos are compiled in - Farstream 0.1.1 compatibility fix (Jakub Adam) - support conf:sip: meeting URIs (Jakub Adam) - updated Adium port (Michael Lamb) version 1.14.1 "Bug Fixes I" (2012-12-26) - Feature #3578132: Kerberos configuration should be passwordless (Stefan Becker) * purple: non Single Sign-on users are asked for the password again - bug & memory leak fixes in sipe-buddy.c (Jakub Adam) version 1.14.0 "Buddy photo & ADFS support, Web Ticket Optimizations" (2012-12-16) - Feature #3585364: Add support for Web Ticket authentication using ADFS (Stefan Becker) * special thanks to user bhakta79 for the hard work taking logs - Feature #3578132: Kerberos configuration should be passwordless (Stefan Becker) - Fixed #3580212: Connection drops after a few hours (Stefan Becker) - add support for buddy photos (Jakub Adam) - add support for call to Audio Test Service (Jakub Adam) - initial implementation for telepathy backend (Stefan Becker) * nothing much to see for end users yet... - reduce Web Ticket traffic by queueing requests & caching tickets (Stefan Becker) - update OBS packaging information for Debian (Stefan Becker) - various minor bug & build fixes version 1.13.3 "Bug Fixes III" (2012-08-19) - Fixed #3537084: OpenBSD build issue (Stefan Becker) - Fixed #3543294: Support Lync 2010 meet URLs (Jakub Adam) - revert to legacy MSOC protocol on Lync FT invitation (Jakub Adam) - fix broken busy->available status switch (Stefan Becker) - updated translations: Portuguese (pt) version 1.13.2 "Bug Fixes II" (2012-06-10) - tls: fix buffer overrun (Oleksandr Hryshchuk, Stefan Becker) - win32: fix TCP connections (Stefan Becker) - nsis: fix broken locale installation (Stefan Becker) - updated translations: French (fr) - various build fixes (Stefan Becker, Jakub Adam) version 1.13.1 "Bug Fixes I" (2012-04-09) - detect incompatible encryption level with Lync (Jakub Adam) - purple: add URI validity check to Add Buddy callback (Stefan Becker) - new translations: Romanian (ro), Turkish (tr) - various build fixes (Stefan Becker) version 1.13.0 "Lync & Office365" (2012-03-14) - added [MS-SIPAE] TLS-DSK authentication scheme (Stefan Becker) * TLS-DSK has been introduced in Lync * mandatory for Office365 accounts * also works for non-public Lync installations * does not work yet with SSPI on Windows - added [MS-DLX] based Get Info/Contact Search (Stefan Becker) * [MS-PRES] SIP-Based Active Directory Search is disabled in Lync - added experimental media TCP transport (Jakub Adam) - make it compile against the latest purple 3.0.x API (Stefan Becker) - make it compile against the latest glib2 2.31.x API (Stefan Becker) - completed cleanup: core no longer requires libpurple (Stefan Becker) - refactored crypto code, ie. NSS can replaced if necessary (Stefan Becker) - sipe-domino.c is no longer built under UNIX to remove dead code (Stefan Becker) - restricted XXX_CFLAGS to modules that need them (Stefan Becker) - NSS is now a mandatory build requirement (Stefan Becker) - decoupled SSPI from HAVE_LIBKRB5 flag. New flag is HAVE_SSPI (Stefan Becker) - OBS mingw packages now use SSPI instead of NTLM (Stefan Becker) - added NSIS package generation to OBS mingw packages (Stefan Becker) - removed kopete backend. KDE is moving to telepathy (Stefan Becker) - added MinGW cross-compilation on Linux instructions (Stefan Becker) version 1.12.0 "Group Chat" (2011-08-29) - Feature #3064877: Add support for OCS2007R2 Group Chat (Stefan Becker) - Feature #3311026: Support for HTTP/1.1 Transfer-Encoding: chunked (Stefan Becker) - Fixed #2834758: First NTLM signature check after startup fails (Stefan Becker) - Fixed #3082602: Crash on Autodiscover (Stefan Becker) - Fixed #3090663: Re-authentication fails (Stefan Becker) - Fixed #3092324: Core dump in "make check" (psfales) - Fixed #3130915: Failed to authenticate to server (Stefan Becker) - Fixed #3148124: sipe segfaults during login on Solaris (Jakub Adam) - Fixed #3150482: "configure --with-vv" test uses wrong include (Stefan Becker) - Fixed #3156430: Messages not Delivered (rwinchsf, Stefan Becker) - Fixed #3161273: Lost Connection Gives No Error Message (rwinchsf, Stefan Becker) - Fixed #3198585: Extra line breaks (Stefan Becker) - Fixed #3267073: False "could not be delivered" errors (sort of..., Stefan Becker) - Fixed #3399007: Crash when sipe_cal_working_hours->days_of_week is NULL (Stefan Becker) - Patch #3091490: Make 1.11.0 Compile on FreeBSD (jprather) - Patch #3108246: Patch for better windows installer (archrival, galiven) - add random Ms-Conversation-ID to INVITE (Jakub Adam) - fix parsing of P-Asserted-Identity header (Jakub Adam) - added MS TURN support (Jakub Adam) - fix crash on zero length password in NTLM (Vladimir Ushakov) - implement timeouts for SIP request. Used for REGISTER (Stefan Becker) - more work on Voice & Video call support (Jakub Adam) - make it compile against the purple 2.8.x & 3.0.x APIs (Stefan Becker) - more internal changes to prepare for non-purple backends (Stefan Becker) - added integration for transifex.net update (Stefan Becker) - configure improvements for 64-bit: use libdir, gsize/size_t compatibility (Stefan Becker) - update compiler warnings configuration for all build platforms (Stefan Becker) - updated Adium port (Matthew Duggan) - mingw build updates (Harris P. Kauffman, Stefan Becker) - added miranda port (Jochen De Smet) - added mingw to OpenSUSE Build Service configuration (Stefan Becker) version 1.11.2 "Hot fixes II" (2010-11-02) - Revert "mingw: add missing purple-notify.c to build" (Anibal Avelar) - Sipe-sign: fix parsing of P-Asserted-Identity header (Jakub Adam) - Fixed memory leaks (Stefan Becker) - Fix #3090663: Re-authentication fails (Stefan Becker) - Fix #3090663: Re-authentication fails (2nd attempt) (Stefan Becker) - Fix #3090663: Re-authentication fails (3rd attempt) (Stefan Becker) - Fix #3090663: Re-authentication fails (4th attempt) (Stefan Becker) - Apply patch #3091490: Make 1.11.0 Compile on FreeBSD (jprather) - Fix #3092324: Core dump in "make check" (Stefan Becker) version 1.11.1 "Hot fixes" (2010-10-24) - mingw: add missing purple-notify.c to build (Stefan Becker) - Fix for bug #2834758: First NTLM signature check after startup fails - purple: fix memory leak in sipe_backend_transport_connect() error path - Fix for bug #3082602: Crash on Autodiscover (Stefan Becker) - configure: use libdir & datadir instead of prefix + path (Stefan Becker) - configure: update 32-bit vs. 64-bit header conflict test (Stefan Becker) - debian: build stability fix in post-install (Stefan Becker) version 1.11.0 "Lotus Domino/Calendar & Voice Call" (2010-10-04) - Feature #2859239: Voice call support (Jakub Adam) * requires updated versions of libnice, farsight & pidgin * only unencrypted calls as SRTP support is missing in farsight - Feature #2945346: Lotus Notes/Domino Calendar integration (pier11): * Sipe can now retrieve calendar data (Meeting schedule/subject/ location) from a web-enabled Lotus Domino server and publish it to OCS2007/LCS2005 as availability information. * Example: "Calendar: Currently Busy. Free at 11:30". * Team members (contacts with access level Team) will be able to see information about our current meeting (subject & location) (OCS2007). * First calendar update is scheduled 1 minute after connect, * Subsequent calendar updates happen in 30 minute intervals. * Manual calendar update can be triggered using the following menu: Accounts->{SIPE_ACCOUNT}->"Republish Calendar" * Though Domino integration can work without any additional settings in account configuration (on Windows), there are options to manually provide Domino Services URL and email address/password if it's different from SIP URI/Password settings on Basic tab. - Fixed #2971422: idle check for OCS2005 presence case (Stefan Becker) - Fixed #2982424: krb5 build errors on FreeBSD (Stefan Becker) - Fixed #2997639: pidgin crash after accepting cert (Stefan Becker) - Fixed #3001523: Cancelling a long pending file transfer crashes Pidgin (Jakub Adam) - Fixed #3002993: Group Name issues with ampersand (Stefan Becker) - Fixed #3029228: Calendar published at/with incorrect time (Stefan Becker) - Fixed #3029929: Crash with outlook 2k3 Calendar (Stefan Becker) - Fix logout from OCS (Jakub Adam) - Implement workaround for buddy list menu memory leaks (Stefan Becker) - Dropped UDP transport support (Stefan Becker) - Rewrote TCP & TLS transport support (Stefan Becker) - HTTP improvements: GET, cookies (pier11) - Alternative crypt/digest implementation based on NSS (pier11) - Rewrote message debug log and implemented an analyzer script for it (Stefan Becker) - More internal changes to prepare for non-purple backends (Stefan Becker) - New translation: Dutch (Flemish) 'nl' (fieona, ridiekel) - New translation: Swedish 'sv' (Rijad) - Added translations 'ar' 'hu' 'ja' 'ko' 'sv' 'zh_TW' submitted by Novell - New build option: nss/mozilla-nss/microb-engine-nss for non-purple backends - Add build option for kopete backend (Stefan Becker) - mingw build using standard approach, i.e. with auto* tools. (pier11) - Updated Adium port (Emanuele Zattin) version 1.10.1 "Bugfix release" (2010-06-27) - Fix broken sipe_ht_equals_nick(); the broken code has been in the 1.10.0 release. As it affects the buddy list it could be the root cause for some of the "buddy appears offline" reports. (Stefan Becker) - Make it compile against the final purple 2.7.0 API; (Stefan Becker) version 1.10.0 "Access Levels" (2010-04-04) - Feature #2823160: Access Levels (2007+ environment). The functionality is available through contact's context menu "Access level" and also presented on contact's tooltip. Current individual access level is marked with star (*), current group access level is marked with equals sign (=). (pier11) - Feature #2957811: add support for "automaton" class (Stefan Becker) - Feature #2972823: fail on in-line variable declarations (Stefan Becker) - Fixed #2971422: handle OCS 2005 idiosyncrasy of varying SIP URI case (pier11) - Fixed #2981563: Authentication protocol v4 - invalid signature of some incoming messages containing P-Asserted-Identity or P-Preferred-Identity with uppercased SIP or TEL in URI. (pier11) - Fixed publication/"409 Conflict" endless looping (Stefan Becker, pier11) - Prepare for Pidgin 2.7.0 (Stefan Becker, pier11) - Windows build updated to gtk+2.14, gettext 0.17, libxml2 2.7.4, gcc 4.4 (pier11) - Added SVG icon artwork (Jakub 'jimmac' Steiner) - Many changes to configure script. Be sure to look at "configure --help"! (Stefan Becker) - Many internal changes to prepare for non-purple backends (Stefan Becker) - New build requirements: libxml2, glib-2.0 >= 2.12.0 - New build option: gmime-2.4 >= 2.4.16 or gmime-2.6 >= 2.5.2 for non-purple backends version 1.9.1 "Hot fixes" (2010-03-16) - Fixed #2969327: Kerberos authentication always fails on *nix platforms (pier11) - Fixed #2968287: Authentication failure in scenario when director server is Office Communications Server 2007 or above and home server is Live Communications Server 2005 (pier11) - Send BYE when response to IM message is 408/480/481 (Stefan Becker) - Re-enable offline status to be user settable (Stefan Becker) version 1.9.0 "File transfer & NTLMv2" (2010-03-10) ***** Important Security Update for *nix users ***** - Contributed File transfer functionality. File encryption is supported. (Jakub Adam, Tomáš Hrabčík) - NTLMv2 and NTLMv2 Session Security support (pier11) - Implemented SIP Authentication Extensions protocol version 4 and 3 (pier11) - Adoption for commercial UNIX - HP/UX, Irix, Solaris - big endian fixes and build improvements (Peter Fales, Stefan Becker) - Packaged for Maemo platform (Nokia N900, etc.) at Maemo.org. Works with Pidgin from the same site. (Stefan Becker) - Adoption of file transfer for Windows build (pier11) - Official Debian package files in contrib (Anibal Avelar) - another shot at presence update problems (Stefan Becker) - fix crash caused by uninitialized security contexts (Stefan Becker) - Code analysis with Coverity Prevent. (Stefan Becker) - Updated translations: 'ru' (100%, pier11), 'de' (100%, Stefan Becker) 'es' & 'pt_BR' (100%, Anibal Avelar) - Fix for "SIP/2.0 481 Call leg unavailable" error (Anibal Avelar) - Increased libpurple build requisite to >= 2.4.0 version 1.8.1 "Exchange/Calendar crash fixes" (2010-02-16) - many crash fixes for error or corner cases in calendar integration (pier11, Stefan Becker) - more detailed code analysis with Coverity Prevent (Stefan Becker) - build fixes for older libpurple/glib2 versions (pier11) - OpenSUSE Build Service configuration files (pier11) - Fix NTLM crash if login setting is undefined (pier11) - Use of g_str_has_prefix() available since glib 2.2 and null-safe (pier11) version 1.8.0 "Exchange/Calendar" (2010-02-07) - Added integration with Exchange 2007/2010. Now Sipe retrieve our Calendar data (Free/Busy, Working Hours, Meeting Subject/Location, Out-of-Office Note) from Exchange Web Services and publishes it to OCS2007/LCS2005. Thus our contacts can see our availability information based on Calendar data. For example: "Calendar: Currently Busy. Free at 11:30" or "Calendar: Currently Free. Outside of working hours at 18:00". Contacts will also see our Out-of-Office message if it's enabled in Exchange/Outlook. Team members (contacts with access level Team) will be able to see information about our current meeting - subject and location (OCS2007). First call to Exchange is scheduled with 1 minute delay after Sipe start. Subsequent update intervals are 30 minutes. There is a way to manually trigger Calendar data update: Accounts->{SIPE_ACCOUNT}->"Republish Calendar" menu option. Though Exchange integration can work without any additional settings in account configuration, there are options to manually provide email address if it's different from SIP URI, Exchange Services URI, email account authentication if it is different from configuration on Basic tab. (pier11) - Added Calendar information to contact's tooltip. You can see it like "Calendar: Currently Tentative. Busy at 11:30" or "Calendar: Outside of working hours for next 8 hours". (pier11) - 2005 presence engine has been completely rewritten. Now it supports "Do not disturb" status(taken from UserState), "In a meetinfg" status (taken from Calendar stream data, and most importantly changing autonomously in line with the stream). "In a meeting" activity reflects scheduled times of activities in Outlook/Exchange with Busy state. Updated 2007 presence engine too. Pidgin Statusbox now reflects last status and note set by our other points of presence; also updates according to our Calendar state (i.e without manual action); displays our Out-of-Office message if any. (pier11) - Added "Accounts->{SIPE_ACCOUNT}->Status Reset" menu option to clean User Status set manually. The latter can override Calendar status for example. (pier11) - Added "Find on LinkedIn" link on contact's User Info screen for more in-depth information about your contact if available. Both SIPE and LinkedIn.com professional network belong to enterprise domain, so match is quite good. (pier11) - Enhanced custom NTLM security provider to pass connection-oriented authentication. Used in Web authentication, for example with Exchange Web Services. In contrast to connectionless (datagram) NTLM authentication type used in SIP. (pier11) - Added Negotiate authentication scheme (Windows only). Used in HTTP authentication. (pier11) - Contributed code for Adium port. (Matt Meissner) - Added Windows Messenger 5.0 (RTC/1.2) compatibility. Though very old, some our clients use it on LCS2005. (pier11) - New BusyIdle status. (pier11) - Placed Sipe to Transifex.net translation portal allowing easily add/modify translations through web interface. (Stefan Becker) - Updated Sipe About screen ("Accounts->{SIPE_ACCOUNT}->About SIPE plugin") with our translation page. (pier11) - New translation: Polish 'pl' (Piotr Drąg) - Updated translations: 'zh_CN' (Kang Kai), 'ru' (pier11) and 'de' (Stefan Becker) - Static/dynamic code analysis with Coverity Prevent, memory leaks fixes (Stefan Becker) - Fixed memory leaks found with cppcheck (http://sourceforge.net/projects/cppcheck) (Edmondas Girkantas) - Fixed localization on Windows platform(!) Now translations are shown. (pier11) - Fix for #2907567 Note parsing issue. Incoming html markup (like < symbol) could wierdly rerender Pidgin's contacts list UI - for example to "rename" contact, or even replace contact name by group name. - Fix for #2908830 Federated contacts appear to be broken. 2005 Public IM Connectivity (PIC) environment. (pier11) - Fix crash when SSL connect fails (Stefan Becker) - Fix for server auto-discovery (Anibal Avelar) - Fix for #2912926 crash on exist. Caused by improper CSTA communications. (pier11) - Fix for xdg-email invocation. (David Woodhouse) - Fix for SLED 10 compilation. (pier11) - Fix for OpenSolaris port - #pragma pack() issue. (Stefan Becker) - Fix compilation without Kerberos (Stefan Becker) - Fix for User Agent string. (pier11) - Fix for compilation with Pidgin 2.6.4 and 2.6.5 on mingw. (pier11) - Fixed #2944156: SIPE Authentication Causes Pidgin Crash(Stefan Becker) - RPM SPEC: add Epoch: for git packages. (Stefan Becker) - Remove static link build option, remove unused config.h, other build improvements. (Stefan Becker) - Make tests compile again; Simplify "make tests" (Stefan Becker) - Code: Take PURPLE_INIT_PLUGIN into use.(Stefan Becker) version 1.7.1 (2009-11-19) - "About SIPE plugin" screen implemented. Accessible as "Accounts->{Your Account}->About SIPE plugin". - New correct 'User Agent:' header in SIP messages with Purple and Sipe versions, host operating system type and processor architecture as well as OCS version. To take advantage of it - empty your old 'User Agent' setting in the account configuration. - Version now stored in single place - VERSION file and used for all builds. - Core - not using bodies for single subscriptions in 2005 environment. Optimized CSeq numbers usage. Beautified debug log output. - Call Control - session timer for dialog with CSTA. Otherwise session with CSTA got expired after ~30 min due to no refresh. RFC4028. - Call Control - removing internal alternative phone number from phone string. - RPM spec file update for CentOS/RHEL 5 - purple-sipe needed a "Group" (John B.) - Fixed crash in 2005 'New Chat' menu. - Fixed #2886534 - routing - Request-URI/Route headers. Problem was a re-subscription failure resulting in stopping contacts' status update after approximately 8 hours. - Fixed #2892842 - interference of conference invite to normal IM dialog on 2007 environment. Incoming BYE from conf invite process used to kill ongoing regular IM dialog with the same user. - Fixed crash in presense processing on Reuters environment (LSC 2005) on Linux. - Fixed #2882304 - 'Note' not working with LCS2005 - Fix for the First message in 2005 multiparty chat. - Fix for initial outgoing messages - some were lost if typed too fast if dialog was not established yet. - Fixed #2882658 - SIGSEGV in process_incoming_info_csta() - happened on some Cisco-Systems 7 CSTA Gateways. - Fixes for SLED 10 SP2 and SP3 compilations. version 1.7.0 "Remote Call Control" (2009-10-19) - Added integration with PBX (external phones) using CSTA protocol (ECMA TR-87). Users can initiate a call by clicking on contact phones in right-click context menu. This feature should be enabled for user on the server side. - Added reconnection to chat after disconnection by continue typing in the same open chat window (OCS 2007 only). Feature request #2866630. - More precise errors shown on message undelivery event. - Better re-subscription logic for contact status updates. - 'Out of Office', 'In a Meeting', 'In a Conference', etc. contact activities are shown now in contast's status and tooltip. Also meeting subject and location are shown in 2007 environment if contact is in Team access category. - New Idle status. - Added 'Site' to User Info panel. Can be a link to user's corporate directory page or similar. - Removed message acceptance in incoming INVITE. - Fixed type errors in sip-sec-ntlm.c for Linux/Unix platforms. - Added 32- and 64-bit header conflict check to configure. - Removed libsipe.so from old incorrect installation location. This makes sure that DIY users don't have two copies of libsipe.so. - Moved libsipe.so to the correct installation directory. The libpurple protocol plugins directory is $(libdir)/purple-2. While the name of the plugin is pidgin-sipe, it works perfectly fine with 'finch' too. - Rewritten transaction payload handling. This should fix memory leaks in transaction handling. - Fixed crash on reauthentication when IM window is open not Chat window - Fixed warnings on compilation. - Fixed memory leaks. version 1.6.3 (2009-09-09) - Added missing krb5-devel BuildRequires to the RPM spec file. - Cleanup of source code compatibility for libpurple 2.4.x - Fixed for msrtc statuses, bug #2843985 - Fixed HTML markup in note, bug #2841095 - Fixed a segmentation fauls when the function sipmsg_parse_header backs a null value. - Possible fixed a crash when SIPE account added, bug #2844545 - Reduced libpurple build requisite to >= 2.3.1 - Fixed NetBSD compilation errors in sipe-utils.c, bug #2847380 - Updated zh_CN translation - Fixed a crash when pidgin auto-away fires, bug #2849156 version 1.6.2 (2009-08-23) - Treat of 504 Server time-out error - Decrease libpurple build prerequisites to >= 2.4.1 - Updated windows build to latest Pidgin 2.6.1 - Fix compilation errors against libpurple >= 2.6.0 API - Publishing optimization - Separate storage for subscription dialogs. - Fixed '409 Conflict' case. We are extracting proper versions of our publications and updating local mirror with them. - Fixed CSeq for subscription dialog. - Fixed for unsubscribes - Fixed for Active Directory search - Make OCS2007+ conditionals self-documenting - Fixed compilation problem on 64-bit and NetBSD platforms #2839689 - The 'context' element only for just added subscriptions #2836290 - Removed dependency on com_err.h - Fixed warnings on compilation. - Fixed memory leaks. - Updated "de" translation version 1.6.1 (2009-08-12) - Fixed missing backslash in "es" translation file - Support publication: modular publications - Support publication: manual vs machine status publication - Fix timeout calculation in sipe_buddy_subscribe_cb(), make sure timeout is never 0 - Fixed crash in sipe_process_roaming_self() - Add purple >= 2.5.0 as configuration requirement - Cleanup username handling in sipe_login() - Make password mandatory again - Fixed memory leaks in sipe_login() error paths - Removed controversial dependency in windows branch used for old MAC code - Got rid of MAC in epid generation - Updated "es" translation - Updated "de" translation - Cleaned up connection configuration and setup - More precise treatment of incoming typing info - Fixed improvements reported in #2833015 - Fixed for "message was not delivered" reported in bug #2832551 - Fixed for CHOWA problems reported in bug #2795132 - Fixed for crash reported in bug #2795132 - Tag all unused function parameters - Added Empathy support - telepathy-haze config for sipe - Code around GCC bug on ppc platforms - License file cleanup - Fixed ppc64 compilation errors - Many refinements to the RPM SPEC file - Added convenience script to (re)build from git repository - Added convenience script for creating git snapshots version 1.6.0 "Multiparty Chat" (2009-07-27) - Multi-party conversation (Chat) functionality for: + OCS 2007+ as a conference [MS-CONFBAS], [MS-CONFIM], [MS-CONFPRO]; + LCS 2005- as a multi-party chat. - Fixed our status in OCS 2007+ environment as seen by others. - Asks user if he wants add counterparty which added him to his contact list (OCS 2007+). User dialog wording improvement. Also defined default group in Add User dialog which appear after user search in catalog. - More strict compilation warnings treatment. - Started code split to functional modules. - Tailored subscriptions for environment - eliminated not needed subscriptions in 2007 environment. - Fixed session closing on unsuccessful message delivery. - More correct incoming BYE processing. - Fixed stealth bug when action payload got freed. Initial bug was that contact could not be removed. - Fix handling of empty note text in incoming rlmi NOTIFY. - patch for FreeBSD 7.x by jprather. - Fix for wrong epid generation on Windows platform in case of missing MAC. - Fix for Route processing. - Improvements for compilation under ppc/ppc64 platfolrm. - Fixed crash if work in non-authenticating environment. version 1.5.0 "Kerberos" (2009-06-24) - Fixed bug #2800325 for crash if wrong IP manually typed into Pidgin. - New Security Framework: dedicated to use plug-able security mechanisms. + Added Kerberos/NTLM SSPI for Windows + Added Kerberos MIT for Linux + Fixed the NTLM native implementation for Linux - New options on Advanced tab: Use Kerberos and Single Sign-on (for both kerberos implements). - Fixed the --with-krb5 compilation option. Now pidgin-sipe can be compiled with krb5 support again. - To send presence subscribe requests to poolFqdn servers - Record-Route header can contain multiple routes. For CHOWA clients. - Fixed 2 bugs with INVITE messages from incoming CHOWA clients. - FreeBSD compatibility - Fixed for 'same conversation ID' issue - Fixed for Reuters(LCS2005) frequent disconnects - Fixed many warnings on compilation. - Fixed many potential memory leaks. Thank you Stefan - Fixed I18n support and sync from launchpad + Added 'it' and 'ru' translations + Updated 'zh', 'cs', 'es' and 'nb' translations. version 1.4.1 (2009-06-01) - Patch for spaces in Login's username - Fixed bugs #2793431, #2793688 for crash in process_incoming_notify_rlmi_resub - Fixed the correct presence for contacts with the presenceMode="msrtc". - Simplify keep alive timeout setting code - Fixed the resubscription process in LCS2005 and OCS2005. - Fixed the support for batched subscription - Fixed a problem when you added a new contact on OCS2007 - Fixed potential memory leaks. Thank you Stefan version 1.4.0 (2009-05-17) - Simplified Account Settings Basic screen - Full support for presence using Batched Category Subscription for OCS2007. [MS-PRES] - Full support for presence usgin Batched Simple Subscription for LCS2005. [MS-SIP] - Active Directory search for 'Get Info' screen. - Buddy URI are replaced with Display Name. - Added non sensitive support between the client and servers answers. - Support for deregistration [MS-SIPREGE] - Support for 2007 R2 client message formatting. - Re-subscription after expiration. LCS2005 - Added escaping to SOAP request parameters - Implemented a new scheduling system - Cleanup status id handling - NOTIFY and BENOTIFY similarly handled - Implemented piggy-back for buddy subscription. - Request for functions only supported for each kind of servers (Allow-Events) OCS2007 and LCS2005. (may be LCS2003?) - Fixed many warnings on compilation. - Fixed many potential memory leaks. Thank you Stefan - Fixed bug #2786479: some messages could not be delivered to all users. - Fixed bug #2779386: fix for invalid NTLM signature. version 1.3.4 (2009-04-13) - Fix segmentation fault caused by redirect - Added check for zlib.h, com_err and purple version on configure script, - Added note to tooltip - Added timeout of security association after eight hours as described in [MS-SIPAE] 3.2.2 Timers. - Updates to enable compilation in a Cygwin/MinGW environments - Fix a problem with the function purple_get_host_name() in libpurple versions < 2.5.x - Support for responses with m=x-ms-message rather than m=message - Support for Reuters Messaging environment (LCS2005 like) - Support for message formatting (font face, style, color). - Introduced invisible mode (Appear Offline). Better status support. official clients - Richer status support with reference to [MS-PRES] and work of - Fix for bug #2528391: Accepts the first message from the first packet (INVITE). Counterparty's first message now can appear up to 2 sec earlier. - Fixed the offline contacts problem in OCS2007 following [MS-PRES] - Added notification support for undelivered messages. version 1.3.3 (2009-02-09) - Implemented keep alive support. Thanks Stefan Becker - Support LCS 2005 buddy auth/blocking. Thanks Gabriel Burt - Fixed SSL errors. - Fixed many segmentation fault errors. - Added many translation files. version 1.3.2 (2008-12-14) - Added support for mingw. Thanks Carl Seutter - Merge with stable branch in the git repository - Fixed SSL input processing - Extended contact search implementation - Path to xBSD support version 1.3.1 (2008-12-09) - Locale/translations support - Search contacts support - Code cleaned - LCS 2005 fixed send/receive messages;partially fixed version 1.3 (2008-12-04) - TLS/SSL support - Fixed signing messages, thanks Gabriel Burt - Fixed LCS contacts with add/remove/modify, thanks Gabriel Burt - Added Presence support for OCS 2007 and LCS 2005 - A lot of patches to fix: +TLS connections, +Send/receive messages, +Full presence, +TCP connections and optimal options in Advanced tap. - A lot bugs fixed version 1.2 (2007-03-22) - Finished the pidgin 2.0 porting - Fixed a NTLM auth problem about the flags on challenge3. - Fixed the Contact problem. - Fixed the segmentation on network detection. - Other minor bugs version 1.1 (2007-03-09) - Fixed a bug in NOTIFY and SUBSCRIBE methods (401 error) - Changed the method PUBLISH(don't aply in LCS) for SERVICE - Added new tags in transactions to cloned the LCS transactions. - The MESSAGE method works. Now you can send a message to online user. version 1.0 (2007-03-04) Gaim plug-in releases. First release using autoconf and automake. Compiles with gaim-2beta5 (or newer). On Debian systeam you need the gaim-dev package for compile it. First version. Just added this functionalities: - Authentication (just type NTLM) - Presence (double sided) - Retrieves all contacts from server (with groups) - Delete contacts and groups ================================================ FILE: Makefile.am ================================================ SUBDIRS = src pixmaps po EXTRA_DIST = \ contrib \ intltool-extract.in \ intltool-merge.in \ intltool-update.in \ pidgin-sipe.nsi \ pidgin-sipe.wxs \ siplcs.vcxproj \ siplcs.vcxproj.filters \ Makefile.mingw \ po/Makefile.mingw CLEANFILES = \ intltool-extract \ intltool-update \ intltool-merge MAINTAINERCLEANFILES = \ aclocal.m4 \ ABOUT-NLS \ compile \ configure \ config.guess \ config.h.in \ config.rpath \ config.sub \ depcomp \ GITVERSION \ INSTALL \ install-sh \ intltool-extract.in \ intltool-merge.in \ intltool-update.in \ ltmain.sh \ Makefile.in \ missing \ mkinstalldirs \ po/Makevars.template \ po/Rules-quot \ po/*.header \ po/*.sed \ po/*.sin \ test-driver \ ylwrap maintainer-clean-local: -rm -rf m4 ACLOCAL_AMFLAGS = -I m4 ================================================ FILE: Makefile.mingw ================================================ ################################### tell Emacs this is a -*- makefile-gmake -*- # # Copyright (C) 2012-2014 SIPE Project # # Makefile.mingw # # Author: pier11@operamail.com # Author: zup@sbox.tugraz.at # Date 11 Nov 2009 # Description: Top Makefile for win32 (mingw) port of LCS protocol plugin # ############################################################################### ifndef PIDGIN_TREE_TOP # standalone MinGW build export PIDGIN_TREE_TOP := ../pidgin-2.10.0 endif include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak export CFLAGS=-Werror WIX3_HOME := /cygdrive/c/Program\ Files/Windows\ Installer\ XML\ v3 WXS_NAME := pidgin-sipe export VERSION := $(shell cat VERSION) PIDGIN_VERSION := $(shell cat $(PIDGIN_TREE_TOP)/VERSION) .PHONY: all clean install msi dev devinst nsis all: $(MAKE) -C src -f $(MINGW_MAKEFILE) $(MAKE) -C po -f $(MINGW_MAKEFILE) dev: $(MAKE) -C src -f $(MINGW_MAKEFILE) clean: $(MAKE) -C src -f $(MINGW_MAKEFILE) clean $(MAKE) -C po -f $(MINGW_MAKEFILE) clean rm -f $(WXS_NAME).wix* rm -f $(WXS_NAME)-$(VERSION).msi rm -f pidgin-sipe-$(VERSION).exe rmbak: rm -f *~ $(MAKE) -C src -f $(MINGW_MAKEFILE) rmbak install: all $(MAKE) -C src -f $(MINGW_MAKEFILE) install $(MAKE) -C po -f $(MINGW_MAKEFILE) install $(MAKE) -C pixmaps -f $(MINGW_MAKEFILE) install devinst: dev $(MAKE) -C src -f $(MINGW_MAKEFILE) install tests: dev $(MAKE) -C src -f $(MINGW_MAKEFILE) tests msi: all PACKAGE_VERSION=$(VERSION) $(WIX3_HOME)/bin/candle $(WXS_NAME).wxs $(WIX3_HOME)/bin/light $(WXS_NAME).wixobj mv $(WXS_NAME).msi $(WXS_NAME)-$(VERSION).msi nsis: install makensis -DTREETOP=..\\pidgin-2.10.0 -DVERSION=$(VERSION) pidgin-sipe.nsi # MinGW cross-compile build (see contrib/mingw-cross-compile/README.txt) .PHONY: cross-compile-nsis cross-compile-nsis: $(MAKE) -f Makefile.mingw install mv $(PIDGIN_INSTALL_DIR)/plugins $(PIDGIN_INSTALL_DIR)/purple-2 perl contrib/opensuse-build-service/generate_nsi.pl po/LINGUAS \ $(PIDGIN_INSTALL_DIR)/pidgin-sipe.nsi set -e; cd $(PIDGIN_INSTALL_DIR); \ makensis \ -DPIDGIN_VERSION=$(PIDGIN_VERSION) \ -DVERSION=$(VERSION) \ -DMINGW_LIBDIR=. \ -DMINGW_DATADIR=. \ pidgin-sipe.nsi mv $(PIDGIN_INSTALL_DIR)/pidgin-sipe-$(VERSION).exe $(PIDGIN_TREE_TOP) rm -rf $(PIDGIN_INSTALL_DIR) ================================================ FILE: NEWS ================================================ version 1.25.0 "Buddy Idle Time, RTF" (2019-10-12) - Feature #107: Provide idle start time for a buddy (Stefan Becker) - Feature #77: RTF support (incoming) (Stefan Becker) * the code only extracts plain text from incoming RTF - Fixed #358: FTBFS with glib-2.0 >= 2.62.0 (Stefan Becker) - Fixed #350: Inconsistent parsing of From:/To: headers (Stefan Becker) - raise BR telepathy-glib >= 0.24.0 (Stefan Becker) - switch from GPLv2+ to SPDX identifier GPL-2.0-or-later (Stefan Becker) ================================================ FILE: README ================================================ Introduction ============ SIPE is a third-party plugin for the Pidgin/Adium/Miranda/Telepathy multi-protocol instant messaging clients/frameworks. It implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) With this plugin you should be able to replace your Microsoft Office Communicator client with Pidgin/Adium/Miranda/Telepathy. Features ======== The plugin has support for * Instant Messaging (IM) * Multiparty chat (OCS 2005) or conference (OCS 2007+) * Group chat (Lync 2010) or Persistent chat (Lync 2013) - [MS-XCCOSIP] * Enhanced presence * Calendar integration with Exchange 2007+ (can be disabled) * Contact details information - company, phones, photo, web site, etc. * Unified Contact Store (Lync 2013) - [MS-OXWSCOS] * Contact search - [MS-PRES] and [MS-DLX] methods supported * Encrypted file transfer (OCS2007: send & receive, Lync: receive only) * Voice & Video calls * Call initiation through PBX * NTLMv2, Kerberos & TLS-DSK authentication methods * Single Sign-On (depends on OS and authentication method) * Automatic SSL/TLS protection for SIP/HTTP connections * Crypto implementation using NSS or OpenSSL * Localization The plugin has backends for * Pidgin/Finch (libpurple) * Adium (libpurple) * Miranda * Telepathy (under development) Advantages over Office Communicator products ============================================ * Available on broader range of platforms: Linux, *BSD, Maemo, OSX, commercial UNIX, Windows. * Ability to simultaneously connect to several Office Communicator accounts in addition to public IM network accounts like MSN, AOL, Yahoo. For example one account is in vendor company and another is in client company. * Keeps history of communications in environments without Outlook. For example in companies where Lotus Notes is deployed as a default groupware client instead of Outlook. * Lotus Notes/Domino calendar integration. Unique to SIPE. * Open development model, open source product. License is GPL-2.0-or-later Support ======= The Pidgin or Adium projects do not support third-party plugins! Please do not ask questions about SIPE in their forums or report SIPE problems to their bug trackers. If you set up your Office Communicator/Lync account with SIPE for the first time then please make sure to read this page: https://sourceforge.net/p/sipe/wiki/How%20to%20setup%20an%20account/ If you encounter problems then please make sure to check out the Frequently Asked Question page: https://sourceforge.net/p/sipe/faq/ If you still have problems then please check the support forum if another user encountered the same problem and maybe solved it already: http://sourceforge.net/p/sipe/discussion/ If you think that you have found a bug in SIPE then please report it to the SIPE bug tracker: https://sourceforge.net/p/sipe/bugs/ The SIPE project kindly requests that you do not ignore the instructions that appear at the head of the "Create Ticket" page. These instructions are there for a reason and if you ignore them then you will only cause unnecessary work for the project and yourself. Please do not report missing features as bugs. New or missing features can be requested here: https://sourceforge.net/p/sipe/feature-requests/ Localization ============ SIPE has already been localized for several languages. You can help to translate SIPE to your native language at Transifex: https://www.transifex.com/stefanb/pidgin-sipe/ The service offers a convenient web editor. D-Bus support (libpurple backend only) ====================================== If libpurple and SIPE have been compiled with D-Bus support and the D-Bus backend has been successfully initialized at libpurple start, then SIPE extends the libpurple D-Bus interface with the following APIs: Function Description SipeCallPhoneNumber(aId, phone_number) Same as "Call a phone number" menu SipeJoinConferenceWithOrganizerAndId(aId, organizer_email, meeting_id) Same as "Organizer email" and "Meeting ID" in the dialog opened by "Join scheduled conference..." SipeJoinConferenceWithUri(aId, uri) Same as "Meeting location" in the dialog opened by "Join scheduled conference..." SipeRepublishCalendar(aId) Same as "Republish Calendar" menu SipeResetStatus(aId) Same as "Reset status" menu To use the APIs from a script you'll need a D-Bus object for the libpurple interface and a valid & connected account ID as first parameter. An invalid account ID will cause SIPE to silently ignore the D-Bus invocation. Example script code (without error checks to keep it simple): Perl: use Net::DBus; my $bus = Net::DBus->session; my $service = $bus->get_service('im.pidgin.purple.PurpleService'); my $purple = $service->get_object('/im/pidgin/purple/PurpleObject', 'im.pidgin.purple.PurpleInterface'); my $accountId = $purple->PurpleAccountsFind($accountName, 'prpl-sipe'); $purple->SipeXYZ($accountId, parameters... ); Python: import dbus bus = dbus.SessionBus() object = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject") purple = dbus.Interface(object, "im.pidgin.purple.PurpleInterface") accountId = purple.PurpleAccountsFind($accountName, 'prpl-sipe') purple.SipeXYZ(accountId, parameters... ); Installing from a distribution repository ========================================= Many Open Source OS distributions have a ready-made package "pidgin-sipe". Before trying to compile it from the source code yourself you should try to install this package with the standard installation method provided by your OS. Once you have SIPE installed and are connected to your account you can check from the following Pidgin menu Accounts -> -> About SIPE plugin... which optional features have been enabled in your SIPE build. Do It Yourself I: compiling against "pidgin" package ==================================================== NOTE: for instructions how to setup a build environment on Windows to compile the Windows Pidgin plugin, please read: https://developer.pidgin.im/wiki/BuildingWinPidgin NOTE: for instructions how to cross-compile the Windows Pidgin plugin on Linux, please read: contrib/mingw-cross-compile/README.txt NOTE: for instructions how to compile the SIPEAdiumPlugin on Mac OS X, please read: src/adium/README.adium If you already have installed the "pidgin" package from your distribution repository and want compile SIPE from source code yourself, then you need to install the necessary headers first. Depending on your distribution you'll need to install one of the following packages libpurple-dev libpurple-devel For the compilation you'll need to install a C compiler and some of the following packages libtool intltool pkg-config libglib2.0-dev libxml2-dev libgmime-2.6-dev (optional) pkgconfig libglib2-devel libxml2-devel gettext-devel gmime-devel (optional) You have to choose between NSS or OpenSSL for the crypto backend. You'll need one of the following packages for NSS: libnss3-dev nss-devel mozilla-nss-devel for OpenSSL: libssl-dev openssl-devel If you want to enable Kerberos authentication support then you'll need one of the following packages libkrb5-dev krb5-devel If you additionally want to disable the internal NTLM implementation and authenticate with NTLM via GSSAPI then you'll need one of the following packages gssntlmssp-devel If you want to enable the D-Bus support then you'll need one of the following packages libdbus-1-dev dbus-devel If you want to enable Voice & Video features then you'll need some of the following packages. You need to install the same version of the GStreamer development packages which where used to compile Pidgin! libfarstream-0.2-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libnice-dev (>= 0.1.13) libpurple-dev (>= 2.10.12) farstream02-devel gstreamer1-devel gstreamer1-plugins-base-devel libnice-devel (>= 0.1.13) libpurple-devel (>= 2.10.12) Now you should be able to compile the source code with ./configure --prefix=/usr make If you get errors then you are missing some required package. After successful compilation you can install SIPE with: su -c "make install" If you get errors from configure or libtool about version conflicts then you need to regenerate the autoconf files. You'll need these additional packages autoconf automake flex Now you should be able to regenerate the files with autopoint --force AUTOPOINT="intltoolize --copy --force --automake" \ autoreconf --force --install After that go back to the configure step above. Do It Yourself II: compiling against self-compiled pidgin ========================================================= If you have compiled pidgin from source code yourself then you'll have to specify the correct installation path in configure. Usually pidgin is installed in /usr/local so the following command should work ./configure --prefix=/usr/local The rest of the steps are the same as in the previous section. Do It Yourself III: building from git checkout ============================================== Building from a git checkout is meant for project maintainers, i.e. you need to generate the configure script first: ./autogen.sh The rest of the steps are the same as in the previous sections. To clean all artifacts and reset the source tree to pristine condition use make maintainer-clean If you just want to quickly run a one-shot build from a git checkout then you can use the following helper script which executes all necessary steps in the correct order: ./git-build.sh Contributing code ================= If you want to contribute to the SIPE project please have a look at http://sipe.sourceforge.net/git/ Some of the instructions are unfortunately out-dated. If in doubt then *ASK* from the current maintainers before committing! Please also have a look at the Pidgin coding style guide https://developer.pidgin.im/wiki/StyleGuide ================================================ FILE: TODO ================================================ Bugs: Big Targets: - Native telepahty backend - Desktop sharing/Live Meeting integration on Windows platform. - Update projects site with * SIPE features * better/more complete screenshots * page about standards used in SIPE with references Call Control: - change user state to On Call when line is active and then back - answer incoming call (logs required) - (?) work with multiple phone lines (not the first one) - (?) Call transfer set up, other RCC features from Communicator. Group Chat: - channel management: add, delete - (?) lock/unlock channel (is this possible?) - file sharing: up & downloading from web server - ??? (unknown as we don't have XCCOS protocol specification) ================================================ FILE: VERSION ================================================ 1.25.0 ================================================ FILE: autogen.sh ================================================ #! /bin/sh ############################################################################### # Generate GITVERSION ############################################################################### _gitversion=$(git describe | grep -e -) if [ -n "${_gitversion}" ]; then _gitversion=$(echo ${_gitversion} | cut -d- -f3 | sed 's/^g//') echo -n ${_gitversion} >GITVERSION else rm -f GITVERSION fi ############################################################################### # Set up build from git tree ############################################################################### set -e # Set up initial NLS stuff... autopoint --force # ...now replace "autopoint" with "intltoolize" in full setup run AUTOPOINT="intltoolize --copy --force --automake" \ autoreconf --force --install ================================================ FILE: configure.ac ================================================ dnl ensure recent enough version of Autoconf AC_PREREQ([2.69]) AC_CONFIG_MACRO_DIRS([m4]) dnl Use 'VERSION' files to get version. m4_define([SIPE_VERSION_DATA], m4_include([VERSION])) dnl Strip off newline characters. m4_define([SIPE_VERSION], m4_substr(SIPE_VERSION_DATA, [0], m4_decr(m4_len(SIPE_VERSION_DATA)))) dnl homepage m4_define([SIPE_HOMEPAGE], [http://sipe.sourceforge.net/]) dnl initialize package name, version, bugtracker & homepage AC_INIT([pidgin-sipe], SIPE_VERSION, [https://sourceforge.net/p/sipe/bugs/], [], SIPE_HOMEPAGE) AC_DEFINE([SIPE_TRANSLATIONS_URL], ["https://www.transifex.com/stefanb/pidgin-sipe/"], [URL for submitting translations.]) dnl define optional git commit ID generated by autogen.sh m4_define([SIPE_GIT_COMMIT], m4_sinclude([GITVERSION])) AS_IF([test "x"SIPE_GIT_COMMIT != x], [AC_MSG_NOTICE([set git commit]) AC_DEFINE(PACKAGE_GIT_COMMIT, "SIPE_GIT_COMMIT", [Define to the git commit for this package.]) ]) dnl setup automake and require recent enough version AM_INIT_AUTOMAKE([1.16 dist-bzip2 dist-xz no-define tar-ustar]) AC_CANONICAL_HOST dnl set PACKAGE_URL for autoconf < 2.64 AS_IF([test "x${PACKAGE_URL}" = x], [AC_MSG_NOTICE([set PACKAGE_URL for autoconf < 2.64]) AC_DEFINE(PACKAGE_URL, "SIPE_HOMEPAGE", [Define to the home page for this package.]) ]) dnl set programming language AC_LANG(C) AM_PROG_CC_C_O AM_PROG_LEX AC_HEADER_STDC dnl setup shared library generation LT_INIT([disable-static]) dnl checks for tools IT_PROG_INTLTOOL([0.41.0]) AC_PROG_INSTALL dnl ****************************** dnl Win32 dnl ****************************** AC_MSG_CHECKING([for Win32]) AS_CASE(["$host"], [*-mingw*], [os_win32=yes], [os_win32=no]) AC_MSG_RESULT([$os_win32]) AM_CONDITIONAL(SIPE_OS_WIN32, [test "x${os_win32}" = xyes]) dnl checks for header files AC_CHECK_HEADERS([]) dnl checks for library functions AC_CHECK_FUNCS([]) dnl tell pkgconfig to look in the same prefix where we're installing this to, dnl as that is likely where libpurple will be found if it is not in the default dnl pkgconfig path PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${libdir}/pkgconfig" export PKG_CONFIG_PATH dnl debug mode AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug], [compile with debugging support @<:@default=no@:>@])], [], [enable_debug=no]) AS_IF([test "x$enable_debug" = xyes], [AC_DEFINE(DEBUG, 1, [Define if debugging is enabled.])]) AC_SUBST(DEBUG_CFLAGS) dnl dnl Quality check mode - try to find bugs instead of hiding them dnl dnl Developers & contributors: dnl dnl You are *NOT* allowed to commit code to SIPE repository that only dnl builds with --disable-quality-check! dnl dnl Distro package maintainers: dnl dnl You may choose to add --disable-quality-check to the package build dnl specification if you can't fix all build dependencies properly. dnl AC_ARG_ENABLE(quality-check, [AS_HELP_STRING([--enable-quality-check], [compile with compiler checks enabled @<:@default=yes@:>@])], [], [enable_quality_check=yes]) dnl default quality configuration QUALITY_CFLAGS="$QUALITY_CFLAGS -Wall" dnl convert warnings to errors AS_IF([test "x$enable_quality_check" = xyes], [QUALITY_CFLAGS="$QUALITY_CFLAGS -Werror"]) dnl enable warnings supported by the GCC on the build system dnl @TODO: not included in -Wall: "-Wwrite-strings" \ for newflag in \ "-Wextra" \ "-Waggregate-return" \ "-Wcast-align" \ "-Wcast-function-type" \ "-Wdeclaration-after-statement" \ "-Wdeprecated-declarations" \ "-Wduplicate-decl-specifier" \ "-Winit-self" \ "-Wmaybe-uninitialized" \ "-Wmissing-declarations" \ "-Wmissing-prototypes" \ "-Wnested-externs" \ "-Wpointer-arith" \ "-Wundef" \ "-Wunused-but-set-variable" \ ; do ac_save_CFLAGS="$CFLAGS" AC_MSG_CHECKING([if $CC supports $newflag]) CFLAGS="$CFLAGS $newflag" AC_COMPILE_IFELSE( [AC_LANG_SOURCE([[]])], [AC_MSG_RESULT(yes) QUALITY_CFLAGS="$QUALITY_CFLAGS $newflag"], [AC_MSG_RESULT(no)] ) CFLAGS="$ac_save_CFLAGS" done AC_SUBST(QUALITY_CFLAGS) dnl check for availability of addition linker flags for newflag in \ "-Wl,-Bsymbolic-functions" \ ; do ac_save_LDFLAGS="$LDFLAGS" AC_MSG_CHECKING([if $CC supports $newflag]) LDFLAGS="$LDFLAGS $ADDITIONAL_LDFLAGS $newflag" AC_LINK_IFELSE( [AC_LANG_SOURCE([[ int main(int argc, char *argv[]) { return(0); } ]])], [AC_MSG_RESULT(yes) ADDITIONAL_LDFLAGS="$ADDITIONAL_LDFLAGS $newflag"], [AC_MSG_RESULT(no)] ) LDFLAGS="$ac_save_LDFLAGS" done AC_SUBST(ADDITIONAL_LDFLAGS) dnl Check for pkg-config before using it PKG_PROG_PKG_CONFIG dnl check for valgrind (optional, only needed for debugging) PKG_CHECK_MODULES(VALGRIND, [valgrind], [AC_DEFINE(HAVE_VALGRIND, 1, [Define to 1 if you have the valgrind headers])], [AC_MSG_RESULT(no)]) dnl build option: with AppStream support AC_ARG_WITH(appstream, [AC_HELP_STRING([--with-appstream], [install AppStream XML file @<:@default=yes@:>@])], [AS_IF([test "x$withval" = xno], [with_appstream=no])], [with_appstream=yes]) AM_CONDITIONAL(SIPE_WITH_APPSTREAM, [test "x$with_appstream" != xno]) dnl build option: with voice & video support (for all backends) AC_ARG_WITH(vv, [AC_HELP_STRING([--with-vv], [compile with voice and video support @<:@default=check@:>@])], [AS_IF([test "x$withval" = xyes], [with_vv=yes])], [with_vv=check]) dnl check for Kerberos 5 support AC_ARG_WITH([krb5], [AC_HELP_STRING([--with-krb5], [compile with Kerberos 5 support @<:@default=check@:>@])], [AS_IF([test "x$withval" = xyes], [with_krb5=check])], [with_krb5=check]) AS_IF([test "x$with_krb5" = xno], [], dnl disabled by user [test "x$with_krb5" = xcheck], dnl autodetect with krb5-config [AC_MSG_CHECKING(if Kerberos 5 is available) AS_IF([AC_RUN_LOG([krb5-config --version])], [KRB5_CFLAGS=`krb5-config --cflags 2>/dev/null` KRB5_LDFLAGS="" dnl same as AC_CHECK_LIB() LIBS="$LIBS `krb5-config --libs gssapi 2>/dev/null`"], [AC_MSG_RESULT(no) with_krb5=no])], [ dnl path specified by user KRB5_CFLAGS="-I${with_krb5}/include" KRB5_LDFLAGS="-L${with_krb5}/lib" ]) AS_IF([test "x$with_krb5" != xno], [ac_save_CFLAGS="$CFLAGS" ac_save_LDFLAGS="$LDFLAGS" CFLAGS="$CFLAGS $KRB5_CFLAGS" LDFLAGS="$LDFLAGS $KRB5_LDFLAGS" AC_CHECK_HEADERS([gssapi/gssapi.h gssapi/gssapi_krb5.h], [], [AC_ERROR([GSSAPI headers not found])]) AC_CHECK_FUNC([gss_init_sec_context], [], [AC_ERROR([GSSAPI libraries not found])]) dnl older GSSAPI releases don't have gss_acquire_cred_with_passwd() AC_CHECK_HEADER([gssapi/gssapi_ext.h], [AC_CHECK_FUNC([gss_acquire_cred_with_password], [AC_DEFINE(HAVE_GSSAPI_PASSWORD_SUPPORT, 1, [Define to 1 if gssapi has gss_acquire_cred_with_passwd()])], [AC_MSG_NOTICE([gss_acquire_cred_with_passwd() required: disabling GSSAPI non-SSO support])])], [AC_MSG_NOTICE([gssapi/gssapi_ext.h required: disabling GSSAPI non-SSO support])]) CFLAGS="$ac_save_CFLAGS" LDFLAGS="$ac_save_LDFLAGS" ]) AM_CONDITIONAL(SIP_SEC_GSSAPI, [test "x$with_krb5" != xno]) AC_SUBST(KRB5_CFLAGS) AC_SUBST(KRB5_LDFLAGS) dnl check for DBUS support AC_ARG_WITH([dbus], [AC_HELP_STRING([--with-dbus], [compile with D-BUS support @<:@default=check@:>@])], [AS_IF([test "x$withval" = xyes], [with_dbus=check])], [with_dbus=check]) AS_IF([test "x$with_dbus" = xno], [], dnl disabled by user [test "x$with_dbus" = xcheck], dnl autodetect with pkg-config [PKG_CHECK_MODULES(DBUS, [dbus-1], [with_dbus=yes], [with_dbus=no]) ]) AM_CONDITIONAL(SIPE_DBUS, [test "x$with_dbus" = xyes]) AS_IF([test "x$with_dbus" = xyes], [AC_DEFINE(HAVE_DBUS, 1, [Define if D-BUS should be used in sipe.])]) AC_ARG_ENABLE(gssapi_only, [AS_HELP_STRING([--enable-gssapi-only], [disable all internal authentication code and only use GSSAPI for authentication @<:@default=yes@:>@])], [], dnl requires Kerberos which provides GSSAPI [enable_gssapi_only=$with_krb5]) AS_IF([test "x$enable_gssapi_only" != xno], [AC_CHECK_HEADER([gssapi/gssapi_ntlmssp.h], [AC_DEFINE(HAVE_GSSAPI_ONLY, 1, [Define to 1 to enable GSSAPI-only mode])], [AC_MSG_NOTICE([gssapi/gssapi_ntlm.h required: falling back to internal authentication implementation]) enable_gssapi_only=no]) ]) AM_CONDITIONAL(SIP_SEC_GSSAPI_ONLY, [test "x$enable_gssapi_only" != xno]) dnl dnl oldest supported LTS release vs. package versions dnl dnl last checked: Jun-2019 dnl dnl package | Debian | RHEL | SLES | Ubuntu | win32 | dnl | 8.0 | 6.10 | 12 SP4 | 16.04 | 2.13.x | dnl EOL | 07/2020 | 12/2020 | 11/2024 | 04/2021 | N/A | dnl ---------------|---------|---------|---------|---------|--------| dnl glib-2.0 | 2.42.x | 2.28.x | 2.48.x | 2.48.x | 2.18.x | dnl gmime-2.6 | YES | NO | YES | YES | NO | dnl gss-ntlmssp | NO | NO | NO | 0.7.x | N/A | dnl intltool | 0.50.2 | 0.41.0 | 0.51.0 | 0.51.0 | 0.40.4 | dnl nice | TOO OLD | TOO OLD | 0.1.13 | 0.1.13 | N/A | dnl purple | 2.11.x | 2.7.x | 2.12.x | 2.10.x | 2.13.x | dnl telepathy-glib | 0.24.x | NO | 0.24.x | 0.24.x | N/A | dnl dnl check for glib PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.18.0]) PKG_CHECK_MODULES(GMODULE, [gmodule-2.0 >= 2.18.0]) dnl check for gmime PKG_CHECK_MODULES(GMIME, [gmime-3.0 >= 3.0.0], [ac_have_gmime=yes], [PKG_CHECK_MODULES(GMIME, [gmime-2.6 >= 2.6.0], [ac_have_gmime=yes], [ac_have_gmime=no]) ]) AM_CONDITIONAL(SIPE_MIME_GMIME, [test "x$ac_have_gmime" = xyes]) AS_IF([test "x$ac_have_gmime" = xyes], [AC_DEFINE(HAVE_GMIME, 1, [Define if gmime should be used in sipe.])]) dnl check for NSS AC_ARG_ENABLE(nss, [AS_HELP_STRING([--enable-nss], [use NSS as crypto backend @<:@default=yes@:>@])], [], [enable_nss=yes]) AS_IF([test "x$enable_nss" != xno], [PKG_CHECK_MODULES(NSS, [nss], [], [PKG_CHECK_MODULES(NSS, [mozilla-nss], [], [PKG_CHECK_MODULES(NSS, [microb-engine-nss], [], [enable_nss=no]) ]) ]) ]) dnl check for OpenSSL AC_ARG_ENABLE(openssl, [AS_HELP_STRING([--enable-openssl], [use OpenSSL as crypto backend @<:@default=yes@:>@])], [], [enable_openssl=yes]) AS_IF([test "x$enable_openssl" != xno], [PKG_CHECK_MODULES(OPENSSL, [libcrypto], [], [enable_openssl=no]) ]) AS_IF( [test "x$enable_nss" != xno], [ dnl NSS has priority over OpenSSL enable_openssl=no AC_MSG_NOTICE([using NSS as crypto backend.])], [test "x$enable_openssl" != xno], [AC_MSG_NOTICE([using OpenSSL as crypto backend.])], [AC_ERROR([nss, mozilla-nss, microb-engine-nss or libcrypto package is required])]) AM_CONDITIONAL(SIPE_OPENSSL, [test "x$enable_openssl" != xno]) dnl check for libxml2 PKG_CHECK_MODULES(LIBXML2, [libxml-2.0]) dnl assumption check: sizof(uuid_t) must be 16 (see uuid.c) AC_MSG_CHECKING([that sizeof(uuid_t) is 16]) ac_save_CFLAGS="$CFLAGS" dnl NOTE: including $QUALITY_CFLAGS can lead to false negative configure checks! CFLAGS="$CFLAGS $GLIB_CFLAGS" dnl note the [[[ quoting: our code contains []! AC_RUN_IFELSE( [AC_LANG_PROGRAM( [[ #include #include /* Copied from uuid.c */ typedef struct { guint32 time_low; guint16 time_mid; guint16 time_hi_and_version; guint8 clock_seq_hi_and_reserved; guint8 clock_seq_low; guint8 node[6]; } uuid_t; ]], [[if (sizeof(uuid_t) == 16) { return(0); } else { printf("\n\nOoops, sizeof(uuid_t) is %" G_GSIZE_FORMAT ".\n\n", sizeof(uuid_t)); return(1); }]])], [AC_MSG_RESULT(yes)], [AC_MSG_FAILURE([sizeof(uuid_t) is not 16 Please notify the SIPE developers. Copy & paste all lines starting with the line checking that sizeof(uuid_t) is 16... to the report and attach the file "config.log". Compiler information: host: $host build: $build compiler: $CC ])], [AC_MSG_WARN([cross compiling: not checking])]) CFLAGS="$ac_save_CFLAGS" dnl build option: OCS2005 client hack AC_ARG_ENABLE([ocs2005-message-hack], [AC_HELP_STRING([--enable-ocs2005-message-hack], [disable message timeout for OCS2005 clients which causes "false" not delivered error messages @<:@default=no@:>@])], [AC_DEFINE([ENABLE_OCS2005_MESSAGE_HACK], [1], [Define to 1 to disable SIP MESSAGE timeout feature. OCS2005 clients don't seem to acknowledge MESSAGEs and disabling the timeout suppresses "false" error messages])]) dnl build option: purple backend AC_ARG_ENABLE([purple], [AC_HELP_STRING([--enable-purple], [build purple plugin @<:@default=yes@:>@])], [], [enable_purple=yes]) ac_have_appshare=no ac_have_appshare_server=no ac_have_xdata=no with_purple_vv=no AS_IF([test "x$enable_purple" != xno], [PKG_CHECK_MODULES(PURPLE, [purple-3], [purple_pkgconfig=purple-3], [PKG_CHECK_MODULES(PURPLE, [purple >= 2.7.0], [purple_pkgconfig=purple], [enable_purple=no])]) AS_IF([test "x$enable_purple" != xno], [AC_MSG_NOTICE([using package "$purple_pkgconfig".]) dnl unfortunately PURPLE_MAJOR_VERSION is not exported by purple.m4 PURPLE_MAJOR_VERSION=`$PKG_CONFIG --modversion $purple_pkgconfig | cut -d. -f 1` AC_SUBST(PURPLE_MAJOR_VERSION) ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" dnl NOTE: including $QUALITY_CFLAGS can lead to false negative configure checks! CFLAGS="$CFLAGS $PURPLE_CFLAGS" LIBS="$LIBS $PURPLE_LIBS" dnl check whether enable voice and video support AS_IF([test "x$with_vv" = xno], [], dnl disabled by user [AC_MSG_CHECKING(for purple voice and video support) purple_has_media=no AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[return (purple_media_get_type() == G_TYPE_NONE ? 1 : 0);]] )], [purple_has_media=yes AC_MSG_RESULT(ok) ], [AC_MSG_RESULT([no - your purple hasn't been compiled with voice and video support.])], [AS_IF([test "x$with_vv" = xyes], [purple_has_media=yes AC_MSG_RESULT([yes - enabling voice and video support for cross compiling on users' request.])], [AC_MSG_RESULT([no - disabled for cross compiling.])]) ] ) AS_IF([test "x$purple_has_media" = xyes], [PKG_CHECK_MODULES(NICE, [nice >= 0.1.0], dnl sipe-media.c uses g_slist_free_full() [PKG_CHECK_MODULES(_SIPE_MEDIA_RECHECK_PLEASE_IGNORE, [$purple_pkgconfig >= 2.8.0], dnl check purple pkgconfig for gstreamer version [gstreamer_pkgconfig=`$PKG_CONFIG --variable=gstreamer $purple_pkgconfig` AS_IF([test "x$gstreamer_pkgconfig" == x], [AS_IF([test "x$purple_pkgconfig" == xpurple-3], [gstreamer_pkgconfig=1.0])]) AS_IF([test "x$gstreamer_pkgconfig" == x1.0], [gstreamer_pkgconfig="gstreamer-$gstreamer_pkgconfig gstreamer-rtp-$gstreamer_pkgconfig" AC_MSG_NOTICE([using packages "$gstreamer_pkgconfig".]) PKG_CHECK_MODULES(GSTREAMER, [$gstreamer_pkgconfig], [PKG_CHECK_MODULES(FARSTREAM, [farstream-0.2], [with_purple_vv=yes AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[return (PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE);]] )], [AC_DEFINE(HAVE_PURPLE_NEW_TCP_ENUMS, 1, [Define if libpurple has new TCP enums in media backend.])] )], [AC_MSG_NOTICE(Farstream required: disabling purple voice and video support)])], [AC_MSG_NOTICE(GStreamer required: disabling purple voice and video support)])], [AC_MSG_NOTICE(your purple hasn't been compiled against GStreamer 1.0: disabling purple voice and video support)])], [AC_MSG_NOTICE(libpurple >= 2.8.0 required: disabling purple voice and video support)])], [AC_MSG_NOTICE(libnice required: disabling purple voice and video support)]) ]) ]) dnl SRTP and XDATA require media backend AS_IF([test "x$with_purple_vv" != xno], [AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[purple_media_set_encryption_parameters(NULL, "", "", "", "", 0); purple_media_set_decryption_parameters(NULL, "", "", "", "", "", 0);]] )], [AC_DEFINE(HAVE_SRTP, 1, [Define if we have SRTP support in media backend.])] ) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[return (PURPLE_MEDIA_APPLICATION);]] )], [dnl libpurple supports raw data RTP connections ac_have_xdata=yes dnl gio-2.0 is mandatory for application sharing support PKG_CHECK_MODULES(GIO, [gio-2.0], [ac_have_appshare=yes dnl freerdp-shadow2 & winpr2 is mandatory for appshare server support PKG_CHECK_MODULES(FREERDP_SHADOW, [freerdp-shadow2 winpr2], [ac_have_appshare_server=yes], [ac_have_appshare_server=no]) ]) ] ) ], [AS_IF([test "x$with_vv" = xyes], dnl explicitly requested by user [AC_ERROR([Voice and video support explicitly requested, but not available])] )] ) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS"])]) AM_CONDITIONAL(SIPE_INCLUDE_PURPLE, [test "x$enable_purple" != xno]) AM_CONDITIONAL(SIPE_PURPLE3, [test "x$purple_pkgconfig" = xpurple-3]) dnl build option: telepathy backend AC_ARG_ENABLE([telepathy], [AC_HELP_STRING([--enable-telepathy], [build telepathy plugin @<:@default=yes@:>@])], [], [enable_telepathy=yes]) AS_IF([test "x$enable_telepathy" != xno], [PKG_CHECK_MODULES(TELEPATHY_GLIB, [telepathy-glib >= 0.24.0], [dnl GMIME is a build requirement AS_IF([test "x$ac_have_gmime" = xyes], [], [AC_ERROR(GMIME package is required for telepathy plugin)]) dnl telepathy is based on GObject & D-Bus GLib PKG_CHECK_MODULES(GOBJECT, [gobject-2.0]) PKG_CHECK_MODULES(DBUS_GLIB, [dbus-glib-1]) dnl telepathy uses from gio: dnl - GIOStream (>= 2.22.0) dnl - GResolver (>= 2.22.0) dnl - GSocketClient (>= 2.32.0) dnl - GTlsConnection (>= 2.28.0) PKG_CHECK_MODULES(GIO, [gio-2.0 >= 2.32.0]) ], [enable_telepathy=no])]) AM_CONDITIONAL(SIPE_INCLUDE_TELEPATHY, [test "x$enable_telepathy" != xno]) dnl sanity check AS_IF([test "x$enable_purple" = xno -a "x$enable_telepathy" = xno], [AC_ERROR(at least one plugin must be selected If you didn't use a --enable option then please check that you have the headers for the packages "purple" or "telepathy-glib" installed. )], []) dnl enable voice & video support if any backend supports it AS_IF([test "x$with_purple_vv" != xno], [AC_DEFINE(HAVE_VV, 1, [Define if voice & video is enabled.])]) AM_CONDITIONAL(SIPE_WITH_VV, [test "x$with_purple_vv" != xno]) dnl raw data RTP streams enable Lync file transfer AM_CONDITIONAL(SIPE_HAVE_XDATA, [test "x$ac_have_xdata" == xyes]) AS_IF([test "x$ac_have_xdata" == xyes], [AC_DEFINE(HAVE_XDATA, 1, [Define if we have raw data RTP in media backend.])]) dnl enable appshare support AM_CONDITIONAL(SIPE_HAVE_APPSHARE, [test "x$ac_have_appshare" == xyes]) AS_IF([test "x$ac_have_appshare" == xyes], [AC_DEFINE(HAVE_APPSHARE, 1, [Define to 1 if we have appshare support.])]) dnl RDP server for sharing local desktop AM_CONDITIONAL(SIPE_HAVE_APPSHARE_SERVER, [test "x$ac_have_appshare_server" = xyes]) AS_IF([test "x$ac_have_appshare_server" == xyes], [AC_DEFINE(HAVE_APPSHARE_SERVER, 1, [Define if appshare server is enabled.])]) dnl libpurple API relies on GParameter (deprecated in glib-2.0 >= 2.62.0) AS_IF([test "x$enable_purple" != xno], [AC_DEFINE(GLIB_VERSION_MIN_REQUIRED, GLIB_VERSION_2_60, [We need GParameter for which no replacement is available]) ]) dnl telepathy code parts rely on interfaces that require GValueArray. This dnl type has been declared "deprectated" in glib-2.0 >= 2.32.0, but there dnl is no backward compatible replacement implementation possible dnl telepathy-glib API relies on GTimeVal (deprecated in glib-2.0 >= 2.62.0) AS_IF([test "x$enable_telepathy" != xno], [AC_DEFINE(GLIB_VERSION_MIN_REQUIRED, GLIB_VERSION_2_30, [We need GValueArray for which no replacement is available]) ]) dnl libpurple 2.x API relies on G_CONST_RETURN (deprecated in glib-2.0 >= 2.30.0) AS_IF([test "x$purple_pkgconfig" == xpurple], [AC_DEFINE(GLIB_VERSION_MIN_REQUIRED, GLIB_VERSION_2_28, [libpurple 2.x API uses G_CONST_RETURN]) ]) dnl i18n AC_MSG_CHECKING([locale_CPPFLAGS]) LOCALE_CPPFLAGS='-DLOCALEDIR=\"$(datadir)/locale\"' AC_SUBST([LOCALE_CPPFLAGS]) AC_MSG_RESULT([$LOCALE_CPPFLAGS]) AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.19.8]) AC_SUBST(GETTEXT_PACKAGE, "${PACKAGE_NAME}") dnl substitutions and generated files AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ Makefile pixmaps/Makefile po/Makefile.in pixmaps/16/Makefile pixmaps/22/Makefile pixmaps/24/Makefile pixmaps/32/Makefile pixmaps/48/Makefile pixmaps/scalable/Makefile src/Makefile src/core/Makefile src/api/Makefile src/purple/Makefile src/telepathy/Makefile src/telepathy/data/Makefile ]) dnl generate files AC_OUTPUT() dnl additional info for the user AS_ECHO() AS_IF([test "x$enable_purple" = xno], [AS_ECHO("Not building purple plugin")], [AS_ECHO("Build purple plugin") AS_ECHO("PURPLE_CFLAGS : $PURPLE_CFLAGS") AS_ECHO("PURPLE_LIBS : $PURPLE_LIBS") AS_ECHO_N("Voice and video: ") AS_IF([test "x$with_purple_vv" = xno], [AS_ECHO("disabled")], [AS_ECHO("enabled")]) ]) AS_ECHO() AS_IF([test "x$enable_telepathy" = xno], [AS_ECHO("Not building telepathy plugin")], [AS_ECHO("Build telepathy plugin") AS_ECHO("TELEPATHY_GLIB_CFLAGS: $TELEPATHY_GLIB_CFLAGS") AS_ECHO("TELEPATHY_GLIB_LIBS : $TELEPATHY_GLIB_LIBS")]) AS_ECHO() AS_IF([test "x$with_krb5" = xno], [AS_ECHO("Not building with Kerberos 5 support")], [AS_ECHO("Build with Kerberos 5 support") AS_ECHO("KRB5_CFLAGS : $KRB5_CFLAGS") AS_ECHO("KRB5_LDFLAGS : $KRB5_LDFLAGS")]) AS_ECHO() AS_IF([test "x$enable_gssapi_only" = xno], [AS_ECHO("Using internal authentication implementation")], [AS_ECHO("Using only GSSAPI for authentication")]) AS_ECHO() AS_IF([test "x$enable_debug" = xno], [AS_ECHO("Debugging not enabled")], [AS_ECHO("Build with debugging enabled") AS_ECHO("DEBUG_CFLAGS : $DEBUG_CFLAGS")]) AS_ECHO() AS_IF([test "x$QUALITY_CFLAGS" = x], [AS_ECHO("Compiler checks disabled")], [AS_ECHO("Build with compiler checks enabled") AS_ECHO("QUALITY_CFLAGS : $QUALITY_CFLAGS")]) AS_ECHO() AS_ECHO("configure complete. Now run 'make'") AS_ECHO() dnl The End. ================================================ FILE: contrib/dbus/SipeHelper.pm ================================================ #!/usr/bin/perl -w # # @file SipeHelper.pm # # pidgin-sipe # # Copyright (C) 2017 SIPE Project # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # Support code for D-Bus test scripts # package SipeHelper; use 5.024; use strict; use warnings; use Carp; use Net::DBus; # Connect to libpurple over the session bus my $purple; sub init() { eval { my $bus = Net::DBus->session; my $service = $bus->get_service('im.pidgin.purple.PurpleService'); $purple = $service->get_object('/im/pidgin/purple/PurpleObject', 'im.pidgin.purple.PurpleInterface'); }; die "ERROR: can't find any active libpurple D-Bus instance, Are you sure you started Pidgin/Finch?\n\n$@" if $@; } # Call code reference for all active SIPE accounts sub forSipeAccounts($) { my($code) = @_; croak "ERROR: ${code} should be code reference" unless ref($code) eq "CODE"; croak "ERROR: called without initializing" unless $purple; # Get list of enabled accounts my $accounts = $purple->PurpleAccountsGetAllActive(); for my $accountId (@{ $accounts }) { my $username = $purple->PurpleAccountGetUsername($accountId); my $protocolId = $purple->PurpleAccountGetProtocolId($accountId); my $protocolName = $purple->PurpleAccountGetProtocolName($accountId); my $connectionId = $purple->PurpleAccountGetConnection($accountId); print "found account ${accountId}: ${username} (${protocolId}/${protocolName}, ${connectionId})\n"; # Filter out SIPE accounts that are online if (($protocolId eq 'prpl-sipe') && ($connectionId != 0)) { # Filter out SIPE accounts that are really connected if ($purple->PurpleConnectionIsConnected($connectionId)) { # Call code reference $code->($purple, $accountId, $username); } } } } # modules need to return a true value 1; ================================================ FILE: contrib/dbus/sipe-call-phone-number.pl ================================================ #!/usr/bin/perl -w # # @file sipe-call-phone-number.pl # # pidgin-sipe # # Copyright (C) 2017 SIPE Project # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # Test code for D-Bus interface "SipeCallPhoneNumber" # use 5.024; use strict; use warnings; use FindBin; use lib $FindBin::Bin; use SipeHelper; # Check command line parameters die "usage: $0 >\n" unless @ARGV >= 1; my($number) = @ARGV; SipeHelper::init(); SipeHelper::forSipeAccounts(sub { my($purple, $accountId, $username) = @_; print "Trying to call phone number '${number}' on SIPE account '${username}'...\n"; $purple->SipeCallPhoneNumber($accountId, $number); }); # That's all folks... exit 0; ================================================ FILE: contrib/dbus/sipe-join-conference-with-organizer-and-id.pl ================================================ #!/usr/bin/perl -w # # @file sipe-join-conference-with-organizer-and-id.pl # # pidgin-sipe # # Copyright (C) 2017 SIPE Project # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # Test code for D-Bus interface "SipeJoinConferenceWithOrganizerAndId" # use 5.024; use strict; use warnings; use FindBin; use lib $FindBin::Bin; use SipeHelper; # Check command line parameters die "usage: $0 \n" unless @ARGV >= 2; my($organizer, $id) = @ARGV; SipeHelper::init(); SipeHelper::forSipeAccounts(sub { my($purple, $accountId, $username) = @_; print "Trying to join ${organizer}'s conference '${id}' on SIPE account '${username}'...\n"; $purple->SipeJoinConferenceWithOrganizerAndId($accountId, $organizer, $id); }); # That's all folks... exit 0; ================================================ FILE: contrib/dbus/sipe-join-conference-with-uri.pl ================================================ #!/usr/bin/perl -w # # @file sipe-join-conference-with-uri.pl # # pidgin-sipe # # Copyright (C) 2017 SIPE Project # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # Test code for D-Bus interface "SipeJoinConferenceWithUri" # use 5.024; use strict; use warnings; use FindBin; use lib $FindBin::Bin; use SipeHelper; # Check command line parameters die "usage: $0 [ ...]\n" unless @ARGV; SipeHelper::init(); SipeHelper::forSipeAccounts(sub { my($purple, $accountId, $username) = @_; for my $uri (@ARGV) { print "Trying to join conference '${uri}' on SIPE account '${username}'...\n"; $purple->SipeJoinConferenceWithUri($accountId, $uri); } }); # That's all folks... exit 0; ================================================ FILE: contrib/dbus/sipe-republish-calendar.pl ================================================ #!/usr/bin/perl -w # # @file sipe-republish-calendar.pl # # pidgin-sipe # # Copyright (C) 2017 SIPE Project # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # Test code for D-Bus interface "SipeRepublishCalendar" # use 5.024; use strict; use warnings; use FindBin; use lib $FindBin::Bin; use SipeHelper; SipeHelper::init(); SipeHelper::forSipeAccounts(sub { my($purple, $accountId, $username) = @_; print "Trying to republish calendar data on SIPE account '${username}'...\n"; $purple->SipeRepublishCalendar($accountId); }); # That's all folks... exit 0; ================================================ FILE: contrib/dbus/sipe-reset-status.pl ================================================ #!/usr/bin/perl -w # # @file sipe-reset-status.pl # # pidgin-sipe # # Copyright (C) 2017 SIPE Project # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # Test code for D-Bus interface "SipeResetStatus" # use 5.024; use strict; use warnings; use FindBin; use lib $FindBin::Bin; use SipeHelper; SipeHelper::init(); SipeHelper::forSipeAccounts(sub { my($purple, $accountId, $username) = @_; print "Trying to reset status on SIPE account '${username}'...\n"; $purple->SipeResetStatus($accountId); }); # That's all folks... exit 0; ================================================ FILE: contrib/debian/changelog ================================================ pidgin-sipe (1.25.0-1) unstable; urgency=low * New version 1.25.0 "Buddy Idle Time, RTF" (2019-10-12) - Feature #107: Provide idle start time for a buddy (Stefan Becker) - Feature #77: RTF support (incoming) (Stefan Becker) * the code only extracts plain text from incoming RTF - Fixed #358: FTBFS with glib-2.0 >= 2.62.0 (Stefan Becker) - Fixed #350: Inconsistent parsing of From:/To: headers (Stefan Becker) - raise BR telepathy-glib >= 0.24.0 (Stefan Becker) - switch from GPLv2+ to SPDX identifier GPL-2.0-or-later (Stefan Becker) -- Stefan Becker Sat, 12 Oct 2019 11:43:00 +0300 pidgin-sipe (1.24.0-1) unstable; urgency=low * New version 1.24.0 "Application Sharing II" (2018-11-10) - Feature #104: Use user agent also for HTTP (Stefan Becker) - Feature #6: Application Sharing Server (Jakub Adam) * requires freerdp-shadow2 - Fixed #343: Build fails on FreeBSD - add timeout handling for media streams (Alaoui Youness) - update AppStream handling (Jakub Adam, Stefan Becker) - fix missing localisation in some code modules (Stefan Becker) - debug log improvements (Stefan Becker) -- Stefan Becker Sat, 10 Nov 2018 17:38:00 +0200 pidgin-sipe (1.23.3-1) unstable; urgency=low * New version 1.23.3 "Bug Fixes III" (2018-08-20) - appshare: fix black screen with Remmina v1.2.0-rcgit.27 (Jakub Adam) - various minor fixes (Jakub Adam, Michael Olbrich) - fix compilation errors with libpurple 2.14.0 & GCC 8.0 (Jakub Adam, Stefan Becker) - mingw: update fetch script to Pidgin 2.13.0 (Stefan Becker) -- Stefan Becker Mon, 20 Aug 2018 16:56:00 +0300 pidgin-sipe (1.23.2-1) unstable; urgency=low * New upstream version 1.23.2 "Bug Fixes II" (2018-03-10) - fix some HTTP requests that were not sent (Jakub Adam, Stefan Becker) -- Stefan Becker Sat, 10 Mar 2018 17:52:35 +0200 pidgin-sipe (1.23.1-1) unstable; urgency=low * New upstream version 1.23.1 "Bug Fixes I" (2018-02-25) - Fixed #338: Incorrect port 0 for IPv6 socket on Windows (Stefan Becker) - Fixed #337: Duplicate candidates in SDP (Jakub Adam, Stefan Becker) - Fixed #336: Lync autodiscover does not follow user redirect (Stefan Becker) - media: unconditionally ignore multichannel codecs (Jakub Adam) - updated translations: Turkish (tr) -- Stefan Becker Sun, 25 Feb 2018 15:05:00 +0200 pidgin-sipe (1.23.0-1) unstable; urgency=low * New upstream version 1.23.0 "D-Bus, IPv6, OS X 10.11+" (2017-10-28) - Feature #101: Mac OS X 10.13 OpenSSL support (Stefan Becker) - Feature #100: Extend libpurple D-Bus interface (Stefan Becker) - Feature #99: IPv6 addresses in SIP & SDP messages (Stefan Becker) - Feature #96: Support for OS X 10.11+ SDK (Stefan Becker) - don't load buddy photos from unknown sites by default (Jakub Adam, Stefan Becker) * custom web URIs pose a security risk as they may be abused * users can override this behaviour in the account settings * Office365 accounts should not be affected by this change - add support for GMime 3.0 API (Stefan Becker) - raise BR glib-2.0 >= 2.18.0 (Stefan Becker) - raise BR purple >= 2.7.0 (Stefan Becker) - drop support for GMime 2.4 (Stefan Becker) - drop support for gstreamer-0.10 (Stefan Becker) -- Stefan Becker Sat, 28 Oct 2017 18:16:00 +0300 pidgin-sipe (1.22.1-1) unstable; urgency=low * New upstream version 1.22.1 "Bug Fixes I" (2017-06-11) - Fixed #320: Multiple client detection broken (Stefan Becker) - speed up Lync Autodiscover by using AccessLocation (Andrey Vaynberger) - adium: update build instructions for Xcode 7.x or newer (Stefan Becker) * the build environment used for releases 1.22.0 or older (Xcode 6.x on OS X 10.11) is no longer available to the project * releases starting with 1.22.1 will use Xcode 8.x on macOS 10.12 * build target continues to be OS X 10.9 - purple: avoid rare SSL read deadlock (Stefan Becker) - various minor fixes (Michael Olbrich) - crypto: make code compile with OpenSSL 1.1.0 (Stefan Becker) - drop references to Reuters Messaging (Stefan Becker) - updated translations: Lithuanian (lt), Russian (ru), Swedish (sv), Turkish (tr) -- Stefan Becker Sun, 11 Jun 2017 18:38:00 +0300 pidgin-sipe (1.22.0-1) unstable; urgency=low * New upstream version 1.22.0 "Application Sharing, Lync Autodiscover & Logging" (2017-02-01) - Feature #93: Support for Lync Autodiscover (Stefan Becker) - Feature #6: Application Sharing Viewer (Jakub Adam) * requires libpurple >= 2.12.0 * needs an external RDP client - remmina and xfreerdp are supported - Fixed #315: Crash when contact list is empty (Stefan Becker) - Fixed #314: sipe login problems with long pw (Stefan Becker) - separate logging and debugging output (Stefan Becker) * logging is always shown, e.g. in the Pidgin debug window * full message debugging now requires PURPLE_UNSAFE_DEBUG=1 - new translations: Greek (el), Lithuanian (lt) -- Stefan Becker Wed, 01 Feb 2017 18:57:00 +0200 pidgin-sipe (1.21.1-1) unstable; urgency=low * New upstream version 1.21.1 "Bug Fixes I" (2016-05-28) - various bug fixes in media support (Jakub Adam) - configure no longer ignores CFLAGS/LDFLAGS/LIBS (Stefan Becker) -- Stefan Becker Sat, 28 May 2016 18:19:00 +0300 pidgin-sipe (1.21.0-1) unstable; urgency=low * New upstream version 1.21.0 "Lync File Transfer" (2016-04-23) - Feature #91: Support embedded XML as buddy photo URL (Stefan Becker) - Feature #90: Add AppStream metadata file (Jiri Eischmann, Stefan Becker) - Feature #89: Improve "Join scheduled conference" dialog (Stefan Becker) - Feature #87: Support multiple HTTP cookies (Stefan Becker) - Feature #85: XML raw extract should ignore name space (Stefan Becker) - Fixed #311: Crash when SIP transport becomes invalid (Stefan Becker) - Fixed #293: Mandatory wsa:MessageID node missing (Stefan Becker) - add support for Lync File Transfer protocol (Jakub Adam) * requires libpurple >= 2.12.0 * Lync FT will be used for sending files when Lync 2013 is detected - add build options to "About SIPE plugin" message (Stefan Becker) -- Stefan Becker Sat, 23 Apr 2016 15:55:00 +0300 pidgin-sipe (1.20.1-1) unstable; urgency=low * New upstream version 1.20.1 "Bug Fixes I" (2015-10-24) - add support for another type of ADFS response (Stefan Becker) - improve configure check for back-ported features (Stefan Becker, Jakub Adam) - updated translations: French (fr), Russian (ru) -- Stefan Becker Sat, 24 Oct 2015 15:20:00 +0300 pidgin-sipe (1.20.0-1) unstable; urgency=low * New upstream version 1.20.0 "SRTP, Conference URL & TLS-DSK Improvements" (2015-08-29) - Feature #82: Parse HTML from Lync conference URL (Stefan Becker) - Feature #69: SRTP Support (Jakub Adam) * requires libpurple >= 3.0.0 - Fixed #285: Office365 rejects RC4 in TLS-DSK (Stefan Becker) * added support for AES-128/256-CBC -- Stefan Becker Sat, 29 Aug 2015 18:07:00 +0300 pidgin-sipe (1.19.1-1) unstable; urgency=low * New upstream version 1.19.1 "Bug Fixes I" (2015-04-04) - Fixed #278: 488 error after libnice upgrade (Jakub Adam) - fix SIP re-authentication timeout to be max. 8 hours (Stefan Becker) -- Stefan Becker Sat, 04 Apr 2015 17:05:00 +0300 pidgin-sipe (1.19.0-1) unstable; urgency=low * New upstream version 1.19.0 "Auto Authentication, MFA & Search Improvements" (2015-02-07) - Feature #80: Move parsing of login name (Stefan Becker) - Feature #79: support for Adium group chat bookmarks (David Matz, Stefan Becker) - Feature #78: Support searching for SIP ID (Stefan Becker) - Feature #76: ADFS can't always be used (Stefan Becker) * for accounts that have Multi-Factor Authentication (MFA) enabled - Feature #73: Support buddy photos from contactCard (Stefan Becker) - Feature #65: Fall back from Kerberos to NTLM (Stefan Becker) - Fixed #277: Raised contact names (Stefan Becker) - Fixed #240: Corrupted HTTP response crashes SIPE (Stefan Becker) - fix calendar state machine when EWS URL is set (Stefan Becker) - fall back to [MS-DLX] BasicSearch to improve search experience (Stefan Becker, various) - implement search functionality for UCS (Stefan Becker) - adium: add chat room list UI (David Matz) - adium: fix duplicate debug log messages (Stefan Becker) - support for libnice TCP mode (Youness Alaoul, Jakub Adam) - refactor CCCP request code (Jakub Adam) -- Stefan Becker Sat, 07 Feb 2015 13:48:00 +0200 pidgin-sipe (1.18.5-1) unstable; urgency=low * New upstream version 1.18.5 "Bug Fixes V" (2014-12-29) - Fixed #276: Redundant "const" breaks build with clang (Stefan Becker) - Fixed #269: purple idle-away converted to Away (Stefan Becker) - svc: use authuser for RealmInfo request (Stefan Becker) - adium: add release checking script (Stefan Becker) - mingw: update fetch script to Pidgin 2.10.11 (Stefan Becker) - updated translations: Italian (it), Swedish (sv) -- Stefan Becker Mon, 29 Dec 2014 21:04:00 +0200 pidgin-sipe (1.18.4-1) unstable; urgency=low * New upstream version 1.18.4 "Bug Fixes IV" (2014-10-18) - Fixed #263: ADFS fails when user and login name differ (Stefan Becker) - Fixed #262: Adium: SIPE doesn't auto- or re-connect (Stefan Becker) - fixed memory leaks (Stefan Becker) - fixed processing of presence publish event response (John Zhang, Stefan Becker) * fixes a longstanding issue that the Pidgin user status sometimes didn't switch back to "Available" after the end of a meeting -- Stefan Becker Sat, 18 Oct 2014 17:24:00 +0300 pidgin-sipe (1.18.3-1) unstable; urgency=low * New upstream version 1.18.3 "Bug Fixes III" (2014-08-16) - Fixed #259: HTML response to EWS autodiscover triggers libxml2 assert (Stefan Becker) - Fixed #258: V&V call gets rejected when IPv6 is enabled (Stefan Becker, Jakub Adam) - Fixed #257: Windows 7: SIPE crashes after a minute (Stefan Becker) - mingw: improve crash information reporting (Stefan Becker) -- Stefan Becker Sat, 16 Aug 2014 14:31:00 +0300 pidgin-sipe (1.18.2-1) unstable; urgency=low * New upstream version 1.18.2 "Bug Fixes II" (2014-06-07) - Fixed #255: Crash when PersistentChat sends BYE instead of response (Stefan Becker) - Fixed #248: Remove libpurple SSL configure check (Stefan Becker) - Fixed #245: "Unable to resolve DNS SRV record" error when joining conference (Stefan Becker) - Fixed #241: Adium filters ":" from "sip:" (Stefan Becker) - Fixed #210: Conference call ends with error message (for real this time, Jakub Adam) - ews: extract settings also from type EXPR (Stefan Becker) - ucs: honor user specified email URL (Stefan Becker) - adium: fix compilation on OS X 10.7 (Stefan Becker) - updated Transifex URLs (Stefan Becker) - updated translations: Hindi (hi), Telugu (te) -- Stefan Becker Sat, 07 Jun 2014 12:29:00 +0300 pidgin-sipe (1.18.1-1) unstable; urgency=low * New upstream version 1.18.1 "Bug Fixes I" (2014-04-12) - Fixed #238: False "not delivered" in conference (Stefan Becker) - Fixed #237: HTML escaping not removed from URL (Stefan Becker) - Fixed #210: Conference call ends with error message (Jakub Adam) - fix endless loop with failed HTTP Basic authentication (Stefan Becker) - fix crash when gstreamer nice plugin is missing (Stefan Becker, Jakub Adam) - fix EWS autodiscover for some Office 365 users (Stefan Becker) - purple: fix missing "Copy to" in buddy menu (Stefan Becker) - purple/adium: ignore empty search values (Stefan Becker) - adium: fix group chat UI (Stefan Becker) - adium: implement BEAST mitigations for 10.8.5 (Michael Lamb) - add indication when user is connected from a mobile device (Harris Kauffman) - updated translations: Chinese (zh_CN), Portuguese (pt) -- Stefan Becker Sat, 12 Apr 2014 17:54:00 +0300 pidgin-sipe (1.18.0-1) unstable; urgency=low * New upstream version 1.18.0 "Adium, GSS-NTLMSSP & OpenSSL" (2014-01-11) - Feature #71: Add support for EWS Autodiscover redirection (Stefan Becker) - Feature #69: Add UI support for (group) chats (Michael Lamb) * NOTE: Adium does not have an UI to fetch the room list - Feature #64: Add support for GSS-NTLMSSP (Stefan Becker) * sip-sec-krb5.c module has been renamed to sip-sec-gssapi.c * if gssapi/gssapi_ntlmssp.h is detected then sip-sec-ntlm.c will be disabled and NTLM will be handled by sip-sec-gssapi.c instead * NOTE: at the time of this writing the user has to set up GSS-NTLMSSP by hand on his system, i.e. /etc/gss/mech - Fixed #227: Adium client doesn't save email option settings (Harris P. Kauffman) - Fixed #216: SIPE stops working on Mavericks (Stefan Becker, Michael Lamb) * add an UI option to disable SSL BEAST mitigations * NOTE: requires Adium 1.5.10 - Fixed #197: Account stays in connecting stage (Harris P. Kauffman) - cleanup for sip-sec Kerberos & SSPI modules (Stefan Becker) * replace old TGT hack with gss_acquire_cred_with_password() * clean up Kerberos detection in configure * remove special case handling; code is more straight-forward now * thanks to David Woodhouse and Simo Sorce for the GSSAPI information - implement internal keepalive handling (Stefan Becker) - implement crypto backend based on OpenSSL (Stefan Becker) - adium: Xcode project files update (Michael Lamb) - adium: replace NSS crypto backend with OpenSSL (Stefan Becker) * NOTE: please make sure to read the updated build instructions! -- Stefan Becker Sat, 11 Jan 2014 16:10:00 +0200 pidgin-sipe (1.17.3-1) unstable; urgency=low * New upstream version 1.17.3 "Bug Fixes III" (2013-12-11) - Fixed #225: HTTP re-authentication with NTLM fails (Stefan Becker) - Fixed #222: SIPE crashes when groupchat session expires (Stefan Becker) - fix UCS Persona key extraction (Stefan Becker) -- Stefan Becker Wed, 11 Dec 2013 21:18:00 +0200 pidgin-sipe (1.17.2-1) unstable; urgency=low * New upstream version 1.17.2 "Bug Fixes II" (2013-11-30) - Fixed #214: Typing notification does not always work (Stefan Becker) * reverted one change which caused problems for some users - Fixed #222: SIPE crashes when groupchat session expires (Stefan Becker) - updated translations: Romanian (ro) -- Stefan Becker Sat, 30 Nov 2013 17:51:00 +0200 pidgin-sipe (1.17.1-1) unstable; urgency=low * New upstream version 1.17.1 "Bug Fixes I" (2013-11-16) - Fixed #215: Password not entity encoded in WSSE element (Stefan Becker) - Fixed #214: Typing notification does not always work (Stefan Becker) - accept alternatives for webticket timestamp/keydata (Stefan Becker) - adium: add "don't publish calendar" to account UI (Stefan Becker) - contrib: add SSL BEAST mitigation patch for Adium (Stefan Becker) - updated translations: French (fr) -- Stefan Becker Sat, 16 Nov 2013 16:21:00 +0200 pidgin-sipe (1.17.0-1) unstable; urgency=low * New upstream version 1.17.0 "Lync 2013" (2013-09-21) - Feature #62: Support for Lync 2013 Unified Contact Store (Stefan Becker) - Feature #59: Support for Lync 2013 Persistent Chats (Stefan Becker) - Fixed #211: Status "away" or "busy" incorrectly mapped to "Invisible" (Michael Lamb) - Fixed #209: group chat doesn't like HTML (Stefan Becker) - Fixed #200: OCS archiving system blocks audio/video connection (Jakub Adam) - Fixed #187: Duplicate messages in group chat (Stefan Becker) - Fixed #184: Duplicate users showing in Group Chat (Stefan Becker) - fix EWS autodiscover for Office 365 (Stefan Becker) - add support for group chat history (Stefan Becker) - add support for buddy photos on Lync 2013 (Stefan Becker) -- Stefan Becker Sat, 21 Sep 2013 16:34:00 +0300 pidgin-sipe (1.16.1-1) unstable; urgency=low * New upstream version 1.16.1 "Bug Fixes I" (2013-07-13) - Feature #66: Windows DLL version information (Stefan Becker) - fix call failure when host has multiple IP addresses (Jakub Adam) - fix buddy list handling after moving to Lync 2013 (Stefan Becker) * Lync 2013 migrates buddy list to Unified Contact Store (UCS) * NOTE: modifying the buddy list is *NOT* supported yet! - crash fixes for new HTTP stack (Stefan Becker) -- Stefan Becker Sat, 13 Jul 2013 16:20:00 +0300 pidgin-sipe (1.16.0-1) unstable; urgency=low * New upstream version 1.16.0 "HTTP Rewrite & Subscription Fixes" (2013-06-14) - Feature #58: Implement Digest authentication scheme for SIP Proxy Authentication (Stefan Becker) - Fixed #196: Useragent value not forwarded to core (Michael Lamb) - Fixed #193: Pidgin Status changes stop working (Stefan Becker) - Fixed #186: Users appear offline when they are not (Stefan Becker) - fix kinit-less use case with krb5 >= 1.11 (Stefan Becker) - rewritten HTTP stack from scratch (Stefan Becker) * cleaner, layered and hopefully less error-prone implementation * HTTP stack internals no longer exposed to user code * reduced network traffic and less SSL handshakes by utilizing HTTP/1.1 connection keep alive for multiple HTTP requests to the same host - switch purple backend to deferred destruction approach (Stefan Becker) * Pidgin should no longer crash at connection close, even in corner cases - add menu entry to make a call with a phone number (Jakub Adam) - some progress on telepathy backend (Stefan Becker) * add TLS certificate accept/reject user interaction * add "Single Sign-On" & "Don't Publish Calendar" account options -- Stefan Becker Fri, 14 Jun 2013 19:57:00 +0300 pidgin-sipe (1.15.1-1) unstable; urgency=low * New upstream version 1.15.1 "Bug Fixes I" - NOTE: SIPE SourceForge project got updated. Because of this all bug and feature request numbers have changed. - Fixed #190: SIP 407 response rejected with invalid message signature (Stefan Becker) - Fixed #189: Adium SIPE plugin vs. libpurple linking issues (Michael Lamb) - fixed free-after-use issue that caused crashes for some users (Stefan Becker) - fixed broken NTLM fallback in Negotiate (Stefan Becker) - fixed subscriptions expiration by subscribing again after re-authentication (Stefan Becker) - allow different user name and login for Office 365 authentication (Stefan Becker) - add SIPE version & git commit ID to debug log (Stefan Becker) - added valgrind log analyzer script (Stefan Becker) - added NTLM message anaylzer (Stefan Becker) - updated translations: Hungarion (hu), Romanian (ro) - updated Adium port (Michael Lamb, Harris P. Kauffman) -- Stefan Becker Sun, 07 Apr 2013 15:09:00 +0300 pidgin-sipe (1.15.0-1) unstable; urgency=low * New upstream version 1.15.0 "Authentication & Autodiscovery Update" - Feature #3578135: Support Kerberos for HTTP(S) authentication w/o SSPI (Stefan Becker) * effective for all platforms that support --with-krb5 * this triggered a series of cleanup & simplification changes and functionality & memory leak fixes in the sip-sec modules * special thanks to Jarek Polok for the logs and testing - Feature #3594094: Add HTTPS to autodiscover probe (Stefan Becker) - Feature #3607040: Simple button to disable calendar integration (Stefan Becker) - Fixed #3603228: Crash on 1.14.1 when connecting to server (Stefan Becker) - Fixed #3604671: sip uri with apostrophe is not valid (Stefan Becker) - fixed HTTP redirect crash (Stefan Becker) - unified Single Sign-On handling in all places (Stefan Becker) * if SSO is enabled then "Login" & "Password" settings are ignored * SSO is now off by default for new accounts * NOTE: if you do *NOT* use SSO, then be sure to disable it in the "Advanced" tab of the account settings after updating! - added implementation for HTTP "WWW-Authenticate: Negotiate" scheme (Stefan Becker) * effective for all platforms that support --with-krb5 * it will try Kerberos first, then fall back to NTLM * valid Kerberos Single Sign-On setup will be detected automatically * setup for a mixed Kerberos/NTLM HTTP environment: - login name: DOMAIN\account - password: domain password - authentication: Kerberos - Single Sign-On: OFF(!) (see above) - enabled TLS-DSK support in Windows SSPI version - TLS-DSK: don't ask for password if SSPI or Kerberos are compiled in - Farstream 0.1.1 compatibility fix (Jakub Adam) - support conf:sip: meeting URIs (Jakub Adam) - updated Adium port (Michael Lamb) -- Stefan Becker Sat, 09 Mar 2013 15:25:00 +0200 pidgin-sipe (1.14.1-1) unstable; urgency=low * New upstream version version 1.14.1 "Bug Fixes I" - Feature #3578132: Kerberos configuration should be passwordless (Stefan Becker) * purple: non Single Sign-on users are asked for the password again - bug & memory leak fixes in sipe-buddy.c (Jakub Adam) -- Stefan Becker Wed, 26 Dec 2012 16:23:00 +0200 pidgin-sipe (1.14.0-1) unstable; urgency=low * New upstream version 1.14.0 "Buddy photo & ADFS support, Web Ticket Optimizations" - Feature #3585364: Add support for Web Ticket authentication using ADFS (Stefan Becker) * special thanks to user bhakta79 for the hard work taking logs - Feature #3578132: Kerberos configuration should be passwordless (Stefan Becker) - Fixed #3580212: Connection drops after a few hours (Stefan Becker) - add support for buddy photos (Jakub Adam) - add support for call to Audio Test Service (Jakub Adam) - initial implementation for telepathy backend (Stefan Becker) * nothing much to see for end users yet... - reduce Web Ticket traffic by queueing requests & caching tickets (Stefan Becker) - update OBS packaging information for Debian (Stefan Becker) - various minor bug & build fixes -- Stefan Becker Sun, 16 Dec 2012 16:28:00 +0200 pidgin-sipe (1.13.3-1) unstable; urgency=low * New upstream version 1.13.3 "Bug Fixes III" (2012-08-19) - Fixed #3537084: OpenBSD build issue (Stefan Becker) - Fixed #3543294: Support Lync 2010 meet URLs (Jakub Adam) - revert to legacy MSOC protocol on Lync FT invitation (Jakub Adam) - fix broken busy->available status switch (Stefan Becker) - updated translations: Portuguese (pt) -- Stefan Becker Sun, 19 Aug 2012 12:03:00 +0300 pidgin-sipe (1.13.2-1) unstable; urgency=low * New upstream version 1.13.2 "Bug Fixes II" (2012-06-10) - tls: fix buffer overrun (Oleksandr Hryshchuk, Stefan Becker) - win32: fix TCP connections (Stefan Becker) - nsis: fix broken locale installation (Stefan Becker) - updated translations: French (fr) - various build fixes (Stefan Becker, Jakub Adam) -- Stefan Becker Sun, 10 Jun 2012 15:48:00 +0300 pidgin-sipe (1.13.1-1) unstable; urgency=low * New upstream version 1.13.1 "Bug Fixes I" - detect incompatible encryption level with Lync (Jakub Adam) - purple: add URI validity check to Add Buddy callback (Stefan Becker) - new translations: Romanian (ro), Turkish (tr) - various build fixes (Stefan Becker) -- Stefan Becker Mon, 09 Apr 2012 12:29:42 +0300 pidgin-sipe (1.13.0-1) unstable; urgency=low * New upstream version 1.13.0 "Lync & Office365" - added [MS-SIPAE] TLS-DSK authentication scheme (Stefan Becker) * TLS-DSK has been introduced in Lync * mandatory for Office365 accounts * also works for non-public Lync installations * does not work yet with SSPI on Windows - added [MS-DLX] based Get Info/Contact Search (Stefan Becker) * [MS-PRES] SIP-Based Active Directory Search is disabled in Lync - added experimental media TCP transport (Jakub Adam) - make it compile against the latest purple 3.0.x API (Stefan Becker) - make it compile against the latest glib2 2.31.x API (Stefan Becker) - completed cleanup: core no longer requires libpurple (Stefan Becker) - refactored crypto code, ie. NSS can replaced if necessary (Stefan Becker) - sipe-domino.c is no longer built under UNIX to remove dead code (Stefan Becker) - restricted XXX_CFLAGS to modules that need them (Stefan Becker) - NSS is now a mandatory build requirement (Stefan Becker) - decoupled SSPI from HAVE_LIBKRB5 flag. New flag is HAVE_SSPI (Stefan Becker) - OBS mingw packages now use SSPI instead of NTLM (Stefan Becker) - added NSIS package generation to OBS mingw packages (Stefan Becker) - removed kopete backend. KDE is moving to telepathy (Stefan Becker) - added MinGW cross-compilation on Linux instructions (Stefan Becker) -- Stefan Becker Wed, 14 Mar 2012 19:30:23 +0200 pidgin-sipe (1.12.0-1) unstable; urgency=low * New upstream version 1.12.0 "Group Chat" - Feature #3064877: Add support for OCS2007R2 Group Chat (Stefan Becker) - Feature #3311026: Support for HTTP/1.1 Transfer-Encoding: chunked (Stefan Becker) - Fixed #2834758: First NTLM signature check after startup fails (Stefan Becker) - Fixed #3082602: Crash on Autodiscover (Stefan Becker) - Fixed #3090663: Re-authentication fails (Stefan Becker) - Fixed #3092324: Core dump in "make check" (psfales) - Fixed #3130915: Failed to authenticate to server (Stefan Becker) - Fixed #3148124: sipe segfaults during login on Solaris (Jakub Adam) - Fixed #3150482: "configure --with-vv" test uses wrong include (Stefan Becker) - Fixed #3156430: Messages not Delivered (rwinchsf, Stefan Becker) - Fixed #3161273: Lost Connection Gives No Error Message (rwinchsf, Stefan Becker) - Fixed #3198585: Extra line breaks (Stefan Becker) - Fixed #3267073: False "could not be delivered" errors (sort of..., Stefan Becker) - Fixed #3399007: Crash when sipe_cal_working_hours->days_of_week is NULL (Stefan Becker) - Patch #3091490: Make 1.11.0 Compile on FreeBSD (jprather) - Patch #3108246: Patch for better windows installer (archrival, galiven) - add random Ms-Conversation-ID to INVITE (Jakub Adam) - fix parsing of P-Asserted-Identity header (Jakub Adam) - added MS TURN support (Jakub Adam) - fix crash on zero length password in NTLM (Vladimir Ushakov) - implement timeouts for SIP request. Used for REGISTER (Stefan Becker) - more work on Voice & Video call support (Jakub Adam) - make it compile against the purple 2.8.x & 3.0.x APIs (Stefan Becker) - more internal changes to prepare for non-purple backends (Stefan Becker) - added integration for transifex.net update (Stefan Becker) - configure improvements for 64-bit: use libdir, gsize/size_t compatibility (Stefan Becker) - update compiler warnings configuration for all build platforms (Stefan Becker) - updated Adium port (Matthew Duggan) - mingw build updates (Harris P. Kauffman, Stefan Becker) - added miranda port (Jochen De Smet) - added mingw to OpenSUSE Build Service configuration (Stefan Becker) -- Anibal Avelar Mon, 29 Aug 2011 00:50:47 -0500 pidgin-sipe (1.11.2-1) unstable; urgency=low * New upstream version 1.11.2 "Hot fixes II" - Revert "mingw: add missing purple-notify.c to build" - Sipe-sign: fix parsing of P-Asserted-Identity header - Fixed memory leaks - Fix #3090663: Re-authentication fails - Fix #3090663: Re-authentication fails (2nd attempt) - Fix #3090663: Re-authentication fails (3rd attempt) - Fix #3090663: Re-authentication fails (4th attempt) - Apply patch #3091490: Make 1.11.0 Compile on FreeBSD - Fix #3092324: Core dump in "make check" -- Anibal Avelar Tue, 02 Nov 2010 23:48:33 -0600 pidgin-sipe (1.11.1-1) unstable; urgency=low * New upstream version 1.11.1 "Hot fixes" - mingw: add missing purple-notify.c to build - Fix for bug #2834758: First NTLM signature check after startup fails - purple: fix memory leak in sipe_backend_transport_connect() error path - Fix for bug #3082602: Crash on Autodiscover - configure: use libdir & datadir instead of prefix + path - configure: update 32-bit vs. 64-bit header conflict test - debian: build stability fix in post-install -- Anibal Avelar Sun, 24 Oct 2010 00:00:00 +0300 pidgin-sipe (1.11.0-1) unstable; urgency=low * New upstream version - Feature #2945346: Lotus Notes/Domino Calendar integration: * Sipe can now retrieve calendar data (Meeting schedule/subject/ location) from a web-enabled Lotus Domino server and publish it to OCS2007/LCS2005 as availability information. * Example: "Calendar: Currently Busy. Free at 11:30". * Team members (contacts with access level Team) will be able to see information about our current meeting (subject & location) (OCS2007) * First calendar update is scheduled 1 minute after connect, * Subsequent calendar updates happen in 30 minute intervals. * Manual calendar update can be triggered using the following menu: Accounts->{SIPE_ACCOUNT}->"Republish Calendar" * Though Domino integration can work without any additional settings in account configuration (on Windows), there are options to manually provide Domino Services URL and email address/password if it's different from SIP URI/Password settings on Basic tab. - Fixed #2971422: idle check for OCS2005 presence case - Fixed #2982424: krb5 build errors on FreeBSD - Fixed #2997639: pidgin crash after accepting cert - Fixed #3001523: Cancelling a long pending file transfer crashes Pidgin - Fixed #3002993: Group Name issues with ampersand - Fixed #3029228: Calendar published at/with incorrect time - Fixed #3029929: Crash with outlook 2k3 Calendar - Fix logout from OCS - Implement workaround for buddy list menu memory leaks - Dropped UDP transport support - Rewrote TCP & TLS transport support - HTTP improvements: GET, cookies - Alternative crypt/digest implementation based on NSS - Rewrote message debug log and implemented an analyzer script for it - More internal changes to prepare for non-purple backends - New translations: 'nl', 'sv', 'ar', 'hu', 'ja', 'ko', 'sv', 'zh_TW' * Fixed pidgin-sipe: Kerberos authentication is broken (Closes: #597437) * Fixed pidgin-sipe: no dependency on pidgin (Closes: #594882) * Fixed pidgin-sipe: new upstream version is available (Closes: #596799) * Fixed pidgin-sipe: FTBFS: sipe.c:10050: error: missing initializer (Closes: #582998) * Added dependency with the libxml2-dev package in debian/control * Added dependency with libnss3-dev package in debian/control * Updated with Standards-Version 3.9.1 -- Anibal Avelar Sun, 03 Oct 2010 22:05:31 +0300 pidgin-sipe (1.10.1-1) unstable; urgency=low * New upstream version - Fix broken sipe_ht_equals_nick(); the broken code has been in the 1.10.0 release. As it affects the buddy list it could be the root cause for some of the "buddy appears offline" reports. - Make it compile against the final purple 2.7.0 API; -- Anibal Avelar Sun, 27 Jun 2010 11:46:59 -0600 pidgin-sipe (1.10.0-1) unstable; urgency=low * New upstream version - Feature #2823160: Access Levels (2007+ environment) - Feature #2957811: add support for "automaton" class - Feature #2972823: fail on in-line variable declarations - Fixed #2971422: handle OCS 2005 idiosyncrasy of varying SIP URI case - Fixed #2981563: Authentication protocol v4 - invalid signature of some incoming messages containing P-Asserted-Identity or P-Preferred-Identity with uppercased SIP or TEL in URI. - Fixed publication/"409 Conflict" endless looping - Prepare for Pidgin 2.7.0 - Added SVG icon artwork - Many changes to configure script. Be sure to look at "configure --help" - Many internal changes to prepare for non-purple backends - New build requirements: libxml2, glib-2.0 >= 2.12.0 - New build option: gmime-2.4 >= 2.4.16 or gmime-2.6 >= 2.5.2 for non-purple backends -- Anibal Avelar Sun, 04 Apr 2010 00:52:59 -0600 pidgin-sipe (1.9.1-1) unstable; urgency=low * New upstream version (emergency release) - Fixed #2969327: Kerberos authentication always fails on *nix platforms - Fixed #2968287: Authentication failure in scenario when director server is Office Communications Server 2007 or above and home server is Live Communications Server 2005 - Send BYE when response to IM message is 408/480/481 - Re-enable offline status to be user settable -- Anibal Avelar Tue, 16 Mar 2010 02:13:59 -0600 pidgin-sipe (1.9.0-1) unstable; urgency=low * New upstream version (Closes: #570735) * File transfer functionality. File encryption is supported * NTLMv2 and NTLMv2 Session Security support * Implemented SIP Authentication Extensions protocol version 4 and 3 * Adoption for commercial UNIX - HP/UX, Irix, Solaris - big endian fixes * Increased libpurple build requisite to >= 2.4.0 * many crash fixes for error or corner cases in calendar integration * more detailed code analysis with Coverity Prevent * build fixes for older libpurple/glib2 versions * OpenSUSE Build Service configuration files * Fix NTLM crash if login setting is undefined * Use of g_str_has_prefix() available since glib 2.2 and null-safe * build fixes for older OS releases, e.g. Ubuntu older than 9.10. * Removed the file libsipe.la from the package * Removed the line dh_pidgin -ppidgin-sipe from debian/rules * Added ./autogen.sh could be run from debian/rules * Changed the Section from net to misc inside Source stanza in debian/control * Changed the Section from net to misc inside Package stanza in debian/control * Added the Priority header inside Package stanza in debian/control * Updated the Description inside Package stanza in debian/control * Updated the debian/copyright file with the new authors list. * Updated the debian/copyright file with the correct PATH to license GPL2. * Removed dependency with the libgtk2.0-dev package in debian/control * Removed dependency with the comerr-dev package in debian/control * Removed debian/README.debian file from the package * Added dependency with the libzephyr-dev package in debian/control -- Anibal Avelar Wed, 10 Mar 2010 01:05:59 -0600 pidgin-sipe (1.8.0-1) unstable; urgency=low * New upstream version (Closes: #536100) * Simplifying the authentication process using only the Basic Settings screen. * Added integration with Exchange 2007/2010 * Added Windows Messenger 5.0 (RTC/1.2) compatibility. * "About SIPE plugin" screen implemented. * Added Kerberos support. * Added Multiparty Chat support. * Fixed many bugs. * Fixed many potencial memory leaks. * Fixed support for LCS2005 and OCS2007 (improvementsi and bugs fixed) * Added "Find on LinkedIn" link on contact's User Info screen * Updated translations: "de", "pl", "ru", "cs", "da", "es", "fi", "fr, "hi", "it", "nb", "ta", "zh_CN". * Fixed for server auto-discovery * Fixed for User Agent string. * Added integration with PBX (external phones) using CSTA protocol * Fixed for CHOWA problems * Fixed for spaces in Login's username * Support for Reuters Messaging environment * Support for message formatting * Added notification support for undelivered messages * Extended contact search implementation * ... and more * Fixed pidgin-sipe: urls appear w/ underscore prefix (Closes: #529523) * Updated the debian/copyright file with the new authors list. * Added DEB_CONFIGURE_EXTRA_FLAGS in debian/rules for extra compile flags * Added dependency with the libkrb5-dev package * Removed to delete the file libsipe.la from debian/rules. * Removed DEB_AUTO_UPDATE_DEBIAN_CONTROL reference from debian/rules. * Updated with Standards-Version 3.8.4 -- Anibal Avelar Sun, 07 Feb 2010 04:48:35 -0500 pidgin-sipe (1.3.3-1) unstable; urgency=low * New upstream version (Closes: #508221) * Added support for OCS 2007, LCS 2003 * Full support for send/receive messages * Full presence support (both sides) * Full support for add/remove/move contacts * TLS/SSL support * Search contacts full support * xBSD support * Added the dependency with the libpurple-dev package * Added the dependency with the intltool package * Added the dependency with the comerr-dev package * Removed the unnecessary debian/pidgin-sipe.dirs file * Changed the package description inside debian/control (Closes: #433920,#490923) * Added the field Homepage inside Source stanza in debian/control file. * Updated the debian/copyright file with the new authors list. * Updated with Standards-Version 3.8.0 -- Anibal Avelar Sun, 1 Mar 2009 12:15:35 -0500 pidgin-sipe (1.2-1) unstable; urgency=low * Initial release (Closes: #418226) -- Anibal Avelar (Fixxxer) Wed, 13 Jun 2007 19:12:35 -0500 ================================================ FILE: contrib/debian/compat ================================================ 5 ================================================ FILE: contrib/debian/control ================================================ Source: pidgin-sipe Section: misc Priority: optional Maintainer: Anibal Avelar Homepage: http://sipe.sourceforge.net/ # FYI: libnss3-dev can be replaced with libssl-dev Build-Depends: cdbs (>= 0.4.23-1.1), autotools-dev, debhelper (>= 5), pkg-config, libglib2.0-dev (>= 2.18.0), libdbus-1-dev, libxml2-dev, libnss3-dev, pidgin-dev, libpurple-dev (>= 2.7.0), libtool, intltool, libkrb5-dev, libzephyr-dev Standards-Version: 3.9.1 Package: pidgin-sipe Section: net Priority: optional Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Pidgin protocol plugin to connect to MS Office Communicator A third-party plugin for the Pidgin multi-protocol instant messenger. It implements the extended version of SIP/SIMPLE used by various products: . * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) . With this plugin you should be able to replace your Microsoft Office Communicator client with Pidgin. ================================================ FILE: contrib/debian/copyright ================================================ This work was packaged for Debian by: Anibal Avelar on Wed, 13 Jun 2007 19:12:35 -0500. It was downloaded from: Upstream Authors: Anibal Avelar Stefan Becker Jakub Adam Tomáš Hrabčík (retired) pier11 (retired) Gabriel Burt (retired) Daniel Beichl (retired) Based on the initial SIP/SIMPLE gaim protocol plugin by: Thomas Butter Copyright: Copyright (C) 2010 Jakub Adam Copyright (C) 2010 Tomáš Hrabčík Copyright (C) 2009-2010 pier11 Copyright (C) 2008 Novell, Inc. Copyright (C) 2007-2012 SIPE Project Copyright (C) 2007-2010 Anibal Avelar Copyright (C) 2005 Thomas Butter License: This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian systems, the complete text of the GNU General Public License version 2 can be found in `/usr/share/common-licenses/GPL-2'. Debian packaging is: Copyright (C) 2007 Anibal Avelar You can redistribute it and/or modify it under the terms of the GNU General Public License as published by Free Software Foundation; either version 2 of the License, or (at your option) any later version. ================================================ FILE: contrib/debian/docs ================================================ AUTHORS ChangeLog NEWS README TODO ================================================ FILE: contrib/debian/rules ================================================ #!/usr/bin/make -f include /usr/share/cdbs/1/class/autotools.mk include /usr/share/cdbs/1/rules/debhelper.mk HAVE_APPSTREAM := $(strip $(shell (/usr/bin/appstream-validate >/dev/null --version || /usr/bin/appstreamcli >/dev/null --version) && echo yes)) HAVE_APPSTREAM_LEGACY := $(strip $(shell . /etc/os-release; echo $${ID}-$${VERSION_ID} | grep -q '^debian-8$$' && echo yes)) DEB_CONFIGURE_EXTRA_FLAGS := --enable-purple # detect if package telepathy-glib is available ifneq ($(shell pkg-config --exists telepathy-glib && echo FOUND),) DEB_CONFIGURE_EXTRA_FLAGS += --enable-telepathy else DEB_CONFIGURE_EXTRA_FLAGS += --disable-telepathy endif ifneq ($(shell pkg-config --exists nice && echo FOUND),) DEB_CONFIGURE_EXTRA_FLAGS += --with-vv else DEB_CONFIGURE_EXTRA_FLAGS += --without-vv endif ifeq ($(HAVE_APPSTREAM),) DEB_CONFIGURE_EXTRA_FLAGS += --without-appstream endif # run tests DEB_MAKE_CHECK_TARGET := check # don't run unnecessary ldconfig on postinst and postrm. DEB_DH_MAKESHLIBS_ARGS_pidgin-sipe=-n clean:: dh_testdir # git snapshot does not contain a configure script debian/stamp-autotools-files: $(DEB_CONFIGURE_SCRIPT) $(DEB_BUILDDIR)/config.status: $(DEB_CONFIGURE_SCRIPT) $(DEB_CONFIGURE_SCRIPT): ./autogen.sh binary-post-install/pidgin-sipe:: rm -f debian/pidgin-sipe/usr/share/doc/pidgin-sipe/README rm -r debian/pidgin-sipe/usr/share/pixmaps/pidgin/protocols/24 rm -r debian/pidgin-sipe/usr/share/pixmaps/pidgin/protocols/32 rm debian/pidgin-sipe/usr/lib/purple-2/libsipe.la ifeq ($(HAVE_APPSTREAM)-$(HAVE_APPSTREAM_LEGACY),yes-yes) mv debian/pidgin-sipe/usr/share/metainfo debian/pidgin-sipe/usr/share/appdata endif .PHONY: update-debian-control ================================================ FILE: contrib/debug/parse_log.pl ================================================ #!/usr/bin/perl -w use 5.010; use strict; use warnings; use File::Spec; use Getopt::Long; use Pod::Usage; # Command line option my %Options = ( directory => ".", ); GetOptions(\%Options, "directory=s", "callid", "from", "method", "transport", "filter", "help|h|?") or pod2usage(2); pod2usage(-verbose => 2) if $Options{help}; ############################################################################### # # Message parsing # ############################################################################### my %callid; my %from; my %method; my %transport; sub AddMessage($$$$@) { my($direction, $type, $transport, $time, @message) = @_; # extract additional information from SIP messages if ($type eq "SIP") { my($index, $callid, $from, $method); foreach my $line (@message) { next if $index++ < 1; last if $line =~ /^\s+$/; next unless my($keyword, $value) = $line =~ /^([^:]+):\s+(.+)/; $callid = $value if $keyword =~ /^call-id$/i; ($from) = $value =~ /^]+)/ if $keyword =~ /^from$/i; ($method) = $value =~ /^\d+\s+(\S+)/ if $keyword =~ /^cseq$/i; } push(@{$callid{$callid}}, \@message) if $Options{callid} && defined $callid; push(@{$from{lc($from)}}, \@message) if $Options{from} && defined $from; push(@{$method{uc($method)}}, \@message) if $Options{method} && defined $method; } # information available for all message types push(@{$transport{$transport}}, \@message) if $Options{transport}; } sub DumpMessages() { foreach my $callid (keys %callid) { if (open(my $fh, ">", File::Spec->catfile($Options{directory}, "callid-${callid}.txt"))) { print $fh @{$_} foreach (@{$callid{$callid}}); close($fh); } } foreach my $from (keys %from) { if (open(my $fh, ">", File::Spec->catfile($Options{directory}, "from-${from}.txt"))) { print $fh @{$_} foreach (@{$from{$from}}); close($fh); } } foreach my $method (keys %method) { if (open(my $fh, ">", File::Spec->catfile($Options{directory}, "method-${method}.txt"))) { print $fh @{$_} foreach (@{$method{$method}}); close($fh); } } foreach my $transport (keys %transport) { if (open(my $fh, ">", File::Spec->catfile($Options{directory}, "transport-${transport}.txt"))) { print $fh @{$_} foreach (@{$transport{$transport}}); close($fh); } } } ############################################################################### # # Main program # ############################################################################### # For all lines from command line files or STDIN my @message; my $counter; while (<>) { # Start of message? if (my ($direction, $type, $transport, $time) = /^MESSAGE START\s+([<>]+)\s+([^(]+)\((0x[\da-f]+)\)\s+-\s+(.+)/) { push(@message, "------------- NEXT MESSAGE: " . (($direction =~ /^>/) ? "outgoing" : "incoming") . " $type($transport) at $time\n"); # End of message? } elsif (($direction, $type, $transport, $time) = /^MESSAGE END\s+([<>]+)\s+([^(]+)\((0x[\da-f]+)\)\s+-\s+(.+)/) { if ($Options{filter}) { print @message; } else { print STDERR "." if (++$counter % 10 == 0); AddMessage($direction, $type, $transport, $time, @message); } # Done with the current message undef @message; # All other lines } else { # Collect message information push(@message, $_) if @message; } } unless ($Options{filter}) { print STDERR "\n" unless $Options{filter}; DumpMessages(); } # That's all folks... exit 0; __END__ =head1 NAME parse_log.pl - parse pidgin-sipe debug log =head1 SYNOPSIS [perl} parse_log.pl --help|-h|-? [perl} parse_log.pl --filter [file ...] [perl} parse_log.pl [--directory ] [--callid] [--from] [--method] [--transport] [file ...] =head1 OPTIONS =over 8 =item B<--callid> Dump all SIP messages belonging to one Call-ID to the same file. =item B<--directory> Directory for output files. Default is the current directory. =item B<--filter> Enable filter mode. Messages are simply printed to stdout. =item B<--from> Dump all SIP messages sent from the same SIP URI to the same file. =item B<--help> =item B<--h> =item B<--?> Print a brief help message and exits. =item B<--method> Dump all SIP messages with the same method to the same file. =item B<--transport> Dump all messages from one transport to the same file. =back =head1 DESCRIPTION B extracts SIP/HTTP messages from pidgin-sipe debug logs. If no file is specified then it reads from STDIN. =cut ================================================ FILE: contrib/debug/parse_valgrind.pl ================================================ #!/usr/bin/perl -w use 5.010; use strict; use warnings; ############################################################################### # # Build SIPE with: # # CFLAGS="-g -O0" ./configure # # Grab a log with: # # G_DEBUG="gc-friendly" G_SLICE="always-malloc" valgrind --leak-check=yes \ # /usr/bin/pidgin --debug 2>&1 | tee pidgin_debug.log # # Analyze log with: # # perl contrib/debug/parse_valgrind.pl pidgin_debug.log # ############################################################################### my @heap_lines; my @last_heap_lines; my $invalid_lines; my @all_invalid_lines; my $other_lines; # For all lines from command line files or STDIN while (<>) { next unless my($remainder) = /^==\d+== (.*)/; if ($remainder eq "HEAP SUMMARY:") { @heap_lines = ($remainder); undef $invalid_lines; undef $other_lines; } elsif ($remainder =~ /^ERROR SUMMARY:/) { # keep only the last heap summary @last_heap_lines = @heap_lines; undef @heap_lines; } elsif ($remainder =~ /^Invalid /) { # collect all invalid lines push(@all_invalid_lines, $remainder); undef @heap_lines; $invalid_lines++; undef $other_lines; } elsif ($remainder =~ /^Conditional/) { undef @heap_lines; undef $invalid_lines; $other_lines++ } elsif (@heap_lines) { push(@heap_lines, $remainder); } elsif (defined($invalid_lines)) { push(@all_invalid_lines, $remainder); } elsif (defined($other_lines)) { undef $other_lines if $remainder eq ""; } else { #print "UNKNOWN: $remainder\n"; } } sub check_blocks($$$) { my($label, $start, $lines) = @_; my @block; my $flagged; print "$label:\n\n"; foreach (@{$lines}) { if (/$start/../^$/) { push(@block, $_); # matcher for SIPE code lines $flagged++ if /\((?:sipe-|sip-|sdpmsg|sipmsg|http-|uuid|purple-|telepathy-)/; if (length($_) == 0) { print join("\n", @block), "\n\n" if $flagged; undef @block; undef $flagged; } } } } check_blocks("INVALID ACCESSES", qr/^Invalid /, \@all_invalid_lines); check_blocks("MEMORY LEAKS", qr/^\d+ bytes in \d+ blocks/, \@last_heap_lines); # That's all folks... exit 0; ================================================ FILE: contrib/mingw-cross-compile/README.txt ================================================ Introduction ============ With these instructions you will be able to generate libsipe.dll on a Linux machine that is compatible with the official Pidgin Windows releases. The approach described here is based on this wiki page: http://code.google.com/p/pidgin-privacy-please/wiki/HowToCrossCompileForWindowsAgainstLatestPidgin The build has been verified to work at the time this text was written. When you read this some things in Pidgin or MinGW might changed, so make sure to check the comments on that wiki page for updates. Preparation =========== You'll need a Linux machine with the following MinGW cross-compilation packages installed: Ubuntu: sudo apt-get install mingw32 mingw32-binutils mingw32-runtime Fedora: sudo yum install mingw32-gcc This will most likely work also for other Linux distros, but you'll have to check what names the MinGW cross-compilation packages are for your distro. If you are trying to build the source code from the git repository then you'll need additional tools installed, at least: autoconf automake flex If you want to build the NSIS installer package then you'll need to install: mingw32-nsis Build ===== - [pidgin-sipe source code from git instead from a release tarball] run the following commands inside the git work area: ./autogen.sh ./configure make dist-gzip This will generate pidgin-sipe-.tar.gz - create an empty directory and cd into it - run contrib/mingw-cross-compile/fetch.sh from pidgin-sipe source * make sure to check for fetch & unpack errors before proceeding! - cd into build-<...REPLACE PIDGIN VERSION HERE...>/pidgin-<...REPLACE PIDGIN VERSION HERE...> - copy/unpack pidgin-sipe source code tree into into current directory - run cp pidgin-sipe-<...REPLACE PIDGIN-SIPE VERSION HERE...>/contrib/mingw-cross-compile/local.mak . - run (this is one line on the command line!) make -C pidgin-sipe-<...REPLACE PIDGIN-SIPE VERSION HERE...>/src/core -f Makefile.mingw (to compile without SSPI support add " USE_SSPI=" to the command line) If everything goes well you should now have pidgin-sipe-<...REPLACE PIDGIN-SIPE VERSION HERE...>/src/core/libsipe.dll which you can copy into your Pidgin Windows installation directory. NOTE: PLEASE make sure that there is NO OTHER libsipe.dll in that installation or in your PATH! NSIS Installer Package ====================== After you have successfully executed the build: - run (this is one line on the command line!) PIDGIN_TREE_TOP=.. make -C pidgin-sipe-<...REPLACE PIDGIN-SIPE VERSION HERE...> -f Makefile.mingw cross-compile-nsis (to compile without SSPI support add " USE_SSPI=" to the command line) If everything goes well you should now have pidgin-sipe-<...REPLACE PIDGIN-SIPE VERSION HERE...>.exe which you now can execute on your Windows machine. ================================================ FILE: contrib/mingw-cross-compile/fetch.sh ================================================ #!/bin/bash # # Based on: # # http://code.google.com/p/pidgin-privacy-please/wiki/HowToCrossCompileForWindowsAgainstLatestPidgin # # Latest Windows Pidgin build instractions: # # https://developer.pidgin.im/wiki/BuildingWinPidgin # # Check these page for latest MinGW/Pidgin URLs if you get fetch errors! # # update Pidgin version here export PIDGIN_VERSION=2.13.0 # must be absolute path export PIDGIN_DEV_ROOT=$(pwd -P)/build-${PIDGIN_VERSION} export SOURCES_DIR=${PIDGIN_DEV_ROOT}/sources/ export DEV_DIR=${PIDGIN_DEV_ROOT}/win32-dev export MINGW_DIR=${DEV_DIR}/mingw export PIDGIN_DIR=${PIDGIN_DEV_ROOT}/pidgin-${PIDGIN_VERSION} exec >fetch-${PIDGIN_VERSION}.log set -e echo 1>&2 create directory tree... rm -rf ${PIDGIN_DEV_ROOT} mkdir -p ${SOURCES_DIR} mkdir -p ${MINGW_DIR} echo 1>&2 fetching mingw... cd ${SOURCES_DIR} #wget -nv https://sourceforge.net/projects/mingw/files/MinGW/Base/binutils/binutils-2.24/binutils-2.24-1-mingw32-bin.tar.xz wget -nv http://sourceforge.net/projects/mingw/files/MinGW/Base/mingwrt/mingwrt-3.20/mingwrt-3.20-2-mingw32-dev.tar.lzma wget -nv http://sourceforge.net/projects/mingw/files/MinGW/Base/mingwrt/mingwrt-3.20/mingwrt-3.20-2-mingw32-dll.tar.lzma wget -nv http://sourceforge.net/projects/mingw/files/MinGW/Base/w32api/w32api-3.17/w32api-3.17-2-mingw32-dev.tar.lzma wget -nv http://sourceforge.net/projects/mingw/files/MinGW/Base/gmp/gmp-5.0.1-1/gmp-5.0.1-1-mingw32-dev.tar.lzma wget -nv http://sourceforge.net/projects/mingw/files/MinGW/Base/libiconv/libiconv-1.14-2/libiconv-1.14-2-mingw32-dev.tar.lzma wget -nv http://sourceforge.net/projects/mingw/files/MinGW/Base/mpfr/mpfr-2.4.1-1/mpfr-2.4.1-1-mingw32-dev.tar.lzma wget -nv http://sourceforge.net/projects/mingw/files/MinGW/Base/pthreads-w32/pthreads-w32-2.9.0-pre-20110507-2/pthreads-w32-2.9.0-mingw32-pre-20110507-2-dev.tar.lzma wget -nv http://sourceforge.net/projects/mingw/files/MinGW/Base/gcc/Version4/gcc-4.7.2-1/gcc-core-4.7.2-1-mingw32-bin.tar.lzma wget -nv http://sourceforge.net/projects/mingw/files/MinGW/Base/gcc/Version4/gcc-4.7.2-1/libgcc-4.7.2-1-mingw32-dll-1.tar.lzma echo 1>&2 unpacking mingw... cd ${MINGW_DIR} for file in ${SOURCES_DIR}/*tar.lzma ; do tar xf ${file} ; done echo 1>&2 fetching pidgin dev stuff... cd ${SOURCES_DIR} wget -nv http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.14/gtk+-bundle_2.14.7-20090119_win32.zip wget -nv http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/gettext-tools-0.17.zip wget -nv http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/gettext-runtime-0.17-1.zip wget -nv https://developer.pidgin.im/static/win32/libxml2-2.9.2_daa1.tar.gz wget -nv https://developer.pidgin.im/static/win32/gtkspell-2.0.16.tar.bz2 wget -nv https://developer.pidgin.im/static/win32/enchant_1.6.0_win32.zip wget -nv https://developer.pidgin.im/static/win32/nss-3.24-nspr-4.12.tar.gz wget -nv https://developer.pidgin.im/static/win32/silc-toolkit-1.1.12.tar.gz wget -nv https://developer.pidgin.im/static/win32/meanwhile-1.0.2_daa3-win32.zip wget -nv https://developer.pidgin.im/static/win32/cyrus-sasl-2.1.26_daa1.tar.gz wget -nv http://ftp.acc.umu.se/pub/GNOME/binaries/win32/intltool/0.40/intltool_0.40.4-1_win32.zip wget -nv http://prdownloads.sourceforge.net/pidgin/pidgin-${PIDGIN_VERSION}.tar.bz2 echo 1>&2 unpacking pidgin dev stuff... unzip ${SOURCES_DIR}/gtk+-bundle_2.14.7-20090119_win32.zip -d ${DEV_DIR}/gtk_2_0-2.14 unzip ${SOURCES_DIR}/gettext-tools-0.17.zip -d ${DEV_DIR}/gettext-0.17 unzip ${SOURCES_DIR}/gettext-runtime-0.17-1.zip -d ${DEV_DIR}/gettext-0.17 unzip ${SOURCES_DIR}/enchant_1.6.0_win32.zip -d ${DEV_DIR}/enchant_1.6.0_win32 unzip ${SOURCES_DIR}/meanwhile-1.0.2_daa3-win32.zip -d ${DEV_DIR} unzip ${SOURCES_DIR}/intltool_0.40.4-1_win32.zip -d ${DEV_DIR}/intltool_0.40.4-1_win32 cd ${DEV_DIR} tar xzf ${SOURCES_DIR}/libxml2-2.9.2_daa1.tar.gz tar xjf ${SOURCES_DIR}/gtkspell-2.0.16.tar.bz2 tar xzf ${SOURCES_DIR}/nss-3.24-nspr-4.12.tar.gz tar xzf ${SOURCES_DIR}/silc-toolkit-1.1.12.tar.gz tar xzf ${SOURCES_DIR}/cyrus-sasl-2.1.26_daa1.tar.gz cd ${PIDGIN_DEV_ROOT} tar xjf ${SOURCES_DIR}/pidgin-${PIDGIN_VERSION}.tar.bz2 echo 1>&2 done ================================================ FILE: contrib/mingw-cross-compile/local.mak ================================================ # # Fedora 19+: upgrade to mingw32-gcc 4.8.x breaks backward compatibility. # Fetch F18 packages of mingw32-gcc and set PIDGIN_MINGW_ROOT # and LD_LIBRARY_PATH to the local install root. # ifneq ($(wildcard $(PIDGIN_MINGW_ROOT)/bin/i686-w64-mingw32-*),) # Fedora 17+ CC := $(PIDGIN_MINGW_ROOT)/bin/i686-w64-mingw32-gcc STRIP := $(PIDGIN_MINGW_ROOT)/bin/i686-w64-mingw32-strip WINDRES := $(PIDGIN_MINGW_ROOT)/bin/i686-w64-mingw32-windres EXTUTILS := /usr/share/perl5/ExtUtils else ifneq ($(wildcard $(PIDGIN_MINGW_ROOT)/usr/bin/i686-pc-mingw32-*),) # Fedora CC := $(PIDGIN_MINGW_ROOT)/usr/bin/i686-pc-mingw32-gcc STRIP := $(PIDGIN_MINGW_ROOT)/usr/bin/i686-pc-mingw32-strip WINDRES := $(PIDGIN_MINGW_ROOT)/usr/bin/i686-pc-mingw32-windres EXTUTILS := /usr/share/perl5/ExtUtils else # Ubuntu CC := /usr/bin/i586-mingw32msvc-cc STRIP := /usr/bin/i586-mingw32msvc-strip WINDRES := /usr/bin/i586-mingw32msvc-windres EXTUTILS := /usr/share/perl/5.10/ExtUtils endif # common GMSGFMT := msgfmt MAKENSIS := /usr/bin/makensis PERL := /usr/bin/perl INCLUDE_PATHS := -I\$(PIDGIN_TREE_TOP)/../win32-dev/w32api/include LIB_PATHS := -L\$(PIDGIN_TREE_TOP)/../win32-dev/w32api/lib ================================================ FILE: contrib/opensuse-build-service/PKGBUILD ================================================ # Maintainer: SIPE Project pkgname=pidgin-sipe pkgver=1.25.0 pkgrel=1 pkgdesc="Libpurple protocol plugin to connect to MS Office Communicator" arch=('x86_64') license=('GPL-2.0-or-later') url="https://sipe.sourceforge.net/" depends=('gmime' 'libpurple') makedepends=('intltool') optdepends=('freerdp: Desktop sharing' 'krb5: Kerberos support') source=( ${pkgname}-${pkgver}.tar.gz ) sha256sums=( '@@SHA256SUM@@' ) build() { cd "${pkgname}-${pkgver}" ./configure --prefix=/usr --with-vv make } package() { cd "${pkgname}-${pkgver}" make DESTDIR="${pkgdir}" install } ================================================ FILE: contrib/opensuse-build-service/generate_debian.sh ================================================ #!/bin/bash abort() { echo "$0: $1 - Aborting" exit 1 } cleanup() { rm -rf debian abort "$1" } # Sanity checks version=$(ls pidgin-sipe-*.tar.gz | sed 's/^pidgin-sipe-//;s/.tar.gz$//') [ -z "${version}" ] && abort "can't find pidgin-sipe archive" [ -e debian ] && abort "directory 'debian' - already exists" # Copy latest source archive cp pidgin-sipe-${version}.tar.gz pidgin-sipe_${version}.orig.tar.gz # Extract contrib/debian directory from release tar --strip-components=2 --wildcards -xvf \ pidgin-sipe-${version}.tar.gz \ "*/contrib/debian" || cleanup "tar failed" [ -e debian ] || cleanup "directory 'debian' - does not exist" # Strip libnss3-dev from debian/control: build setup is controlled by .dsc's sed -i.ORIG -e 's/libnss3-dev, //' debian/control touch -r debian/control.ORIG debian/control rm debian/control.ORIG # Have the contents changed? if tar 2>/dev/null -df pidgin-sipe_${version}-1.debian.tar.gz; then echo "contrib/debian is unchanged - not updating .debian.tar.gz." else # Update debian archive tar cfz pidgin-sipe_${version}-1.debian.tar.gz debian || cleanup "can't create tar archive" fi rm -rf debian # Update .dsc files for p in \ "Checksums-Sha1=sha1sum" \ "Checksums-Sha256=sha256sum" \ "Files=md5sum"; do \ label=${p%=*}; \ program=${p#*=}; \ echo "${label}:" for t in \ pidgin-sipe_${version}.orig.tar.gz \ pidgin-sipe_${version}-1.debian.tar.gz; \ do \ echo " $(${program} ${t} | cut -d' ' -f1) $(wc -c ${t})"; \ done \ done >checksums.txt for d in *.dsc; do cat checksums.txt >>${d}; done rm checksums.txt # Newer platforms have support for freerdp2 - use only default .dsc cp pidgin-sipe-freerdp2.dsc pidgin-sipe.dsc # All other platforms at least support for gstreamer1.0 - override those for os in \ Debian_9.0 \ xUbuntu_16.04 \ ; do \ cp pidgin-sipe-gstreamer1.dsc pidgin-sipe-${os}.dsc; \ done # All other platforms at least support telepathy - override those for os in \ Debian_8.0 \ ; do \ cp pidgin-sipe-telepathy.dsc pidgin-sipe-${os}.dsc; \ done # Update SHA-2 256 checksum in Arch Linux PKGBUILD sed -i -e "s/@@SHA256SUM@@/$(sha256sum pidgin-sipe-${version}.tar.gz | cut -d' ' -f1)/" PKGBUILD # That's all folks... echo "Done." osc status exit 0 ================================================ FILE: contrib/opensuse-build-service/generate_nsi.pl ================================================ #!/usr/bin/perl use 5.008; use strict; use warnings; # check commandline arguments die "Usage: $0 < nsi-template > nsi-output\n" if @ARGV < 1; # process LINGUAS file open(my $fh, "<", $ARGV[0]) or die "$0: can't open LINGUAS file '$ARGV[0]': $!\n"; my %languages = map { ($_, 1) } map { chomp; s/^\s+//; s/\s+$//; $_ } <$fh>; close($fh) or die "$0: error while reading LINGUAS file '$ARGV[0]': $!\n"; print STDERR "Found ", scalar(keys %languages), " language(s): ", join(" ", sort keys %languages), "\n"; # read .nsi template from STDIN # write .nsi file to STDOUT while () { if (/^;;; INSTALL_FILES_LOCALE/) { print map({ ("SetOutPath \"\$INSTDIR\\locale\\$_\\LC_MESSAGES\"\n", "File \"\${MINGW_DATADIR}\\locale\\$_\\LC_MESSAGES\\pidgin-sipe.mo\"\n") } sort keys %languages); } elsif (/^;;; DELETE_FILES_LOCALE/) { print map({ "Delete \"\$INSTDIR\\locale\\$_\\LC_MESSAGES\\pidgin-sipe.mo\"\n" } sort keys %languages); } else { print; } } # That's all folks... exit 0; ================================================ FILE: contrib/opensuse-build-service/pidgin-sipe-freerdp2.dsc ================================================ Format: 3.0 (quilt) Source: pidgin-sipe Version: 1.25.0-1 Binary: pidgin-sipe Maintainer: pidgin-sipe Architecture: any Standards-Version: 3.9.1 Build-Depends: cdbs (>= 0.4.23-1.1), autotools-dev, debhelper (>= 5), pkg-config, libglib2.0-dev (>= 2.28.0), libdbus-1-dev, libxml2-dev, libssl-dev, pidgin-dev, libpurple-dev (>= 2.8.0), appstream, libtool, intltool, libkrb5-dev, libzephyr-dev, libnice-dev (>= 0.1.0), libgstreamer1.0-dev, libgstreamer-plugins-base1.0-dev, libfarstream-0.2-dev, freerdp2-dev, libtelepathy-glib-dev (>= 0.24.0), libgmime-2.6-dev, gss-ntlmssp-dev (>= 0.5.0) Homepage: http://sipe.sourceforge.net/ ================================================ FILE: contrib/opensuse-build-service/pidgin-sipe-gstreamer1.dsc ================================================ Format: 3.0 (quilt) Source: pidgin-sipe Version: 1.25.0-1 Binary: pidgin-sipe Maintainer: pidgin-sipe Architecture: any Standards-Version: 3.9.1 Build-Depends: cdbs (>= 0.4.23-1.1), autotools-dev, debhelper (>= 5), pkg-config, libglib2.0-dev (>= 2.28.0), libdbus-1-dev, libxml2-dev, libssl-dev, pidgin-dev, libpurple-dev (>= 2.8.0), appstream, libtool, intltool, libkrb5-dev, libzephyr-dev, libnice-dev (>= 0.1.0), libgstreamer1.0-dev, libgstreamer-plugins-base1.0-dev, libfarstream-0.2-dev, libtelepathy-glib-dev (>= 0.24.0), libgmime-2.6-dev, gss-ntlmssp-dev (>= 0.5.0) Homepage: http://sipe.sourceforge.net/ ================================================ FILE: contrib/opensuse-build-service/pidgin-sipe-telepathy.dsc ================================================ Format: 3.0 (quilt) Source: pidgin-sipe Version: 1.25.0-1 Binary: pidgin-sipe Maintainer: pidgin-sipe Architecture: any Standards-Version: 3.9.1 Build-Depends: cdbs (>= 0.4.23-1.1), autotools-dev, debhelper (>= 5), pkg-config, libglib2.0-dev (>= 2.28.0), libdbus-1-dev, libxml2-dev, libssl-dev, pidgin-dev, libpurple-dev (>= 2.8.0), appstream-index, libtool, intltool, libkrb5-dev, libzephyr-dev, libtelepathy-glib-dev (>= 0.24.0), libgmime-2.6-dev Homepage: http://sipe.sourceforge.net/ ================================================ FILE: contrib/opensuse-build-service/pidgin-sipe.changes ================================================ ------------------------------------------------------------------- Sat Oct 12 08:44:00 UTC 2019 - chemobejk@gmail.com - Update to version 1.25.0 "Buddy Idle Time, RTF" (2019-10-12) - Feature #107: Provide idle start time for a buddy (Stefan Becker) - Feature #77: RTF support (incoming) (Stefan Becker) * the code only extracts plain text from incoming RTF - Fixed #358: FTBFS with glib-2.0 >= 2.62.0 (Stefan Becker) - Fixed #350: Inconsistent parsing of From:/To: headers (Stefan Becker) - raise BR telepathy-glib >= 0.24.0 (Stefan Becker) - switch from GPLv2+ to SPDX identifier GPL-2.0-or-later (Stefan Becker) ------------------------------------------------------------------- Sat Nov 10 15:40:00 UTC 2018 - chemobejk@gmail.com - Update to version 1.24.0 "Application Sharing II" (2018-11-10) - Feature #104: Use user agent also for HTTP (Stefan Becker) - Feature #6: Application Sharing Server (Jakub Adam) * requires freerdp-shadow2 - Fixed #343: Build fails on FreeBSD - add timeout handling for media streams (Alaoui Youness) - update AppStream handling (Jakub Adam, Stefan Becker) - fix missing localisation in some code modules (Stefan Becker) - debug log improvements (Stefan Becker) ------------------------------------------------------------------- Mon Aug 20 14:47:00 UTC 2018 - chemobejk@gmail.com - Update to version 1.23.3 "Bug Fixes III" (2018-08-20) - appshare: fix black screen with Remmina v1.2.0-rcgit.27 (Jakub Adam) - various minor fixes (Jakub Adam, Michael Olbrich) - fix compilation errors with libpurple 2.14.0 & GCC 8.0 (Jakub Adam, Stefan Becker) - mingw: update fetch script to Pidgin 2.13.0 (Stefan Becker) ------------------------------------------------------------------- Sat Mar 10 15:54:29 UTC 2018 - chemobejk@gmail.com - Update to version 1.23.2 "Bug Fixes II" (2018-03-10) - fix some HTTP requests that were not sent (Jakub Adam, Stefan Becker) ------------------------------------------------------------------- Sun Feb 25 13:05:00 UTC 2018 - chemobejk@gmail.com - Update to version 1.23.1 "Bug Fixes I" (2018-02-25) - Fixed #338: Incorrect port 0 for IPv6 socket on Windows (Stefan Becker) - Fixed #337: Duplicate candidates in SDP (Jakub Adam, Stefan Becker) - Fixed #336: Lync autodiscover does not follow user redirect (Stefan Becker) - media: unconditionally ignore multichannel codecs (Jakub Adam) - updated translations: Turkish (tr) ------------------------------------------------------------------- Sat Oct 28 15:18:00 UTC 2017 - chemobejk@gmail.com - Update to version 1.23.0 "D-Bus, IPv6, OS X 10.11+" (2017-10-28) - Feature #101: Mac OS X 10.13 OpenSSL support (Stefan Becker) - Feature #100: Extend libpurple D-Bus interface (Stefan Becker) - Feature #99: IPv6 addresses in SIP & SDP messages (Stefan Becker) - Feature #96: Support for OS X 10.11+ SDK (Stefan Becker) - don't load buddy photos from unknown sites by default (Jakub Adam, Stefan Becker) * custom web URIs pose a security risk as they may be abused * users can override this behaviour in the account settings * Office365 accounts should not be affected by this change - add support for GMime 3.0 API (Stefan Becker) - raise BR glib-2.0 >= 2.18.0 (Stefan Becker) - raise BR purple >= 2.7.0 (Stefan Becker) - drop support for GMime 2.4 (Stefan Becker) - drop support for gstreamer-0.10 (Stefan Becker) ------------------------------------------------------------------- Sun Jun 11 15:38:00 UTC 2017 - chemobejk@gmail.com - Update to version 1.22.1: - Fixed #320: Multiple client detection broken (Stefan Becker) - speed up Lync Autodiscover by using AccessLocation (Andrey Vaynberger) - adium: update build instructions for Xcode 7.x or newer (Stefan Becker) * the build environment used for releases 1.22.0 or older (Xcode 6.x on OS X 10.11) is no longer available to the project * releases starting with 1.22.1 will use Xcode 8.x on macOS 10.12 * build target continues to be OS X 10.9 - purple: avoid rare SSL read deadlock (Stefan Becker) - various minor fixes (Michael Olbrich) - crypto: make code compile with OpenSSL 1.1.0 (Stefan Becker) - drop references to Reuters Messaging (Stefan Becker) - updated translations: Lithuanian (lt), Russian (ru), Swedish (sv), Turkish (tr) ------------------------------------------------------------------- Wed Feb 1 16:58:00 UTC 2017 - chemobejk@gmail.com - Update to version 1.22.0: - Feature #93: Support for Lync Autodiscover (Stefan Becker) - Feature #6: Application Sharing Viewer (Jakub Adam) * requires libpurple >= 2.12.0 * needs an external RDP client - remmina and xfreerdp are supported - Fixed #315: Crash when contact list is empty (Stefan Becker) - Fixed #314: sipe login problems with long pw (Stefan Becker) - separate logging and debugging output (Stefan Becker) * logging is always shown, e.g. in the Pidgin debug window * full message debugging now requires PURPLE_UNSAFE_DEBUG=1 - new translations: Greek (el), Lithuanian (lt) ------------------------------------------------------------------- Sat May 28 16:50:00 UTC 2016 - chemobejk@gmail.com - Update to version 1.21.1: - various bug fixes in media support (Jakub Adam) - configure no longer ignores CFLAGS/LDFLAGS/LIBS (Stefan Becker) ------------------------------------------------------------------- Sat Apr 23 14:16:00 UTC 2016 - chemobejk@gmail.com - Update to version 1.21.0: - Feature #91: Support embedded XML as buddy photo URL (Stefan Becker) - Feature #90: Add AppStream metadata file (Jiri Eischmann, Stefan Becker) - Feature #89: Improve "Join scheduled conference" dialog (Stefan Becker) - Feature #87: Support multiple HTTP cookies (Stefan Becker) - Feature #85: XML raw extract should ignore name space (Stefan Becker) - Fixed #311: Crash when SIP transport becomes invalid (Stefan Becker) - Fixed #293: Mandatory wsa:MessageID node missing (Stefan Becker) - add support for Lync File Transfer protocol (Jakub Adam) * requires libpurple >= 2.12.0 * Lync FT will be used for sending files when Lync 2013 is detected - add build options to "About SIPE plugin" message (Stefan Becker) ------------------------------------------------------------------- Sat Oct 24 13:36:00 UTC 2015 - chemobejk@gmail.com - Update to version 1.20.1: - add support for another type of ADFS response (Stefan Becker) - improve configure check for back-ported features (Stefan Becker, Jakub Adam) - updated translations: French (fr), Russian (ru) ------------------------------------------------------------------- Sat Aug 29 16:41:00 UTC 2015 - chemobejk@gmail.com - Update to version 1.20.0: - Feature #82: Parse HTML from Lync conference URL (Stefan Becker) - Feature #69: SRTP Support (Jakub Adam) * requires libpurple >= 3.0.0 - Fixed #285: Office365 rejects RC4 in TLS-DSK (Stefan Becker) * added support for AES-128/256-CBC ------------------------------------------------------------------- Sat Apr 4 15:13:00 UTC 2015 - chemobejk@gmail.com - Update to version 1.19.1: - Fixed #278: 488 error after libnice upgrade (Jakub Adam) - fix SIP re-authentication timeout to be max. 8 hours (Stefan Becker) ------------------------------------------------------------------- Sat Feb 7 11:48:00 UTC 2015 - chemobejk@gmail.com - Update to version 1.19.0: - Feature #80: Move parsing of login name (Stefan Becker) - Feature #79: support for Adium group chat bookmarks (David Matz, Stefan Becker) - Feature #78: Support searching for SIP ID (Stefan Becker) - Feature #76: ADFS can't always be used (Stefan Becker) * for accounts that have Multi-Factor Authentication (MFA) enabled - Feature #73: Support buddy photos from contactCard (Stefan Becker) - Feature #65: Fall back from Kerberos to NTLM (Stefan Becker) - Fixed #277: Raised contact names (Stefan Becker) - Fixed #240: Corrupted HTTP response crashes SIPE (Stefan Becker) - fix calendar state machine when EWS URL is set (Stefan Becker) - fall back to [MS-DLX] BasicSearch to improve search experience (Stefan Becker, various) - implement search functionality for UCS (Stefan Becker) - adium: add chat room list UI (David Matz) - adium: fix duplicate debug log messages (Stefan Becker) - support for libnice TCP mode (Youness Alaoul, Jakub Adam) - refactor CCCP request code (Jakub Adam) ------------------------------------------------------------------- Tue Dec 29 23:52:00 UTC 2014 - chemobejk@gmail.com - Update to new upstream version, 1.18.5: - Fixed #276: Redundant "const" breaks build with clang (Stefan Becker) - Fixed #269: purple idle-away converted to Away (Stefan Becker) - svc: use authuser for RealmInfo request (Stefan Becker) - adium: add release checking script (Stefan Becker) - mingw: update fetch script to Pidgin 2.10.11 (Stefan Becker) - updated translations: Italian (it), Swedish (sv) ------------------------------------------------------------------- Sat Oct 18 15:42:00 UTC 2014 - chemobejk@gmail.com - Update to new upstream version, 1.18.4: - Fixed #263: ADFS fails when user and login name differ (Stefan Becker) - Fixed #262: Adium: SIPE doesn't auto- or re-connect (Stefan Becker) - fixed memory leaks (Stefan Becker) - fixed processing of presence publish event response (John Zhang, Stefan Becker) * fixes a longstanding issue that the Pidgin user status sometimes didn't switch back to "Available" after the end of a meeting ------------------------------------------------------------------- Sat Aug 16 12:50:00 UTC 2014 - chemobejk@gmail.com - Update to new upstream version, 1.18.3: - Fixed #259: HTML response to EWS autodiscover triggers libxml2 assert (Stefan Becker) - Fixed #258: V&V call gets rejected when IPv6 is enabled (Stefan Becker, Jakub Adam) - Fixed #257: Windows 7: SIPE crashes after a minute (Stefan Becker) - mingw: improve crash information reporting (Stefan Becker) ------------------------------------------------------------------- Sat Jun 7 18:08:00 UTC 2014 - chemobejk@gmail.com - Update to new upstream version, 1.18.2: - Fixed #255: Crash when PersistentChat sends BYE instead of response (Stefan Becker) - Fixed #248: Remove libpurple SSL configure check (Stefan Becker) - Fixed #245: "Unable to resolve DNS SRV record" error when joining conference (Stefan Becker) - Fixed #241: Adium filters ":" from "sip:" (Stefan Becker) - Fixed #210: Conference call ends with error message (for real this time, Jakub Adam) - ews: extract settings also from type EXPR (Stefan Becker) - ucs: honor user specified email URL (Stefan Becker) - adium: fix compilation on OS X 10.7 (Stefan Becker) - updated Transifex URLs (Stefan Becker) - updated translations: Hindi (hi), Telugu (te) ------------------------------------------------------------------- Sat Apr 12 18:08:00 UTC 2014 - chemobejk@gmail.com - Update to new upstream version, 1.18.1: - Fixed #238: False "not delivered" in conference (Stefan Becker) - Fixed #237: HTML escaping not removed from URL (Stefan Becker) - Fixed #210: Conference call ends with error message (Jakub Adam) - fix endless loop with failed HTTP Basic authentication (Stefan Becker) - fix crash when gstreamer nice plugin is missing (Stefan Becker, Jakub Adam) - fix EWS autodiscover for some Office 365 users (Stefan Becker) - purple: fix missing "Copy to" in buddy menu (Stefan Becker) - purple/adium: ignore empty search values (Stefan Becker) - adium: fix group chat UI (Stefan Becker) - adium: implement BEAST mitigations for 10.8.5 (Michael Lamb) - add indication when user is connected from a mobile device (Harris Kauffman) - updated translations: Chinese (zh_CN), Portuguese (pt) ------------------------------------------------------------------- Sat Jan 11 15:43:00 UTC 2014 - chemobejk@gmail.com - Update to new upstream version, 1.18.0: - Feature #71: Add support for EWS Autodiscover redirection (Stefan Becker) - Feature #69: Add UI support for (group) chats (Michael Lamb) * NOTE: Adium does not have an UI to fetch the room list - Feature #64: Add support for GSS-NTLMSSP (Stefan Becker) * sip-sec-krb5.c module has been renamed to sip-sec-gssapi.c * if gssapi/gssapi_ntlmssp.h is detected then sip-sec-ntlm.c will be disabled and NTLM will be handled by sip-sec-gssapi.c instead * NOTE: at the time of this writing the user has to set up GSS-NTLMSSP by hand on his system, i.e. /etc/gss/mech - Fixed #227: Adium client doesn't save email option settings (Harris P. Kauffman) - Fixed #216: SIPE stops working on Mavericks (Stefan Becker, Michael Lamb) * add an UI option to disable SSL BEAST mitigations * NOTE: requires Adium 1.5.10 - Fixed #197: Account stays in connecting stage (Harris P. Kauffman) - cleanup for sip-sec Kerberos & SSPI modules (Stefan Becker) * replace old TGT hack with gss_acquire_cred_with_password() * clean up Kerberos detection in configure * remove special case handling; code is more straight-forward now * thanks to David Woodhouse and Simo Sorce for the GSSAPI information - implement internal keepalive handling (Stefan Becker) - implement crypto backend based on OpenSSL (Stefan Becker) - adium: Xcode project files update (Michael Lamb) - adium: replace NSS crypto backend with OpenSSL (Stefan Becker) * NOTE: please make sure to read the updated build instructions! ------------------------------------------------------------------- Wed Dec 11 19:40:00 UTC 2013 - chemobejk@gmail.com - Update to new upstream version, 1.17.3: - Fixed #225: HTTP re-authentication with NTLM fails (Stefan Becker) - Fixed #222: SIPE crashes when groupchat session expires (Stefan Becker) - fix UCS Persona key extraction (Stefan Becker) ------------------------------------------------------------------- Sat Nov 30 16:49:00 UTC 2013 - chemobejk@gmail.com - Update to new upstream version, 1.17.2: - Fixed #214: Typing notification does not always work (Stefan Becker) * reverted one change which caused problems for some users - Fixed #222: SIPE crashes when groupchat session expires (Stefan Becker) - updated translations: Romanian (ro) ------------------------------------------------------------------- Sat Nov 16 14:46:00 UTC 2013 - chemobejk@gmail.com - Update to new upstream version, 1.17.1: - Fixed #215: Password not entity encoded in WSSE element (Stefan Becker) - Fixed #214: Typing notification does not always work (Stefan Becker) - accept alternatives for webticket timestamp/keydata (Stefan Becker) - adium: add "don't publish calendar" to account UI (Stefan Becker) - contrib: add SSL BEAST mitigation patch for Adium (Stefan Becker) - updated translations: French (fr) ------------------------------------------------------------------- Sat Sep 21 14:06:00 UTC 2013 - chemobejk@gmail.com - Update to new upstream version, 1.17.0: - Feature #62: Support for Lync 2013 Unified Contact Store (Stefan Becker) - Feature #59: Support for Lync 2013 Persistent Chats (Stefan Becker) - Fixed #211: Status "away" or "busy" incorrectly mapped to "Invisible" (Michael Lamb) - Fixed #209: group chat doesn't like HTML (Stefan Becker) - Fixed #200: OCS archiving system blocks audio/video connection (Jakub Adam) - Fixed #187: Duplicate messages in group chat (Stefan Becker) - Fixed #184: Duplicate users showing in Group Chat (Stefan Becker) - fix EWS autodiscover for Office 365 (Stefan Becker) - add support for group chat history (Stefan Becker) - add support for buddy photos on Lync 2013 (Stefan Becker) ------------------------------------------------------------------- Sat Jul 13 14:29:00 UTC 2013 - chemobejk@gmail.com - Update to new upstream version, 1.16.1: - Feature #66: Windows DLL version information (Stefan Becker) - fix call failure when host has multiple IP addresses (Jakub Adam) - fix buddy list handling after moving to Lync 2013 (Stefan Becker) * Lync 2013 migrates buddy list to Unified Contact Store (UCS) * NOTE: modifying the buddy list is *NOT* supported yet! - crash fixes for new HTTP stack (Stefan Becker) ------------------------------------------------------------------- Sat Jun 8 16:58:00 UTC 2013 - chemobejk@gmail.com - Update to new upstream version, 1.16.0: - Feature #58: Implement Digest authentication scheme for SIP Proxy Authentication (Stefan Becker) - Fixed #196: Useragent value not forwarded to core (Michael Lamb) - Fixed #193: Pidgin Status changes stop working (Stefan Becker) - Fixed #186: Users appear offline when they are not (Stefan Becker) - fix kinit-less use case with krb5 >= 1.11 (Stefan Becker) - rewritten HTTP stack from scratch (Stefan Becker) * cleaner, layered and hopefully less error-prone implementation * HTTP stack internals no longer exposed to user code * reduced network traffic and less SSL handshakes by utilizing HTTP/1.1 connection keep alive for multiple HTTP requests to the same host - switch purple backend to deferred destruction approach (Stefan Becker) * Pidgin should no longer crash at connection close, even in corner cases - add menu entry to make a call with a phone number (Jakub Adam) - some progress on telepathy backend (Stefan Becker) * add TLS certificate accept/reject user interaction * add "Single Sign-On" & "Don't Publish Calendar" account options ------------------------------------------------------------------- Sun Apr 7 12:44:00 UTC 2013 - chemobejk@gmail.com - Update to new upstream version, 1.15.1: - NOTE: SIPE SourceForge project got updated. Because of this all bug and feature request numbers have changed. - Fixed #190: SIP 407 response rejected with invalid message signature (Stefan Becker) - Fixed #189: Adium SIPE plugin vs. libpurple linking issues (Michal Lamb) - fixed free-after-use issue that caused crashes for some users (Stefan Becker) - fixed broken NTLM fallback in Negotiate (Stefan Becker) - fixed subscriptions expiration by subscribing again after re-authentication (Stefan Becker) - allow different user name and login for Office 365 authentication (Stefan Becker) - add SIPE version & git commit ID to debug log (Stefan Becker) - added valgrind log analyzer script (Stefan Becker) - added NTLM message anaylzer (Stefan Becker) - updated translations: Hungarion (hu), Romanian (ro) - updated Adium port (Michael Lamb, Harris P. Kauffman) ------------------------------------------------------------------- Mon Apr 1 13:42:00 UTC 2013 - chemobejk@gmail.com - New project maintainer - Fast forward files to prepare for upcoming 1.15.1 release - Replace old Debian packaging files with new 3.0 (quilt) ones - Add repository specific Debian .dsc to enable V&V where possible ------------------------------------------------------------------- Wed Aug 8 11:57:14 UTC 2012 - john@redux.org.uk - Update to new upstream version, 1.13.2: - tls: fix buffer overrun (Oleksandr Hryshchuk, Stefan Becker) - win32: fix TCP connections (Stefan Becker) - nsis: fix broken locale installation (Stefan Becker) - updated translations: French (fr) - various build fixes (Stefan Becker, Jakub Adam) ------------------------------------------------------------------- Wed May 16 10:56:37 UTC 2012 - john@redux.org.uk - Update to new upstream version, 1.13.1: - detect incompatible encryption level with Lync (Jakub Adam) - purple: add URI validity check to Add Buddy callback (Stefan Becker) - new translations: Romanian (ro), Turkish (tr) - various build fixes (Stefan Becker) - added [MS-SIPAE] TLS-DSK authentication scheme (Stefan Becker) * TLS-DSK has been introduced in Lync * mandatory for Office365 accounts * also works for non-public Lync installations * does not work yet with SSPI on Windows - added [MS-DLX] based Get Info/Contact Search (Stefan Becker) * [MS-PRES] SIP-Based Active Directory Search is disabled in Lync - added experimental media TCP transport (Jakub Adam) - make it compile against the latest purple 3.0.x API (Stefan Becker) - make it compile against the latest glib2 2.31.x API (Stefan Becker) - completed cleanup: core no longer requires libpurple (Stefan Becker) - refactored crypto code, ie. NSS can replaced if necessary (Stefan Becker) - sipe-domino.c is no longer built under UNIX to remove dead code (Stefan Becker) - restricted XXX_CFLAGS to modules that need them (Stefan Becker) - NSS is now a mandatory build requirement (Stefan Becker) - decoupled SSPI from HAVE_LIBKRB5 flag. New flag is HAVE_SSPI (Stefan Becker) - OBS mingw packages now use SSPI instead of NTLM (Stefan Becker) - added NSIS package generation to OBS mingw packages (Stefan Becker) - removed kopete backend. KDE is moving to telepathy (Stefan Becker) - added MinGW cross-compilation on Linux instructions (Stefan Becker) ------------------------------------------------------------------- Tue Sep 20 20:27:56 UTC 2011 - john@redux.org.uk - Update to version 1.12.0 + Feature #3064877: Add support for OCS2007R2 Group Chat (Stefan Becker) + Feature #3311026: Support for HTTP/1.1 Transfer-Encoding: chunked (Stefan Becker) + Fixed #2834758: First NTLM signature check after startup fails (Stefan Becker) + Fixed #3082602: Crash on Autodiscover (Stefan Becker) + Fixed #3090663: Re-authentication fails (Stefan Becker) + Fixed #3092324: Core dump in "make check" (psfales) + Fixed #3130915: Failed to authenticate to server (Stefan Becker) + Fixed #3148124: sipe segfaults during login on Solaris (Jakub Adam) + Fixed #3150482: "configure --with-vv" test uses wrong include (Stefan Becker) + Fixed #3156430: Messages not Delivered (rwinchsf, Stefan Becker) + Fixed #3161273: Lost Connection Gives No Error Message (rwinchsf, Stefan Becker) + Fixed #3198585: Extra line breaks (Stefan Becker) + Fixed #3267073: False "could not be delivered" errors (sort of..., Stefan Becker) + Fixed #3399007: Crash when sipe_cal_working_hours->days_of_week is NULL (Stefan Becker) + Patch #3091490: Make 1.11.0 Compile on FreeBSD (jprather) + Patch #3108246: Patch for better windows installer (archrival, galiven) + add random Ms-Conversation-ID to INVITE (Jakub Adam) + fix parsing of P-Asserted-Identity header (Jakub Adam) + added MS TURN support (Jakub Adam) + fix crash on zero length password in NTLM (Vladimir Ushakov) + implement timeouts for SIP request. Used for REGISTER (Stefan Becker) + more work on Voice & Video call support (Jakub Adam) + make it compile against the purple 2.8.x & 3.0.x APIs (Stefan Becker) + more internal changes to prepare for non-purple backends (Stefan Becker) + added integration for transifex.net update (Stefan Becker) + configure improvements for 64-bit: use libdir, gsize/size_t compatibility (Stefan Becker) + update compiler warnings configuration for all build platforms (Stefan Becker) + updated Adium port (Matthew Duggan) + mingw build updates (Harris P. Kauffman, Stefan Becker) + added miranda port (Jochen De Smet) + added mingw to OpenSUSE Build Service configuration (Stefan Becker) ------------------------------------------------------------------- Fri Feb 4 20:24:34 UTC 2011 - john@redux.org.uk - Update to version 1.11.2 + fix parsing of P-Asserted-Identity header + Fixed memory leaks + Fixed bug #3090663: Re-authentication failures + Fixed bug #3092324: Core dump in "make check" + Fixed bug #2834758: First NTLM signature check after starup fails + Fix memory leak in sipe_backend_transport_connect() error path + Fixed bug #3082602: Crash on Autodiscover + Configure now uses libdir and datadir + configure: update 32-bit vs. 64-bit header conflict test + debian: build stability fix in post-install ------------------------------------------------------------------- Wed Oct 6 08:36:02 UTC 2010 - john@redux.org.uk - Update to version 1.11.0 ------------------------------------------------------------------- Mon Jun 28 08:43:10 UTC 2010 - john@redux.org.uk - Update to version 1.10.1 + Fixed bug in the SIPE buddy list hash table functionality reported by a RHEL5 user. + Fixed Pidgin 2.7.0 API compatibility. ------------------------------------------------------------------- Thu Feb 11 01:15:00 CET 2010 - pier11@operamail.com - Update to version 1.8.1: + many crash fixes for error or corner cases in calendar integration + more detailed code analysis with Coverity Prevent + OpenSUSE Build Service configuration files + Fix NTLM crash if login setting is undefined + Use of g_str_has_prefix() available since glib 2.2 and null-safe + build fixes for older OS releases, e.g. Ubuntu older than 9.10 - Changed source from .bz2 to .gz. Seems OBS for Debian doesn't understand .bz2 source file. ------------------------------------------------------------------- Thu Feb 11 01:15:00 CET 2010 - vuntz@opensuse.org - Update to version 1.8.0: + Added integration with Exchange 2007/2010. + Added Calendar information to contact's tooltip. + 2005 presence engine has been completely rewritten, with new supported status. + Added "Accounts->{SIPE_ACCOUNT}->Status Reset" menu option to clean User Status set manually. + Added "Find on LinkedIn" link on contact's User Info screen. + Enhanced custom NTLM security provider to pass connection-oriented authentication. + Added Negotiate authentication scheme (Windows only). + Contributed code for Adium port. + Added Windows Messenger 5.0 (RTC/1.2) compatibility. + New BusyIdle status. + Fix memory leaks. + Fixed localization on Windows platform. + Fix bad rendering with html markup. + Fix broken federated contacts. + Fix for server auto-discovery + Fix for xdg-email invocation. + Fix for User Agent string. + Code: Take PURPLE_INIT_PLUGIN into use. + Various other fixes, including crashes and build fixes. + Updated translations. ------------------------------------------------------------------- Sun Dec 27 00:30:08 CET 2009 - vuntz@opensuse.org - Clean up packaging. - Remove gconf_schemas_prereq macro, since there's nothing involving gconf. - Rename purple-sipe subpackage to libpurple-plugin-sipe. - Add pidgin BuildRequires for directory ownership. - Add pidgin Requires for pidgin-sipe. - Add libpurple Enhances for libpurple-plugin-sipe. ------------------------------------------------------------------- Thu Nov 26 09:54:24 UTC 2009 - john@redux.org.uk - Update to version 1.7.1: + See ChangeLog for the full list of changes since version 1.3.3. + Support for Reuters Messaging environment. + Support for message formatting. + Invisible mode. + Full support for presence. + Better support of protocol. + New security framework. + Multi-party conversation. + Add telepathy-haze support. + Add integration with PBX (external phones). + New Idle status. + Many improvements and bug fixes. + Fix crashes. + Build fixes. + Updated translations. - Split the package into pidgin-sipe and purple-sipe packages. - Add gettext-devel, libtool BuildRequires. - Remove pidgin, pidgin-devel, zlib-devel BuildRequires. - Pass --with-krb5 to configure. - Enable parallel build. ------------------------------------------------------------------- Thu Sep 17 00:13:39 CEST 2009 - crrodriguez@suse.de - use find_lang macro ------------------------------------------------------------------- Mon Feb 2 17:04:37 CST 2009 - gburt@suse.de - Fix total loss of functionality if user was logged on in more than one location (bnc#459117) ------------------------------------------------------------------- Thu Jan 22 16:38:11 CST 2009 - gburt@suse.de - Patch fixing support for LCS 2005 buddy auth/blocking - Fix logging in with TCP when no SRV records found - Fix several crasher bugs ------------------------------------------------------------------- Mon Jan 5 11:37:47 CST 2009 - gburt@suse.de - Update to 1.3.3 which includes fixes for translation support, typos, crashes, and error handling. ------------------------------------------------------------------- Tue Dec 9 17:42:50 CST 2008 - gburt@suse.de - Remove -lang package, too late for package splits ------------------------------------------------------------------- Sun Dec 7 17:42:13 CST 2008 - gburt@suse.de - Create -lang package ------------------------------------------------------------------- Sun Dec 7 15:38:42 CST 2008 - gburt@suse.de - Fix a segfault, fix compatibility bug with OCS 2005 (not handling 180 Ringing responses properly). ------------------------------------------------------------------- Fri Dec 5 12:13:58 CST 2008 - gburt@suse.de - Fix automatic support (redirecting) for LCS/OCS proxy servers, and show the user the error when 403 forbidden is received. ------------------------------------------------------------------- Thu Dec 4 17:47:08 CST 2008 - gburt@suse.de - Fix bugs with ability to set status, add support for setting a note (at least against some servers), fix OCS 2005 support, and improve account editing dialog to be more friendly, and improve error messages. ------------------------------------------------------------------- Wed Dec 3 18:40:14 CST 2008 - gburt@suse.de - Fix bugs with ability to add, modify, and remove contacts and groups. Fix support for detecting contacts' away status, and to send them ours. Fix support for saving and reading buddy aliases to/from the server. Fix many crashes. ------------------------------------------------------------------- Thu Nov 20 19:16:41 CST 2008 - gburt@suse.de - Fix compatibility with Microsoft OCS 2007; 2-way IMing working ------------------------------------------------------------------- Fri Nov 7 12:33:37 CST 2008 - gburt@suse.de - Replace gz with bz2 ------------------------------------------------------------------- Fri Nov 7 12:22:30 CST 2008 - gburt@suse.de - Update tarball with latest code from git plus a patch to get NTLM signing of messages working (eco#5322) ------------------------------------------------------------------- Tue Sep 2 10:21:53 CDT 2008 - gburt@suse.de - Clarify and simplify the package description ------------------------------------------------------------------- Mon Aug 11 11:21:48 CDT 2008 - gburt@suse.de - Initial package for pidgin-sipe - SIPE 1.2 - http://sipe.sourceforge.net/ ================================================ FILE: contrib/opensuse-build-service/pidgin-sipe.nsi.template ================================================ ;NSIS Modern User Interface ;Basic Example Script ;Written by Joost Verburg ;-------------------------------- ;Include Modern UI !include "MUI.nsh" ;-------------------------------- ;General ;Name and file Name "Pidgin SIPE Plugin" OutFile "pidgin-sipe-${VERSION}.exe" ;Default installation folder InstallDir "$PROGRAMFILES\Pidgin" ;Get installation folder from registry if available InstallDirRegKey HKLM "Software\pidgin" "" ; SetCompressor /FINAL /SOLID lzma SetCompressorDictSize 64 ;-------------------------------- ;Interface Settings !define MUI_ABORTWARNING ;-------------------------------- ;Pages ;!insertmacro MUI_PAGE_LICENSE "Basic.nsi" ;!insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ;-------------------------------- ;Languages !insertmacro MUI_LANGUAGE "English" ;-------------------------------- ;Installer Sections Section "PidginSIPE" SecPidginSIPE SetOutPath "$INSTDIR\plugins" File "${MINGW_LIBDIR}\purple-2\libsipe.dll" SetOutPath "$INSTDIR\pidgin-${PIDGIN_VERSION}-dbgsym\plugins" File "${MINGW_LIBDIR}\purple-2\libsipe.dll.dbgsym" SetOutPath "$INSTDIR\pixmaps\pidgin\protocols\16" File "${MINGW_DATADIR}\pixmaps\pidgin\protocols\16\sipe.png" SetOutPath "$INSTDIR\pixmaps\pidgin\protocols\22" File "${MINGW_DATADIR}\pixmaps\pidgin\protocols\22\sipe.png" SetOutPath "$INSTDIR\pixmaps\pidgin\protocols\48" File "${MINGW_DATADIR}\pixmaps\pidgin\protocols\48\sipe.png" SetOutPath "$INSTDIR\pixmaps\pidgin\protocols\scalable" File "${MINGW_DATADIR}\pixmaps\pidgin\protocols\scalable\sipe.svg" ;;; INSTALL_FILES_LOCALE ;Create uninstaller WriteUninstaller "$INSTDIR\Uninstall-pidgin-sipe.exe" SectionEnd ;-------------------------------- ;Descriptions ;Language strings LangString DESC_SecPidginSIPE ${LANG_ENGLISH} "The Pidgin SIPE Plugin." ;Assign language strings to sections ;!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN ; !insertmacro MUI_DESCRIPTION_TEXT ${SecPidginPP} $(DESC_SecPidginPP) ;!insertmacro MUI_FUNCTION_DESCRIPTION_END ;-------------------------------- ;Uninstaller Section Section "Uninstall" ;ADD YOUR OWN FILES HERE... Delete "$INSTDIR\Uninstall-pidgin-sipe.exe" Delete "$INSTDIR\plugins\libsipe.dll" Delete "$INSTDIR\pidgin-${PIDGIN_VERSION}-dbgsym\plugins\libsipe.dll.dbgsym" Delete "$INSTDIR\pixmaps\pidgin\protocols\16\sipe.png" Delete "$INSTDIR\pixmaps\pidgin\protocols\22\sipe.png" Delete "$INSTDIR\pixmaps\pidgin\protocols\48\sipe.png" Delete "$INSTDIR\pixmaps\pidgin\protocols\scalable\sipe.svg" ;;; DELETE_FILES_LOCALE SectionEnd ================================================ FILE: contrib/opensuse-build-service/pidgin-sipe.spec ================================================ # # OBS SPEC file to generate a RPM for pidgin-sipe. # # It has support for: # # RedHat family (CentOS, Fedora, RHEL, ScientificLinux) # SUSE family (openSUSE, SLED, SLES) # Mageia # Windows (mingw32, mingw64) # # Build options %undefine build_telepathy # Check for mingw32 cross compilation build # # Manually add this repository to your private OBS project: # # # # i586 # # %if "%{_repository}" == "mingw32" %define purple_sipe_mingw32 1 %define mingw_prefix mingw32- %define mingw_cache %{_mingw32_cache} %define mingw_configure %{_mingw32_configure} %define mingw_datadir %{_mingw32_datadir} %define mingw_debug_package %{_mingw32_debug_package} %define mingw_ldflags MINGW32_LDFLAGS %define mingw_libdir %{_mingw32_libdir} %define mingw_make %{_mingw32_make} %define mingw_makeinstall %{_mingw32_makeinstall} %define __strip %{_mingw32_strip} %define __objdump %{_mingw32_objdump} %define _use_internal_dependency_generator 0 %define __find_requires %{_mingw32_findrequires} %define __find_provides %{_mingw32_findprovides} %define __os_install_post %{_mingw32_debug_install_post} \ %{_mingw32_install_post} %endif # Check for mingw64 cross compilation build # # Manually add this repository to your private OBS project: # # # # i586 # # %if "%{_repository}" == "mingw64" %define purple_sipe_mingw64 1 %define mingw_prefix mingw64- %define mingw_cache %{_mingw64_cache} %define mingw_configure %{_mingw64_configure} %define mingw_datadir %{_mingw64_datadir} %define mingw_debug_package %{_mingw64_debug_package} %define mingw_ldflags MINGW64_LDFLAGS %define mingw_libdir %{_mingw64_libdir} %define mingw_make %{_mingw64_make} %define mingw_makeinstall %{_mingw64_makeinstall} %define __strip %{_mingw64_strip} %define __objdump %{_mingw64_objdump} %define _use_internal_dependency_generator 0 %define __find_requires %{_mingw64_findrequires} %define __find_provides %{_mingw64_findprovides} %define __os_install_post %{_mingw64_debug_install_post} \ %{_mingw64_install_post} %endif %define purple_plugin %{?mingw_prefix:%{mingw_prefix}}libpurple-plugin-sipe %define telepathy_plugin %{?mingw_prefix:%{mingw_prefix}}telepathy-plugin-sipe %define nsis_package %{?mingw_prefix:%{mingw_prefix}}pidgin-sipe-nsis %define common_files sipe-common %define empathy_files empathy-sipe %define ktp_files ktp-accounts-kcm-sipe %if 0%{?mageia} %if %{mageia} > 6 %define has_freerdp 1 %define has_gssntlmssp 1 %else %define has_appstream_legacy 1 %endif %define has_appstream 1 %define has_gstreamer 1 %endif %if 0%{?suse_version} %if 0%{?is_opensuse} %define has_appstream 1 %define has_appstream_legacy 1 %if 0%{?suse_version} >= 1500 %define has_freerdp 1 %else %define build_empathy 1 %endif %endif %define has_gstreamer 1 %define build_telepathy 1 %endif %if 0%{?suse_version} %define pkg_group Productivity/Networking/Instant Messenger %else %define pkg_group Applications/Communications %endif # workaround for Fedora Rawhide %if 0%{?fedora_version} %if 0%{?fedora} %else %define fedora %{?fedora_version} %endif %endif %if 0%{?fedora} %define has_appstream 1 %if %{fedora} <= 29 %define build_empathy 1 %endif %if %{fedora} <= 26 %define has_appstream_legacy 1 %endif %if %{fedora} > 25 %define has_freerdp 1 %endif %define has_gssntlmssp 1 %define has_gstreamer 1 %define build_telepathy 1 %define build_ktp 1 %endif %if 0%{?centos_version} || 0%{?scientificlinux_version} %define has_krb5devel 1 %define rhel_base_version %{?centos_version}%{?scientificlinux_version} %if %{rhel_base_version} >= 800 %define has_gstreamer 1 %endif %endif %if 0%{?purple_sipe_mingw32} Name: mingw32-pidgin-sipe %else %if 0%{?purple_sipe_mingw64} Name: mingw64-pidgin-sipe %else Name: pidgin-sipe %endif %endif Summary: Pidgin protocol plugin to connect to MS Office Communicator Version: 1.25.0 Release: 1 Source: pidgin-sipe-%{version}.tar.gz Group: %{pkg_group} License: GPL-2.0-or-later URL: http://sipe.sourceforge.net/ BuildRoot: %{_tmppath}/%{name}-%{version}-build %if 0%{?mingw_prefix:1} # # Windows cross-compilation build setup # BuildArch: noarch #!BuildIgnore: post-build-checks BuildRequires: libtool BuildRequires: intltool BuildRequires: %{mingw_prefix}filesystem >= 23 BuildRequires: %{mingw_prefix}cross-gcc BuildRequires: %{mingw_prefix}cross-binutils BuildRequires: %{mingw_prefix}gettext-runtime BuildRequires: %{mingw_prefix}cross-pkg-config BuildRequires: %{mingw_prefix}glib2-devel >= 2.18.0 BuildRequires: %{mingw_prefix}libxml2-devel BuildRequires: %{mingw_prefix}mozilla-nss-devel BuildRequires: %{mingw_prefix}libpurple-devel >= 2.7.0 BuildRequires: %{mingw_prefix}cross-nsis # For directory ownership BuildRequires: %{mingw_prefix}pidgin # Make sure telepathy is disabled for Windows builds %undefine build_telepathy %else # # Standard Linux build setup # BuildRequires: gcc BuildRequires: libtool BuildRequires: intltool BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(glib-2.0) >= 2.18.0 BuildRequires: pkgconfig(gmodule-2.0) >= 2.18.0 BuildRequires: pkgconfig(libxml-2.0) BuildRequires: pkgconfig(nss) BuildRequires: pkgconfig(purple) >= 2.7.0 %if 0%{?mageia} # It seems linking against -lpurple is severely broken on Mageia... BuildRequires: pkgconfig(libgadu) %if %{mageia} > 7 # -lpurple depends on this but doesn't have a dependency in -devel? BuildRequires: pkgconfig(libnm) %endif %endif %if 0%{?has_appstream:1} %if 0%{?suse_version} BuildRequires: AppStream %else BuildRequires: appstream %endif %endif %if 0%{?has_gstreamer:1} BuildRequires: pkgconfig(farstream-0.2) BuildRequires: pkgconfig(gio-2.0) BuildRequires: pkgconfig(gstreamer-1.0) BuildRequires: pkgconfig(gstreamer-rtp-1.0) BuildRequires: pkgconfig(nice) >= 0.1.0 # Requirements for appshare server %if 0%{?has_freerdp:1} BuildRequires: pkgconfig(freerdp-shadow2) %endif %endif # Requirements for telepathy backend %if 0%{?build_telepathy:1} BuildRequires: gmime-devel BuildRequires: pkgconfig(dbus-glib-1) BuildRequires: pkgconfig(gio-2.0) >= 2.32.0 BuildRequires: pkgconfig(glib-2.0) >= 2.32.0 BuildRequires: pkgconfig(gobject-2.0) BuildRequires: pkgconfig(telepathy-glib) >= 0.24.0 %endif # Configurable components # Use "--without kerberos" to disable krb5 %if !0%{?_without_kerberos:1} %if 0%{?has_krb5devel:1} BuildRequires: krb5-devel %else BuildRequires: pkgconfig(krb5) %endif %if 0%{?has_gssntlmssp:1} BuildRequires: gssntlmssp-devel >= 0.5.0 Requires: gssntlmssp >= 0.5.0 %endif %endif # For directory ownership BuildRequires: pidgin Requires: pidgin %if 0%{?build_telepathy:1} %if 0%{?build_empathy:1} BuildRequires: empathy %endif %endif # End Windows cross-compilation/Linux build setup %endif Requires: %{purple_plugin} = %{?epoch:%{epoch}:}%{version}-%{release} %description A third-party plugin for the Pidgin multi-protocol instant messenger. It implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) With this plugin you should be able to replace your Microsoft Office Communicator client with Pidgin. This package provides the icon set for Pidgin. %package -n %{purple_plugin} Summary: Libpurple protocol plugin to connect to MS Office Communicator Group: %{pkg_group} License: GPL-2.0-or-later Obsoletes: purple-sipe %if 0%{?build_telepathy:1} Requires: %{common_files} = %{?epoch:%{epoch}:}%{version}-%{release} %endif %description -n %{purple_plugin} A third-party plugin for the Pidgin multi-protocol instant messenger. It implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) This package provides the protocol plugin for libpurple clients. %if 0%{?build_telepathy:1} %if 0%{?build_empathy:1} %package -n %{empathy_files} Summary: Telepathy connection manager to connect to MS Office Communicator Group: %{pkg_group} License: GPL-2.0-or-later Requires: empathy Requires: %{telepathy_plugin} = %{?epoch:%{epoch}:}%{version}-%{release} %description -n %{empathy_files} A Telepathy connection manager that implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) This package provides the icon set for Empathy. %endif %if 0%{?build_ktp:1} %package -n %{ktp_files} Summary: Telepathy connection manager to connect to MS Office Communicator Group: %{pkg_group} License: GPL-2.0-or-later Requires: %{telepathy_plugin} = %{?epoch:%{epoch}:}%{version}-%{release} %description -n %{ktp_files} A Telepathy connection manager that implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) This package provides the profile for KTP account manager. %endif %package -n %{telepathy_plugin} Summary: Telepathy connection manager to connect to MS Office Communicator Group: %{pkg_group} License: GPL-2.0-or-later Requires: %{common_files} = %{?epoch:%{epoch}:}%{version}-%{release} %description -n %{telepathy_plugin} A Telepathy connection manager that implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) This package provides the protocol support for Telepathy clients. %package -n %{common_files} Summary: Common files for SIPE protocol plugins Group: %{pkg_group} License: GPL-2.0-or-later BuildArch: noarch %description -n %{common_files} This package provides common files for the SIPE protocol plugins: * Localisation %endif %if 0%{?mingw_prefix:1} %package -n %{nsis_package} Summary: Windows Pidgin protocol plugin to connect to MS Office Communicator Group: %{pkg_group} License: GPL-2.0-or-later %description -n %{nsis_package} A third-party plugin for the Pidgin multi-protocol instant messenger. It implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) This package contains the NSIS installer package of the protocol plugin for Pidgin on Windows. %endif %{mingw_debug_package} %prep %setup -q -n pidgin-sipe-%{version} %build %if 0%{?mingw_prefix:1} # # Windows cross-compilation build # %{?env_options} echo "lt_cv_deplibs_check_method='pass_all'" >>%{mingw_cache} autoreconf --verbose --install --force %{mingw_ldflags}="-Wl,--exclude-libs=libintl.a -Wl,--exclude-libs=libiconv.a -lws2_32" %{mingw_configure} \ --enable-purple \ %if 0%{?build_telepathy:1} --enable-telepathy %else --disable-telepathy %endif %{mingw_make} %{?_smp_mflags} || %{mingw_make} %else # # Standard Linux build # # Special case handling for Mageia %if 0%{?mageia} %configure2_5x \ --with-krb5 \ --with-vv \ --disable-telepathy %make_build %make_build check # All other Linuxes %else %configure \ %if !0%{?has_appstream:1} --without-appstream \ %endif %if !0%{?_without_kerberos:1} --with-krb5 \ %endif %if 0%{?has_gstreamer:1} --with-vv \ %endif --enable-purple \ %if 0%{?build_telepathy:1} --enable-telepathy %else --disable-telepathy %endif make %{?_smp_mflags} make %{?_smp_mflags} check %endif # End Windows cross-compilation/Linux build setup %endif %install %if 0%{?mingw_prefix:1} # # Windows cross-compilation install # %{mingw_makeinstall} rm -f %{buildroot}%{mingw_libdir}/purple-2/*.dll.a # generate .dbgsym file rm -f %{buildroot}%{mingw_libdir}/purple-2/libsipe.dll.dbgsym mv \ %{buildroot}%{mingw_libdir}/purple-2/libsipe.dll \ %{buildroot}%{mingw_libdir}/purple-2/libsipe.dll.dbgsym %{__strip} --strip-unneeded \ %{buildroot}%{mingw_libdir}/purple-2/libsipe.dll.dbgsym \ -o %{buildroot}%{mingw_libdir}/purple-2/libsipe.dll \ # generate NSIS installer package perl contrib/opensuse-build-service/generate_nsi.pl po/LINGUAS \ %{buildroot}/pidgin-sipe.nsi ( \ set -e; \ cd %{buildroot}; \ makensis \ -DPIDGIN_VERSION=UNKNOWN \ -DVERSION=%{version} \ -DMINGW_LIBDIR=%{buildroot}%{mingw_libdir} \ -DMINGW_DATADIR=%{buildroot}%{mingw_datadir} \ pidgin-sipe.nsi \ ) rm -f %{buildroot}/pidgin-sipe.nsi %else # # Standard Linux install # %make_install # End Windows cross-compilation/Linux build setup %endif find %{buildroot} -type f -name "*.la" -delete -print # Pidgin doesn't have 24 or 32 pixel icons rm -f \ %{buildroot}%{_datadir}/pixmaps/pidgin/protocols/24/sipe.png \ %{buildroot}%{_datadir}/pixmaps/pidgin/protocols/32/sipe.png %if 0%{?has_appstream_legacy:1} mv %{buildroot}/%{_datadir}/metainfo %{buildroot}/%{_datadir}/appdata %endif %if 0%{?build_telepathy:1} %if !0%{?build_empathy:1} rm -r %{buildroot}%{_datadir}/empathy %endif %if !0%{?build_ktp:1} rm -r %{buildroot}%{_datadir}/telepathy %endif %endif %find_lang pidgin-sipe %if 0%{?build_telepathy:1} %files -n %{purple_plugin} %else %files -n %{purple_plugin} -f pidgin-sipe.lang %endif %defattr(-,root,root,-) %doc AUTHORS ChangeLog COPYING NEWS README TODO %if 0%{?mingw_prefix:1} %{mingw_libdir}/purple-2/libsipe.dll %{mingw_libdir}/purple-2/libsipe.dll.dbgsym %else %{_libdir}/purple-2/libsipe.so %endif %if 0%{?build_telepathy:1} %if 0%{?build_empathy:1} %files -n %{empathy_files} %defattr(-,root,root,-) %{_datadir}/empathy/icons/hicolor/*/apps/im-sipe.png %{_datadir}/empathy/icons/hicolor/*/apps/im-sipe.svg %endif %if 0%{?build_ktp:1} %files -n %{ktp_files} %defattr(-,root,root,-) %doc AUTHORS COPYING %{_datadir}/telepathy/profiles/sipe.profile %endif %files -n %{telepathy_plugin} %defattr(-,root,root,-) %doc AUTHORS ChangeLog COPYING NEWS README TODO %{_datadir}/dbus-1/services/*.sipe.service %{_libexecdir}/telepathy-sipe %files -n %{common_files} -f pidgin-sipe.lang %defattr(-,root,root,-) %endif %files %defattr(-,root,root,-) %doc AUTHORS COPYING %if 0%{?mingw_prefix:1} %if 0%{?has_appstream:1} %{mingw_datadir}/metainfo/pidgin-sipe.metainfo.xml %endif %{mingw_datadir}/pixmaps/pidgin/protocols/*/sipe.png %{mingw_datadir}/pixmaps/pidgin/protocols/*/sipe.svg %else %if 0%{?has_appstream:1} %if 0%{?has_appstream_legacy:1} %{_datadir}/appdata/%{name}.metainfo.xml %else %{_datadir}/metainfo/%{name}.metainfo.xml %endif %endif %{_datadir}/pixmaps/pidgin/protocols/*/sipe.png %{_datadir}/pixmaps/pidgin/protocols/*/sipe.svg %endif %if 0%{?mingw_prefix:1} %files -n %{nsis_package} %defattr(-, root, root) /pidgin-sipe-%{version}.exe %endif %changelog * Tue Oct 15 2019 J. D. User 1.25.0-*git* - make sure V&V features are really enabled when requested * Sat Oct 12 2019 J. D. User 1.25.0 - update to 1.25.0 * Fri Oct 04 2019 J. D. User 1.24.0-*git* - add BR libnm for Mageia 8+ * Tue Jul 02 2019 J. D. User 1.24.0-*git* - add freerdp & gssntlmssp for Mageia 7+ * Sun Jun 30 2019 J. D. User 1.24.0-*git* - switch to SPDX identifier GPL-2.0-or-later * Sun Jun 30 2019 J. D. User 1.24.0-*git* - newer distros no longer package empathy * Sun Jun 30 2019 J. D. User 1.24.0-*git* - raise BR telepathy-glib >= 0.24.0 * Sat Nov 10 2018 J. D. User 1.24.0 - update to 1.24.0 * Sat Sep 01 2018 J. D. User 1.23.3-*git* - update support for CentOS & Scientific Linux 7 * Mon Aug 27 2018 J. D. User 1.23.3-*git* - add BR freerdp-shadow2 * Mon Aug 20 2018 J. D. User 1.23.3 - update to 1.23.3 * Sat Mar 10 2018 J. D. User 1.23.2 - update to 1.23.2 * Sun Feb 25 2018 J. D. User 1.23.1 - update to 1.23.1 * Sun Feb 18 2018 J. D. User 1.23.0-*git* - remove obsolete clean section * Sun Feb 18 2018 J. D. User 1.23.0-*git* - add BR gcc * Mon Nov 06 2017 J. D. User 1.23.0-*git* - add BR appstream * Sun Nov 05 2017 J. D. User 1.23.0-*git* - add support for new AppStream metadata file location * Sat Oct 28 2017 J. D. User 1.23.0 - update to 1.23.0 - raise BR glib-2.0 >= 2.18.0 - raise BR purple >= 2.7.0 * Fri Aug 11 2017 J. D. User 1.22.1-*git* - add BR dbus-1 - fix incorrect BR gstreamer-0.10 when gstreamer-1.0 is selected * Sun Jun 11 2017 J. D. User 1.22.1 - update to 1.22.1 * Wed Feb 01 2017 J. D. User 1.22.0 - update to 1.22.0 * Sun Dec 18 2016 J. D. User 1.21.1-*git* - add BR gio-2.0 * Wed Jun 15 2016 J. D. User 1.21.1-*git* - add BR farstream-0.2 * Sat May 28 2016 J. D. User 1.21.1 - update to 1.21.1 * Sat Apr 23 2016 J. D. User 1.21.0 - update to 1.21.0 * Tue Apr 05 2016 J. D. User 1.20.1-*git* - align with Fedora SPEC file * Fri Jan 01 2016 J. D. User 1.20.1-*git* - add AppStream metadata file * Sun Nov 08 2015 J. D. User 1.20.1-*git* - add dependency on pkgconfig(gstreamer-1.0) for F22+ & Leap 42.1+ * Sat Oct 24 2015 J. D. User 1.20.1 - update to 1.20.1 * Thu Sep 03 2015 J. D. User 1.20.0-*git* - we also support "Skype for Business" * Sat Aug 29 2015 J. D. User 1.20.0 - update to 1.20.0 * Sat Apr 04 2015 J. D. User 1.19.1 - update to 1.19.1 * Sat Feb 07 2015 J. D. User 1.19.0 - update to 1.19.0 * Tue Jan 06 2015 J. D. User 1.18.5-*git* - add dependency on gssntlmssp(-devel) >= 0.5.0 for F21+ * Mon Jan 05 2015 J. D. User 1.18.5-*git* - remove support for obsolete distributions - Fedora < 19 - Mandriva - OpenSUSE < 13.x * Mon Dec 29 2014 J. D. User 1.18.5 - update to 1.18.5 * Thu Dec 18 2014 J. D. User 1.18.4-*git* - improve support for CentOS & Scientific Linux * Sat Oct 18 2014 J. D. User 1.18.4 - update to 1.18.4 * Sat Aug 16 2014 J. D. User 1.18.3 - update to 1.18.3 * Sat Jun 07 2014 J. D. User 1.18.2 - update to 1.18.2 * Sat Apr 12 2014 J. D. User 1.18.1 - update to 1.18.1 * Tue Mar 04 2014 J. D. User 1.18.0-*git* - F20+/openSUSE 12.2+ require libnice gstreamer-0.10 plugin * Sat Jan 11 2014 J. D. User 1.18.0 - update to 1.18.0 * Wed Dec 11 2013 J. D. User 1.17.3 - update to 1.17.3 * Sat Nov 30 2013 J. D. User 1.17.2 - update to 1.17.2 * Sat Nov 16 2013 J. D. User 1.17.1 - update to 1.17.1 * Sat Sep 21 2013 J. D. User 1.17.0 - update to 1.17.0 * Sat Jul 13 2013 J. D. User 1.16.1 - update to 1.16.1 * Fri Jun 14 2013 J. D. User 1.16.0 - update to 1.16.0 * Sun Apr 07 2013 J. D. User 1.15.1 - update to 1.15.1 * Fri Mar 29 2013 J. D. User 1.15.0-*git* - update package description texts * Sat Mar 09 2013 J. D. User 1.15.0 - update to 1.15.0 * Wed Dec 26 2012 J. D. User 1.14.1 - update to 1.14.1 * Sun Dec 16 2012 J. D. User 1.14.0 - update to 1.14.0 * Tue Sep 25 2012 J. D. User 1.13.3-*git* - change BR gstreamer-devel to pkgconfig(gstreamer-0.10) * Sun Sep 09 2012 J. D. User 1.13.3-*git* - BR telepathy-glib-devel >= 0.18.0 * Wed Sep 05 2012 J. D. User 1.13.3-*git* - BR telepathy-glib-devel >= 0.14.0 * Sun Sep 02 2012 J. D. User 1.13.3-*git* - really disable telepathy for Windows cross-compilation builds * Thu Aug 30 2012 J. D. User 1.13.3-*git* - updates to enable telepathy build * Sun Aug 19 2012 J. D. User 1.13.3 - update to 1.13.3 * Sun Jun 10 2012 J. D. User 1.13.2 - update to 1.13.2 * Mon Apr 09 2012 J. D. User 1.13.1 - update to 1.13.1 * Wed Mar 14 2012 J. D. User 1.13.0 - update to 1.13.0 * Mon Dec 12 2011 J. D. User 1.12.0-*git* - we do support Microsoft Lync Server 2010 now. * Tue Dec 06 2011 J. D. User 1.12.0-*git* - update GPL2 license name * Sat Nov 12 2011 J. D. User 1.12.0-*git* - add BR gmime-devel for Fedora to have at least one verification platform * Sun Nov 06 2011 J. D. User 1.12.0-*git* - fix Mandriva 2011 unresolvable BR * Mon Oct 31 2011 J. D. User 1.12.0-*git* - add BR nss-devel * Sat Oct 01 2011 J. D. User 1.12.0-*git* - add NSIS package for mingw builds * Sat Oct 01 2011 J. D. User 1.12.0-*git* - add mingw64 build * Wed Sep 28 2011 J. D. User 1.12.0-*git* - remove BR mingw32-mozilla-nss-devel, not needed for SSPI. * Mon Sep 19 2011 J. D. User 1.12.0-*git* - update mingw32 build - update descriptions * Mon Aug 29 2011 J. D. User 1.12.0 - update to 1.12.0 * Wed Jun 22 2011 J. D. User 1.11.2-*git* - add gstreamer-devel to enable Voice & Video features * Sat Dec 11 2010 J. D. User 1.11.2-*git* - add optional subpackage for telepathy connection manager * Tue Nov 02 2010 J. D. User 1.11.2 - update to 1.11.2 * Sun Oct 24 2010 J. D. User 1.11.1 - update to 1.11.1 * Fri Oct 15 2010 J. D. User 1.11.0-*git* - add mingw32 build configuration * Sun Oct 03 2010 J. D. User 1.11.0 - update to 1.11.0 * Thu Sep 02 2010 J. D. User pre-1.11.0-*git* - Mandriva config for OBS has changed * Tue May 04 2010 J. D. User 1.10.0-*git* - add libnice build information discovered through OBS testing * Mon Apr 12 2010 J. D. User 1.10.0-*git* - add NSS build information discovered through OBS testing * Sun Apr 04 2010 pier11 1.10.0 - release * Fri Apr 02 2010 J. D. User pre-1.10.0-*git* - Mandriva has too old libtool version * Fri Apr 02 2010 J. D. User pre-1.10.0-*git* - SLE11, openSUSE 11.0/1 don't have pidgin/protocols/scalable directory * Thu Apr 01 2010 pier11 pre-1.10.0-*git* - OBS tests of pre-1.10.0 git-snapshot 4fa20cd65e5be0e469d4aa55d861f11c5b08b816 * Sun Mar 28 2010 J. D. User 1.9.1-*git* - added --enable/--disable build options * Sun Mar 28 2010 J. D. User 1.9.1-*git* - removed --with-krb5 configure option as it is autodetected now * Tue Mar 23 2010 J. D. User 1.9.1-*git* - add SVG icon * Sat Mar 20 2010 J. D. User 1.9.1-*git* - add BR glib2-devel >= 2.12.0 * Wed Mar 17 2010 J. D. User 1.9.1-*git* - add tests to build * Tue Mar 16 2010 J. D. User 1.9.1 - update to 1.9.1 * Thu Mar 11 2010 J. D. User 1.9.0-*git* - add BR libxml2-devel * Wed Mar 10 2010 pier11 1.9.0 - release - dropped SLE 10 due to libpurple min version increase - updated target distros in comment line * Mon Mar 08 2010 J. D. User 1.9.0-*git* - increased libpurple build requisite to >= 2.4.0 * Sun Mar 07 2010 pier11 pre-1.9.0-*git* - OBS tests of pre-1.9.0 git-snapshot 61ea0856855483b9e18f23a87afe47437e526f0e * Sun Mar 07 2010 J. D. User 1.8.1-*git* - sync with RPM SPEC from contrib/rpm * Mon Feb 08 2010 pier11 1.8.0 - source is an original 1.8.0 with patch: git(upstream) 9c34cc3557daa3d61a002002492c71d0343c8cae - temp hack - renamed source in spec from .bz2 to .gz as the latter was prepared with the patch. * Sun Nov 22 2009 pier11 1.7.1 - reinstated enable-quality-check * Wed Nov 04 2009 John Beranek 1.7.0 - Spec file modifications to allow SLES/D 10 and Mandriva 2009.1 builds * Tue Nov 03 2009 John Beranek 1.7.0 - Spec file modifications for openSUSE build service * Sun Oct 11 2009 J. D. User 1.6.3-*git* - move non-Pidgin files to new sub-package purple-sipe * Sun Oct 11 2009 J. D. User 1.6.3-*git* - remove directory for emoticon theme icons * Sun Oct 11 2009 J. D. User 1.6.3-*git* - libpurple protocol plugins are located under %%{_libdir}/purple-2 * Mon Sep 28 2009 J. D. User 1.6.3-*git* - added directory for emoticon theme icons * Wed Sep 09 2009 J. D. User 1.6.3 - update to 1.6.3 * Fri Aug 28 2009 J. D. User 1.6.2-*git* - reduce libpurple-devel requirement to >= 2.3.1 * Mon Aug 24 2009 J. D. User 1.6.2 - update to 1.6.2 * Fri Aug 21 2009 J. D. User 1.6.1-*git* - reduce libpurple-devel requirement to >= 2.4.1 * Mon Aug 17 2009 J. D. User 1.6.1-*git* - com_err.h only required for kerberos * Tue Aug 11 2009 J. D. User 1.6.0-*git* - require libpurple-devel >= 2.5.0 * Sun Aug 09 2009 J. D. User 1.6.0-*git* - refactor configure parameters - make kerberos configurable - don't hard code prefix for git builds * Sun Aug 09 2009 J. D. User 1.6.0-*git* - removed unnecessary zlib-devel * Sat Aug 08 2009 J. D. User 1.6.0-*git* - fix prefix for git builds * Sat Aug 01 2009 J. D. User 1.6.0-*git* - append -Wno-unused-parameter for GCC <4.4 compilation errors * Thu Jul 30 2009 J. D. User 1.6.0-*git* - remove duplicate GPL2 * Thu Jul 30 2009 J. D. User 1.6.0-*git* - use "--with git" to build from git - corrected download URL for release archive - add missing BR gettext-devel * Wed Jul 29 2009 J. D. User 1.6.0-*git* - use default rpmbuild CFLAGS also for git builds - merge with SPEC files created by mricon & jberanek * Tue Jul 28 2009 J. D. User 1.6.0-*git* - initial RPM SPEC example generated ================================================ FILE: contrib/rpm/pidgin-sipe.spec ================================================ # # Example SPEC file to generate a RPM for pidgin-sipe. # It should work out-of-the-box for any current Fedora or RHEL release. # %if 0%{?_with_git:1} #------------------------------- BUILD FROM GIT ------------------------------- # Add "--with git" to the rpmbuild command line to build from git # # Instructions how to access the repository: http://sipe.sourceforge.net/git/ # # Run "./git-snapshot.sh ." in your local repository. # Then update the following line from the generated archive name %define git 20180827git1297745c # Increment when you generate several RPMs on the same day... %define gitcount 0 #------------------------------- BUILD FROM GIT ------------------------------- %endif %define purple_plugin purple-sipe %define telepathy_plugin telepathy-sipe %define common_files sipe-common %define empathy_files empathy-sipe %define ktp_files ktp-accounts-kcm-sipe %define pkg_group Applications/Communications Name: pidgin-sipe Summary: Pidgin protocol plugin to connect to MS Office Communicator Version: 1.25.0 %if 0%{?_with_git:1} Release: %{gitcount}.%{git}%{?dist} Source0: %{name}-%{git}.tar.bz2 # git package overrides official released package Epoch: 1 %else Release: 1%{?dist} Source0: https://downloads.sourceforge.net/project/sipe/sipe/%{name}-%{version}/%{name}-%{version}.tar.bz2 %endif Group: %{pkg_group} License: GPL-2.0-or-later URL: http://sipe.sourceforge.net/ BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(glib-2.0) >= 2.18.0 BuildRequires: pkgconfig(gmodule-2.0) >= 2.18.0 BuildRequires: pkgconfig(libxml-2.0) BuildRequires: pkgconfig(nss) BuildRequires: pkgconfig(purple) >= 2.7.0 %if 0%{?_with_git:1} BuildRequires: autoconf BuildRequires: automake BuildRequires: flex %endif BuildRequires: appstream BuildRequires: gcc BuildRequires: gettext BuildRequires: intltool BuildRequires: libtool # Use "--without vv" to disable Voice & Video features %if !0%{?_without_vv:1} BuildRequires: pkgconfig(purple) >= 2.8.0 BuildRequires: pkgconfig(farstream-0.2) BuildRequires: pkgconfig(freerdp-shadow2) BuildRequires: pkgconfig(gio-2.0) BuildRequires: pkgconfig(gstreamer-1.0) BuildRequires: pkgconfig(gstreamer-rtp-1.0) BuildRequires: pkgconfig(nice) >= 0.1.0 %endif # Use "--without telepathy" to disable telepathy %if !0%{?_without_telepathy:1} BuildRequires: gmime-devel BuildRequires: pkgconfig(dbus-glib-1) BuildRequires: pkgconfig(gio-2.0) >= 2.32.0 BuildRequires: pkgconfig(glib-2.0) >= 2.32.0 BuildRequires: pkgconfig(gobject-2.0) BuildRequires: pkgconfig(telepathy-glib) >= 0.24.0 %endif # Configurable components # Use "--without kerberos" to disable krb5 %if !0%{?_without_kerberos:1} BuildRequires: pkgconfig(krb5) %if 0%{?fedora} || 0%{?rhel} >= 7 BuildRequires: gssntlmssp-devel >= 0.5.0 %define requires_gssntlmssp 1 %endif %endif Requires: %{purple_plugin} = %{?epoch:%{epoch}:}%{version}-%{release} %description A third-party plugin for the Pidgin multi-protocol instant messenger. It implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) With this plugin you should be able to replace your Microsoft Office Communicator client with Pidgin. This package provides the icon set for Pidgin. %package -n %{purple_plugin} Summary: Libpurple protocol plugin to connect to MS Office Communicator Group: %{pkg_group} License: GPL-2.0-or-later Requires: %{common_files} = %{?epoch:%{epoch}:}%{version}-%{release} %if 0%{?requires_gssntlmssp} Requires: gssntlmssp >= 0.5.0 %endif %description -n %{purple_plugin} A third-party plugin for the Pidgin multi-protocol instant messenger. It implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) This package provides the protocol plugin for libpurple clients. %if !0%{?_without_telepathy:1} %package -n %{empathy_files} Summary: Telepathy connection manager to connect to MS Office Communicator Group: %{pkg_group} License: GPL-2.0-or-later Requires: %{telepathy_plugin} = %{?epoch:%{epoch}:}%{version}-%{release} %description -n %{empathy_files} A Telepathy connection manager that implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) This package provides the icon set for Empathy. %package -n %{ktp_files} Summary: Telepathy connection manager to connect to MS Office Communicator Group: %{pkg_group} License: GPL-2.0-or-later Requires: %{telepathy_plugin} = %{?epoch:%{epoch}:}%{version}-%{release} %description -n %{ktp_files} A Telepathy connection manager that implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) This package provides the profile for KTP account manager. %package -n %{telepathy_plugin} Summary: Telepathy connection manager to connect to MS Office Communicator Group: %{pkg_group} License: GPL-2.0-or-later Requires: %{common_files} = %{?epoch:%{epoch}:}%{version}-%{release} %if 0%{?requires_gssntlmssp} Requires: gssntlmssp >= 0.5.0 %endif %description -n %{telepathy_plugin} A Telepathy connection manager that implements the extended version of SIP/SIMPLE used by various products: * Skype for Business * Microsoft Office 365 * Microsoft Business Productivity Online Suite (BPOS) * Microsoft Lync Server * Microsoft Office Communications Server (OCS 2007/2007 R2) * Microsoft Live Communications Server (LCS 2003/2005) This package provides the protocol support for Telepathy clients. %endif %package -n %{common_files} Summary: Common files for SIPE protocol plugins Group: %{pkg_group} License: GPL-2.0-or-later BuildArch: noarch %description -n %{common_files} This package provides common files for the SIPE protocol plugins: * Localisation %prep %if 0%{?_with_git:1} %setup -q -n %{name}-%{git} %else %setup -q %endif %build %if 0%{?_with_git:1} ./autogen.sh %endif %configure \ %if 0%{?_without_vv:1} --without-vv \ %else --with-vv \ %endif %if !0%{?_without_kerberos:1} --with-krb5 \ %endif --enable-purple \ %if !0%{?_without_telepathy:1} --enable-telepathy %else --disable-telepathy %endif make %{?_smp_mflags} %install %make_install find %{buildroot} -type f -name "*.la" -delete -print # Pidgin doesn't have 24 or 32 pixel icons rm -f \ %{buildroot}%{_datadir}/pixmaps/pidgin/protocols/24/sipe.png \ %{buildroot}%{_datadir}/pixmaps/pidgin/protocols/32/sipe.png %find_lang %{name} %check make %{?_smp_mflags} check %files -n %{purple_plugin} %defattr(-,root,root,-) %doc AUTHORS ChangeLog COPYING NEWS README TODO %{_libdir}/purple-2/libsipe.so %if !0%{?_without_telepathy:1} %files -n %{empathy_files} %defattr(-,root,root,-) %{_datadir}/empathy/icons/hicolor/*/apps/im-sipe.png %{_datadir}/empathy/icons/hicolor/*/apps/im-sipe.svg %files -n %{ktp_files} %defattr(-,root,root,-) %doc AUTHORS COPYING %{_datadir}/telepathy/profiles/sipe.profile %files -n %{telepathy_plugin} %defattr(-,root,root,-) %doc AUTHORS ChangeLog COPYING NEWS README TODO %{_datadir}/dbus-1/services/*.sipe.service %{_libexecdir}/telepathy-sipe %endif %files -n %{common_files} -f %{name}.lang %defattr(-,root,root,-) %files %defattr(-,root,root,-) %{_datadir}/metainfo/%{name}.metainfo.xml %{_datadir}/pixmaps/pidgin/protocols/*/sipe.png %{_datadir}/pixmaps/pidgin/protocols/*/sipe.svg %changelog * Tue Oct 15 2019 J. D. User 1.25.0-*git* - make sure V&V features are really enabled when requested * Sat Oct 12 2019 J. D. User 1.25.0 - update to 1.25.0 * Sun Jun 30 2019 J. D. User 1.24.0-*git* - switch to SPDX identifier GPL-2.0-or-later * Sun Jun 30 2019 J. D. User 1.24.0-*git* - raise BR telepathy-glib >= 0.24.0 * Fri Dec 28 2018 J. D. User 1.24.0-*git* - add BR flex for git builds * Sat Nov 10 2018 J. D. User 1.24.0 - update to 1.24.0 * Mon Aug 27 2018 J. D. User 1.23.3-*git* - add BR freerdp-shadow2 * Mon Aug 20 2018 J. D. User 1.23.3 - update to 1.23.3 * Sat Mar 10 2018 J. D. User 1.23.2 - update to 1.23.2 * Sun Feb 25 2018 J. D. User 1.23.1 - update to 1.23.1 * Sun Feb 18 2018 J. D. User 1.23.0-*git* - remove obsolete clean section * Sun Feb 18 2018 J. D. User 1.23.0-*git* - change source URL to https:// * Sun Feb 18 2018 J. D. User 1.23.0-*git* - add BR gcc * Mon Nov 06 2017 J. D. User 1.23.0-*git* - add BR appstream * Sun Nov 05 2017 J. D. User 1.23.0-*git* - add support for new AppStream metadata file location * Sat Oct 28 2017 J. D. User 1.23.0 - update to 1.23.0 - raise BR glib-2.0 >= 2.18.0 - raise BR purple >= 2.7.0 * Fri Aug 11 2017 J. D. User 1.22.1-*git* - add BR dbus-1 * Sun Jun 11 2017 J. D. User 1.22.1 - update to 1.22.1 * Wed Feb 01 2017 J. D. User 1.22.0 - update to 1.22.0 * Sun Dec 18 2016 J. D. User 1.21.1-*git* - add BR gio-2.0 * Wed Jun 15 2016 J. D. User 1.21.1-*git* - add BR farstream-0.2 * Sat May 28 2016 J. D. User 1.21.1 - update to 1.21.1 * Sat Apr 23 2016 J. D. User 1.21.0 - update to 1.21.0 * Tue Apr 05 2016 J. D. User 1.20.1-*git* - align with Fedora SPEC file * Fri Jan 01 2016 J. D. User 1.20.1-*git* - add AppStream metadata file * Sun Nov 08 2015 J. D. User 1.20.1-*git* - add dependency on pkgconfig(gstreamer-1.0) for F22+ * Sat Oct 24 2015 J. D. User 1.20.1 - update to 1.20.1 * Thu Sep 03 2015 J. D. User 1.20.0-*git* - we also support "Skype for Business" * Sat Aug 29 2015 J. D. User 1.20.0 - update to 1.20.0 * Sat Apr 04 2015 J. D. User 1.19.1 - update to 1.19.1 * Sat Feb 07 2015 J. D. User 1.19.0 - update to 1.19.0 * Tue Jan 06 2015 J. D. User 1.18.5-*git* - add dependency on gssntlmssp(-devel) >= 0.5.0 for F21+ - enable Voice & Video features by default - move dependency on libnice-gstreamer to correct packages * Mon Dec 29 2014 J. D. User 1.18.5 - update to 1.18.5 * Sat Oct 18 2014 J. D. User 1.18.4 - update to 1.18.4 * Sat Aug 16 2014 J. D. User 1.18.3 - update to 1.18.3 * Sat Jun 07 2014 J. D. User 1.18.2 - update to 1.18.2 * Sat Apr 12 2014 J. D. User 1.18.1 - update to 1.18.1 * Sat Mar 08 2014 J. D. User 1.18.0-*git* - New Fedora packaging guidelines suggest to use pkgconfig() for BRs * Tue Mar 04 2014 J. D. User 1.18.0-*git* - F20+ require libnice-gstreamer for correct operation * Sat Jan 11 2014 J. D. User 1.18.0 - update to 1.18.0 * Wed Dec 11 2013 J. D. User 1.17.3 - update to 1.17.3 * Sat Nov 30 2013 J. D. User 1.17.2 - update to 1.17.2 * Sat Nov 16 2013 J. D. User 1.17.1 - update to 1.17.1 * Sat Sep 21 2013 J. D. User 1.17.0 - update to 1.17.0 * Sat Jul 13 2013 J. D. User 1.16.1 - update to 1.16.1 * Fri Jun 14 2013 J. D. User 1.16.0 - update to 1.16.0 * Thu May 16 2013 J. D. User 1.15.1-*git* - BR glib-2.0 >= 2.28.0 no longer required for Voice & Video features * Sun Apr 07 2013 J. D. User 1.15.1 - update to 1.15.1 * Fri Mar 29 2013 J. D. User 1.15.0-*git* - update package description texts * Sat Mar 09 2013 J. D. User 1.15.0 - update to 1.15.0 * Wed Dec 26 2012 J. D. User 1.14.1 - update to 1.14.1 * Sun Dec 16 2012 J. D. User 1.14.0 - update to 1.14.0 * Sun Sep 09 2012 J. D. User 1.13.3-*git* - BR telepathy-glib-devel >= 0.18.0 * Wed Sep 05 2012 J. D. User 1.13.3-*git* - BR telepathy-glib-devel >= 0.14.0 * Mon Aug 27 2012 J. D. User 1.13.3-*git* - add ktp-accounts-kcm-sipe package * Sun Aug 26 2012 J. D. User 1.13.3-*git* - telepathy now requires glib-2.0 >= 2.22.0 - use "--without telepathy" to disable telepathy packages * Fri Aug 24 2012 J. D. User 1.13.3-*git* - add empathy-sipe package * Wed Aug 22 2012 J. D. User 1.13.3-*git* - add telepathy-sipe & sipe-common packages * Sun Aug 19 2012 J. D. User 1.13.3 - update to 1.13.3 * Sun Jun 10 2012 J. D. User 1.13.2 - update to 1.13.2 * Mon Apr 09 2012 J. D. User 1.13.1 - update to 1.13.1 * Wed Mar 14 2012 J. D. User 1.13.0 - update to 1.13.0 * Mon Dec 12 2011 J. D. User 1.12.0-*git* - we do support Microsoft Lync Server 2010 now. * Tue Dec 06 2011 J. D. User 1.12.0-*git* - update GPL2 license name * Sat Nov 12 2011 J. D. User 1.12.0-*git* - add BR gmime-devel * Mon Oct 31 2011 J. D. User 1.12.0-*git* - add BR nss-devel * Mon Aug 29 2011 J. D. User 1.12.0 - update to 1.12.0 * Wed Jun 22 2011 J. D. User 1.11.2-*git* - add "--with vv" option to enable Voice & Video features * Tue Nov 02 2010 J. D. User 1.11.2 - update to 1.11.2 * Sun Oct 24 2010 J. D. User 1.11.1 - update to 1.11.1 * Mon Oct 04 2010 J. D. User 1.11.0 - update to 1.11.0 * Thu Sep 02 2010 J. D. User 1.10.1-*git* - add (commented out) BR libnice-devel * Sun Jun 27 2010 J. D. User 1.10.1 - update to 1.10.1 * Mon Apr 12 2010 J. D. User 1.10.0-*git* - add (commented out) BR nss-devel * Sun Apr 04 2010 J. D. User 1.10.0 - update to 1.10.0 * Sun Mar 28 2010 J. D. User 1.9.1-*git* - changed --with/--without options to --enable/--disable * Sun Mar 28 2010 J. D. User 1.9.1-*git* - removed --with-krb5 configure option as it is autodetected now * Tue Mar 23 2010 J. D. User 1.9.1-*git* - add SVG icon * Sat Mar 20 2010 J. D. User 1.9.1-*git* - add BR glib2-devel >= 2.12.0 * Wed Mar 17 2010 J. D. User 1.9.1-*git* - add tests to build * Tue Mar 16 2010 J. D. User 1.9.1 - update to 1.9.1 * Thu Mar 11 2010 J. D. User 1.9.0-*git* - add BR libxml2-devel * Wed Mar 10 2010 J. D. User 1.9.0 - update to 1.9.0 * Mon Mar 08 2010 J. D. User 1.8.1-*git* - increased libpurple build requisite to >= 2.4.0 * Sun Mar 07 2010 J. D. User 1.8.1-*git* - sync with RPM SPEC from contrib/OBS * Sat Mar 06 2010 J. D. User 1.8.1-*git* - update package summary & description * Tue Feb 16 2010 J. D. User 1.8.1 - update to 1.8.1 * Sun Feb 07 2010 J. D. User 1.8.0 - update to 1.8.0 * Thu Jan 14 2010 J. D. User 1.7.1-*git* - autogen.sh no longer runs configure * Tue Dec 29 2009 J. D. User 1.7.1-*git* - add configure parameters for purple and telepathy * Sat Dec 12 2009 J. D. User 1.7.1-*git* - add Epoch: for git packages to avoid update clash with official packages * Thu Nov 19 2009 J. D. User 1.7.1 - update to 1.7.1 * Wed Oct 28 2009 J. D. User 1.7.0-*git* - add missing Group: to purple-sipe * Mon Oct 19 2009 J. D. User 1.7.0 - update to 1.7.0 * Sun Oct 11 2009 J. D. User 1.6.3-*git* - move non-Pidgin files to new sub-package purple-sipe * Sun Oct 11 2009 J. D. User 1.6.3-*git* - remove directory for emoticon theme icons * Sun Oct 11 2009 J. D. User 1.6.3-*git* - libpurple protocol plugins are located under %%{_libdir}/purple-2 * Mon Sep 28 2009 J. D. User 1.6.3-*git* - added directory for emoticon theme icons * Wed Sep 09 2009 J. D. User 1.6.3 - update to 1.6.3 * Fri Aug 28 2009 J. D. User 1.6.2-*git* - reduce libpurple-devel requirement to >= 2.3.1 * Mon Aug 24 2009 J. D. User 1.6.2 - update to 1.6.2 * Fri Aug 21 2009 J. D. User 1.6.1-*git* - reduce libpurple-devel requirement to >= 2.4.1 * Mon Aug 17 2009 J. D. User 1.6.1-*git* - com_err.h only required for kerberos * Tue Aug 11 2009 J. D. User 1.6.0-*git* - require libpurple-devel >= 2.5.0 * Sun Aug 09 2009 J. D. User 1.6.0-*git* - refactor configure parameters - make kerberos configurable - don't hard code prefix for git builds * Sun Aug 09 2009 J. D. User 1.6.0-*git* - removed unnecessary zlib-devel * Sat Aug 08 2009 J. D. User 1.6.0-*git* - fix prefix for git builds * Sat Aug 01 2009 J. D. User 1.6.0-*git* - append -Wno-unused-parameter for GCC <4.4 compilation errors * Thu Jul 30 2009 J. D. User 1.6.0-*git* - remove duplicate GPL2 * Thu Jul 30 2009 J. D. User 1.6.0-*git* - use "--with git" to build from git - corrected download URL for release archive - add missing BR gettext-devel * Wed Jul 29 2009 J. D. User 1.6.0-*git* - use default rpmbuild CFLAGS also for git builds - merge with SPEC files created by mricon & jberanek * Tue Jul 28 2009 J. D. User 1.6.0-*git* - initial RPM SPEC example generated ================================================ FILE: git-build.sh ================================================ #!/bin/sh # # Convenience to (re-)build pidgin-sipe from git repository. # # Example: add configure parameters # # $ ./git-build.sh --with-krb5 # # Example: setup debug build, e.g. for valgrind ((ba)sh style) # # $ CFLAGS="-g -O0" ./git-build.sh # # Sanity check if [ ! -x autogen.sh ]; then echo 1>&2 "Your pidgin-sipe repository seems to be broken..." exit 1 fi # Check for previous build artifacts rm -f build.log if [ -r Makefile ]; then echo "Cleaning up previous build artifacts..." echo >build.log "------ Cleanup ------" make >>build.log 2>&1 -k maintainer-clean fi # Rebuild ( set -e echo "Generating configure script..." echo >>build.log "------ Generate Configure Script ------" ./autogen.sh >>build.log 2>&1 echo -n "Configuring build with" if [ $# -eq 0 ]; then echo "out any options..." else echo " '$@'..." fi echo >>build.log "------ Configure ------" ./configure >>build.log 2>&1 "$@" echo "Running build..." echo >>build.log "------ Build ------" make >>build.log 2>&1 ) if [ $? -eq 0 ]; then echo >>build.log "------ SUCCESS ------" echo "Congratulations: the build was successful!" else echo >>build.log "------ FAILED ------" echo 1>&2 "Build FAILED!" fi echo "Details can be found in 'build.log'." ================================================ FILE: git-snapshot.sh ================================================ #!/bin/sh # # Take a snapshot of the current pidgin-sipe git HEAD from the mob branch. # # You can specify the path to a local repository to speed up the cloning # process. The output will be a bzip2 compressed tarball whose name includes # the current date and the abbreviated commit object name of HEAD. # # Adapted from several examples found on the Internet. # # Configuration PROJECT=pidgin-sipe REPOSITORY="git+ssh://mob@repo.or.cz/srv/git/siplcs.git" BRANCH=mob # Create clone set -e TODAY=$(date +%Y%m%d) CLONEDIR=${PROJECT}-${TODAY} echo "Clone directory '$CLONEDIR'." REFERENCE=${1:+--reference $1} if [ -n "$1" ]; then echo "Using local repository under '$1'." fi if [ -n "$2" ]; then REPOSITORY=$2 fi echo "Cloning from repository URL '$REPOSITORY'." rm -rf $CLONEDIR git clone -n $REFERENCE $REPOSITORY $CLONEDIR cd $CLONEDIR git checkout -q -b $CLONEDIR origin/$BRANCH # Create archive COMMIT=$(git log -n 1 --abbrev-commit --pretty=oneline | cut -d' ' -f1| sed -e 's/\.//g') PREFIX=${PROJECT}-${TODAY}git${COMMIT} ARCHIVE=${PREFIX}.tar.bz2 echo "Creating archive '$ARCHIVE'..." git archive --format=tar --prefix=$PREFIX/ HEAD | bzip2 >../${PREFIX}.tar.bz2 # Cleanup echo "Cleanup..." cd .. rm -rf $CLONEDIR echo "DONE." ================================================ FILE: pidgin-sipe.nsi ================================================ ;NSIS Modern User Interface ;Basic Example Script ;Written by Joost Verburg ;-------------------------------- ;Include Modern UI !include "MUI.nsh" ;-------------------------------- ;General ;Name and file Name "Pidgin SIPE Plugin" OutFile "pidgin-sipe-${VERSION}.exe" ;Default installation folder InstallDir "$PROGRAMFILES\Pidgin" ;Get installation folder from registry if available InstallDirRegKey HKLM "Software\pidgin" "" ; SetCompressor /FINAL /SOLID lzma SetCompressorDictSize 64 ;-------------------------------- ;Interface Settings !define MUI_ABORTWARNING ;-------------------------------- ;Pages ;!insertmacro MUI_PAGE_LICENSE "Basic.nsi" ;!insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ;-------------------------------- ;Languages !insertmacro MUI_LANGUAGE "English" ;-------------------------------- ;Installer Sections Section "PidginSIPE" SecPidginSIPE SetOutPath "$INSTDIR" File "${TREETOP}\..\win32-dev\mingw\bin\libiconv-2.dll" SetOutPath "$INSTDIR\plugins" File "${TREETOP}\win32-install-dir\plugins\libsipe.dll" SetOutPath "$INSTDIR\locale\ar\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\ar\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\cs\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\cs\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\da\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\da\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\de\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\de\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\es\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\es\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\fi\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\fi\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\fr\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\fr\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\hi\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\hi\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\hu\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\hu\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\it\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\it\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\ja\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\ja\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\ko\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\ko\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\nb\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\nb\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\nl\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\nl\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\pl\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\pl\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\pt_BR\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\pt_BR\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\ru\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\ru\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\sv\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\sv\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\ta\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\ta\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\zh_CN\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\zh_CN\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\locale\zh_TW\LC_MESSAGES" File "${TREETOP}\win32-install-dir\locale\zh_TW\LC_MESSAGES\pidgin-sipe.mo" SetOutPath "$INSTDIR\pixmaps\pidgin\protocols\16" File "${TREETOP}\win32-install-dir\pixmaps\pidgin\protocols\16\sipe.png" SetOutPath "$INSTDIR\pixmaps\pidgin\protocols\22" File "${TREETOP}\win32-install-dir\pixmaps\pidgin\protocols\22\sipe.png" SetOutPath "$INSTDIR\pixmaps\pidgin\protocols\48" File "${TREETOP}\win32-install-dir\pixmaps\pidgin\protocols\48\sipe.png" ;Create uninstaller WriteUninstaller "$INSTDIR\Uninstall-pidgin-sipe.exe" SectionEnd ;-------------------------------- ;Descriptions ;Language strings LangString DESC_SecPidginSIPE ${LANG_ENGLISH} "The Pidgin SIPE Plugin." ;Assign language strings to sections ;!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN ; !insertmacro MUI_DESCRIPTION_TEXT ${SecPidginPP} $(DESC_SecPidginPP) ;!insertmacro MUI_FUNCTION_DESCRIPTION_END ;-------------------------------- ;Uninstaller Section Section "Uninstall" ;ADD YOUR OWN FILES HERE... Delete "$INSTDIR\Uninstall-pidgin-sipe.exe" Delete "$INSTDIR\libiconv-2.dll" Delete "$INSTDIR\plugins\libsipe.dll" Delete "$INSTDIR\locale\ar\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\cs\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\da\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\de\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\es\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\fi\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\fr\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\hi\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\hu\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\it\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\ja\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\ko\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\nb\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\nl\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\output\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\pl\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\pt_BR\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\ru\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\sv\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\ta\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\zh_CN\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\locale\zh_TW\LC_MESSAGES\pidgin-sipe.mo" Delete "$INSTDIR\pixmaps\pidgin\protocols\16\sipe.png" Delete "$INSTDIR\pixmaps\pidgin\protocols\22\sipe.png" Delete "$INSTDIR\pixmaps\pidgin\protocols\48\sipe.png" SectionEnd ================================================ FILE: pidgin-sipe.wxs ================================================ ================================================ FILE: pixmaps/16/Makefile.am ================================================ pixmapsize = 16 pixmapinst = 16x16 pixmapext = png include $(srcdir)/../Makefile.common.am ================================================ FILE: pixmaps/22/Makefile.am ================================================ pixmapsize = 22 pixmapinst = 22x22 pixmapext = png include $(srcdir)/../Makefile.common.am ================================================ FILE: pixmaps/24/Makefile.am ================================================ pixmapsize = 24 pixmapinst = 24x24 pixmapext = png include $(srcdir)/../Makefile.common.am ================================================ FILE: pixmaps/32/Makefile.am ================================================ pixmapsize = 32 pixmapinst = 32x32 pixmapext = png include $(srcdir)/../Makefile.common.am ================================================ FILE: pixmaps/48/Makefile.am ================================================ pixmapsize = 48 pixmapinst = 48x48 pixmapext = png include $(srcdir)/../Makefile.common.am ================================================ FILE: pixmaps/Makefile.am ================================================ SUBDIRS = 16 22 24 32 48 scalable EXTRA_DIST = \ Makefile.mingw MAINTAINERCLEANFILES = \ Makefile.in ================================================ FILE: pixmaps/Makefile.common.am ================================================ MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = sipe.$(pixmapext) if SIPE_INCLUDE_PURPLE pidginprotocolpixdir = $(datadir)/pixmaps/pidgin/protocols/$(pixmapsize) pidginprotocolpix_DATA = sipe.$(pixmapext) endif if SIPE_INCLUDE_TELEPATHY empathyappsiconsdir = $(datadir)/empathy/icons/hicolor/$(pixmapinst)/apps empathyappsicons_DATA = im-sipe.$(pixmapext) DISTCLEANFILES = im-sipe.$(pixmapext) im-sipe.$(pixmapext): $(srcdir)/sipe.$(pixmapext) cp $< $@ endif ================================================ FILE: pixmaps/Makefile.mingw ================================================ # # Makefile.mingw # # Description: Makefile for win32 (mingw) version of Pidgin pixmaps # OLD_PIDGIN_TREE_TOP := $(PIDGIN_TREE_TOP) PIDGIN_TREE_TOP := ../$(OLD_PIDGIN_TREE_TOP) include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak datadir := $(PIDGIN_INSTALL_DIR) pidginprotocolpixdir = $(datadir)/pixmaps/pidgin/protocols PROTOCOLS_16 = \ 16/sipe.png PROTOCOLS_22 = \ 22/sipe.png PROTOCOLS_48 = \ 48/sipe.png PROTOCOLS_scalable = \ scalable/sipe.svg nobase_dist_pidginpixmap_DATA = \ $(PROTOCOLS_16) \ $(PROTOCOLS_22) \ $(PROTOCOLS_48) \ $(PROTOCOLS_scalable) \ .PHONY: install install: if test '$(nobase_dist_pidginpixmap_DATA)'; then \ list='$(nobase_dist_pidginpixmap_DATA)'; for file in $$list; do \ dir=`dirname $$file`; \ mkdir -p $(pidginprotocolpixdir)/$$dir; \ cp $$file $(pidginprotocolpixdir)/$$dir/; \ done; \ fi; ================================================ FILE: pixmaps/scalable/Makefile.am ================================================ pixmapsize = scalable pixmapinst = scalable pixmapext = svg include $(srcdir)/../Makefile.common.am # original artwork is only distributed, not installed EXTRA_DIST += sipe-original.svg ================================================ FILE: po/LINGUAS ================================================ ar cs da de el es fi fr fr_CA hi hu it ja ko lt nb nl pl pt pt_BR ro ru sv ta te tr zh_CN zh_TW ================================================ FILE: po/Makefile.mingw ================================================ # Makefile.mingw # # Description: Makefile to generate mo files # OLD_PIDGIN_TREE_TOP := $(PIDGIN_TREE_TOP) PIDGIN_TREE_TOP := ../$(OLD_PIDGIN_TREE_TOP) include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak PACKAGE = pidgin-sipe .SUFFIXES: .SUFFIXES: .po .gmo ## ## SOURCES, OBJECTS ## CATALOGS = $(patsubst %.po,%.gmo,$(wildcard *.po)) ## ## RULES ## .po.gmo: rm -f $@ && $(GMSGFMT) --statistics -o $@ $< ## ## TARGETS ## .PHONY: all install clean all: $(CATALOGS) install: all mkdir -p $(PURPLE_INSTALL_PO_DIR) @catalogs='$(CATALOGS)'; \ for cat in $$catalogs; do \ cat=`basename $$cat`; \ lang=`echo $$cat | sed 's/\.gmo$$//'`; \ dir=$(PURPLE_INSTALL_PO_DIR)/$$lang/LC_MESSAGES; \ mkdir -p $$dir; \ if test -r $$cat; then \ cp $$cat $$dir/$(PACKAGE).mo; \ echo "installing $$cat as $$dir/$(PACKAGE).mo"; \ else \ cp $(PURPLE_PO_TOP)/$$cat $$dir/$(PACKAGE).mo; \ echo "installing $(PURPLE_PO_TOP)/$$cat as" \ "$$dir/$(PACKAGE).mo"; \ fi; \ done clean: rm -f *.gmo ================================================ FILE: po/POTFILES.in ================================================ # List of source files containing translatable strings. [encoding: UTF-8] src/adium/ESPurpleSIPEAccount.h src/core/sip-transport.c src/core/sipe-appshare.c src/core/sipe-buddy.c src/core/sipe-cal.c src/core/sipe-certificate.c src/core/sipe-chat.c src/core/sipe-conf.c src/core/sipe-core.c src/core/sipe-domino.c src/core/sipe-ft.c src/core/sipe-ft-lync.c src/core/sipe-ft-tftp.c src/core/sipe-group.c src/core/sipe-groupchat.c src/core/sipe-im.c src/core/sipe-incoming.c src/core/sipe-media.c src/core/sipe-notify.c src/core/sipe-ocs2007.c src/core/sipe-status.c src/core/sipe-subscriptions.c src/core/sipe-ucs.c src/core/sipe-user.c src/purple/purple-buddy.c src/purple/purple-chat.c src/purple/purple-groupchat.c src/purple/purple-im.c src/purple/purple-plugin-common.c src/purple/purple-search.c src/purple/purple-transport.c src/purple/purple-user.c src/telepathy/telepathy-protocol.c src/telepathy/telepathy-transport.c ================================================ FILE: po/ar.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Arabic (http://www.transifex.com/stefanb/pidgin-sipe/language/ar/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ar\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "لقد تم رفضك من قِبل الخادم: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "السبب غير محدد" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "الخدمة غير متاحة: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "تم استلام توقيع رسالة غير صالح" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "" msgstr[1] "" msgstr[2] "" msgstr[3] "" msgstr[4] "" msgstr[5] "" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (أكثر تطابقًا للاستعلام الخاص بك)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "مشغول" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "تعذر إنشاء مقبس استماع" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "الشركة" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "البلد" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "رسالة" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "اتصال" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "إل_غاء" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "تلقائي" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "وكيل المستخدم" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "الاسم" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "البريد الإلكتروني" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "بحث" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_بحث" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "تم قطع الاتصال بالخادم" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "تعذر الاتصال" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "تعذر إنشاء سياق SSL" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/cs.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Jakub Adam , 2011-2013,2015-2018 # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-20 16:38+0000\n" "Last-Translator: Jakub Adam \n" "Language-Team: Czech (http://www.transifex.com/stefanb/pidgin-sipe/language/cs/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: cs\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Nepodařilo se ověřit přihlašovací údaje na serveru" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Selhal požadavek na certifikát od %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Nebylo zadáno URI služby poskytující certifikáty" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Nepodařilo se ověřit přihlašovací údaje na serveru" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Zvoleno nekompatibilní autentizační schéma" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Následující server odmítl připojení: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "bez udání důvodu" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Uživatel %s nenalezen. Prosím kontaktujte svého administrátora" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP není povolen pro cílové URI nebo neexistuje" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Služba není dostupná: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Přijatá zpráva je poškozená" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Přijatý podpis zprávy je neplatný" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s chce spustit prezentaci" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Přijmout" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Odmítnout" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Problém při sdílení plochy" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Nastavený klient vzdálené plochy není podporován." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Nepodařilo se připojit ke vzdálené ploše" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Nelze vytvořit RDP server." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Nelze nastavit parametry RDP serveru." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Nelze spustit RDP server." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Sdílím plochu s: %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Ukončit prezentaci" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Nelze zahájit sdílení plochy" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Celá plocha" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Monitor k nasdílení" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Mobilní telefon" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Stav" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Kalendář" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Schůzka v" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Schůzka o" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Poznámka \"mimo kancelář\"" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Poznámka" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Úroveň přístupu" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Nalezen %d kontakt%s:" msgstr[1] "Nalezeny %d kontakty%s:" msgstr[2] "Nalezeno %d kontaktů%s:" msgstr[3] "Nalezeno %d kontaktů%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (další shody s dotazem)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Žádné kontakty nebyly nalezeny" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Nemohu zobrazit výsledky hledání." #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Vyhledání kontaktu selhalo" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Chybný vyhledávací dotaz" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Učinit vedoucím '%s'" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Odstranit z '%s'" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Pozvat do '%s'" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Nový chat" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Telefon do práce" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Domů" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Jiný telefon" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Vlastní1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Odeslat e-mail..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Odebrat kontrolu nad plochou" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Předat kontrolu nad plochou" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Sdílet plochu" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Volný" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Nezávazně zaneprázdněn" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Zaneprázdněn" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Mimo kancelář" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "není znám stav kontaktu" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Právě %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Příštích 8 hodin není v práci" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s dalších 8 hodin" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Není v práci" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s do %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Pracovní doba mu končí %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s v %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Požadavek na certifikát od %s selhal" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Požadavek na Web ticket od %s selhal" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Chat #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Nepodařilo se připojit ke konferenci" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Nezdařilo se nalézt URI konference na této stránce:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" není platná adresa konference" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Zadány neúplné údaje o konferenci" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nProtože tento klient nebyl přeložen se zapnutou podporou audiohovoru, pokud se připojíte, budete moci ostatní účastníky kontaktovat pouze pomocí textových zpráv." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "vás chce přizvat ke konferenčnímu hovoru%s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Tato konference již není uzamčená. Mohou se připojovat další účastníci." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Tato konference je uzamčená. Dokud tomu tak je, nikdo další se do ní nemůže připojit." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Byl jste od této konference odpojen." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Údaje pro připojení telefonem" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Telefonní číslo" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "ID konference" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "URL konference" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organizátor" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Alternativní telefonní čísla" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Plugin třetí strany poskytující rozšířenou verzi SIP/SIMPLE, používanou v různých produktech." #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Domovská stránka" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Uživatelská podpora" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Diskuzní forum s pomocí" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Nahlásit problémy" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Sledování nahlášených chyb" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Překlady" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licence" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Prosím pomozte přeložit SIPE do vašeho mateřského jazyka" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "použitím pohodlného webového rozhraní" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Autoři" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Překlad do češtiny (cs): psilo a Jakub Adam" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "Uživatelské jméno pro SIP Exchange obsahuje neplatné znaky" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Uživatelské jméno musí být platné SIP URI\nNapříklad: uzivatel@spolecnost.cz" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Pokud není povoleno jednotné přihlašování (Single Sign-On), je nutné zadat heslo" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "E-mailová adresa musí mít správný formát nebo zůstat nevyplněná\nNapříklad: uzivatel@spolecnost.cz" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "Uživatelské jméno služby SIP Exchange obsahuje mezery" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "URL e-mailové služby musí být ve správném tvaru nebo zůstat nevyplněné\nNapříklad: https://exchange.corp.com/EWS/Exchange.asmx\nNapříklad: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Místo" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Selhalo čtení ze socketu" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Obdržený šifrovací klíč nemá správnou délku " #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Obdržený hashovací klíč nemá správnou délku " #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Soket pro naslouchání nelze vytvořit" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Došlo k chybě" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Problém při vytváření spojení" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Selhal zápis do socketu" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Skutečná velikost souboru se liší od udávané." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Obdržený MAC je poškozený" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Obdržený soubor je poškozený" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Nepodařilo se zahájit přenos souboru" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Selhala autentizace při přenosu souboru" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Nedostatek paměti" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Ostatní kontakty" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Proxy server pro skupinový chat je chybný:\n\n\t%s\n\nOpravte prosím nastavení vašeho účtu." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Nelze nalézt server pro skupinový chat!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Tato zpráva nebyla doručena do místnosti „%s“" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Chyba při načítání seznamu místností" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Chyba při připojování k místnosti" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Pozvat uživatele %s se nezdařilo" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Obdržena zpráva s nerozpoznaným obsahem od uživatele %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Nelze vytvořit datový proud" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Spojení se nezdařilo navázat v daném čase" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Požadavek nebyl zpracován v daném čase" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Volaný uživatel neodpovídá" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Hovor nelze spojit" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Nastavení šifrování na straně kontaktu není kompatibilní s naším" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "Uživatel %s hovor odmítl " #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Hovor odmítnut" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Chyba při vytváření audio proudu" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Chyba při vytváření video proudu" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Připojit ke konferenčnímu hovoru" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Tento server nepodporuje konferenční hovory." #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Chybné telefonní číslo" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Služba zkušební hovor není k dispozici." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Uživatel nedostupný" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s si nepřeje být vyrušován" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "Uživatel %s je nedostupný" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Nepodporovaný formát média" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Problém se spojením" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Problém při čtení dat" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "už jsi přihlášen(a) z jiného místa" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "uživatel je zakázán" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "uživatel přesunut" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Zablokováno" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Osobní" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Team" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Společnost" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Veřejný" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Neznámý" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Odebrat nastavení" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Lidé v mé společnosti" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Lidé v doménách propojených s mou společností" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Lidé ve veřejných doménách" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Lidé v %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Přidat další doménu..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Online nápověda..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Přístupové skupiny" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Neaktivní" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Zaneprázdněn" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Za chvíli budu zpět" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Na obědě" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Telefonuji" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "Na konferenci" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "Na schůzce" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Pouze urgentní záležitosti" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Prezentuji" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Nelze odebírat informace o přítomnosti uživatelů!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Některé kontakty se proto budou trvale jevit jako nedostupné.\n\nProsím zkontroluje jestli váš seznam kontaktů neobsahuje neplatná SIP URI." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "Nelze inicializovat jednotné úložiště kontaktů (UCS)!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Nepodařilo se nalézt server Exchange pomocí výchozího nastavení emailu, seznam kontaktů nebude proto fungovat.\n\nBude potřeba vyplnit parametry emailu v nastavení účtu." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Nepodařilo se nalézt server Exchange pomocí parametrů emailu v nastavení účtu, seznam kontaktů nebude proto fungovat.\n\nProsím opravte své nastavení emailu." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Vaše zpráva nebo pozvánka nebyla doručena, pravděpodobně obsahuje hypertextový odkaz nebo jiný obsah, který administrátor zablokoval." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Tato zpráva nebyla uživateli %s doručena, služba není dostupná" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Tato zpráva nebyla uživateli %s doručena, jeden nebo více uživatelů nechce být vyrušováno" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Tato zpráva nebyla uživateli %s doručena, jeden nebo více uživatelů nepodporuje tento typ zprávy" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Tato zpráva nebyla uživateli %s doručena, jeden nebo více uživatelů není dostupno" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Zobrazit jméno" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Funkce" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Město" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Stát" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Kancelář" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Země" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Telefon do práce" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "E-mailová adresa" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "WWW stránka" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Přezdívka" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Jméno zařízení" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "ty" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Doména" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Přidat doménu" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Přidat" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Zrušit" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Zkopírovat do" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Zamknout" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Odemknout" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Zobrazit prezentaci" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Přístupové údaje" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Uživatelé" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Pozvat do „%s“" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Soukromá" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Logovaná" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Popisek" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Téma konverzace: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Zpráva" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Připojování" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Je vyžadováno heslo" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "uzivatel@spolecnost.cz" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Telefonní číslo" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Volat na telefonní číslo" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Volat" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Storno" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Adresa konference" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Nebo" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "Email organizátora" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "ID konference" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Připojit ke konferenci" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Připojit k naplánované konferenci" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Zadejte adresu konference, kterou jste obdrželi v pozvánce.\n\nPlatné adresy bývají ve tvaru:\nmeet:sip:uzivatel@spolecnost.cz;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:uzivatel@spolecnost.cz;gruu;opaque=app:conf:focus:id:abcdef1234\nnebo\nhttps://meet.spolecnost.cz/uzivatel/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Připojit" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Zveřejňování informací z kalendáře bylo zakázáno" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "O SIPE pluginu..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Hledání kontaktů..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Volat na telefonní číslo..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Zkušební hovor" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Připojit k naplánované konferenci..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Znovu zveřejnit kalendář" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Obnovit stav" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Server[:Port]\n(pro automatické zjištění nevyplňujte)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Typ spojení" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automaticky" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "Protokol SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "Protokol TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Uživatelský agent" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Ověřovací schéma" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Použít Single Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Nezveřejňovat informace z mého kalendáře" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Stahovat fotografie uživatelů z webu\n(potenciálně nebezpečné)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "URL e-mailové služby\n(pro automatické zjištění nevyplňujte)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "E-mailová adresa\n(liší-li se od SIP adresy)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "Uživatelské jméno pro e-mail\n(liší-li se od uživatelského jména)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Heslo pro e-mail\n(liší-li se od hesla pro komunikátor)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Proxy server pro skupinový chat\n spolecnost.com nebo uzivatel@spolecnost.com\n(nechte prázdné pro odvození z uživatelského jména)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Klient vzdálené plochy" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Šifrování hovorů" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Podle politiky serveru" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Vždy" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Volitelně" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Nikdy" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Přihlašovací jméno\n uzivatel nebo DOMENA\\uzivatel nebo\n uzivatel@spolecnost.cz" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Jméno uživatele" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Název" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-mailová adresa" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Jméno" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Příjmení" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Hledat" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Hledat kontakt" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Zadejte informaci o osobě, kterou si přejete najít. Prázdná pole budou ignorována." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Hledat" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Chyba čtení" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Došlo k odpojení serveru." #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Spojení nelze navázat" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Kontext protokolu SSL se nepodařilo vytvořit." #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Nemohu vytvořit socket" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Chyba zápisu" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "OK" ================================================ FILE: po/da.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Joe Hansen , 2013,2015-2018 # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-11-08 07:32+0000\n" "Last-Translator: Joe Hansen \n" "Language-Team: Danish (http://www.transifex.com/stefanb/pidgin-sipe/language/da/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: da\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Kunne ikke godkende til serveren" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Kan ikke anmode om certifikat fra %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Ingen URI for certifikatprovisioningtjeneste tilbudt" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Godkendelse mislykkedes" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Inkompatibelt godkendelsesskema valgt" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Du er blevet afvist af serveren: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "ingen årsag angivet" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Ikke fundt: %s. Kontakt venligst din administrator" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP er enten ikke aktiveret for destinations-URI'en eller den findes ikke" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Tjenesten er ikke tilgængelig: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Ødelagt besked modtaget" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Ugyldig meddelelsessignatur modtaget" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s ønsker at starte præsentation" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Accepter" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Afvis" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Programdelingsfejl" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Ukendt ekstern skrivebordsklient konfigureret." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Kunne ikke forbinde programdeling" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Kunne ikke oprette RDP-server." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Kunne ikke initialisere RDP-server." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Kunne ikke starte RDP-server." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Deler skrivebord med %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Stop præsentation" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Kunne ikke initialisere programdeling" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Hele skrivebordet" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Skærm at dele" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Mobil" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Status" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Kalender" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Møde i" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Møde om" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Væk fra kontoret-note" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Note" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Adgangsniveau" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Fandt %d kontakt%s:" msgstr[1] "Fandt %d kontakter%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (mere stemte med din forespørgsel)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Ingen kontakter fundet" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Kunne ikke vise søgeresultaterne" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Kontaktsøgning mislykkedes" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Ugyldig kontaktsøgningsforespørgsel" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Gør »%s« til leder" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Fjern fra »%s«" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Inviter til »%s" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Ny snak (chat)" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Arbejde" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Hjem" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Andet" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Tilpasset1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Send e-post ..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Overtag skrivebordskontrol" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Giv skrivebordskontrol" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Del mit skrivebord" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Fri" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Foreløbig" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Optaget" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Væk fra kontoret" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Ingen data" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "I øjeblikket %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Uden for arbejdstid de næste 8 timer" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s for de næste 8 timer" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Arbejder ikke" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s indtil %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Uden for arbejdstid kl. %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s kl. %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Certifikatforespørgsel til %s mislykkedes" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Netbilletforespørgsel til %s mislykkedes" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Snak #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Kunne ikke slutte til konferencen" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Kan ikke finde en konference-URI på denne side:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "»%s« er ikke en gyldig konference-URI" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Ufuldstændig konferenceinformation" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nDa denne klient ikke blev kompileret med understøttelse af stemmekald (voice call), vil du kun kunne kontakte de andre deltagere via IM-session, såfremt du accepterer dette." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "ønsker at invitere dig til et konferencekald%s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Denne konference er ikke længere låst. Yderligere deltagere kan nu slutte sig til." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Denne konference er låst. Ingen andre kan slutte sig til konferencen mens den er låst." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Du er blevet frakoblet fra denne konference." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Opkaldsinfo" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Nummer" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "Konference-id" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Mødehenvisning" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organisator" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Alternative opkaldsnumre" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Et tredjepartudvidelsesmodul der implementerer en udvidet version af SIP/SIMPLe brug af diverse produkter" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Hjemmeside" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Hjælp" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Hjælpeforum" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Rapporter problemer" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Fejlrapporter" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Oversættelser" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licens" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Hjælp os venligst med at oversætte SIPE til dit sprog via" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "bruger praktisk netgrænseflade" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Forfattere" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Oprindelige tekster på engelsk (en): SIPE-udviklere" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "SIP Exchange-brugernavn indeholder ugyldige tegn" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Brugernavn skal være en gyldig SIP URI\nEksempel: bruger@firma.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Adgangskode er krævet når Single Sign-On ikke er aktiveret" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "E-postadresse skal være gyldig hvis angivet\nEksempel: bruger@firma.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "SIP Exchange-brugernavn indeholder mellemrum" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "E-posttjenestens adresse skal være gyldig hvis angivet\nEksempel: https://exchange.corp.com/EWS/Exchange.asmx\nEksempel: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Sted:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Sokkellæsning mislykkedes" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Modtaget krypteringsnøgler har forkert størrelse." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Modtaget hash-nøgle har forkert størrelse." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Kunne ikke oprette lyttende sokkel" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Der opstod en fejl" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Der opstod en fejl under oprettelse af datastrøm" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Sokkelskrivning mislykkedes" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Fuld størrelse er anderledes end den annoncerede værdi." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Modtaget MAC er ødelagt" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Modtaget fil er ødelagt" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Initialisering af filoverførsel mislykkedes." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Godkendelse for filoverførsel mislykkedes." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Ikke nok hukommelse" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Andre kontakter" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Group Chat Proxy-indstilling er forkert:\n\n\t%s\n\nOpdater venligst din konto." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Kunne ikke finde Group Chat-server!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Denne besked blev ikke leveret til snakkerum (chat) »%s«" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Fejl ved indhentelse af rumliste" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Kunne ikke slutte til snakkerum (chat)" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Kunne ikke invitere %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Modtog en besked med ukendt indhold fra %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Kunne ikke oprette strøm" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Forbindelse fik tidsudløb" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Forespørgsel fik tidsudløb" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Opkald kunne ikke besvares" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Kunne ikke etablere et opkald" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Krypteringsindstillinger for modpart er ikke kompatible med vores." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "Bruger %s afviste kald" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Kald afvist" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Fejl under oprettelse af lydstrøm" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Fejl ved oprettelse af videostrøm" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Gå til konferencekald" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Konferencekald er ikke understøttet på denne server." #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Ugyldigt telefonnummer" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Audio Test Service er ikke tilgængelig." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Bruger utilgængelig" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s ønsker ikke at blive forstyrret" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "Bruger %s er ikke tilgængelig" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Medietype er ikke understøttet" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Mediefejl" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Der opstod en fejl under læsning fra strøm" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "du er allerede indregistreret på et andet sted" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "bruger deaktiveret" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "bruger flyttet" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Blokeret" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Personlig" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Hold" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Firma" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Offentlig" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Ukendt" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Fjern specifikation" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Folk i mit firma" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Folk i domæner forbundet med mit firma" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Folk i offentlige domæner" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Folk i %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Tilføj et nyt domæne ..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Hjælp på nettet ..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Tilgå grupper" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inaktiv" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Optaget-ledig" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Er snart tilbage" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Væk til frokost" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Optaget på telefonen" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "Til konference" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "I møde" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Kun vigtige afbrydelser" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Præsenterer" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Presence-abonnement mislykkedes!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "En eller flere venner vil derfor permanent blive vist frakoblet.\n\nKontroller at der ikke er ødelagte SIP URI'er i din kontaktliste." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "Initialisering af UCS mislykkedes!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Kunne ikke finde en Exchangeserver med standardindstillinger for e-post. Derfor vil kontaktlisten ikke fungere.\n\nDu skal angive indstillinger for e-post i kontoopsætningen." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Kunne ikke finde en Exchangeserver med indstillinger for e-post i kontoopsætningen. Derfor vil kontaktlisten ikke fungere.\n\nRet venligst dine indstillinger for e-post." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Din besked eller invitation blev ikke leveret, muligvis fordi den indeholder et hyperlink eller andet indhold som systemadministratoren har blokeret." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Denne besked blev ikke leveret til %s, da tjenesten ikke er tilgængelig" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Denne besked blev ikke leveret til %s da en eller flere modtager ikke ønskede at blive afbrudt" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Denne besked blev ikke leveret til %s da en eller flere af modtagerne ikke understøtter denne beskedtype" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Denne besked blev ikke leveret til %s da en eller flere modtagere er frakoblede" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Vist navn" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Stilling" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "By" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Stat" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Kontor" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Land" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Forretningstelefon" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "E-postadresse" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Side" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Alias" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Enhed" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "dig" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domæne" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Tilføj nyt domæne" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Tilføj" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Afbryd" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Kopier til" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Lås" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Åbn" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Vis præsentation" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Info om mødepunkt" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Brugere" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Inviter" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privat" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Log" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Beskrivelse" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Konversationsemne: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Besked" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Forbinder" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Adgangskode krævet" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "bruger@firma.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Telefonnummer" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Foretag opkald til telefonnummer" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Opkald" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Afbryd" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Mødested" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Alternativt" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "Organisators e-post" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "Møde-id" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Slut til konference" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Slut til planlagt konference" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Indtast mødestedsstreng du modtog i invitationen.\n\nGyldigt sted vil ligne\nmeet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\neller\nhttps://meet.company.com/someone/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Tilslut" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Udgivelse af kalenderinformation er blevet deaktiveret" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Om SIPE-udvidelsesmodul ..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Kontaktsøgning ..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Foretag opkald til et telefonnummer ..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Testkald" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Slut til planlagt konference ..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Genudsend kalender" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Nulstil status" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Server[:Port]\n(tom for automatisk registrering)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Forbindelsestype" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Auto" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Brugeragent" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Godkendelsesskema" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Brug Single Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Udgiv ikke min kalenderinformation" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Vis profilbilleder fra nettet\n(kan være farligt)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "E-posttjenestens adresse\n(tom for automatisk registrering)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "E-postadresse\n(hvis forskellig fra brugernavn)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "E-postlogind\n(hvis forskellig fra logind)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Adgangskode for e-post\n(hvis forskellig fra adgangskode)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Group Chat Proxy\nfirma.com eller bruger@firma.com\n(tom hvis brugernavn skal bruges)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Ekstern skrivebordsklient" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Mediekryptering" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Overhold serverpolitik" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Altid" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Valgfrit" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Deaktiveret" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Logind\n bruger eller DOMÆNE\\bruger eller\n bruger@firma.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Brugernavn" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Navn" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-post" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Fornavn" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Efternavn" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP-ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Søg" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Søg efter kontakt" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Indtast informationen for personen du ønsker at finde. Tomme felter vil blive ignoreret." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Søg" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Læsefejl" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Server har afbrudt forbindelsen" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Kunne ikke forbinde" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Kunne ikke oprette SSL-kontekst" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Kunne ikke oprette sokkel" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Skrivefejl" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "O.k." ================================================ FILE: po/de.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Stefan Becker , 2011-2013,2015-2018 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:23+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: German (http://www.transifex.com/stefanb/pidgin-sipe/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Authentifizierung mit dem Server fehlgeschlagen." #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Kann kein Zertifikat bei %s anfordern" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Keine URI für den Zertifikationsbereitstellungsdienst angegeben" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Authentifizierung fehlgeschlagen" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Sie haben ein nicht unterstütztes Authentizierungsverfahren konfiguriert" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Sie wurden vom Server '%s' abgewiesen" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "Kein Grund angegeben" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Konnte %s nicht finden. Bitte wenden Sie sich an Ihren Systemadministrator" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "entweder wurde der SIP-Service nicht für die Ziel-URI aktiviert oder die URI existiert nicht" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Der Dienst %s ist nicht verfügbar" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Beschädigte Nachricht empfangen" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Ungültige Nachrichtensignatur erhalten" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s will mit der Präsentation beginnen" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Akzeptieren" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Ablehnen" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Anwendungsfreigabefehler" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Unbekannter Remote-Desktop-Client konfiguriert" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Verbindung zur Anwendungsfreigabe fehlgeschlagen" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Konnte den RDP-Server nicht erstellen" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Konnte den RDP-Server nicht initialisieren" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Konnte den RDP-Server nicht starten" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Teile den Desktop mit %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Beende Präsentation" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Konnte Desktop-Teilen nicht initialisieren" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Den ganzen Desktop" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Welchen Bildschirm teilen" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Telefon (Handy)" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Status" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Kalender" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Besprechungsraum" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Besprechungsthema" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Abwesenheitsnotiz" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Notiz" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Sichtbarkeitsniveau" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "%d Kontakt%s gefunden:" msgstr[1] "%d Kontakte%s gefunden:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (es gab noch mehr Ergebnisse für Ihrer Suche)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Es wurden keine Einträge gefunden" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Kann die Suchergebnisse nicht anzeigen" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Suche im Adressbuch fehlgeschlagen" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Ungültige Suchanfrage" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Ernenne zum Leiter der Konferenz '%s'" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Aus der Konferenz '%s' entfernen" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Zu der Konferenz '%s' einladen" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Neue Konferenz" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Telefon (Arbeit)" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Telefon (Privat)" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Telefon" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Telefon (benutzerdefiniert)" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Sende E-Mail..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Übernehme Kontrolle über den Deskop" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Erlaube Kontrolle des Desktops" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Teile meinen Desktop" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Verfügbar" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Vorläufig" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Beschäftigt" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Nicht im Büro" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Keine Angaben" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Zurzeit %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Nicht im Büro in den nächsten 8 Stunden" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s für die nächsten 8 Stunden" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Freizeit" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s bis %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Verlässt das Büro um %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s ab %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Zertifikatsanforderung bei %s fehlgeschlagen" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Web-Ticket-Anforderung bei %s fehlgeschlagen" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Konferenz #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Konnte der Konferenz nicht beitreten" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Kann keine Konferenz-Kennung auf der folgenden Seite finden:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" ist keine gültige Konferenz-Kennung" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Die Konferenz-Daten sind unvollständig " #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nWenn sie die Einladung akzeptieren, dann können Sie sich mit den anderen Teilnehmer nur via Textmitteilungen unterhalten, da Ihr Client keinen Voice Call unterstützt." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "möchte Sie zu der Telefonkonferenz %s einladen" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Diese Konferenz ist nicht mehr gesperrt. Andere Teilnehmer können jetzt ungehindert daran teilnehmen." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Diese Konferenz ist gesperrt. Niemand kann ihr beitreten solange sie gesperrt ist." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Sie haben die Verbindung mit dieser Konferenz verloren." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Einwahlinformation" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Telefonnummer" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "Konferenz-Kennung" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Veranstaltungs-URL" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organisator" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Alternative Einwahlnummern" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Ein Third-Party Plugin, das die von den folgenden Produkten eingesetzte erweiterte Version des SIP/SIMPLE-Protokolls implementiert" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Homepage" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Forum" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Hilfe-Forum (in Englisch)" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Probleme melden im" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Bugtracker" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Übersetzungen" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Lizenz" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Wir bitten um Ihre Hilfe bei der Anpassung von SIPE an weitere Sprachen. Auf " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " gibt es dazu eine einfach zu erlernende Web-Benutzerschnittstelle" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Autoren" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Deutsche Übersetzung (de): Stefan Becker" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "Der SIP-Exchange-Benutzername enthält unerlaubte Zeichen" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Benutzername sollte eine gültige SIP URI sein\nBeispiel: benutzer@domäne.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Sie müssen ein Passwort setzen, wenn Sie Single Sign-On nicht aktiviert haben" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "Falls angegeben, dann sollte der Benutzername eine gültige SIP URI sein\nBeispiel: benutzer@domäne.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "Der SIP-Exchange-Benutzername enthält Leerzeichen" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "Die Email-Service-URL muss gültig sein, wenn angegeben\nBeispiel 1: https://exchange.corp.com/EWS/Exchange.asmx\nBeispiel 2: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Veranstaltungsort:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Netzwerklesefehler" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Der empfangene Chiffrierschlüssel hat die falsche Länge." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Der empfangene Hash-Schlüssel hat die falsche Länge." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Konnte den Listen-Socket nicht erstellen" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Ein Fehler ist aufgetreten" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Es konnte keine Daten-Verbindung hergestellt werden" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Netzwerksendefehler" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Die Dateigröße entspricht nicht dem vorgegebenen Wert." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Die empfangene MAC-Adresse ist fehlerhaft" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Die empfangene Datei ist fehlerhaft" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Der Start der Dateiübertragung ist fehlgeschlagen." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Die Authentifizierung für die Dateiübertragung ist fehlgeschlagen." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Es ist nicht genügend freier Speicher verfügbar." #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Andere Kontakte" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Die Einstellung für den Group Chat Proxy ist falsch:\n\n\t%s\n\nBitte korrigieren Sie Ihr Konto." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Der Group Chat Server konnte nicht gefunden werden!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Diese Nachricht konnte nicht an den Chatraum '%s' zugestellt werden" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Fehler beim Empfangen der Raumliste" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Fehler beim Betreten des Chatraums" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Kann den Benutzer %s nicht einladen" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Es wurde eine Nachricht von %s mit unbekanntem Inhalt empfangen" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Konnte die Daten-Verbindung nicht erstellen" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Zeitüberschreitung beim Aufbau der Verbindung" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Zeitüberschreitung bei der Anfrage" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Der Anruf konnte nicht beantwortet werden" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Der Anruf ist fehlgeschlagen" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Es konnten keine gemeinsamen Verschlüsselungseinstellungen mit der Gegenseite ausgehandelt werden." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "Der Benutzer %s hat den Anruf abgewiesen" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Der Anruf wurde abgewiesen" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Es konnte keine Audio-Verbindung hergestellt werden" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Es konnte keine Video-Verbindung hergestellt werden" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Telefonkonferenz beitreten" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Telefonkonferenzen werden von diesem Server nicht unterstützt." #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Ungültige Telefonnummer" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Der Audio Test-Service ist nicht verfügbar" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Der Benutzer ist nicht verfügbar" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s möchte nicht gestört werden" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "Der Benutzer %s ist nicht verfügbar" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Medientyp wird nicht unterstützt" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Fehler in der Daten-Verbindung" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Fehler beim Lesen von der Daten-Verbindung" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "Sie haben sich bereits von einem anderen Ort aus angemeldet" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "Benutzer ist gesperrt" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "Benutzer ist auf einen neuen Server umgezogen" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Unsichtbar" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Persönlich" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Arbeitsgruppe" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Firma" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Öffentlich" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Unbekannt" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Zurücksetzen" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Personen in meiner Firma" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Personen in Domänen, die mit meiner Firma verbunden sind" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Personen in öffentlichen Domänen" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Personen in %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Neue Domäne hinzufügen..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Online-Hilfe..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Zugriffsgruppen" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inaktiv" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Nicht verfügbar" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Bin gleich zurück" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Zur Mittagspause" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "In einem Telefonat" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "In einer Konferenz" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "In einer Besprechung" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Bitte nur in dringenden Fällen stören" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Präsentiert" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Registrierung für Präsenz-Informationen fehlgeschlagen!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Ein oder mehrere Kontakte werden daher als offline angezeigt.\n\nBitte überprüfen Sie, dass sich keine fehlerhafte SIP URI in Ihrer Kontakte-Liste befindet." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "UCS-Initialisierung fehlgeschlagen! " #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Mit den Standard E-Mail Einstellungen konnte kein Exchange-Server gefunden werden. Daher wird die Kontakte-Liste nicht funktionieren.\n\nSie müssen Ihrem Konto E-Mail Einstellungen hinzufügen." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Mit den E-Mail Einstellungen Ihres Kontos konnte kein Exchange-Server gefunden werden. Daher wird die Kontakte-Liste nicht funktionieren.\n\nBitte überprüfen Sie die E-Mail Einstellungen in Ihrem Konto." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Ihre Mitteilung oder Einladung konnte nicht zugestellt werden. Vermutlich enthält sie einen Hyperlink oder irgendeinen anderen Inhalt, der von dem Systemadministrator gesperrt wurde." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Diese Nachricht konnte nicht an %s zugestellt werden, da der Dienst nicht verfügbar ist" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Diese Nachricht konnte nicht an %s zugestellt werden, da ein oder mehrere Empfänger nicht gestört werden wollen" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Diese Nachricht konnte nicht an %s zugestellt werden, da ein oder mehrere Empfänger diesen Nachrichtentyp nicht unterstützen" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Diese Nachricht konnte nicht an %s zugestellt werden, da ein oder mehrere Empfänger offline sind" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Display-Name" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Beruf" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Stadt" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Provinz/Bundesland" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Büro" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Land" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Geschäftstelefon" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "E-Mail Adresse" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Standort" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Alias" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Gerät" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "Sie" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domäne" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Neue Domäne hinzufügen" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Hinzufügen" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Abbrechen" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Kopiere nach" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Sperren" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Freigeben" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Präsentation anzeigen" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Veranstaltungsinformation" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Teilnehmer" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Einladung" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privat" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Mitschnitt" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Beschreibung" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Gesprächsthema: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Nachricht" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Verbindungsaufbau" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Sie müssen ein Password setzen" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "benutzer@domäne.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Telefonnummer" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Telefonnummer anrufen" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Anrufen" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Abbrechen" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Konferenz-Bezeichner" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Alternative Veranstaltungsort" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "E-Mail des Organisators" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "Veranstaltungs-Kennung" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Konferenz beitreten" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Geplanter Konferenz beitreten" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Geben Sie bitte die Konferenz-Kennung ein, die sie in der Einladung erhalten haben.\n\nGültige Kennungen sehen wie folgt aus\nmeet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\noder\nhttps://meet.company.com/someone/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Beitreten" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Sie haben die Veröffentlichung ihrer Kalenderdaten unterdrückt" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Informationen zum SIPE Plugin..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Suche im Adressbuch..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Telefonnummer anrufen..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Test-Anruf" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Geplanter Konferenz beitreten..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Ihre Kalenderdaten erneut veröffentlichen" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Den Status zurücksetzen" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Server[:Port]\n(Bitte leer lassen für voreingestellten Server)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Verbindungstyp" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Auto" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Benutzeragent" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Authentizierungsschema" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Benutze Single Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Meine Kalenderdaten sollen nicht veröffentlicht werden" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Profilbilder aus dem Internet anzeigen\n(potenziell gefährlich)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "E-Mail-Service URL\n(Bitte leer lassen für voreingestellten Dienst)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "E-Mail Adresse\n(falls sie sich vom Benutzernamen unterscheiden sollte)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "E-Mail Anmeldenamen\n(falls er sich vom Anmeldenamen unterscheiden sollte)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "E-Mail Passwort\n(falls die Passwörter unterschiedlich sein sollten)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Group Chat Proxy\n domäne.com oder benutzer@domäne.com\n(Bitte leer lassen um vom Benutzernamen abzuleiten)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Remote-Desktop-Client" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Audio/Video-Verschlüsselung" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Server-Vorgabe" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Immer" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Optional" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Abgeschaltet" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Anmeldenamen\nBenutzer oder Domäne\\Benutzer oder\n benutzer@domäne.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Benutzername" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Name" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-Mail" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Vorname" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Nachname" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP-Identität" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Suchen" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Suche im Adressbuch" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Geben Sie bitte die Informationen für die Person ein, die Sie im Adressbuch suchen möchten. Leere Felder werden ignoriert." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Suchen" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Lesefehler" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Der Server hat die Verbindung abgebrochen" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Es konnte keine Verbindung hergestellt werden" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Es konnte kein SSL-Kontext erstellt werden" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Es konnte kein Socket erzeugt werden" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Schreib-Fehler" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "OK" ================================================ FILE: po/el.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Greek (http://www.transifex.com/stefanb/pidgin-sipe/language/el/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: el\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "" msgstr[1] "" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr "" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/es.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Anibal Avelar , 2011, 2012 # Maximiliano Rico , 2016-2017 # Sebastian Muniz , 2013 # Stefan Becker , 2011 # Ubay Manuel González Díaz , 2018 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-29 08:50+0000\n" "Last-Translator: Ubay Manuel González Díaz \n" "Language-Team: Spanish (http://www.transifex.com/stefanb/pidgin-sipe/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Fallo de autenticación con el servidor" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "No se puede solicitar certificado de %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "No URI para el certificado de servicio de aprovisionamiento" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Fallo de autenticación" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Seleccionado un esquema de autenticación incompatible" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "El servidor %s ha rechazado al usuario" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "no se indicó una razón" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "No encontró: %s. Por favor, contacte a su administrador" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP está deshabilitado para la URI de destino o la URI no existe" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Servicio no disponible: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "El mensaje recibido está corrupto" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Recibida firma de mensaje no valida" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%squiere empezar a presentar" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Aceptar" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Declinado" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Error al compartir" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "configuración de cliente de escritorio remota desconocida" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "No se pudo compartir" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "No se ha podido crear el servidor RDP." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "No se ha podido inicializar el servidor RDP." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "No se ha podido iniciar el servidor RDP." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Compartiendo escritorio con %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Detener presentación" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "No se pudo inicializar el uso compartido" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Todo el escritorio" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Monitor para compartir" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Móvil" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Status" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Calendario" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Reunión en" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Reunión sobre" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Mensaje de ausencia" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Nota" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Nivel de Acceso" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Se ha encontrado %d contacto%s:" msgstr[1] "Se han encontrado %d contactos%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (hay más que coinciden con la consulta)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "No se encontrarón contactos" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Imposible mostrar los resultados de la búsqueda." #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Búsqueda de contacto fallo" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Búsqueda de contact inválida" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Hazte líder de '%s'" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Eliminar de '%s'" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Invitar a '%s'" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Chat nuevo" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Trabajo" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Inicio" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Otro" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Custom1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Enviar E-Mail..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Tomar control del escritorio" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Dar control del escritorio" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Compartir mi escritorio" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Libre" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Tentativo" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Ocupado" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Fuera de la oficina" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Sin datos" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Actualmente %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Fuera de horas de trabajo durante las siguientes 8 horas" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s durante las siguientes 8 horas" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Sin trabajar" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s hasta que %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Fuera del trabajo a las %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s en %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Petición de certificado a %s fallo" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Petición de Web ticket a %s fallo" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Conversación #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Fallo para unirse a la conferencia" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "No se puede encontrar una URI de conferencia en esta página:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "La URI \"%s\" no es una conferencia válida" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "La Información de conferencia es inconmpleta" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nComo el cliente no fue compilado con soporte de llamadas de voz, sí tú la aceptas, tú debes ser capaz de contactar a los demás participantes sólo vía sesión de IM." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "te invita a una conferencia %s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Esta conferencia ya no esta bloqueada. Mas participantes pueden unirse." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Esta conferencia esta bloqueada. Nadie mas puede unirse a la conferencia mientras esté bloqueada." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Usted ha sido desconectado de esta conferencia." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Información de discado" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Número" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "ID de conferencia" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "enlace a la reunión" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organizador" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Números de discado alternativos" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Un plugin desarrollado por terceros que implementa una versión del protocolo SIP/SIMPLE usado por varios productos." #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Página principal" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Soporte" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Foro de Ayuda" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Reportar Problemas" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Seguimiento de Errores" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Traducciones" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licencia" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Por favor, ayudanos a traducir SIPE para su lenguaje nativo en" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "usando una interfaz web" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Autores" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Textos originales en Ingles (en): Desarrolladores de SIPE" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "El nombre de usuario de SIP Exchange contiene caracteres inválidos" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "El nombre de usuario debe ser una URI SIP válida\nEjemplo: usuario@dominio.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "La contraseña es requerida cuando la Autenticación única no se encuentra habilitada." #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "Si se proporciona una direccion de correo, debe ser válida\nEjemplo: usuario@dominio.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "El nombre de cuenta SIP Exchange contiene espacios" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "Si se proporciona una URL para el servicio de correo debe ser válida\nEjemplo: https://exchange.corp.com/EWS/Exchange.asmx\nEjemplo: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Localización:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Fallo en lectura de socket" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "La clave cifrada recibida tiene un tamaño erroneo." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "La clave hash recibida tiene un tamaño incorrecto." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Imposible crear el socket de escucha" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Ocurrió un error" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Error creando el flujo de datos" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Fallo en escritura de socket" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "El tamaño del archivo es diferente del valor anunciado." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "La dirección MAC está corrupta" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "El archivo recibido está corrupto" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Fallo en la inicializacion de la transferencia de archivos" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Fallo en la autenticación de la transferencia de archivos" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Memoria insuficiente" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Otros contactos" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Proxy de chat de grupo is incorrecto:\n\n»%s\n\nPor favor, actualiza tu cuenta." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "No encontró servidor de Chat de Grupo" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Este mensaje no fue enviado a la sala chat '%s'" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Error en recuperación de lista de salas" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Error al unirse a la sala de chat" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Fallo invitando a %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Se recibió un mensaje con contenido no reconocido de %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "No se pudo crear la secuencia" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Tiempo de conexión agotado" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Tiempo de espera agotado" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "La llamada no ha podido ser contestada" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Imposible establecer la llamada" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "La configuración de encriptación del destino es incompatible con la nuestra." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "El usuario %s rechazo la llamada" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Llamada rechazada" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Error creando el flujo de audio" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Error creando el flujo de video" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Unirse a llamada de conferencia" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Las llamadas en conferencia no son soportadas en este servidor." #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Número de teléfono inválido" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "El servicio para Test de Audio no se encuentra disponible" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Usuario no disponible" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s no quiere ser molestado" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "El usuario %s no esta disponible" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "medio no soportado." #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Error media" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Error leyendo desde flujo" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "ya está conectado desde otro lugar" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "usuario deshabilitado" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "usuario movido" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Bloqueado" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Personal" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Equipo" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Compañía" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Publico" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Desconocido" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "No especificado" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Personas de mi compañia" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Personas de dominios conectados con mi compañia" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Personas de dominios públicos" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Persona en %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Agregar nuevo dominio..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Ayuda en linea ..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Agregar grupos" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inactivo" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Ocupado-Inactivo" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Regreso en un momento" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "He salido a comer" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "En una llamada" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "En una conferencia" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "En una reunión" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Sólo interrupciones urgentes" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Presentando" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "falló la suscripción" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Uno o más contactos se van a mostrar offline de manera permanente. Por favor chequea que no haya URIs SIP corrompidas en tu lista de contactos" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "Fallo en inicialización UCS!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "No se pudo encontrar un servidor Exchange con la configuración de Email por defecto, por lo tanto la lista de contactos no va a funcionar.\n\nNecesitará configurar un correo electrónico en la configuración de la cuenta." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "No se pudo encontrar un servidor Exchange con la configuración de Email provista en la instalación de la cuenta, por lo tanto la lista de contactos no va a funcionar.\n\nPor favor corrija su configuración de Email" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Su mensaje o invitación no fue enviado, posiblemente porque contiene un hipervínculo u otro contenido que el administrador del sistema ha bloqueado." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Este mensaje no fue enviado a %s porque el servicio no esta disponible" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Este mensaje no fue enviado a %s porque uno o mas recipientes no desean ser molestados" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Este mensaje no fue enviado a %s porque uno o mas recipientes no soportan este tipo de mensajes" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Este mensaje no fue enviado a %s porque uno o mas recipientes desconectados" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Nombre completo" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Puesto" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Ciudad" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Estado" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Oficina" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "País" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Telefono de oficina" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "Dirección de E-Mail" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Sitio" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Alias" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Dispositivo" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "tu" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Dominio" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Agregar nuevo dominio" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Agregar" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Cancelar" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Copiar a" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Bloquear" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Desbloquear" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Mostrar presentación" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Información de la reunión" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Usuarios" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Invitar" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privado" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Log" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Descripción" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Tema de conversación: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Mensaje" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Conectando" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Contraseña requerida" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "usuario@dominio.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Número de teléfono" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Llamar a un número de teléfono" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Llamada" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Cancelar" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Lugar de la reunión" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Altenativamente" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "Email del organizador" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "ID de reunión" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Unirse a la conferencia" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Unirse a la conferencia programada" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Introduzca el URI de reunión que recibió en la inviación.⏎\nUn URI válido es de la forma:⏎\nmeet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234⏎\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234⏎\no sinó⏎\nhttps://meet.company.com/someone/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Unirse" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "La publicacion de la información de calendario ha sido deshabilitada" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Acerca del plugin SIPE" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Búsqueda de Contactos..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Llamar a un número de teléfono..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Llamada de prueba" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Unirse a la conferencia programada ..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Republicar calendario" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Reinicializar status" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Server[:Port]\n(dejar vacio para autoconfiguración)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Tipo de conexión" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automático" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "User Agent" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Esquema de autenticación" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Usar Single Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "No publique mi información de calendario" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Mostrar imágen de perfil desde la web\n(potencialmente peligroso)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "URL de servicios de E-mail" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "Dirección de E-mail\n(si es diferente al Usuario)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "Login de E-mail\n(si es diferente al Login)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Clave de E-mail\n(si es diferente a la clave)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Proxy para Chat de Grupo\n company.com o user@company.com\n (dejar vácio para dejar el Username)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Cliente de escritorio remoto" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Encriptación media" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Seguir política del servidor" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Siempre" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Opcional" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Deshabilitado" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Login\n usuario o DOMINIO\\usuario o\n usuario@dominio.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Usuario" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Nombre" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "Correo electrónico" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Nombre" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Apellidos" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Búsqueda" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Buscar un contacto" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Introduce la información de la persona que usted desea encontrar. Los campos en blanco serán ignorados." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Buscar" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Error de lectura" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "El servidor ha desconectado" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "No es posible conectar" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "No es posible crear el contexto SSL" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "No se pudo crear el socket" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Error de escritura" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "Ok" ================================================ FILE: po/fi.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Finnish (http://www.transifex.com/stefanb/pidgin-sipe/language/fi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fi\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Varmentaminen epäonnistui palvelimelle" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Palvelu ei käytettävissä: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Virheellinen viesti allekirjoitus vastaanotettu" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Tila" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Kalenteri" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "" msgstr[1] "" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr "" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Uusi ryhmäkeskustelu" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Kotisivu" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Lähetä sähköpostia..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Vapaa" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Alustavia" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Varattu" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Ei tietoja" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Nykyään %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s seuraavan 8 tuntia" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Ei töissä" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Ryhmäkeskustelu #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "SIP Exchange:n käyttäjätunnus sisältää virheellisiä merkkejä" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Käyttäjätunnus on voimassa SIP-URI\nEsimerkki: user@company.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Muut yhteystiedot" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Epäonnistui kutsua %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Vastaanotettu viesti tuntemattomat sisältö %s:lta" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "Olet jo kirjautunut toisesta paikasta" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "käyttäjä siirretty" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Yritys" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inaktiivinen" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Kohta takaisin" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Tämä viesti ei ole toimitettu %s:lle, koska palvelu ei ole käytettävissä" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Tämä viesti ei ole toimitettu %s:lle, koska yhdelle tai useammalle vastaanottajalle ei halua tulla häirityksi" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Tämä viesti ei ole toimitettu %s:lle, koska yhdelle tai useammalle vastaanottajalle on offline-tilassa" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Näyttönimen" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Työnimike" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Kaupunki" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Osavaltio" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Maa" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "Sähköpostiosoite" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Lempinimi" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "sinä" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Lukitse" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Aukaista" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Viesti" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Peru" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automaattinen" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Käytä Single Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Käyttäjätunnus" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Nimi" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "Sähköposti" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Etunimi" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Sukunimi" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Etsi" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Etsi" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Ei voitu yhdistää" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Ei voitu luoda socket" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/fr.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Clément SABATTIÉ, 2019 # djano , 2011 # Jeremy Brown, 2017 # Julien Rabier <>, 2012 # Kevin C , 2013,2015-2016 # Stefan Becker , 2011,2013 # lkppo, 2012-2013 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2019-03-07 14:05+0000\n" "Last-Translator: Clément SABATTIÉ\n" "Language-Team: French (http://www.transifex.com/stefanb/pidgin-sipe/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Échec de l'authentification sur le serveur" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Impossible de demander le certificat auprès de %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Aucun URI fourni pour le service de certification" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Échec de l'authentification" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Le protocole d'authentification choisi est incompatible" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Vous avez été rejeté par le serveur : %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "pas de raison donnée" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "%s introuvable. Merci de contacter l'Administrateur" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP n'est pas activé pour l'URI de destination ou bien l'URI n'existe pas." #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Service indisponible : %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Message corrompu reçu" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "La signature de message reçue est invalide" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s veut débuter une présentation" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Accepter" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Refuser" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Erreur de partage d'application" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Client de bureau à distance inconnu configuré" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Échec de connexion au partage d'application" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Impossible de créer le serveur RDP" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Impossible d'initialiser le serveur RDP" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Impossible de démarrer le serveur RDP" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Présenter son bureau avec %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Arrêter de présenter" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Ne peut pas démarrer le partage" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Bureau entier" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Écran à partager" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Portable" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "État" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Calendrier" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Réunion en cours" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Réunion au sujet de" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Message d'absent du bureau" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Note" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Niveau d'accès" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "%d contact%s trouvé :" msgstr[1] "%d contacts%s trouvés :" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (il y avait plus de résultats pour votre requête)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Aucun contact trouvé" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Impossible d'afficher les résultats de la recherche" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Échec de la recherche de contact" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Requête de recherche de contact invalide" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Promouvoir à la tête de '%s'" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Supprimer de '%s'" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Inviter à '%s'" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Nouvelle discussion" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Travail" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Accueil" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Autres" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Personnalisé1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Envoyer un courriel…" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Prendre le contrôle du bureau" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Donner le contrôle du bureau" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Présenter mon bureau" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Disponible" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "En réunion acceptée provisoirement" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Occupé" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Absent du bureau" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Aucune donnée" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Actuellement %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "En repos pour les 8 prochaines heures" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s pour les 8 prochaines heures" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "En repos" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s jusqu'à %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. En repos à partir de %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s à %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "La demande de certificat auprès de %s a échoué" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "La demande de ticket web auprès de %s a échoué" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Chat #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Impossible de rejoindre la conférence" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Impossible de trouver un URI de conférence sur cette page:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" n'est pas un URI de conférence valide" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Les informations fournies pour la conférence sont incomplètes" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nPuisque ce client n'a pas été compilé avec la prise en charge des appels vocaux, si vous acceptez, vous pourrez seulement contacter les autres participants par chat." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "veut vous inviter à un appel conférence%s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Cette conférence n'est plus verrouillée. D'autres participants peuvent maintenant se joindre à celle-ci." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Cette conférence est verrouillée. Personne d'autres ne peut participer à celle-ci tant qu'elle le sera." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Vous avez été déconnecté de cette conférence." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Information de connexion" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Numéro" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "ID de la conférence" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Lien de la réunion" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organisateur" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Numéros alternatifs" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Une extension tierce qui implémente une version étendue de SIP / SIMPLE utilisée par divers produits." #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Page d'accueil" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Support" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Forum d'aide" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Signaler un problème" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Système de suivi des bogues" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Traductions" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licence" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Merci de nous aider à traduire SIPE dans votre langue maternelle ici " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " utilisation d'une interface web conviviale" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Auteurs" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Textes originaux en Anglais (en) : développeurs SIPE" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "Le nom d'utilisateur SIP Exchange contient des caractères invalides" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Le nom d'utilisateur doit être une URI SIP valide\nExemple : utilisateur@company.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Un mot de passe est requis lorsque l'authentification unique (SSO) n'est pas activée" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "Si l'adresse email est fournie, elle doit être valide\nExemple : utilisateur@company.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "Les noms SIP Exchange ne peuvent contenir d'espace" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "L'URL des services email doit être valide si elle est indiquée\nExemple : https://exchange.corp.com/EWS/Exchange.asmx\nExemple : https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Lieu :" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "La lecture du socket a échoué" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "La clef de chiffrement reçue a une longueur incorrecte." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "La clef de hachage reçue a une longueur incorrecte." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Impossible de créer le socket d'écoute" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Une erreur est survenue" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Erreur lors de la création du flux de données" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "L'écriture du socket a échoué" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "La taille du fichier est différente de la valeur annoncée." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "L’adresse MAC reçue est corrompue" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Le fichier reçu est corrompu" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "L'initialisation du transfert de fichier a échoué." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "L'authentification du transfert de fichier a échoué." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Plus de mémoire." #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Autres contacts" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Le paramètre du proxy de chat de groupe est incorrect :\n\n\t%s\n\nMettez votre compte à jour s'il vous plaît." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Impossible de trouver le serveur de chat de groupe !" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Ce message n'a pu être remis au canal de chat '%s'" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Impossible de récupérer la liste des canaux de chat." #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Impossible de rejoindre le canal de chat." #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "L'invitation de %s a échoué" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Message reçu de %s avec un contenu non reconnaissable" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Impossible de créer le flux" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Connexion tombée en time out" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Requête tombée en time out" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "L'appel n'a pas abouti" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Impossible d'établir un appel" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Les paramètres d'encodage du correspondant sont incompatibles avec les votres." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "L'utilisateur %s a rejeté l'appel" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Appel rejeté" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Impossible de créer le flux audio" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Impossible de créer le flux vidéo" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Rejoindre l'appel en conférence" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Les audio-conférences ne sont pas supportées sur ce serveur." #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Numéro de téléphone invalide" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Le service de test audio est indisponible." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Utilisateur indisponible" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s ne veut pas être dérangé" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "L'utilisateur %s est indisponible" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Format non supporté" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Erreur du média" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Erreur lors de la lecture des informations du flux" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "Vous êtes déjà connecté depuis un autre lieu" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "Utilisateur désactivé" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "Utilisateur déplacé" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Bloqué" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Personnel" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Équipe" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Société" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Public" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Inconnu" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Non spécifié" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Personnes de ma société" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Personnes provenant de domaines connectés à mon entreprise" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Personnes de la fonction publique" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Personnes à %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Ajouter un nouveau domaine..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Aide en ligne…" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Groupes d'accès" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inactif" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Occupé-inactif" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Bientôt de retour" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Sorti manger" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Au téléphone" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "En conférence" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "En réunion" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Urgences uniquement" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "En présentation" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Échec de l'alerte de présence " #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Un ou plusieurs de vos amis seront marqué comme hors ligne de manière permanente.\n\nMerci de vérifier qu'il n'y a pas de URIs SIP corrompues dans votre liste de contact." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "L'initialisation UCS a échoué !" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Ne parvient pas à se connecter à un serveur Exchange avec les paramètres mail par défaut. La liste de contacts ne sera pas disponible.\n\nVous devez saisir des paramètres de mail dans la configuration du compte." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Ne parvient pas à se connecter à un serveur Exchange avec les paramètres mail saisis dans la configuration du compte. La liste de contacts ne sera pas disponible.\n\nMerci de corriger ces paramètres." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Votre message ou invitation n'a pas pu être remis, peut-être parce qu'il contient un lien hypertexte ou un autre contenu que l'administrateur a bloqué." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Ce message n'a pas pu être remis à %s parce que le service n'est pas disponible." #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Ce message n'a pas pu être remis à %s parce qu'un ou plusieurs destinataires ne veulent pas être dérangés" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Ce message n'a pas pu être remis à %s parce qu'un ou plusieurs destinataires n'acceptent pas ce type de message" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Ce message n'a pas pu être remis à %s parce qu'un ou plusieurs destinataires ne sont pas connectés" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Afficher le nom" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Intitulé du poste" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Ville" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Pays" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Bureau" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Pays" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Téléphone professionnel" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "Adresse email" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Site" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Pseudonyme" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Appareil" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "vous" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domaine" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Ajouter un nouveau domaine" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Ajouter" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Annuler" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Copier vers" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Bloquer" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Débloquer" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Afficher la présentation" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Information d'entrée en réunion" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI :" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Utilisateurs" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Inviter" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privé" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Journal" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Description" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Sujet de conversation : %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Message" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Connexion" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Mot de passe nécessaire" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "utilisateur@company.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Numéro de téléphone" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Composer un numéro de téléphone" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Appeler" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Annuler" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Lieu de réunion" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Alternativement" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "E-mail de l'organisateur" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "ID de la réunion" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Rejoindre la conférence" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Rejoindre une conférence planifiée" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Saisissez l'adresse de conversation que vous avez reçu dans l'invitation.\n\nUne adresse valide devrait ressembler à ceci :\nmeet:sip:quelquun@societeexemple.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nou cela :\nhttps://rencontrer.societeexemple.com/quelquun/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Rejoindre" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "La publication du calendrier a été désactivée" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "À propos de l'extension SIPE..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Recherche du contact..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Composer un numéro de téléphone…" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Test d'appel" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Rejoindre la conférence planifiée..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Calendrier républicain" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Réinitialiser l'état" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Serveur[:Port]\n(laisser vide pour utiliser la découverte automatique)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Type de connection" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Auto" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL / TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Agent utilisateur" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Schéma d'authentification" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Utiliser l'authentification unique" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Ne pas rendre public mon calendrier" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Voir la photo de profil depuis Internet\n(potentiellement dangereux)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "URL des services email\n(laisser vide pour utiliser la découverte automatique)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "Adresse email\n(si différente du nom d'utilisateur)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "Email de login\n(si différent du login)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Mot de passe de l'email\n(si différent du mot de passe)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Proxy pour le chat en groupe\n entreprise.com or utilisateur@entreprise.com\n(laisser vide pour le déterminer à partir du nom d'utilisateur)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Client de bureau à distance " #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Chiffrement du média" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Respecte la politique du serveur" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Toujours" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Optionnel" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Désactivé" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Login\n utilisateur ou DOMAINE\\utilisateur ou\n utilisateur@entreprise.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Nom d'utilisateur" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Nom" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "Adresse électronique" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Prénom" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Nom" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Rechercher" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Rechercher un contact" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Entrez les critères de recherche de la personne. Les champs vides seront ignorés." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Rechercher" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Erreur de lecture" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Le serveur s'est déconnecté" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Impossible de se connecter" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Impossible de créer un contexte SSL" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Impossible de créer le socket" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Erreur d'écriture" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "Ok" ================================================ FILE: po/fr_CA.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: French (Canada) (http://www.transifex.com/stefanb/pidgin-sipe/language/fr_CA/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fr_CA\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "" msgstr[1] "" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr "" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/hi.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # krupa.sagar , 2014 # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Hindi (http://www.transifex.com/stefanb/pidgin-sipe/language/hi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: hi\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "सर्वर से प्रमाणीकरण करने मे विफल " #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "प्रमाणीकरण विफल " #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "असंगत प्रमाणीकरण व्यवस्था चुना गया " #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "%s: आप सर्वर द्वारा अस्वीकार किये गए" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "कोई कारण नहीं दिया गया " #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "%s मौजूद नहीं। अपने व्यवस्थापक से संपर्क करें " #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "अवैध संदेश हस्ताक्षर प्राप्त" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "स्थिति " #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "कैलेंडर " #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "" msgstr[1] "" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr "" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "खोज परिणामों को प्रदर्शित करने में तकलीफ़ " #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "अवैध कांटेक्ट खोज क्वेरी " #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "नया चैट " #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "होम " #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "अन्य " #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "ईमेल भेजिए ... " #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "व्यस्त " #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "छुट्टी पे " #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "होम पेज " #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "सपोर्ट " #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "सहायता फोरम " #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "समस्याए बतायें " #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "बग ट्रैकर " #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "अनुवाद " #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "लाइसेंस " #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "SIPE को अपनी भाषा में अनुवाद करने में हमारी मदद करें - यहाँ पे " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "सुविधाजनक वेब इंटरफ़ेस का उपयोग करके " #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "ऑथर्स " #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "स्थान " #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "सॉकेट से पढ़ना नाकाम हुआ " #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "सॉकेट को लिखने में तकलीफ " #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "अन्य कांटेक्ट " #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "%s को आमंत्रित करने मे विफल " #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "ऑडियो टेस्ट सेवा उपलब्ध नही है " #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr " " #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "यूज़र डिसेबल किये गए " #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "यूज़र मूव किये गए " #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "ब्लॉक किये गए " #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "व्यक्तिगत " #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "टीम " #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "संस्था" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "सार्वजानिक " #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "अनजाना " #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "मेरे संस्था के लोग " #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "निष्क्रिय" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "व्यस्त-निष्क्रिय" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "थोड़ी ही देर में वापस " #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "भोजन विराम में " #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "कॉल में " #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "कॉन्फरेंस में " #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "मीटिंग में " #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "सिर्फ़ ज़रूरी रुकावटें ही " #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "दिखने वाला नाम " #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "शहर " #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "राज्य " #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "दफ्तर " #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "देश" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "ईमेल एड्रेस " #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "आप " #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "संदेश" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "पासवर्ड ज़रूरी" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "फ़ोन नंबर " #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "किसी फ़ोन नंबर पर कॉल करें " #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_कॉल " #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "किसी फ़ोन नंबर पर कॉल करें ..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "टेस्ट कॉल " #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "यूज़र नाम " #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "नाम" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "ईमेल " #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "सर्वर ने कनेक्शन तोड़ा" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "कनेक्ट नहीं कर पाया" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/hu.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Laszlo Pal , 2012-2013,2016 # Stefan Becker , 2011,2013 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Hungarian (http://www.transifex.com/stefanb/pidgin-sipe/language/hu/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: hu\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Sikertelen bejelentkezés a szerverre" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Nem sikerült tanúsítványt kérni %s tól/től" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Nincs megadva URI a tanúsítvány elosztó szolgáltatáshoz" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Sikertelen azonosítás" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Nem kompatibilis azonosítási módszer" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "A(z) %s kiszolgáló elutasította" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "nincs ok megadva" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "%s nem található. Kérem lépjen kapcsolatba a rendszergazdával" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "A SIP protokol nincs engedélyezve a megadott URI-n, vagy az URI nem létezik " #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "A(z) %s szolgáltatás nem érhető el" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Sérült üzenet" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Érvénytelen üzenetaláírás érkezett" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Elfogad" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Elutasít" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Mobil" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Állapot" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Naptár" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Megbeszélés -kor" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Megbeszélés témája" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "\"Irodán kívül\" üzenet" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Megjegyzés" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Hozzáférési szint" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Found %d contacts%s:" msgstr[1] "Found %d contacts%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (a keresésnek több találata van)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Nem található kapcsolat" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "A keresési eredményeket nem lehet megjeleníteni" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Kapcsolat keresés meghiúsult" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Érvénytelen kapcsolat keresési lekérdezés" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Vezetőnek jelölés %s -hez/höz" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Eltávolítás %s -ból/ből" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Meghívás %s -ba/be" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Új társalgás" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Munka" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Kiindulás" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Más" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Speciális1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "E-mail küldése..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Szabad" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Kétséges" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Elfoglalt" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Irodán kívül" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Nincs információ" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Jelenleg %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "A következő 8 órában nem dolgozik" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "A következő nyolc órában %s" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Nem dolgozik" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s %.2d:%.2d -ig" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. \"Nem dolgozik\" %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s %.2d:%.2d -kor" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Nem sikerült a tanúsítvány igénylés %s-hez " #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Nem sikerült Web-es jegyet nyitni %s-hez" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Beszélgetés #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Nem sikerült csatlakozni a konferenciához" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "A megadott oldalon konferencia URI nem található:\n\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "A következő URI nem valós konferencia cím: \"%s\"" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Hiányos konferencia információ" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nMivel ez a kliens hang-hívás támogatás nélkül került lefordításra, ezért amennyiben elfogadja a másik féllel csak azonnali üzeneteket fog tudni váltani." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "A megbeszélés feloldva, további résztvevők csatlakozhatnak" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Megbeszélés lezárva, további résztvevők nem csatlakozhatnak" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Ön eltávolításra került a megbeszélésről" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Behívási adatok" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Szám" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "Konferencia azonosító" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "A megbeszéléshez tartozó link" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Szervező" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Alternatív behívószám" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Egy harmadik féltől származó kiegészítés amely a SIP/SIMPLE egy kiterjesztett verzióját valósítja meg." #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Web-lap" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Támogatás" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Közösségi segítség" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Problémák jelzése" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Hiba követés" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Fordítás" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licensz" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Kérem segítse munkánkat a a SIPE fordításával más nyelvekre a következő címen: " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "egy könnyen használható web-es interfész használatával" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Fejlesztők" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Az eredeti szöveg Angolul (en): SIPE Developers" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "Az Exchange SIP felhasználó név tiltott karaktereket tartalmaz" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "A felhasználó névnek egy valós SIP URI-nak kell lennie⏎\nmint például \"felhasznalo@ceg.hu\"" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "A jelszó megadása kötelező amennyiben az Egyszeri bejelentkezés (Single Sign-On) nincs engedélyezve" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "Ha megadott e-mail címet, akkor ennek valósnak kell lennie⏎\nmint például \"felhasznalo@ceg.hu\"" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "Az Exchange SIP felhasználó név üres karaktert tartalmaz" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "Az e-mail szolgáltatásokhoz megadott URL-nek valósnak kell lennie⏎\nPélda 1.: https://exchange.corp.com/EWS/Exchange.asmx⏎ \nPélda 2.: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Helyszín" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "\"Socket\" olvasási hiba" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "A kapott titkosítási kulcs rossz méretű" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "A kapott \"hash\" kulcs rossz méretű" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Nem sikerült létrehozni a figyelő foglalatot" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Hiba történt" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Adatfolyam létrehozása sikertelen" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "\"Socket\" írási hiba" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "A fájl méret különbözik a megadott mérettől" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "A kapott \"MAC\" érvénytelen" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "A kapott fájl sérült" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Fájl átvitel indítása meghiúsult" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Fájl átviteli azonosítás sikertelen" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Elfogyott a memória" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Egyéb kapcsolatok" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "A csoport-társalgás proxy beállítása érvénytelen:\n\n»%s\n\nKérem frissítse a fiókbeállításokat" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "A csoport-társalgás szerver nem található" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Az üzenet továbbítása a %s szobába sikertelen" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "A szoba lista letöltése sikertelen" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Csatlakozás a szobához sikertelen" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "%s meghívása sikertelen" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "%s értelmezhetetlen tartalmú üzenetet küldött" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Sikertelen hívás" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "A partner titkosítási beállításai nem egyeznek a mienkkel" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "%s visszautasította a hívást" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Hívás visszautasítva" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "A hang adatfolyam létrehozása nem sikerült" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "A videó adatfolyam létrehozása nem sikerült" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Csatlakozás telefon konferenciához" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "A konferenciahívások nem támogatottak ezen a kiszolgálón" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Érvénytelen telefonszám" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Hang tesztelési szolgáltatás nem elérhető" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Felhasználó nem elérhető" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s állapota: \"Ne zavarj!\"" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "%s állapota: \"Nem elérhető\"" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Nem támogatott médiatípus " #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Média hiba" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Hiba az adatfolyam olvasásakor" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "Egy másik helyről már be va jelentkezve" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "Felhasználó letiltva" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "Felhasználó áthelyezve" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Blokkolva" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Személyes" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Csapat" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Cég" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Nyilvános" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Ismeretlen" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Mellőz" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Emberek a cégemnél" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Emberek a cégemmel kapcsolódott domainekben" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Emberek a nyílvános domainekben" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Emberek %s -nál/nél" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Domain hozzáadása..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Online segítség..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Hozzáférési csoportok..." #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Tétlen" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Elfoglalt->Tétlen" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Rögtön jön" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Ebédel" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Hívásban" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "Telefonkonferenciában" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "Megbeszélésen" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Csak sürgős esetben elérhető" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "UCS inicializálás sikertelen!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Az üzenet vagy meghívó továbbítása sikertelen mivel linket vagy egyéb, az adminisztrátor által letiltott elemet tartalmaz" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Az üzenet továbbítása %s számára nem lehetséges mivel a szolgáltatás nem elérhető" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Az üzenet továbbítása %s számára nem lehetséges mivel egy vagy több címzett állapota \"Ne zavarj!\"" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Az üzenet továbbítása %s számára nem lehetséges mivel egy vagy több címezett nem támogatja az üzenetek ezen formáját." #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Az üzenet továbbítása %s számára nem lehetséges mivel egy vagy több címzett ki van jelentkezve." #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Megjelenített név" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Munkakör" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Város" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Állam" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Iroda" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Ország" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Hivatali telefon" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "E-mail cím" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Telephely" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Álnév" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Eszköz" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "Te" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domain" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Új domain hozzáadása" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Hozzáad" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Mégsem" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Másolás..." #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Kizár" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Kizárást felold" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "A megbeszélés belépési adatai" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Felhasználók" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Meghívás" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Magánszféra" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Napló" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Leírás" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "A beszélgetés témája: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Üzenet" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Kapcsolódás" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Jelszó megadása kötelező" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "user@company.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Telefonszám" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Telefonszám felhívása" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Hívás" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Mégse" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Megbeszélés helyszíne" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "A szervező e-mail címe" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "Megbeszélés azonosítója" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Kapcsolódás a konferenciához" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Kapcsolódás tervezett konferenciához" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Add meg a megbeszélés helyére vonatkozó hivatkozást a meghívóból.\n\nEgy valós hivatkozás formátuma valami ehhez hasonló\nmeet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nor\nhttps://meet.company.com/someone/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Csatlakozás" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "A naptár információk közzététele letiltva" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "A SIPE kiegészítőről..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Kapcsolatok Keresése..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Telefonszám felhívása..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Teszt hívás" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Kapcsolódás tervezett konferenciához..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "A naptár közzététele" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Állapot alaphelyzetbe" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Server[:Port]⏎\n(az automatikus kitöltéshez hagyja üresen!)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Kapcsolat típusa" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automatikus" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Felhasználó ügynökprogram" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Autentikációs séma" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Egyszeri-bejelentkezés (SSO) használata" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Ne publikáld a naptár információkat!" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "E-mail szolgáltatások URI-ja⏎\n(az automatikus kitöltéshez hagyja üresen!)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "E-mail cím\n(ha különbözik a felhasználó névtől)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "E-mail bejelentkezési név\n(ha különbözik a bejelentkezési névtől)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "E-mail jelszó\n(ha különbözik a jelszótól)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Csoportos beszélgetés proxy\nceg.hu vagy felhasznalo@ceg.hu\n(hagyja üresen a felhasználó névből való meghatározáshoz!)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Média titkosítás" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Mindig" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Opcionális" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Letiltva" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Bejelentkezés⏎\n \"Felhasználó név\" vagy \"DOMAIN\\felhasználó név\" vagy\n felhasznalo@ceg.hu" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Felhasználónév" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Név" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-mail" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Keresztnév" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Vezetéknév" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP azonosító" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Keresés" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Kapcsolat keresése" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Adja meg a keresett személy adatait. Az üres mezők figyelmen kívül lesznek hagyva" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Keresés" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Olvasási hiba" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "A kiszolgáló bontotta a kapcsolatot" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Nem sikerült kapcsolódni" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Az SSL-környezet nem hozható létre" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Nem tudom létrehozni a \"Socket\"-et" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Írási hiba" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/it.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Saverio , 2014 # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Italian (http://www.transifex.com/stefanb/pidgin-sipe/language/it/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Errore di autenticazione con il server" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Impossibile richiedere il certificato da %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Nessun URI fornito per il servizio di rilascio certificati" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Errore di autenticazione" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Lo schema di autenticazione scelto è incompatibile." #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Connessione rifiutata dal server: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "nessun motivo fornito" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Non trovato: %s. Contattare l'amministratore" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP non abilitato per la URI di destinazione oppure non esistente" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Servizio non disponibile:%s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "La firma del messaggio ricevuta non è valida" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Accetta" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Rifiuta" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Mobile" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Stato" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Calendario" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Riunione in" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Riunione su" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Nota per il Fuori Ufficio" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Nota" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Livello di accesso" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "%d contatto%s trovato:" msgstr[1] "%d contatti%s trovati:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (altri risultati per l'interrogazione)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Nessun contatto trovato" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Non è stato possibile mostrare i risultati della ricerca." #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Impossibile cercare il contatto" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Criteri non validi per la ricerca del contatto" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Rendi leader di '%s'" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Rimuovi da '%s'" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Invita a '%s'" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Nuova chat" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Lavoro" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Home" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Altro" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Personalizzato1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Spedisci email..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Libero" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Incerto" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Occupato" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Fuori ufficio" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Nessun dato" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Attualmente %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Fuori sede per le prossime 8 ore" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s per le prossime 8 ore" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Non al lavoro" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s fino a %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Fuori dagli orari di lavoro al %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s al %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "La richiesta del certificato a %s non è andata a buon fine" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "La richiesta del web ticket a %s non è andata a buon fine" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Chat #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Impossibile collegarsi alla conferenza" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" non è un URI di conferenza valido" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nPoichè il client non è stato compilato con il supporto alla chiamata vocale, se si accetta, si potranno contattare gli altri partecipanti solo tramite sessione IM." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Questa conference non è più bloccata. Ora possono connnettersi altri partecipanti." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Questa conference è bloccata. Nessun altro può partecipare fino a che sarà bloccata." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Sei stato disconnesso da questa conferenza" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Un plugin che implementa una versione estesa di SIP/SIMPLE usato in molti prodotti" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Pagina principale" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Supporto" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Forum di Aiuto" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Riporta Problemi" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Bug Tracker" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Traduzioni" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licenza" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Per favore aiutaci a tradurre SIPI nella tua lingua madra qui in" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "usando una comoda interfaccia web" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Autori" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Traduzione in Italiano (it): Emanuele Zattin" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "Il nome utente di SIP Exchange contiene caratteri non validi" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Il nome utente deve essere un SIP URI valido\nEsempio: utente@azienda.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "L'indirizzo email deve essere valido se fornito\nEsempio: utente@azienda.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "Il nome utente di SIP Exchange contiene spazi bianchi" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "Gli URL dei servizi email devono essere validi se inseriti\nEsempio: https://exchange.corp.com/EWS/Exchange.asmx\nEsempio: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Locazione:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Errore di lettura del socket." #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "La chiave di cifratura ricevuta ha una dimensione errata." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "L' hash ricevuto ha una dimensione errata." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Impossibile creare il socket in ascolto" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Errore di scrittura nel socket" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Le dimensioni del file sono diverse da quelle attese." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Il MAC ricevuto è corrotto." #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Il file ricevuto è corrotto" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Errore di inizializzazione nel trasgerimento del file" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Errore di autenticazione nel trasferimento del file" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Memoria esaurita" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Altri Contatti" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "L'impostazione del proxy per la chat di gruppo è errata: \n\n %s\n\nAggiornare il proprio account." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Impossibile trovare un server per la chat di gruppo!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Questo messaggio non è stato consegnato alla chat room '%s'" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Impossibile ricevere la lista delle chat room" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Impossibile entrare nella chat room" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Errore nell'invitare %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "È stato ricevuto un messaggio con contenuti non riconosciuti da %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Impossibile stabilire una chiamata" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "I settaggi del criptaggio del peer sono incompatibili con i nostri." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "L'utente %s ha rifiutato la chiamata" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Chiamata rifiutata" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Errore durante la creazione dello stream audio" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Errore durante la creazione dello stream video" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Partecipa alla chiamata in conferenza" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Numero di telefono errato" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Servizio per il test audio non disponibile" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Utente non disponibile:%s" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s non vuole essere disturbato/a" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "L'utente %s non é disponibile" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Tipo di supporto non gestito" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "sei giá collegato da un'altra locazione" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "utente disabilitato" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "utente spostato" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Bloccato" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Personali" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Team" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Società" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Pubblici" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Sconosciuti" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Rimuovi" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Persone nella mia azienda" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Persone in domini connessi alla mia azienda" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Persone in domini pubblici" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Persone in %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Aggiungi nuovo dominio..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Aiuto online..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Gruppi di accesso" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inattivo" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Occupato-Inattivo" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Torno subito" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "A pranzo" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Al telefono" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "In una conferenza" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "In riunione" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Solo interruzioni urgenti" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Il tuo messaggio o invito non è stato recapitato, forse perchè contenente un link o altro che l'amministratore di sistema ha bloccato." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Questo messaggio non é stato inoltrato a %s perché il servizio non é disponibile" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Questo messaggio non é stato inoltrato a %s perché uno e piú destinatari non vogliono essere disturbati" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Questo messaggio non é stato inoltrato a %s perché uno o piú destinatari non supportano questo tipo di messaggio" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Questo messaggio non é stato inoltrato a %s perché uno e piú destinatari non sono collegati" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Nome visualizzato" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Ruolo" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Città" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Stato" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Ufficio" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Paese" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Telefono Ufficio" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "Indirizzo Email" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Sede" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Alias" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Periferica" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "tu" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Dominio" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Aggiungi un nuovo dominio" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Aggiungi" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Annulla" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Copia in" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Blocca" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Sblocca" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Utenti" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Invita" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privato" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Log" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Descrizione" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Oggetto della conversazione: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Messaggio" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Connessione in corso" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Password richiesta" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "utente@azienda.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Numero di telefono" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Chiama un numero telefonico" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Chiamata" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Annulla" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Luogo della riunione" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Partecipa alla conferenza" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Partecipa alla conferenza pianificata" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Inserisci la frase del luogo della riunione che hai ricevuto nell'invito.\n\nIl luogo valido sarà quacosa di simile a\nmeet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\noppure\nhttps://meet.company.com/someone/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Partecipa" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "La pubblicazione delle informazioni di calendario è stata disabilitata." #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Informazioni sul plugin SIPE..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Cerca contatto..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Chiama il numero telefonico..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Chiamata di test" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Partecipa alla conferenza pianificata..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Ripubblica il Calendario" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Resetta lo stato" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Server[:Porta]\n(lascia vuoto per auto-discovery)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Tipo di connessione" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Auto" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Agente utente" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Schema per l'autenticazione" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Utilizza Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Non pubblicare le mie informazioni di calendario" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "URL Servizio Email\n(lascia vuoto per ricerca automatica)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "Indirizzo Email\n(se diverso dallo Username)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "Email login\n(se diverso da Login)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Email password\n(se diversa da Password)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Proxy della chat di gruppo\ncompany.com oppure user@company.com\n(lasciare vuoto per rilevare il proxy dal nome utente) " #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Login\n utente or DOMINIO\\utente or\n utente@azienda.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Nome utente" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Nome" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-mail" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Nome" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Cognome" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Ricerca" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Cerca un contatto" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Inserisci le informazioni della persona che vuoi trovare. I campi lasciati vuoti saranno ignorati." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Cerca" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Errore di lettura" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Il server si è disconnesso" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Impossibile connettersi" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Impossibile creare il contesto SSL" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Errore nella creazione del socket" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Errore di scrittura" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/ja.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Matthew Duggan , 2011,2015 # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Japanese (http://www.transifex.com/stefanb/pidgin-sipe/language/ja/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ja\n" "Plural-Forms: nplurals=1; plural=0;\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "サーバーに認証できませんでした" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "認証は失敗しました" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "サーバに拒否されました: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "理由はわかりません" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "サービスが利用できません: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "受信したメッセージ署名が正しくありません" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "承諾" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "否定" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "携帯" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "状態" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "カレンダー" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "会議室" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "会議課題" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "外出参考" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "メモ" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "アクセスレベル" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "%d連絡先が見つかりました%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (クエリに一致したものが他にもあります)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "連絡先は見つかりませんでした" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "検索結果は表示できませんでした" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "連絡先検索失敗しました" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "「%s」のリーダーにします" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "「%s」から取り除く" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "「%s」に誘います" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "新しいチャット" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "勤務先" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "自宅" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "その他" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "カスタム1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "メールを送ります。。。" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "開いている" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "保留" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "取り込み中" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "外出中" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "データなし" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "現在%s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "これから8時間%s" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s、%.2d:%.2dまで" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s。%s、%.2d:%.2dから" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "チャット%d番" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "会議のロックは解除されました。他の人は参加できるようになりました。" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "会議はロックされています。ロックは解除されたまで他の人は参加できません。" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "オンライン会議からログアウトされました" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "ホームページ" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "サポート" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "翻訳" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "ライセンス" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "作者" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "ソケット読み込みエラー" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "監視ソケットを生成できませんでした" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "ソケット書き込みエラー" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "メモリー不足" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "その他の連絡先" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "チャットに参加するのを失敗しました" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "%sを招待できませんでした" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "呼び出しは否定されました" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "オンライン会議に参加する" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "ブロックされました" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "パーソナル" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "チーム" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "勤務先" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "一般公開" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "不明" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "指定をなくする" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "%sにいる人々" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "ドメインを追加する。。。" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "オンラインヘルプ。。。" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "一時退席" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "昼休み" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "話し中" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "会談中" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "会議中" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "連絡先はログアウトされた為、%sに送れませんでした" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "市" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "州" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "オフィス" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "国" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "メール宛先" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "デバイス" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "あなた" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "ドメイン" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "ドメインを追加する" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "追加" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "キャンセル" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "コピー" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "ロック" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "ロック解除" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "ユーザー" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "招待" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "非公開" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "ログ" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "説明" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "メッセージ" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "接続中です" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "パースワードが必要" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "user@company.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "電話番号" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "通話(_C)" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "キャンセル(_C)" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "会議場" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "参加(_J)" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "SIPEプラグインについて。。。" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "連絡先検索。。。" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "試験通話" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "ステータスをリセット" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "接続方式" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "自動" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "ユーザエージェント" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "認証方式" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "ケルベロス" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "シングルサインオンを利用する" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "カレンダーを公表しない" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "メール宛先\n(ログインと異なる場合)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "メールのログイン\n(ログインと異なる場合)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "メールのパスワード\n(パスワードと異なる場合)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "ユーザー名" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "名前" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-メール" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "名" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "氏" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "検索" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "連絡先を検索する" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "検索(_S)" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "読み込みエラー" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "サーバが切断しました" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "接続できませんでした" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "SSLコンテキストを作成できませんでした" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "ソケットを作成できませんでした" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "書き込みエラー" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/ko.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Korean (http://www.transifex.com/stefanb/pidgin-sipe/language/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ko\n" "Plural-Forms: nplurals=1; plural=0;\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "서버에서 거부되었습니다. %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "이유가 없습니다." #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "서비스를 이용할 수 없습니다. %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "잘못된 메시지 서명이 수신되었습니다." #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "%d contact%s 찾음:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (더 많은 항목이 쿼리와 일치)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "다른 용무 중" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "수신 소켓을 생성할 수 없습니다." #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "회사" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "국가" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "메시지" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "연결 중" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "취소(_C)" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "자동" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "사용자 에이전트" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "이름" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "전자 메일" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "검색" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "검색(_S)" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "서버의 연결이 끊겼습니다." #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "연결할 수 없습니다." #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "SSL 컨텍스트를 생성할 수 없습니다." #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/lt.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Moo, 2016-2018 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 18:45+0000\n" "Last-Translator: Moo\n" "Language-Team: Lithuanian (http://www.transifex.com/stefanb/pidgin-sipe/language/lt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: lt\n" "Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Nepavyko nustatyti tapatybės serveriui" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Nepavyksta užklausti liudijimo iš %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Nebuvo pateiktas liudijimų aprūpinimo paslaugos URI" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Tapatybės nustatymas nepavyko" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Pasirinkta nesuderinama tapatybės nustatymo schema" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Serveris jus atmetė: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "priežastis nenurodyta" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Nerasta: %s. Prašome susisiekti su savo administratoriumi" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "Arba SIP, skirtas paskirties URI neįjungtas, arbo jo nėra" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Paslauga neprieinama: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Gauta sugadinta žinutė" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Gautas neteisingas žinutės parašas" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s nori pradėti pristatymą" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Priimti" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Atmesti" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Programos bendrinimo klaida" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Sukonfigūruotas nežinomas nuotolinis darbalaukio klientas." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Nepavyko prijungti programos bendrinimą" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Nepavyko sukurti RDP serverio." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Nepavyko inicijuoti RDP serverio." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Nepavyko paleisti RDP serverio." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Bendrinamas darbalaukis su %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Stabdyti pristatymą" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Nepavyko inicijuoti programos bendrinimo" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Visas darbalaukis" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Monitorius, kurį bendrinti" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Mobilusis telefonas" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Būsena" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Kalendorius" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Susitikimo vieta" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Susitikimas apie" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "\"Nėra biure\" raštelis" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Raštelis" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Prieigos lygis" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Rastas %d kontaktas%s:" msgstr[1] "Rasti %d kontaktai%s:" msgstr[2] "Rasta %d kontaktų%s:" msgstr[3] "Rasta %d kontaktų%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (daugiau atitiko jūsų užklausą)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Kontaktų nerasta" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Nepavyksta parodyti paieškos rezultatų" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Kontaktų paieška nepavyko" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Neteisinga kontaktų paieškos užklausa" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Padaryti \"%s\" lyderiu" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Šalinti iš \"%s\"" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Pakviesti į \"%s\"" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Naujas pokalbis" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Darbas" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Namai" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Kita" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Tinkinta1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Siųsti el. paštą..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Perimti darbalaukio valdymą" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Suteikti darbalaukio valdymą" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Bendrinti savo darbalaukį" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Laisvas" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Preliminariai užimtas" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Užimtas" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Nėra biure" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Nėra duomenų" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Šiuo metu %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Kitas 8 valandas ne darbe" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s kitas 8 valandas" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Nedirba" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s iki %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Darbo laikas baigiasi %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "%s liudijimo užklausa nepavyko" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "%s saityno bilieto užklausa nepavyko" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Pokalbis #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Nepavyko prisijungti prie konferencijos" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Nepavyksta šiame puslapyje rasti konferencijos URI:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" nėra teisingas konferencijos URI" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Pateikta ne visa konferencijos informacija" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nKadangi šis klientas nebuvo kompiliuotas su balso skambučių palaikymu, jeigu priimsite, jūs galėsite susisiekti su kitais dalyviais tik per žinučių seansą." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "nori jus pakviesti į konferencinį skambutį%s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Ši konferencija daugiau nebeužrakinta. Dabar, papildomi dalyviai gali prisijungti." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Ši konferencija yra užrakinta. Niekas negali prisijungti prie konferencijos, kol ji yra užrakinta." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Jūs buvote atjungti nuo šios konferencijos." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Numerio rinkimo informacija" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Numeris" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "Konferencijos ID" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Susitikimo nuoroda" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organizatorius" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Alternatyvūs numerio rinkimo numeriai" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Trečiosios šalies papildinys, įgyvendinantis įvairių produktų naudojamą išplėstinę SIP/SIMPLE versiją" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Tinklalapis" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Palaikymas" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Pagalbos forumas" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Pranešti apie klaidas" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Klaidų seklys" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Vertimai" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licencija" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Padėkite mums išversti SIPE į jūsų gimtąją kalbą, apsilankydami " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " bei naudodamiesi patogia saityno sąsaja" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Autoriai" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Lokalizavimas į (): " #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "SIP Exchange naudotojo varde yra neteisingų simbolių" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Naudotojo vardas turėtų būti tinkamas SIP URI\nPavyzdys: naudotojas@kompanija.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Slaptažodis reikalingas, kai nėra įjungtas vieningas prisijungimas (Single Sign-On)" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "Pateikiant el. pašto adresą, jis turi būti teisingas\nPavyzdys: naudotojas@kompanija.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "SIP Exchange naudotojo varde yra tarpų" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "Pateikiant el. pašto paslaugų URL, jis turi būti teisingas\nPavyzdys: https://exchange.corp.com/EWS/Exchange.asmx\nPavyzdys: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Vieta:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Lizdo skaitymas nepavyko" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Gautas šifravimo raktas yra neteisingo dydžio." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Gautas maišos raktas yra neteisingo dydžio." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Nepavyko sukurti klausymosi lizdo" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Įvyko klaida" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Klaida, kuriant duomenų srautą" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Lizdo rašymas nepavyko" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Failo dydis skiriasi nuo skelbiamos reikšmės." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Gautas MAC yra sugadintas" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Gautas failas yra sugadintas" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Failo persiuntimo inicijavimas nepavyko." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Failo persiuntimo tapatybės nustatymas nepavyko." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Trūksta atminties" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Kiti kontaktai" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Grupės pokalbio įgaliotojo serverio nustatymas yra neteisingas:\n\n\t%s\n\nPrašome atnaujinti savo paskyrą." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Nepavyko rasti grupės pokalbio serverio!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Ši žinutė nebuvo pristatyta į pokalbių kambarį \"%s\"" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Klaida, gaunant kambarių sąrašą" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Klaida, prisijungiant prie pokalbių kambario" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Nepavyko pakviesti %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Gauta žinutė su nepažįstamu turiniu nuo %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Nepavyko sukurti srauto" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Pasibaigė ryšiui skirtas laikas" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Pasibaigė užklausai skirtas laikas" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Skambutis negalėjo būti atsilieptas" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Nepavyksta užmegzti skambutį" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Pašnekovo šifravimo nustatymai yra nesuderinami su mūsų." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "Naudotojas %s atmetė skambutį" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Skambutis atmestas" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Klaida, kuriant garso srautą" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Klaida, kuriant vaizdo srautą" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Prisijungti prie konferencinio skambučio" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Konferenciniai skambučiai šiame serveryje nėra palaikomi." #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Neteisingas telefono numeris" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Garso bandymo paslauga neprieinama." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Naudotojas neprieinamas" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s nenori būti trukdomas(-a)" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "Naudotojas %s yra neprieinamas" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Nepalaikomas medijos tipas" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Medijos klaida" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Klaida, skaitant iš srauto" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "jūs jau esate prisijungę kitoje vietoje" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "naudotojas išjungtas" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "naudotojas perkeltas" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Užblokuotas" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Asmeninis" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Komanda" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Kompanija" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Viešas" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Nežinoma" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Nebenurodinėti" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Žmonės mano kompanijoje" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Žmonės viešuosiuose tinkluose, prijungtuose prie mano kompanijos" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Žmonės viešuosiuose tinkluose" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Žmonės ties %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Pridėti naują sritį..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Žinynas internete..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Prieigos grupės" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Neaktyvus" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Užimtas-Neveiklus" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Netrukus grįšiu" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Pietauju" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Dalyvauju skambutyje" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "Konferencijoje" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "Susitikime" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Tik skubūs pertraukimai" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Pristatinėjama" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Prisijungimo būsenos prenumerata nepavyko!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Dėl to, vienas ar daugiau bičiulių bus visą laiką rodomi kaip atsijungę.\n\nPrašome patikrinti ar jūsų kontaktų sąraše nėra sugadintų SIP URI." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "UCS inicijavimas nepavyko!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Nepavyko rasti Exchange serverio su numatytaisiais el. pašto nustatymais. Dėl to, kontaktų sąrašas neveiks.\n\nJums reikės pateikti el. pašto nustatymus paskyros sąrankoje." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Nepavyko rasti Exchange serverio su paskyros sąrankoje pateiktais el. pašto nustatymais. Dėl to, kontaktų sąrašas neveiks.\n\nPrašome ištaisyti savo el. pašto nustatymus." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Jūsų žinutė ar pakvietimas nebuvo pristatytas, tikriausiai, dėl to, kad jame yra saitas arba kitas sistemos administratoriaus užblokuotas turinys." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Ši žinutė nebuvo pristatyta pašnekovui %s, kadangi paslauga yra neprieinama" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Ši žinutė nebuvo pristatyta pašnekovui %s, kadangi vienas ar daugiau gavėjų nenori būti trukdomi" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Ši žinutė nebuvo pristatyta pašnekovui %s, kadangi vienas ar daugiau gavėjų nepalaiko šio žinutės tipo" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Ši žinutė nebuvo pristatyta pašnekovui %s, kadangi vienas ar daugiau gavėjų yra atsijungę" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Rodomas vardas" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Pareigos" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Miestas" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Valstija" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Biuras" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Šalis" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Darbo telefonas" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "El. pašto adresas" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Svetainė" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Slapyvardis" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Įrenginys" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "jūs" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Sritis" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Pridėti naują sritį" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Pridėti" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Atsisakyti" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Kopijuoti į" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Užrakinti" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Atrakinti" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Rodyti pristatymą" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Susitikimo įrašo informacija" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Naudotojai" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Pakviesti" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privatus" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Žurnalas" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Aprašas" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Pokalbio tema: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Žinutė" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Jungiamasi" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Reikalingas slaptažodis" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "naudotojas@kompanija.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Telefono numeris" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Skambinti telefono numeriu" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Skambinti" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Atsisakyti" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Susitikimo vieta" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Kitu atveju" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "Organizatoriaus el. paštas" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "Susitikimo ID" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Prisijungti prie konferencijos" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Prisijungti prie suplanuotos konferencijos" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Įveskite susitikimo vietos eilutę, kurią gavote pakvietime.\n\nTeisinga vieta būtų kažkas panašaus į\nmeet:sip:kažkas@kompanija.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:kažkas@kompanija.com;gruu;opaque=app:conf:focus:id:abcdef1234\narba\nhttps://susitikimas.kompanija.com/kažkas/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Prisijungti" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Kalendoriaus informacijos skelbimas buvo išjungtas" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Apie SIPE papildinį..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Kontaktų paieška..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Skambinti telefono numeriu..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Bandomasis skambutis" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Prisijungti prie suplanuotos konferencijos..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Iš naujo skelbti kalendorių" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Atstatyti būseną" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Serveris[:Prievadas]\n(automatiniam aptikimui palikite tuščią)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Ryšio tipas" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automatiškai" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Naudotojo agentas" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Tapatybės nustatymo schema" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Naudoti vieningą prisijungimą (Single Sign-On)" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Neskelbti mano kalendoriaus informacijos" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Rodyti profilio paveikslus iš saityno\n(potencialiai pavojinga)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "El. pašto paslaugų URL\n(automatiniam aptikimui palikite tuščią)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "El. pašto adresas\n(jeigu kitoks nei Naudotojo vardas)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "El. pašto prisijungimas\n(jeigu kitoks nei Prisijungimas)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "El. pašto slaptažodis\n(jeigu kitoks nei Slaptažodis)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Grupės pokalbio įgaliotasis serveris\n kompanija.com arba naudotojas@kompanija.com\n(palikite tuščią, kad būtų nustatyta pagal naudotojo vardą)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Nuotolinio darbalaukio klientas" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Medijos šifravimas" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Paklusti serverio politikai" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Visada" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Pasirinktinai" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Išjungta" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Prisijungimas\n naudotojas ar SRITIS\\naudotojas ar\n naudotojas@kompanija.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Naudotojo vardas" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Vardas" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "El. paštas" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Vardas" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Pavardė" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Paieška" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Kontakto paieška" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Įrašykite norimo rasti asmens informaciją. Tuščių laukų bus nepaisoma." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Ieškoti" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Skaitymo klaida" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Serveris atsijungė" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Nepavyko prisijungti" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Nepavyko sukurti SSL konteksto" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Nepavyko sukurti lizdo" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Rašymo klaida" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "Gerai" ================================================ FILE: po/nb.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Espen Stefansen , 2011 # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Norwegian Bokmål (http://www.transifex.com/stefanb/pidgin-sipe/language/nb/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: nb\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Klarte ikke å autentisere til tjener" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Autentisering feilet" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Du har blitt avslått av tjeneren: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "ingen grunn gitt" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Ikke funnet: %s. Vennligst ta kontakt med administrator" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Tjeneste utilgjengelig: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Ugyldig meldingssignatur mottatt" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Aksepter" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Avslå" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Status" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Kalender" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Møte i" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Møte om" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Ute av kontoret merknad" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Merknad" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Tilgangsnivå" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Fant %d kontakt%s:" msgstr[1] "Fant %d kontakter%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr "" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Klarte ikke vise søkeresultater" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Fjern fra '%s'" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Inviter til '%s'" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Ny chat" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Hjemmeside" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Send e-post..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Ledig" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Foreløpig" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Opptatt" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Ikke på kontoret" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Ingen informasjon" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "I øyeblikket %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Utenfor arbeidstid de neste 8 timene" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s for de neste 8 timene" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Fritid" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s til %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Konferansen er ikke lengre låst. Nå kan andre deltakere koble til" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Konferansen er låst. Ingen andre kan koble til så lenge konferansen er låst." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Du har blitt koblet fra denne konferansen." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Et tredjeparts programtillegg som implementerer en utvidet versjon av SIP/SIMPLE i bruk av diverse produkter" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Hjelpeforum (på engelsk)" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Rapporter problemer" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Oversettelser" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Lisens" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Hjelp oss å oversette SIPE til flere språk på" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "ved å bruke et lettvindt vev-grensesnitt" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Utviklere" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Norsk oversettelse (nb): Andreas Angerman" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "SIP Exchange brukernavn inneholder ugyldige tegn" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Brukernavn skal være en gyldig SIP URI\nEksempel: bruker@firma.no" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "E-postadresse skal være gyldig hvis oppgitt\nEksempel: bruker@firma.no" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "Brukernavnet til SIP Exchange inneholder mellomrom" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Lokasjon:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Forbindelse feilet" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Mottatt krypteringsnøkkel har feil størrelse" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Mottatt hash-nøkkel har feil størrelse." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Kunne ikke lage lytte-sokkel" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Størrelsen er ulik fra forespeilet verdi." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Mottatt MAC-adresse er skadet" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Mottatt fil er skadet" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Start av filoverføring feilet." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Autentisering av filoverføring feilet." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Tomt for minne" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Andre kontakter" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Mislykkes med å invitere %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Mottok en melding med ukjent innhold fra %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Kunne ikke etablere en samtale" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Bruker utilgjengelig" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s ønsker ikke å bli forstyrret" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "Bruker %s er ikke tilgjengelig" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "du er allerede logget på et annet sted" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "bruker er deaktivert" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Blokkert" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Firma" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Ukjent" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Personer i mitt firma" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Personer i domener koblet til mitt firma" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Personer i offentlige domener" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Personer hos %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Legg til nytt domene..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Ikke tilgjengelig" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Er snart tilbake" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Ute til lunsj" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "I en samtale" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "I en konferanse" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "I et møte" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Bare viktige forstyrrelser" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Meldingen eller invitasjonen ble ikke levert, sansynligvis fordi den inneholder en hyperlenke eller annet innhold som systemadministratoren har blokkert." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Denne meldingen ble ikke levert til %s fordi tjenesten ikke er tilgjengelig" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Denne meldingen ble ikke levert til %s fordi en eller flere av mottakerne ikke ville bli forstyrret" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Denne meldingen ble ikke levert til %s fordi en eller flere av mottakerne støtter ikke denne type melding" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Denne meldingen ble ikke levert til %s fordi en eller flere av mottakerne er frakoblet" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Skjermnavn" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Stillingstittel" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "By" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Fylke" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Kontor" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Land" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Arbeidstelefon" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "E-postadresse" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Alias" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "du" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domene" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Legg til nytt domene" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Legg til" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Avbryt" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Kopier til" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Lås" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Lås opp" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Brukere" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Inviter" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privat" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Logg" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Beskrivelse" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Melding" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Kobler til" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "bruker@firma.no" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Avbryt" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Om SIPE-programtillegget..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Kontaktsøk..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Republiser kalender" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Tilbakestill status" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Tjener[:port]\n(la stå tom for automatisk søk)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Tilkoplingstype" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automatisk" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Brukeragent" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Bruk Single Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "E-posttjeneste URL\n(lå stå tom for automatisk søk)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "E-postadresse\n(hvis ulik fra brukernavn)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "E-post brukernavn\n(hvis ulik brukernavn)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "(E-post passord\n(hvis ulik passord)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Pålogging\n brukernavn eller DOMENE\\brukernavn eller\n brukernavn@firma.no" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Brukernavn" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Navn" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-post" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Fornavn" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Etternavn" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Søk" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Søk etter en kontakt" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Legg inn informasjon om personen du ønsker å finne. Tomme felt vil bli ignorert." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Søk" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Lesefeil" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Tjener har koblet fra" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Kunne ikke koble til" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Skrivefeil" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/nl.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # fieona , 2012 # Stefan Becker , 2011 # Tonnes , 2016-2018 # Yves Jacmans , 2017 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-12-22 12:31+0000\n" "Last-Translator: Tonnes \n" "Language-Team: Dutch (http://www.transifex.com/stefanb/pidgin-sipe/language/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: nl\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Authenticatie bij server mislukt" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Kan geen certificaat aanvragen bij %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Geen URI voor certificaat verstrekkende service opgegeven" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Authenticatie mislukt" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Niet-compatibel authenticatieschema gekozen" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "U bent geweigerd door de server %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "geen reden opgegeven" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Niet gevonden: %s. Neem contact op met uw beheerder" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP is niet ingeschakeld voor de bestemmings-URI, of deze bestaat niet" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Service niet beschikbaar: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Beschadigd bericht ontvangen" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Ongeldige berichthandtekening ontvangen" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s wil een presentatie starten" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Accepteren" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Weigeren" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Fout bij delen van toepassingen" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Onbekende extern-bureaubladclient geconfigureerd." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Kon het delen van toepassingen niet starten" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Kon RDP-server niet maken." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Kon RDP-server niet initialiseren." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Kon RDP-server niet starten." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Bureaublad gedeeld met %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Presenteren stoppen" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Kon delen van toepassingen niet initialiseren" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Hele bureaublad" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Te delen monitor" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Mobiel" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Status" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Agenda" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Vergadering in" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Vergadering over" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Afwezigheidsbericht" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Bericht" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Toegangsniveau" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "%d contact gevonden%s:" msgstr[1] "%d contacten gevonden%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (meer resultaten voor uw query)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Geen contacten gevonden" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Kan de zoekresultaten niet weergeven" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Zoeken naar contact mislukt" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Ongeldige query voor zoeken naar contact" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Leider maken van ‘%s’" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Verwijderen van ‘%s’" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Uitnodigen voor ‘%s’" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Nieuwe chat" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Werk" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Thuis" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Anders" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Aangepast1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "E-mail sturen..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Bureaubladbesturing overnemen" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Bureaubladbesturing overgeven" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Mijn bureaublad delen" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Beschikbaar" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Voorlopig" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Bezet" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Niet aanwezig" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Geen gegevens" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Momenteel %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Buiten werktijden voor de komende 8 uur" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s voor de komende 8 uur" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Niet aan het werk" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s tot %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Buiten werktijden om %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s om %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Certificaataanvraag bij %s mislukt" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Webticketaanvraag bij %s mislukt" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Chat #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Deelnemen aan de vergadering mislukt" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Kan geen vergadering-URI vinden op deze pagina:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "‘%s’ is geen geldige vergadering-URI" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Onvolledige vergaderingsgegevens opgegeven" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nAangezien deze client niet met ondersteuning voor audiogesprekken is gecompileerd, zult u bij accepteren alleen met de andere deelnemers in contact kunnen komen via een IM-sessie." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "wil u uitnodigen voor een telefonische vergadering%s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Deze vergadering is niet meer vergrendeld. Er kunnen nu anderen aan de vergadering deelnemen." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Deze vergadering is vergrendeld. Er kunnen geen anderen aan de vergadering deelnemen zolang deze is vergrendeld." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Uw verbinding met deze vergadering is verbroken." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Inbelgegevens" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Nummer" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "Vergadering-id" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Koppeling naar vergadering" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organisator" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Alternatieve inbelnummers" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Een plug-in van derden voor implementatie van een uitgebreide versie van SIP/SIMPLE, dat door diverse producten wordt gebruikt" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Startpagina" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Ondersteuning" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Helpforum" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Problemen melden" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Bugtracker" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Vertalingen" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licentie" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Help SIPE naar uw eigen taal te vertalen op " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " via een handige webinterface" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Auteurs" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Nederlandse vertaling (nl): Fieona, Tonnes" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "SIP Exchange-gebruikersnaam bevat ongeldige tekens" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Gebruikersnaam moet een geldige SIP-URI zijn\nVoorbeeld: gebruiker@bedrijf.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Wachtwoord is vereist als eenmalige aanmelding niet is ingeschakeld" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "E-mailadres moet geldig zijn wanneer opgegeven\nVoorbeeld: gebruiker@bedrijf.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "SIP Exchange-gebruikersnaam bevat spatie" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "URL van e-mailservices moet geldig zijn wanneer opgegeven\nVoorbeeld: https://exchange.corp.com/EWS/Exchange.asmx\nVoorbeeld: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Locatie:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Lezen van socket mislukt" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Ontvangen encryptiesleutel heeft onjuiste grootte." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Ontvangen hash-sleutel heeft onjuiste grootte." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Kon geen listen-socket maken" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Er is een fout opgetreden" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Fout bij het maken van gegevensstroom" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Schrijven naar socket mislukt" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Bestandsgrootte verschilt van de aangekondigde waarde." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Ontvangen MAC is beschadigd" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Ontvangen bestand is beschadigd" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Initialisatie van bestandsoverdracht mislukt." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Authenticatie voor bestandsoverdracht mislukt." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Onvoldoende geheugen" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Andere contacten" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Proxyinstelling voor groepschat is onjuist:\n\n\t%s\n\nWerk uw account bij." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Kon server voor groepschat niet vinden!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Dit bericht is niet afgeleverd bij chatruimte ‘%s’" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Fout bij het ophalen van ruimtelijst" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Fout bij het bijwonen van chatruimte" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Uitnodigen van %s mislukt" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Bericht met niet-herkende inhoud ontvangen van %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Kon geen stream maken" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Time-out bij verbinding" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Time-out bij aanvraag" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Oproep kon niet worden beantwoord" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Kan geen gesprek tot stand brengen" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Versleutelingsinstellingen van peer zijn niet compatibel met de onze." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "Gebruiker %s heeft oproep geweigerd" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Oproep geweigerd" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Fout bij het maken van audiostream" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Fout bij het maken van videostream" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Deelnemen aan telefonische vergadering" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Telefonische vergaderingen worden niet ondersteund op deze server." #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Ongeldig telefoonnummer" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Audiotestservice is niet beschikbaar." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Gebruiker niet beschikbaar" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s wil niet gestoord worden" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "Gebruiker %s is niet beschikbaar" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Niet-ondersteund mediatype" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Mediafout" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Fout bij het lezen van stream" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "u bent al aangemeld op een andere locatie" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "gebruiker uitgeschakeld" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "gebruiker verplaatst" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Geblokkeerd" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Persoonlijk" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Team" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Bedrijf" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Openbaar" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Onbekend" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Wissen" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Personen in mijn bedrijf" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Personen in domeinen verbonden met mijn bedrijf" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Personen in openbare domeinen" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Personen bij %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Nieuw domein toevoegen..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Onlinehulp..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Toegangsgroepen" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inactief" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Bezig-Inactief" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Ben zo terug" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Lunchpauze" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "In gesprek" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "In conferentie" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "In vergadering" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Alleen urgente onderbrekingen" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Presenteren" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Aanwezigheidsabonnement mislukt!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Een of meer buddy’s verschijnen daarom permanent als offline.\n\nControleer of er geen beschadigde SIP-URI’s in uw contactenlijst staan." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "UCS-initialisatie mislukt!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Kon geen Exchange-server vinden met de standaard e-mailinstellingen. Daardoor zal de contactenlijst niet werken.\n\nU dient e-mailinstellingen op te geven in de accountinstellingen." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Kon geen Exchange-server vinden met de opgegeven e-mailinstellingen in de accountinstellingen. Daardoor zal de contactenlijst niet werken.\n\nCorrigeer uw e-mailinstellingen." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Uw bericht of uitnodiging is niet afgeleverd, omdat het mogelijk een hyperlink of andere inhoud bevat die de systeembeheerder heeft geblokkeerd." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Dit bericht is niet bij %s afgeleverd, omdat de service niet beschikbaar is" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Dit bericht is niet bij %s afgeleverd, omdat een of meer ontvangers niet gestoord willen worden" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Dit bericht is niet bij %s afgeleverd, omdat een of meer ontvangers dit type bericht niet ondersteunen" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Dit bericht is niet bij %s afgeleverd, omdat een of meer ontvangers offline zijn" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Weergavenaam" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Functie" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Plaats" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Provincie" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Kantoor" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Land" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Telefoon op werk" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "E-mailadres" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Website" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Alias" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Apparaat" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "u" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domein" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Nieuw domein toevoegen" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Toevoegen" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Annuleren" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Kopiëren naar" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Vergrendelen" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Ontgrendelen" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Presentatie tonen" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Toegangsgegevens voor de vergadering" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Gebruikers" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Uitnodigen" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privé" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Logboek" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Beschrijving" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Gespreksonderwerp: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Bericht" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Verbinding maken" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Wachtwoord vereist" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "gebruiker@bedrijf.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Telefoonnummer" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Een telefoonnummer bellen" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Bellen" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Annuleren" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Vergaderlocatie" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Alternatief" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "E-mailadres van organisator" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "Vergadering-id" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Deelnemen aan vergadering" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Deelnemen aan geplande vergadering" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Voer de locatie van de vergadering in die u in de uitnodiging hebt ontvangen.\n\nEen geldige locatie ziet eruit als\nmeet:sip:iemand@bedrijf.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:iemand@bedrijf.com;gruu;opaque=app:conf:focus:id:abcdef1234\nof\nhttps://meet.bedrijf.com/iemand/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Deelnemen" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Het publiceren van agendagegevens is uitgeschakeld" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Over SIPE-plug-in..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Contacten zoeken..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Een telefoonnummer bellen..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Testgesprek" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Deelnemen aan geplande vergadering..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Agenda opnieuw publiceren" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Status opnieuw instellen" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Server[:Poort]\n(laat leeg voor automatische herkenning)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Verbindingstype" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automatisch" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Gebruikersagent" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Authenticatieschema" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Eenmalige aanmelding gebruiken" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Mijn agendagegevens niet publiceren" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Profielafbeeldingen van web tonen\n(mogelijk gevaarlijk)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "URL van e-mailservices\n(laat leeg voor automatische detectie)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "E-mailadres\n(wanneer anders dan Gebruikersnaam)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "E-mailaanmelding\n(wanneer anders dan Aanmelding)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "E-mailwachtwoord\n(wanneer anders dan Wachtwoord)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Proxy voor groepschat\nbedrijf.com of gebruiker@bedrijf.com\n(laat leeg voor bepaling uit Gebruikersnaam)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Extern-bureaubladclient" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Mediaversleuteling" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Serverbeleid volgen" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Altijd" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Optioneel" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Uitgeschakeld" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Aanmelding\ngebruiker of DOMEIN\\gebruiker of\ngebruiker@bedrijf.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Gebruikersnaam" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Naam" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-mail" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Voornaam" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Achternaam" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP-ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Zoeken" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Contactpersoon zoeken" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Vul de gegevens in voor de persoon die u probeert te vinden. Lege velden worden genegeerd." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Zoeken" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Leesfout" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Server heeft verbinding verbroken" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Kon geen verbinding maken" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Kon geen SSL-context maken" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Kon geen socket maken" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Schrijffout" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "OK" ================================================ FILE: po/pidgin-sipe.pot ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: English (http://www.transifex.com/stefanb/pidgin-sipe/language/en/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: en\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Failed to authenticate to server" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Can't request certificate from %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "No URI for certificate provisioning service provided" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Authentication failed" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Incompatible authentication scheme chosen" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "You have been rejected by the server: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "no reason given" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Not found: %s. Please contact your Administrator" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP is either not enabled for the destination URI or it does not exist" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Service unavailable: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Corrupted message received" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Invalid message signature received" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s wants to start presenting" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Accept" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Decline" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Application sharing error" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Unknown remote desktop client configured." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Couldn't connect application sharing" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Could not create RDP server." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Could not initialize RDP server." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Could not start RDP server." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Sharing desktop with %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Stop presenting" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Couldn't initialize application sharing" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Whole desktop" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Monitor to share" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Mobile" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Status" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Calendar" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Meeting in" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Meeting about" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Out of office note" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Note" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Access level" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Found %d contact%s:" msgstr[1] "Found %d contacts%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (more matched your query)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "No contacts found" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Unable to display the search results" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Contact search failed" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Invalid contact search query" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Make leader of '%s'" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Remove from '%s'" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Invite to '%s'" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "New chat" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Work" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Home" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Other" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Custom1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Send email..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Take desktop control" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Give desktop control" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Share my desktop" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Free" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Tentative" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Busy" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Out of office" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "No data" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Currently %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Outside of working hours for next 8 hours" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s for next 8 hours" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Not working" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s until %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Outside of working hours at %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s at %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Certificate request to %s failed" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Web ticket request to %s failed" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Chat #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Failed to join the conference" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Can't find a conference URI on this page:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" is not a valid conference URI" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Incomplete conference information provided" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nAs this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "wants to invite you to a conference call%s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "This conference is no longer locked. Additional participants can now join." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "This conference is locked. Nobody else can join the conference while it is locked." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "You have been disconnected from this conference." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Dial-in info" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Number" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "Conference ID" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Meeting link" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organizer" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Alternative dial-in numbers" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "A third-party plugin implementing extended version of SIP/SIMPLE used by various products" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Home Page" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Support" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Help Forum" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Report Problems" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Bug Tracker" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Translations" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "License" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Please help us to translate SIPE to your native language here at " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " using convenient web interface" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Authors" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Original texts in English (en): SIPE developers" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "SIP Exchange user name contains invalid characters" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "User name should be a valid SIP URI\nExample: user@company.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Password is required when Single Sign-On is not enabled" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "Email address should be valid if provided\nExample: user@company.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "SIP Exchange user name contains whitespace" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "Email services URL should be valid if provided\nExample: https://exchange.corp.com/EWS/Exchange.asmx\nExample: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Location:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Socket read failed" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Received encryption key has wrong size." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Received hash key has wrong size." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Could not create listen socket" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Error occurred" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Error creating data stream" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Socket write failed" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "File size is different from the advertised value." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Received MAC is corrupted" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Received file is corrupted" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "File transfer initialization failed." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "File transfer authentication failed." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Out of memory" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Other Contacts" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Group Chat Proxy setting is incorrect:\n\n\t%s\n\nPlease update your Account." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Couldn't find Group Chat server!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "This message was not delivered to chat room '%s'" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Error retrieving room list" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Error joining chat room" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Failed to invite %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Received a message with unrecognized contents from %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Couldn't create stream" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Connection timed out" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Request timed out" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Call could not be answered" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Unable to establish a call" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Encryption settings of peer are incompatible with ours." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "User %s rejected call" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Call rejected" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Error creating audio stream" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Error creating video stream" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Join conference call" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Conference calls are not supported on this server." #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Invalid phone number" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Audio Test Service is not available." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "User unavailable" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s does not want to be disturbed" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "User %s is not available" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Unsupported media type" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Media error" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Error while reading from stream" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "you are already signed in at another location" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "user disabled" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "user moved" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Blocked" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Personal" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Team" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Company" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Public" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Unknown" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Unspecify" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "People in my company" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "People in domains connected with my company" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "People in public domains" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "People at %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Add new domain..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Online help..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Access groups" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inactive" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Busy-Idle" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Be right back" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Out to lunch" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "In a call" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "In a conference" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "In a meeting" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Urgent interruptions only" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Presenting" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Presence subscription failed!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "One or more buddies will therefore permanently show as offline.\n\nPlease check that there are no corrupted SIP URIs in your contacts list." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "UCS initialization failed!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n\nYou'll need to provide Email settings in the account setup." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n\nPlease correct your Email settings." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Your message or invitation was not delivered, possibly because it contains a hyperlink or other content that the system administrator has blocked." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "This message was not delivered to %s because the service is not available" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "This message was not delivered to %s because one or more recipients do not want to be disturbed" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "This message was not delivered to %s because one or more recipients don't support this type of message" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "This message was not delivered to %s because one or more recipients are offline" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Display name" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Job title" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "City" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "State" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Office" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Country" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Business phone" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "Email address" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Site" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Alias" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Device" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "you" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domain" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Add new domain" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Add" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Cancel" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Copy to" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Lock" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Unlock" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Show presentation" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Meeting entry info" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Users" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Invite" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Private" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Log" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Description" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Conversation subject: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Message" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Connecting" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Password required" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "user@company.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Phone number" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Call a phone number" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Call" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Cancel" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Meeting location" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Alternatively" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "Organizer email" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "Meeting ID" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Join conference" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Join scheduled conference" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Enter meeting location string you received in the invitation.\n\nValid location will be something like\nmeet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nor\nhttps://meet.company.com/someone/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Join" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Publishing of calendar information has been disabled" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "About SIPE plugin..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Contact search..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Call a phone number..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Test call" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Join scheduled conference..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Republish Calendar" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Reset status" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Server[:Port]\n(leave empty for auto-discovery)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Connection type" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Auto" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "User Agent" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Authentication scheme" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Use Single Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Don't publish my calendar information" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Show profile pictures from web\n(potentially dangerous)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "Email services URL\n(leave empty for auto-discovery)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "Email address\n(if different from Username)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "Email login\n(if different from Login)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Email password\n(if different from Password)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Group Chat Proxy\n company.com or user@company.com\n(leave empty to determine from Username)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Remote desktop client" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Media encryption" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Obey server policy" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Always" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Optional" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Disabled" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Login\n user or DOMAIN\\user or\n user@company.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "User name" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Name" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "Email" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "First name" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Last name" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Search" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Search for a contact" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Enter the information for the person you wish to find. Empty fields will be ignored." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Search" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Read error" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Server has disconnected" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Could not connect" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Could not create SSL context" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Could not create socket" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Write error" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "OK" ================================================ FILE: po/pl.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Piotr Drąg , 2011-2013,2015-2018 # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 18:22+0000\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish (http://www.transifex.com/stefanb/pidgin-sipe/language/pl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pl\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Uwierzytelnienie do serwera się nie powiodło" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Nie można zażądać certyfikatu od %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Nie podano adresu URI dla usługi zabezpieczania certyfikatów" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Uwierzytelnienie się nie powiodło" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Wybrano niezgodny schemat uwierzytelnienia" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Użytkownik został odrzucony przez serwer: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "nie podano przyczyny" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Nie odnaleziono: %s. Proszę skontaktować się z administratorem" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP nie jest włączony dla docelowego adresu URI lub nie istnieje" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Usługa jest niedostępna: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Odebrano uszkodzoną wiadomość" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Odebrano nieprawidłowy podpis wiadomości" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "Użytkownik %s chce zacząć prezentację" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Odbierz" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Odmów" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Błąd podczas udostępniania aplikacji" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Skonfigurowano nieznanego zdalnego klienta." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Nie można połączyć udostępniania aplikacji" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Nie można utworzyć serwera RDP." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Nie można zainicjować serwera RDP." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Nie można uruchomić serwera RDP." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Udostępnianie pulpitu użytkownikowi %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Zatrzymaj prezentację" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Nie można zainicjować udostępniania aplikacji" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Cały pulpit" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Udostępniany monitor" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Komórka" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Stan" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Kalendarz" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Spotkanie za" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Spotkanie w" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Notatka poza biurem" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Notatka" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Poziom dostępu" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Odnaleziono %d kontakt%s:" msgstr[1] "Odnaleziono %d kontakty%s:" msgstr[2] "Odnaleziono %d kontaktów%s:" msgstr[3] "Odnaleziono %d kontaktów%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (więcej wyników zapytania)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Nie odnaleziono kontaktów" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Nie można wyświetlić wyników wyszukiwania" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Wyszukanie kontaktu się nie powiodło" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Nieprawidłowe zapytanie wyszukiwania kontaktu" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Uczyń przywódcą „%s”" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Usuń z „%s”" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Zaproś do „%s”" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Nowa rozmowa" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Praca" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Strona domowa" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Inne" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Własne1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Wyślij wiadomość e-mail…" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Przejmij sterowanie pulpitem" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Oddaj sterowanie pulpitem" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Udostępnij pulpit" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Wolne" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Wstępnie" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Zajęty" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Poza biurem" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Brak danych" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Obecnie %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Poza godzinami pracy przez następne 8 godzin" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s przez następne 8 godzin" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Nie pracuje" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s od %.2d∶%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Poza godzinami pracy o %.2d∶%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s o %.2d∶%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Żądanie certyfikatu do %s się nie powiodło" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Żądanie zgłoszenia WWW do %s się nie powiodło" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Rozmowa #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Dołączenie do konferencji się nie powiodło" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Nie można odnaleźć adresu URI konferencji na tej stronie:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "„%s” nie jest prawidłowym adresem URI konferencji" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Podano niepełne informacje o konferencji" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nPonieważ klient nie został skompilowany z obsługą rozmów głosowych, po odebraniu będzie można kontaktować się z innymi uczestnikami tylko za pomocą komunikatora." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "chce zaprosić użytkownika do pokoju konferencji%s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Ta konferencja nie jest już zablokowana. Dodatkowi uczestnicy mogą teraz dołączyć." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Ta konferencja jest zablokowana. Nikt więcej nie może teraz dołączyć." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Rozłączono z tej konferencji." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Informacje o wdzwanianiu" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Numer" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "Identyfikator konferencji" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Odnośnik do spotkania" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organizator" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Alternatywne numery wdzwaniania" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Wtyczka firmy trzeciej implementująca rozszerzoną wersję protokołu SIP/SIMPLE używaną przez różne produkty" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Strona domowa" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Wsparcie" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Forum pomocy" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Zgłaszanie problemów" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "System śledzenia błędów" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Tłumaczenia" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licencja" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Prosimy o pomoc w przetłumaczeniu wtyczki SIPE na inne języki pod adresem " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " za pomocą wygodnego interfejsu WWW" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Autorzy" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Tłumaczenie na język polski (pl): Piotr Drąg" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "Nazwa użytkownika SIP Exchange zawiera nieprawidłowe znaki" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Nazwa użytkownika powinna być prawidłowym adresem URI protokołu SIP\nPrzykład: użytkownik@example.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Wymagane jest hasło, kiedy logowanie pojedyncze nie jest włączone" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "Jeśli podano adres e-mail, powinien on być prawidłowy\nPrzykład: użytkownik@example.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "Nazwa użytkownika SIP Exchange zawiera spację" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "Adres e-mail serwisów powinien być prawidłowy, jeśli został podany\nPrzykład: https://exchange.corp.com/EWS/Exchange.asmx\nPrzykład: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Położenie:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Odczyt z gniazda się nie powiódł" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Pobrany klucz szyfrowania ma błędny rozmiar." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Pobrany klucz mieszania ma błędny rozmiar." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Nie można utworzyć gniazda nasłuchiwania" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Wystąpił błąd" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Błąd podczas tworzenia strumienia danych" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Zapis do gniazda się nie powiódł" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Rozmiar pliku jest różny od ogłoszonej wartości." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Otrzymany adres MAC jest uszkodzony" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Pobrany plik jest uszkodzony" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Zainicjowanie przesyłania pliku się nie powiodło." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Uwierzytelnienie przesyłania pliku się nie powiodło." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Brak pamięci" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Inne kontakty" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Ustawienie pośrednika rozmów grupowych jest niepoprawne:\n\n\t%s\n\nProszę zaktualizować konto." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Nie można odnaleźć serwera rozmów grupowych." #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Ta wiadomość nie została dostarczona do pokoju rozmów „%s”" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Błąd podczas pobierania listy pokoi" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Błąd podczas dołączania do pokoju rozmów" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Zaproszenie %s się nie powiodło" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Odebrano wiadomość z nierozpoznaną zawartością od %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Nie można utworzyć strumienia" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Połączenie przekroczyło czas oczekiwania" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Żądanie przekroczyło czas oczekiwania" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Nie można odebrać rozmowy" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Nie można nawiązać rozmowy" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Ustawienia szyfrowania partnera są niezgodne z lokalnymi." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "Użytkownik %s odrzucił rozmowę" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Odrzucono rozmowę" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Błąd podczas tworzenia strumienia dźwięku" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Błąd podczas tworzenia strumienia obrazu" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Dołącz do konferencji" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Ten serwer nie obsługuje konferencji" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Nieprawidłowy numer telefonu" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Usługa testowania dźwięku jest niedostępna." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Użytkownik jest niedostępny" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "Użytkownik %s nie chce, aby mu przeszkadzać" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "Użytkownik %s jest niedostępny" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Nieobsługiwany typ multimediów" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Błąd multimediów" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Błąd podczas odczytywania ze strumienia" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "zalogowano się z innego położenia" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "użytkownik został wyłączony" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "użytkownik został przeniesiony" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Zablokowane" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Osobiste" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Zespół" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Firma" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Publiczne" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Nieznane" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Nieokreślony" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Osoby z firmy" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Osoby w domenach połączonych z firmą" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Osoby w domenach publicznych" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Osoby w %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Dodaj nową domenę…" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Pomoc online…" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Grupy dostępu" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Nieaktywny" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Bezczynny" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Zaraz wracam" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Na obiedzie" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Przy telefonie" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "Na konferencji" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "Na spotkaniu" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Przeszkadzać tylko w pilnych przypadkach" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Trwa prezentacja" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Subskrypcja obecności się nie powiodła." #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Jeden lub więcej znajomych będzie wyświetlanych jako w trybie offline.\n\nProszę sprawdzić, czy na liście kontaktów nie ma uszkodzonych adresów URI protokołu SIP." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "Zainicjowanie UCS się nie powiodło." #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Nie można odnaleźć serwera Exchange za pomocą domyślnych ustawień poczty. z tego powodu lista kontaktów nie będzie działała.\n\nNależy podać ustawienia poczty w ustawieniach konta." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Nie można odnaleźć serwera Exchange za pomocą ustawień poczty podanych w ustawieniach konta. Z tego powodu lista kontaktów nie będzie działała.\n\nNależy poprawić ustawienia poczty." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Wiadomość lub zaproszenie nie zostało dostarczone, prawdopodobnie dlatego, że zawiera odnośnik lub inną zawartość zablokowaną przez administratora systemu." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Nie dostarczono wiadomości do %s, ponieważ usługa jest niedostępna" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Nie dostarczono wiadomości do %s, ponieważ jeden lub więcej odbiorców nie chce, aby im przeszkadzano" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Nie dostarczono wiadomości do %s, ponieważ jeden lub więcej odbiorców nie obsługuje tego typu wiadomości" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Nie dostarczono wiadomości do %s, ponieważ jeden lub więcej odbiorców jest trybie offline" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Wyświetlana nazwa" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Stanowisko" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Miasto" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Stan/województwo" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Biuro" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Kraj" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Telefon służbowy" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "Adres e-mail" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Witryna" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Alias" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Urządzenie" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "ty" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domena" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Dodaj nową domenę" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Dodaj" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Anuluj" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Skopiuj do" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Zablokuj" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Odblokuj" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Wyświetl prezentację" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Informacje o spotkaniu" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "Adres _URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Użytkownicy" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Zaproś" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Prywatny" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Dziennik" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Opis" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Temat rozmowy: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Wiadomość" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Łączenie" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Wymagane jest hasło" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "użytkownik@example.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Numer telefonu" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Zadzwoń na numer telefonu" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Zadzwoń" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Anuluj" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Położenie spotkania" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Alternatywnie" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "Adres e-mail organizatora" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "Identyfikator spotkania" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Dołączenie do konferencji" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Dołączenie do zaplanowanej konferencji" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Proszę podać ciąg położenia spotkania, który otrzymano w zaproszeniu.\n\nPrawidłowe położenie wygląda podobnie do\nmeet:sip:ktoś@example.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:ktoś@example.com;gruu;opaque=app:conf:focus:id:abcdef1234\nlub\nhttps://meet.example.com/ktoś/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Dołącz" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Publikowanie informacji kalendarza zostało wyłączone" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "O wtyczce SIPE…" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Wyszukaj kontakt…" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Zadzwoń na numer telefonu…" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Rozmowa testowa" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Dołącz do zaplanowanej konferencji…" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Ponownie opublikuj kalendarz" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Przywróć stan" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Serwer[:port]\n(należy pozostawić puste, aby wykryć automatycznie)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Typ połączenia" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automatycznie" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Agent użytkownika" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Schemat uwierzytelniania" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Logowanie pojedyncze" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Bez publikowania informacji kalendarza" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Wyświetlanie obrazów profilu z Internetu\n(potencjalnie niebezpieczne)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "Adres URL usług e-mail\n(należy pozostawić puste, aby wykryć automatycznie)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "Adres e-mail\n(jeśli różni się od nazwy użytkownika)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "Login adresu e-mail\n(jeśli różni się od loginu)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Hasło adresu e-mail\n(jeśli różni się od hasła)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Pośrednik rozmów grupowych\n example.com lub użytkownik@example.com\n(należy pozostawić puste, aby ustalić z nazwy użytkownika)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Zdalny klient" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Szyfrowanie multimediów" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Przestrzeganie polityki serwera" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Zawsze" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Opcjonalnie" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Wyłączone" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Login\n użytkownik lub DOMENA\\użytkownik lub\n użytkownik@example.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Nazwa użytkownika" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Nazwa" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "Adres e-mail" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Imię" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Nazwisko" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "Identyfikator SIP" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Wyszukiwanie" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Wyszukiwanie kontaktu" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Proszę podać informacje o wyszukiwanej osobie. Puste pola zostaną zignorowane." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "Wy_szukaj" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Błąd odczytu" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Serwer został rozłączony" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Nie można połączyć" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Nie można utworzyć kontekstu SSL" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Nie można utworzyć gniazda" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Błąd zapisu" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "OK" ================================================ FILE: po/pt.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Anibal Avelar , 2012 # Bruno Martins , 2012 # Bruno Queiros , 2014-2015,2017 # Filipe Boleto , 2011,2015-2016,2019 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2019-08-13 18:42+0000\n" "Last-Translator: Filipe Boleto \n" "Language-Team: Portuguese (http://www.transifex.com/stefanb/pidgin-sipe/language/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Falha ao autenticar no servidor" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Não é possível solicitar o certificado de %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Nenhum URI fornecido para o serviço de provisionamento" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Autenticação falhou" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "O método de autenticação escolhido é incompatível" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Você foi rejeitado pelo servidor: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "nenhuma razão apresentada" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Não encontrado: %s. Por favor contacte o seu Administrador " #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "Ou o SIP não está habilitado no URI de destino, ou não existe" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Serviço indisponível: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Mensagem corrompida recebida" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Recebida uma assinatura de mensagem inválida" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s deseja iniciar a apresentação" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Aceitar" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Negar" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Erro na partilha de aplicação" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Cliente de desktop remoto desconhecido configurado." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Não foi possível ligar à partilha de aplicação" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Não foi possível criar servidor de RDP " #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Não foi possível iniciar o servidor de RDP" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Não foi possível começar o servidor de RDP" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "A partilhar o ambiente de trabalho com %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Parar a apresentação" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Não foi possível iniciar a partilha de aplicação " #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Todo o ambiente de trabalho" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Monitor a partilhar" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Móvel" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Estado" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Calendário" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Reunião em" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Reunião sobre" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Nota de fora do escritório" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Notas" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Nivel de acesso " #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Encontrados %d contactos%s:" msgstr[1] "Encontrados %d contactos%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr "(mais correspondente à sua consulta)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Nenhum contacto encontrado" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Impossivel de apresentar resultados da pesquisa " #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Pesquisa por contato falhou" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Comando de procura de contacto inválido" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Tornar líder de '%s'" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Remover de '%s'" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Convidar para '%s'" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Novo chat" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Trabalhar" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Home" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Outro" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Custom1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Enviar email..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Tomar controle do ambiente de trabalho" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Dar controle do ambiente de trabalho" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Partilhar o meu ambiente de trabalho" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Livre" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Tentativa" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Ocupado" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Fora do escritório" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Sem data" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Atualmente %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Fora das horas de trabalho nas próximas 8 horas" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s para as próximas 8 horas" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Não funciona" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s até %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Fora das horas de trabalho de %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s at %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Pedido de certificado para %s falhou" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Solicitação da Web bilhete para %s falhou" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Chat #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Falhou a entrada na conferência" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Não foi possível encontrar o URI da conferência nesta página\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" não é um URI de conferência válido" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Informação de conferência incompleta " #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nComo este cliente não foi compilado com suporte a chamadas de voz, se você aceitar, você poderá entrar em contato com os outros participantes da sessão somente via IM." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "deseja convidá-lo para uma chamada de conferência%s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Esta conferência não é mais protegida. Participantes adicionais podem agora juntar-se." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Esta conferência está bloqueada. Ninguém mais pode participar na conferência, enquanto esta estiver bloqueada." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Você foi desconectado desta conferência." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Informação de número Dial-in" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Número" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "ID da conferência " #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Link da reunião" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organizador" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Números alternativos de dial-in" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Um plugin de terceiros implementa versão estendida do SIP / SIMPLE usado por vários produtos" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Homepage" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Apoiar" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Forum de Ajuda" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Reportar problemas" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Bug Tracker" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Traduções" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licença" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Por favor, ajude-nos a traduzir SIPE à sua língua nativa aqui no" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "utilizar web interface conviniente " #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Autores" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Textos originais em Inglês (en): desenvolvedores SIPE" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "Utilizador de SIP Exchange contém caracteres inválidos" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Nome do utilizador deve ser um URI SIP\nExemplo: user@company.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Palavra passe é necessária quando o Single Sign-on não está activo" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "Endereço de e-mail deve ser válido se especificado\nExemplo: user@company.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "SIP Exchange contém espaços em branco" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "URL serviços de e-mail deve ser válido se especificados\nExample: https://exchange.corp.com/EWS/Exchange.asmx\nExample: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Localização:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Leitura de Socket falhou" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Chave de criptografia recebida tem tamanho errado." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Chave recebida de hash tem tamanho errado." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Não foi possível criar socket de escuta" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Ocorreu um erro" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Erro ao criar o fluxo de dados" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Escrita de Socket falhou" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Tamanho do arquivo é diferente do valor anunciado." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Endereço de MAC recebido está corrompido" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Arquivo recebido está corrompido" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Transferência de arquivos de inicialização falhou." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Autenticação de transferência de arquivo falhou." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Sem memória" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Outros Contactos" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Definições de Chat Proxy de grupo estão incorrectas:\n\n»%s\n\nPor favor actualize a sua conta." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Não foi possível localizar o servidor de bate-papo do grupo!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Esta mensagem não foi entregue ao chat room '%s'" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Erro lista sala de recuperação" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Erro juntando sala de chat" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Falhou o convite %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Recebeu uma mensagem com conteúdo não reconhecido de %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Não foi possível criar stream" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "conexão esgotada" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Solicitação expirada" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Não foi possível atender a chamada" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Não é possível estabelecer a chamada" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Configurações de criptografia de pares são incompatíveis com o nossa." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "Utilizador %s rejeitou a chamada" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Chamada rejeitada" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Erro ao criar fluxo de áudio" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Erro ao criar fluxo de vídeo" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Entrar em chamada de conferência" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Chamadas de conferência não são suportadas neste servidor" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Número de telefone inválido" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Serviço de Testes de Áudio não está disponível." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Usuário não está disponível" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s não quer ser incomodado" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "%s usuário não está disponível" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Tipo de média não suportado" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Erro de Media" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Erro durante a leitura do fluxo" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "Já está ligado noutro local" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "Utilizador desabilitado" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "Utilizador movido" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Bloqueado" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Pessoal" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Equipa" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Empresa" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Público" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Desconhecido" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Unspecify" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Pessoas na minha empresa" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Pessoas em domínios conectados com a minha empresa" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Pessoas em dominios publicos " #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Pessoas em %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Adicionar novo domínio... " #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Ajuda Online..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Grupos de acesso " #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inativo" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Ocupado-Ocioso" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Volto já" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Ausente para almoço" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Numa chamada" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "Numa conferência" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "Numa reunião" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Apenas interrupções urgentes" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Apresentando" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Subscrição de presença falhou!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Um ou mais amigos irão por isso aparecer como offline permanentemente..\n\nPor favor verifique que não existem SIP URIs corrompidos na sua lista de contactos." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "Inicialização UCS falhou!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Não foi possível encontrar um servidor Exchange com as configurações Email fornecidas na configuração de conta. Como tal a lista de contactos não funcionará.\n\nNecessita de fornecer as configurações Email na configuração de conta." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Não foi possível encontrar um servidor Exchange com as configurações Email fornecidas na configuração de conta. Como tal a lista de contactos não funcionará.\n\nPor favor corriga as configurações Email." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "A sua mensagem ou convite não foram entregues, possivelmente porque o contém um link ou outro conteúdo bloqueado pelo administrador" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Esta mensagem não foi entregue a %s porque o serviço não está disponível." #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Esta mensagem não foi entregue a %s porque um ou mais recipientes não quer ser distribuído " #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Esta mensagem não foi entregue a %s porque um ou mais recipientes não suporta este tipo de mensagem" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Esta mensagem não foi entregue a %s um ou mais recipientes porque estão offline" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Nome de exibição" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Titulo profissional" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Cidade" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Estado" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Escritorio " #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "País" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Telefone profissional" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "Endereço de email" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Site" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "pseudônimo" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Dispositivo" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "tu" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Dominio " #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Adicionar Dominio " #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Adicionar" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Cancelar" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Copiar para" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Bloquear" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Desbloquear" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Mostrar apresentação" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Reunião Informação de entrada" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Utilizadores" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Convidar" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privado" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Log" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Descrição" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Assunto da conversa: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Mensagem" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "A ligar" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Palavra-chave necessária" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "user@company.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Número de telefone" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Chamar um número de telefone" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Chamar" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Cancelar" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Local da reunião" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Alternativamente" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "Email do organizador" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "ID da reunião" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Entrar na conferência" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Entrar na conferência agendada" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Introduza a frase da localização da reunião que recebeu no convite.\n\nUma localização válida será algo como\nmeet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nou\nhttps://meet.company.com/someone/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Entrar" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "A publicação da informação do calendário foi desativada" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Sobre plugin SIPE..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Pesquisar contacto..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Chamar um número de telefone..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Chamada de teste" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Junte-se conferência agendada ..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Re-publicar calendario " #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Redefinir o estado" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Servidor [: porta]\n (deixe em branco para auto-descoberta)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Tipo de conexão" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automático" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "User Agent" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Esquema de autenticação" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Utilizar Single Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Não publicar a minha informação de calendário" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Mostrar imagem de perfil da web\n(potencialmente perigoso)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "URL serviços de e-mail \n(deixe em branco para auto-descoberta)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "Endereço de email\n (se for diferente do nome do utilizador)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "Login E-mail\n (se diferente do Login)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Password de e-mail\n(se diferente da Password)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Proxy de chat de grupo \n company.com ou user@company.com \n(deixe em branco para determinar a partir de nome de utilizador)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Cliente de desktop remoto" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Encriptação do média" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Obedecer à política do servidor" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Sempre" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Opcional" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Desactivado" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Login\n utilizador ou DOMINIO\\utilizador ou\n user@company.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Nome do utilizador" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Nome" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "Email" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Primeiro nome" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Sobrenome" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "ID de SIP" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Pesquisar" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Procurar por um contacto" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Digite informações sobre a pessoa que você deseja encontrar. Campos vazios serão ignorados." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Procurar" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Erro de leitura" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Servidor foi desligado" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Não foi possível conectar" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Não foi possível criar contexto SSL" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Não foi possível criar socket" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Erro de escrita" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "OK" ================================================ FILE: po/pt_BR.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Anibal Avelar , 2011-2012 # M3741 , 2018 # M3741 , 2018 # M3741 , 2018 # Raul Liota da Rosa , 2013,2017 # Raul Liota da Rosa , 2013 # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-11-28 18:41+0000\n" "Last-Translator: M3741 \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/stefanb/pidgin-sipe/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "A autenticação com o servidor falhou" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Não é possível solicitar o certificado de %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Nenhuma URI para o serviço de provisionamento certificado provida" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Falha de autenticação" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Um esquema de autenticação incompatível foi escolhido" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Você foi rejeitado pelo servidor: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "nenhum motivo especificado" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Não encontrado: %s. Por favor, entre em contato com seu Administrador de Rede." #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP não está habilitado para a URI de destino ou a mesma não existe" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Serviço indisponível: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Mensagem corrompida recebida" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Assinatura da mensagem inválida recebida" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s quer iniciar uma apresentação" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Aceitar" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Recusar" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Erro de compartilhamento na aplicação" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Cliente de área de trabalho remota desconhecido." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Aplicação de compartilhamento não consegue conectar" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Não foi possível criar servidor RDP." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Não foi possível inicializar servidor RDP." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Não foi possível iniciar servidor RDP." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Compartilhando a área de trabalho com %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Finalizar a apresentação." #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Não foi possível inicar o compartilhamento de aplicações" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Toda a área de trabalho" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Monitor a ser compartilhado" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Celular" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Status" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Calendário" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Reunião em" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Reunião sobre" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Mensagem de ausência temporária" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Nota" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "O nível de acesso" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "%d contato%s encontrado:" msgstr[1] "%d contatos%s encontrados:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (mais correspondências à sua consulta)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Nenhum contato encontrado" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Não foi possível exibir os resultados da busca" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Busca de contato falhou" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Consulta inválida de pesquisa de contato" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Tornar líder de '%s'" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Remover de '%s'" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Convidar a '%s'" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Nova conversa" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Trabalhar" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Início" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Outro" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Custom1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Enviar email..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Controlar a área de trabalho" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Conceder controle da área de trabalho" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Compartilhar minha área de trabalho" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Gratuito" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Tentativa" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Ocupado" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Fora do escritório" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Sem dados" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Atualmente %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Fora do horário de trabalho pelas próximas 8 horas" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s pelas próximas 8 horas" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Não trabalhando" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s até %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Fora do horário de trabalho às %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s às %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Pedido de certificado para %s falhou" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Solicitação da Web ticket para %s falhou" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Conversa #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Falha ao participar da conferência" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Não foi possível encontrar uma URI de conferência nesta página:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" não é uma URI de conferência válida" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Informação fornecida da conferência incompleta" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nComo esse cliente não foi compilado com suporte a chamadas de voz, se você aceitar, você poderá entrar em contato com os outros participantes da sessão somente via IM." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "quer convidá-lo para uma teleconferência %s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Esta conferência não está mais bloqueada. Demais participantes podem se conectar agora." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Esta conferência está bloqueada. Nenhum novo participante pode se conectar enquanto a mesma se encontrar bloqueada." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Você foi desconectado desta conferência." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Info de discagem" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Número" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "ID da conferência" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Link da reunião" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organizador" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Números de discagem alternativos" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Um plugin desenvolvido por terceiros que implementa uma versão do protocolo SIP/SIMPLE usado por vários produtos" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Homepage" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Apoio" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Fórum de ajuda" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Reportar problemas" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Acompanhamento de bugs" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Traduções" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licença" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Por favor nos ajude a traduzir o SIPE para seu idioma nativo em " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "usando uma conveniente interface web" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Autores" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Textos originais em Português (pt_BR): ?" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "O nome de usuário SIP Exchange contém caracteres inválidos" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "O nome de usuário deve ser uma URI SIP válida\nExemplo: usuario@dominio.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "A senha é necessária quando o Single Sign-On está habilitado" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "O endereço de email deve ser válido, se fornecido\nExemplo: usuario@dominio.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "O nome de usuario SIP Exchange contém espaços em branco" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "Quando fornecida, a URL de serviços de email deve ser válida\nExemplo: https://exchange.corp.com/EWS/Exchange.asmx\nExemplo: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Localização:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Leitura do socket falhou" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "A chave de criptografia recebida possui um tamanho incorreto." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "A chave de hash recebida possui um tamanho incorreto." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Não foi possível criar socket de escuta" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Ocorreu um erro" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Erro ao criar fluxo de dados" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Escrita no socket falhou" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "O tamanho do arquivo é diferente do valor anunciado." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "O MAC recebido está corrompido" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "O arquivo recebido está corrompido" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "A inicialização da transferência de arquivo falhou." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "A autenticação da transferência de arquivos falhou." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Esgotamento de memória" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Outros contatos" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "As configurações de proxy para conversas em grupo está incorreta:\n\n»%s\n\nPor favor, atualize sua conta." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Não foi possível encontrar o servidor de conversas em grupo!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Esta mensagem não foi enviada a %s porque o serviço se encontra indisponível" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Falha em obter a lista de salas" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Erro ao ingressar na conversa" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "O convite a %s falhou" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Uma mensagem com conteúdo desconhecido for recebida de %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Não foi possível criar transmissão" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Tempo de conexão esgotado" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Tempo de requisição esgotado" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "A ligação não pôde ser atendida" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Não foi possível realizar uma ligação" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "As configurações de criptografia dos pares são incompatíveis com as nossas." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "O usuário %s rejeitou a ligação" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Ligação rejeitada" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Erro ao criar fluxo de áudio" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Erro ao criar fluxo de vídeo" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Participar de conferência" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Teleconferências não são suportadas nesse servidor." #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Número de telefone inválido" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "O serviço de Teste de Áudio está indisponível." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Usuário indisponível" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s não quer ser incomodado" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "O usuário %s está indisponível" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Tipo de mídia não suportada" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Erro na mídia" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Erro durante a leitura do fluxo" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "você já se encontra conectado em outro lugar" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "usuário desabilitado" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "usuário movido" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Bloqueados" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Pessoal" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Equipe" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Empresa" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Público" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Desconhecido" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Indeterminado" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "As pessoas na minha empresa" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Pessoas em domínios conectados a minha empresa" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Pessoas em domínios públicos" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Pessoas em %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Adicionar novo domínio..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Ajuda Online" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Grupos de acesso" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inativo" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Ocupado-inativo" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Volto já" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Saí para almoçar" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Em uma ligação" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "Em uma conferência" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "Em uma reunião" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Apenas interrupções urgentes" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Apresentando" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Falha na inscrição de assinatura!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Um ou mais contatos estão permanentemente offline.\n\nVerifique se não há URIs de SIP corrompidas na sua lista de contatos." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "Inicialização UCS falhou!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Não foi possível encontrar um servidor Exchange com as configurações de E-mail padrão. Portanto, a lista de contatos não funcionará.\nVocê precisa realizar as configurações de E-mail nas configurações de conta." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Não foi possível encontrar um servidor Exchange com as configurações providas na configuração de conta. Portanto, a lista de contatos não funcionará.\n\nPor favor corrija suas configurações de E-mail." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Sua mensagem ou o convite não foi entregue, possivelmente porque ele contém um hiperlink, ou outro conteúdo que o administrador do sistema tenha bloqueado." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Esta mensagem não foi enviada a %s porque o serviço se encontra indisponível" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Esta mensagem não foi enviada a %s porque um ou mais destinatários não querem ser incomodados" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Esta mensagem não foi entregue ao %s porque um ou mais destinatários não possuem suporte a esse tipo de mensagem" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Esta mensagem não foi enviada a %s porque um ou mais destinatários estão desconectados" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Nome" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Cargo" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Cidade" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Estado" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Escritório" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "País" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Telefone comercial" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "Email" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Site" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Apelido" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Dispositivo" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "você" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domínio" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Adicionar novo domínio" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Adicionar" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Cancelar" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Copiar para" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Bloquear" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Desbloquear" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Mostrar apresentação" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Informação de entrada de reunião" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Usuários" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Convidar" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privado" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Log" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Descrição" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Assunto da conversa: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Mensagem" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Conectando" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Senha requerida" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "usuario@dominio.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Número de telefone" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Ligar para um número de telefone" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Ligar" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Cancelar" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Local da reunião" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Alternativamente" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "E-mail do organizador" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "ID da reunião" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Participar da conferência" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Participar de uma conferência agendada" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Insira a string de localização da reunião que você recebeu no convite.\n\nUma localização válida deve ser algo como\nmeet:sip:alguem@companhia.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:alguem@companhia.com;gruu;opaque=app:conf:focus:id:abcdef1234\nou\nhttps://meet.companhia.com/alguem/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Participar" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "A publicação de informação no calendário foi desabilitada" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Sobre o plugin SIPE" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Busca de contatos..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Ligar para um número de telefone..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Teste de ligação" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Participar de uma conferência agendada ..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Republicar Calendário" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Reinicializar status" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Servidor[:Porta]\n(deixar vazio para autoconfiguração)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Tipo de conexão" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automático" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Agente do Usuário" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Esquema de autenticação" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Usar Single Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Não publicar minha informação de calendário" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Mostrar fotos de perfil da web\n(potencialmente perigoso)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "URL dos serviços de Email\n(deixar vazio para autoconfiguração)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "Endereço de email\n(se diferente do Nome do Usuário)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "Login do email\n(se diferente do Login)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Senha do email\n(se diferente da Senha)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Proxy para Conversas em Grupo\n companhia.com ou usuario@companhia.com\n(deixe em branco para determinar a partir de nome do Usuário)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Cliente de área de trabalho remota" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Criptografia de mídia" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Obedecer a política do servidor" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Sempre" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Opcional" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Desativado" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Login\n usuario ou DOMINIO\\usuario ou\n usuario@dominio.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Usuário" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Nome" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-mail" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Nome" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Sobrenome" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "ID SIP" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Buscar" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Buscar por um contato" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Insira os dados da pessoa que você deseja encontrar. Campos vazios serão ignorados" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Buscar" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Erro de leitura" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "O servidor se desconectou" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Não foi possível se conectar" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Não foi possível criar contexto SSL" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Não foi possível criar socket" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Erro de gravação" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "OK" ================================================ FILE: po/ro.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Cristian Măgherușan-Stanciu , 2012-2013,2015 # Stefan Becker , 2013 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Romanian (http://www.transifex.com/stefanb/pidgin-sipe/language/ro/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ro\n" "Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Nu s-a putut face autentificarea la server" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Nu s-a putut cere certificatul de la %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Nu a fost configurat nici un URI pentru serviciul de distribuție a certificatelor" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Autentificarea a eșuat" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Schema de autentificare este incompatibilă" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Ați fost refuzat de către server: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "nu s-a dat nici un motiv" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Nu s-a putut găsi: %s. Contactați administratorul sistemului" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "Protocolul SIP este dezactivat pentru URI-ul destinație sau acesta nu există" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Serviciu indisponibil: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "A fost recepționat un mesaj corupt" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Semnătura mesajului primit este invalidă" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Acceptă" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Refuză" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Mobil" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Stare" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Calendar" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Ședință în" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Ședință despre" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Mesaj de absență" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Notă" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Nivel de acces" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "S-a găsit %d contact%s:" msgstr[1] "S-au găsit %d contacte%s:" msgstr[2] "S-au găsit %d contacte%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr "(căutarea a avut mai multe rezultate)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Nu s-a găsit nimic" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Nu s-au putut afișa rezultatele căutării" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Căutarea contactelor a eșuat" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Căutarea de contacte este invalidă" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Setează lider pentru '%s'" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Elimină din '%s'" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Invită în '%s'" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Convorbire nouă" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Muncă" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Acasă" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Altceva" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Personalizat1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Trimite e-mail" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Liber" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Încerc" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Ocupat" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "În afara serviciului" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Nu există date" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Momentan %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "În afara programului de lucru pentru următoarele 8 ore" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s pentru următoarele 8 ore" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Nu lucrez" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s până la %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. În afara orelor de serviciu la %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s la %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Cererea certificatului către %s a eșuat" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Cererea pentru ticketul web către %s a eșuat" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Conversație #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Nu s-a putut conecta in conferință" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" este un URI de conferință invalid" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nDeoarece clientul a fost compilat fără suport audio, în cazul în care acceptați, veți putea contacta pe ceilalți participanți doar prin mesaje scrise." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Această conferință nu mai este închisă. Alți participanți pot să se alăture începând de acum." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Această conferință este închisă. Nimeni nu mai poate să se alăture până la deschidere." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Ați fost deconectat din această conferință" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Un modul adițional care implementează o versiune extinsă a protocolului SIP/SIMPLE utilizat de diverse produse" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Pagină de start" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Suport tehnic" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Forum de ajutor" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Raportați Probleme" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Urmărirea Erorilor" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Traduceri" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licență" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Vă rugăm să ne ajutați traducând SIPE în limba dvs. aici " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " folosind o interfață web prietenoasă" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Autori" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Texte originale în engleză(en): dezvoltatorii SIPE" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "Numele de utilizator SIP Exchange conține caractere invalide" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Numele de utilizator ar trebui să fie un URI valid de SIP\nExemplu: utilizator@companie.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Parola este necesară dacă opțiunea Single Sign-On nu este activată" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "Adresa de e-mail ar trebui să fie validă, dacă a fost introdusă\nExemplu: utilizator@companie.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "Numele de utilizator SIP Exchange conține spații" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "URL-ul serviciilor de e-mail trebuie să fie valid, dacă a fost introdus\nExemplu: https://exchange.corp.com/EWS/Exchange.asmx\nExemplu: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Locație:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Primirea datelor prin rețea a eșuat" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Cheia de criptare primită are dimensiunea greșită." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Suma de control primită are dimensiunea greșită." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Nu s-a putut crea soclul de ascultare prin rețea" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Trimiterea datelor prin rețea a eșuat" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Dimensiunea fișierului este diferită față de cea așteptată" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "MAC-ul primit este corupt" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Fișierul primit este corupt" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Inițializarea transferului de fișiere a eșuat." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Autentificarea pentru transferul de fișiere a eșuat" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Memorie insuficientă" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Alte contacte" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Opțiunea de proxy pentru conversațiile în grup este greșită:\n\n\t%s\n\nVă rog să vă actualizați configurația contului." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Nu s-a putut găsi serverul pentru conversațiile în grup!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Acest mesaj nu a fost livrat in camera de chat '%s'" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Eroare la primirea listei camerelor de chat" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Eroare la intrarea in camera de chat" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Nu s-a putut invita %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "S-a primit un mesaj cu conținut recunoscut de la %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Nu s-a putut stabili apelul" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Configurarea de criptare a partenerului de conversație este incompatibilă cu a dvs." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "Utilizatorul %s a respins apelul" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Apel respins" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Eroare la crearea semnalului audio" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Eroare la crearea semnalului video" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Sună în conferință" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Acest server nu oferă suport pentru teleconferințe" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Număr de telefon invalid" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Serviciul de testare a apelurilor audio este indisponibil" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Utilizator indisponibil" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s nu dorește să fie deranjat" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "Utilizatorul %s nu este disponibil" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Tip nesuportat" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "sunteți deja conectat dintr-o altă locație" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "utilizator dezactivat" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "utilizator mutat" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Blocat" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Personal" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Echipă" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Companie" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Public" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Necunoscut" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Elimină" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Oameni din companie" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Oameni din domenii conectate cu compania mea" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Oameni din domenii publice" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Oameni de la %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Adaugă domeniu nou..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Ajutor online" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Grupuri de acces" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inactiv" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Ocupat-Absent" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Revin imediat" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "La masă" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Într-un apel" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "Într-o conferință" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "Într-o ședință" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Întrerupeți doar în caz de urgență" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Mesajul sau invitația nu s-a putut trimite, probabil conține un link sau alt tip de conținut care este blocat de către administratorul sistemului." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Acest mesaj nu a fost livrat la %s deoarece serviciul este indisponibil" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Acest mesaj nu a fost livrat la %s deoarece cel puțin unul dintre destinatari nu dorește să fie deranjat" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Acest mesaj nu a fost livrat la %s deoarece unul dintre destinatari nu suportă acest tip de mesaj" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Acest mesaj nu a fost livrat la %s deoarece cel puțin unul dintre destinatari este deconectat" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Nume de afișat" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Titlul job-ului" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Oraș" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Județ" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Birou" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Țara" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Telefon de serviciu" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "Adresă de e-mail" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Site" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Alias" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Dispozitiv" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "dvs." #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domeniu" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Adaugă domeniu nou" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Adaugă" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Anulează" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Copiază în" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Blochează" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Deblochează" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Utilizatori" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Invită" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privat" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Jurnal" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Descriere" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Subiectul conversației: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Mesaj" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Conectare" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Parola este necesară" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "utilizator@companie.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Număr de telefon" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Apelează un număr de telefon" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Apelează" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Anulează" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Locația întâlnirii" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Intră în conferință" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Intră în conferință planificată" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Introduceți locația întâlnirii pe care ați primit-o în invitație.\n\nExemplu de locație validă: \nmeet:sip:cineva@companie.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nsau\nhttps://meet.companie.com/cineva/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Intră" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Publicarea orarului personal a fost dezactivată" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Despre modulul SIPE..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Caută contacte..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Apelează un număr de telefon..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Apel de test" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Intră într-o conferință planificată..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Publică din nou calendarul" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Resetează statusul" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Server[:Port]\n(lăsați gol pentru a se descoperi in mod automat)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Tipul conexiunii" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automat" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Agent de utilizator" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Un modul adițional care implementează o versiune extinsă a protocolului SIP/SIMPLE utilizat de diverse produse" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Utilizați autentificarea unică" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Nu publica orarul personal" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "URL-ul serviciilor de e-mail\n(lăsați gol pentru a se descoperi in mod automat)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "Adresă de e-mail\n(dacă diferă de numele de utilizator)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "Nume de utilizator pentru e-mail\n(dacă diferă de numele de utilizator SIPE)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Parola de e-mail\n(dacă diferă de parola SIPE)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Proxy pentru convorbiri de grup\n companie.com say utilizator@companie.com\n(lăsați liber pentru a se determina din numele de utilizator)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Autentificare\n utilizator sau DOMENIU\\utilizator sau\n utilizator@companie.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Nume de utilizator" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Numele" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-mail" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Prenumele" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Numele" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "Identificator SIP" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Caută" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Caută un contact" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Introduceți informații despre persoana pe care o căutați. Câmpurile goale vor fi ignorate." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Caută" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Eroare la citire" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Serverul s-a deconectat" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Nu s-a putut conecta" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Nu s-a putut stabili sesiunea SSL" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Nu s-a putut crea slotul" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Eroare la scriere" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/ru.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Amber , 2011 # Gennadiy Zolotaryov , 2018 # Ivan Gromov , 2016 # someone328 , 2011 # Stefan Becker , 2011 # Ivan Gromov , 2015 # G0LDEN_key , 2013,2015 # Дмитрий Д. , 2017 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-25 10:11+0000\n" "Last-Translator: Gennadiy Zolotaryov \n" "Language-Team: Russian (http://www.transifex.com/stefanb/pidgin-sipe/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ru\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Не удалось проверить подлинность сервера" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Невозможно запросить сертификат от %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Нет URI для сертификата" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Ошибка аутентификации" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Выбрана несовместимая схема проверки подлинности пользователя" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Вы были отклонены сервером: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "причина не указана" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Не найден: %s. Пожалуйста, обратитесь к администратору" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP-адрес либо не включен для URI назначения либо не существует" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Сервис недоступен: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Получено искажённое сообщение" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Получена недопустимая подпись сообщения" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s желает начать презентацию" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Принять" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Отклонить" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Ошибка совместного использования приложения" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Неизвестный клиент удаленного рабочего стола настроен." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Не удалось подключить общий доступ к приложению" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Не удалось создать RDP-сервер." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Не удалось инициализировать RDP-сервер." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Не удалось запустить RDP-сервер." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Совместное использование рабочего стола с %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Остановить представление" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Не удалось инициализировать совместное использование приложения" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Весь рабочий стол" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Монитор для совместного использования" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Мобильный" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Состояние" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Календарь" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Место собрания" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Тема собрания" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Сообщение \"Нет на работе\"" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Личное сообщение" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Уровень доступа" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Найден %d один контакт %s :" msgstr[1] "Найдено несколько %d контактов %s :" msgstr[2] "Найдены %d другие контакты %s :" msgstr[3] "Найдены %d другие контакты %s :" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (большее соответствие вашему запросу)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Контакты не найдены" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Не удается отобразить результаты поиска" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Поиск контактов неудачен" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Неверный запрос поиска контакта" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Сделать ведущим '%s'" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Удалить из '%s'" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Пригласить в '%s'" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Новый чат" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Рабочий" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Домашняя страница" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Другой" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Свой тип" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Отправить сообщение электронной почты..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Получить управление рабочим столом" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Предоставить управление рабочим столом" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Совместное использование моего рабочего стола" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Свободен" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Под вопросом" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Занят" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Нет на месте" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Нет данных" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "в настоящий момент %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Нерабочее время в течение следующих 8 часов" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s в течение следующих 8 часов" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "не работаю" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s до %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Нерабочее время в %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s в %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Не удалось запросить сертификат %s" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Не удачный запрос вебтикета %s " #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Чат #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Не удалось присоединиться к конференции" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "URL aдрес конференции отсутствует на этой странице:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" не верный URI конференции" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Предоставлена неполная информация о конференции" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nДанное приложение не поддерживает голосовых звонков, если вы согласитесь, вы будете иметь возможность связаться с другими участниками только через сеанс обмена мгновенными сообщениями." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "хочет пригласить вас на конференцию %s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Конференция разблокирована. Новые участники могут присоединиться." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Конференция заблокирована. Пока доступ к конференции заблокирован, к ней не могут присоединяться новые участники." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Ведущий конференции удалил вас из конференции." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Информация о подключении по телефону" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Номер" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "Идентификатор конференции" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Сылка на собрание" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Организатор" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Другие номера для дозвона" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Независимый плагин реализующий расширенную версию SIP/SIMPLE используемую в таких продуктах, как" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Домашняя страничка" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Поддержка" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Форум" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Сообщить о проблеме" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "База дефектов" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Страница локализации" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Лицензия" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Пожалуйста, помогите нам перевести SIPE на Ваш родной язык здесь " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " с помощью удобного веб-интерфейса" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Авторы" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Русский перевод (ru): pier11, g.zolotaryov@gmail.com" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "Имя пользователя содержит недопустимые символы" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Имя пользователя должно быть верным SIP URI\nПример: user@company.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Если не включен Single Sign-On, требуется пароль" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "Адрес электронной почты должен быть верным если введен\nПример: user@company.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "Имя пользователя не может содержать пробелы" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "URL службы электронной почты должен быть верным, если введен\nПример: https://exchange.corp.com/EWS/Exchange.asmx\nПример: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Место:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Не удалось прочитать из сокета" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Принятый ключ шифрования имеет неверный размер." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Принятый ключ хеширования имеет неверный размер." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Не удалось создать сокет для прослушивания" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Произошла ошибка" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Ошибка при создании потока данных" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Не удалось записать в сокет" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Размер файла отличается от заявленного." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Принятая подпись повреждена" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Принятый файл поврежден" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Инициализация передачи файлов не удалась." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Не удалось проверить подлинность при передаче файла." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Недостаточно памяти" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Другие контакты" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Настройки прокси для групппового чата не верны:\n\n»%s\n\nПожалуйста измените настройки аккаунта." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Сервер группового чата не найден!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Сообщение не было доставлено в чат канал '%s'" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Не удалось " #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Ошибка подключения к чату" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Не удалось пригласить %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Поступило сообщение неподдерживаемого типа от %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Не удалось создать стрим" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Время на соединение истекло" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Истекло время запроса" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Не удалось ответить на вызов" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Не удалось сделать вызов" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Настройки шифрования собеседника несовместимы с нашими." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "Пользователь %s отклонил вызов" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Вызов отклонен" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Ошибка при создании аудио потокa" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Ошибка при создании видео потокa" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Присоединиться к конференции" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Этот сервер не поддерживает конференции" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Неверный номер телефона" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Служба аудиотеста недоступна" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Пользователь недоступен" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s не хочет, чтобы его беспокоили" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "Пользователь %s недоступен" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Неподдерживаемый тип носителя" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Ошибка данных" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Ошибка чтения из потока" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "Вы уже вошли в систему в другом месте" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "пользователь отключен" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "пользователь перемещен" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Заблокирован" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Личный" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Группа" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Компания" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Общедоступный" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Неизвестный" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Не определять" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Пользователи в моей организации" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Пользователи в доменах, подключенных к моей организации" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Пользователи в общедоступных доменах" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Пользователи в %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Добавить новый домен..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Онлайн помощь..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Группы доступа" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Неактивен" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Занят-Неактивен" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Скоро вернусь" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "На обеде" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Говорю по телефону" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "На конференции" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "На собрании" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Беспокоить только по срочным делам" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Представление" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Ошибка присутствия!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Поэтому один или несколько собеседников будут постоянно отображаться как офлайн.\n\nУбедитесь, что в списке контактов нет некорректных SIP URI." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "Ошибка инициализации USC." #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Не удалось найти сервер Exchange с настройками Email по умолчанию. Из-за этого список контактов не будет работать.\n\nНеобходимо указать настройки Email в конфигурации учётной записи." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Не удалось найти сервер Exchange с указанными настройками Email. Из-за этого список контактов не будет работать.\n\nНеобходимо исправить настройки Email." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Ваше сообщение или приглашение не было доставлено. Возможно, оно содержит гиперссылку или другое содержимое, заблоктрованное системным администратором." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Сообщение не доставлено %s, потому что сервис недоступен" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Сообщение не доставлено %s, поскольку один или нескольно получателей были недоступны" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Это сообщение не было доставлено %s, поскольку один или нескольно получателей не поддерживают данный тип сообщения" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Сообщение не доставлено %s, поскольку один или нескольно получателей были не в сети" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Отображаемое имя" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Должность" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Город" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Штат" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Офис" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Страна" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Рабочий телефон" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "Адрес электронной почты" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Сайт" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Псевдоним" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Устройство" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "Вас" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=%D0%A3%D1%80%D0%BE%D0%B2%D0%BD%D0%B8_%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%B0" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Домен" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Добавить новый домен" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Добавить" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Отмена" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Скопировать в" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Заблокировать" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Разблокировать" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Показать презентацию" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Информация о собрании" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Пользователи" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Пригласить" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Приват" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Лог" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Описание" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Тема обсуждения: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Сообщение" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Установка соединения" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Требуется пароль" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "user@company.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Номер телефона" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Позвонить по номеру телефона" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Вызов" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "О_тменить" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Место совещания" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Другие варианты" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "Email организатора" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "Идентификатор собрания" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Присоединиться к конференции" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Присоедениться к запланированной конференции" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Укажите место встречи из полученного Вами приглашения\nКорректное место встречи выглядит примерно так:\nmeet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nлибо\nhttps://meet.company.com/someone/abcdef1234\n " #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Регистрация" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Публикация данных из календаря выключена" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "О плагине SIPE..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Поиск контактов..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Позвонить по номеру телефона..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Тестовый звонок" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Регистрация на запланированной конференции ..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Опубликовать календарь" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Сбросить состояние" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Сервер[:Порт]\n(оставьте пустым для авто-обнаружения)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Тип подключения" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Автоматически" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Агент пользователя" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Схема проверки подлинности " #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Использовать Single Sign-On" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Не публиковать данные из моего календаря" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Показывать картинки профиля из web\n(потенциально опасно)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "URL службы электронной почты\n(оставьте пустым для авто-обнаружения)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "Адрес электронной почты\n(если отличен от Имени пользователя)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "Логин электронной почты\n(если отличен от Логина)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "Пароль электронной почты\n(если отличен от Пароля)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Прокси пруппового чата \ncompany.com или user@company.com \n(оставьте пустым, чтобы использовать \"Имя пользователя\")" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Клиент удалённого рабочего стола" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Шифрование данных" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Следовать политике сервера" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Всегда" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Не обязательно" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Отключен" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Логин\n user или DOMAIN\\user или\n user@company.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Имя пользователя" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Имя" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "Email" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Имя" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Фамилия" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Искать" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Поиск контактов" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Введите информацию о человеке, которого вы хотите найти. Пустые поля будут игнорироваться." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Искать" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Ошибка чтения" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Сервер прервал соединение" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Не удалось установить соединение" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Не удалось создать контекст SSL" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Не удалось создать сокет" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Ошибка записи" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "OK" ================================================ FILE: po/sv.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Göran Uddeborg , 2014-2015,2017-2018 # Karl-Johan Karlsson , 2015-2016 # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 20:43+0000\n" "Last-Translator: Göran Uddeborg \n" "Language-Team: Swedish (http://www.transifex.com/stefanb/pidgin-sipe/language/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: sv\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Misslyckades att autentisera till servern" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "Kan inte begära certifikat ifrån %s" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Ingen URI för tjänsten som tillhandahåller certifikat gavs" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Autentiseringen misslyckades" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Inkompatibelt autentiseringsschema valt" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Du nekades av servern: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "ingen motivering angiven" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Hittades inte: %s. Vänligen kontakta administratören" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP är antingen inte aktiverad för den destinationen (URI) eller så den finns inte" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Tjänsten är ej tillgänglig: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Ett trasigt meddelande togs emot" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "En ogiltig meddelandesignatur togs emot" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s vill börja presentera" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Godta" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Neka" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Programdelningsfel" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Okänd klient för fjärrskrivbord konfigurerad." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Kunde inte ansluta till programdelning" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "Kunde inte skapa en RDP-server." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "Kunde inte initiera RDP-servern." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "Kunde inte starta RDP-servern." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Delar skrivbord med %s" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Avsluta presentationen" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Kunde inte initiera delningen av programmet" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Hela skrivbordet" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Monitor att dela" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Mobil" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Status" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Kalender" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Möte om" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Möte gällande" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "Inte på kontoret - Notifiering" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Notifiering" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Åtkomst nivå" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "Hittade %d kontakt%s:" msgstr[1] "Hittade %d kontakter%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (flera matchade din fråga)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Inga kontakter hittade" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Det går inte att visa sökresultatet." #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Kontaktsökningen misslyckades" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Ogiltig kontaktsökningsfråga" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "Gör ledare av ”%s”" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "Ta bort från ”%s”" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "Bjud in till ”%s”" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Ny chatt" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "Arbete" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Hem" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Annat" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Anpassad1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "Skicka e-post …" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Ta kontroll över skrivbordet" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Ge bort kontrollen över skrivbordet" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Dela mitt skrivbord" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Ledig" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Preliminärt" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Upptagen" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Inte på kontoret" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Inga uppgifter" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "För närvarande %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Utanför arbetstid (närmsta 8 timmarna)" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s (närmsta 8 timmarna)" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Fungerar ej" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s till och med %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. Utanför arbetstid mellan %.2d:%.2d" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %s till %.2d%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "Certifikatsbegäran av %s misslyckades" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "Webbiljettsbegäran av %s misslyckades" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Chatt nr. %d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Misslyckades att gå med i konferensen" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Kan inte hitta någon konferens-URI på denna sida:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "”%s” är inte en giltig konferens-URI" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Ofullständig konferens information angiven" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nEftersom denna klient inte kompilerades med stöd för röstsamtal kommer du, om du godtar, bara kunna kontakta de andra deltagarna via IM-sessioner." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "vill bjuda in dig till ett konferenssamtal%s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Denna konferens är inte längre låst. Ytterligare deltagare kan nu gå med." #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Denna konferens är låst. Ingen annan kan delta i konferensen medan den är låst." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Du har kopplats bort från denna konferens." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Inkommande information" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Nummer" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "Konferens-ID" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Möteslänk" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Organisatör" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Alternativa inkommande nummer" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "En tredje parts tillägg med stöd för den utökade versionen av SIP/SIMPLE som kan användas av olika produkter" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Hemsida" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Support" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Hjälpforum" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Rapportera fel" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Felhanteringssystem" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Översättningar" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Licenser" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Hjälp oss att översätta sipe till ditt modersmål här på " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " med ett bekvämt webbgränssnitt" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Författare" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Originaltexter på Engelska (en): SIPE-utvecklarna" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "SIP Exchange-användarnamn innehåller ogiltiga tecken" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Användarnamnet måste vara ett giltigt SIP URI\nExempel: user@company.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Lösenord krävs när Single Sign-on inte är aktiverat" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "E-postadressen bör vara giltig enligt nedan\n\nExempel: user@company.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "SIP Exchange-användarnamn får inte innehålla blanksteg" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "E-tjänstens webbadress ska vara giltig om det föreskrivs\n\nExempel: https://exchange.corp.com/EWS/Exchange.asmx\n Exempel: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Plats:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Uttagsläsning misslyckades" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Mottagen krypteringsnyckel har fel storlek." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Mottagen hash-nyckel har fel storlek." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Det gick inte att skapa lyssnaruttag" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Ett fel inträffade" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Fel när dataström skapades" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Uttagsskrivning misslyckades" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Filstorleken skiljer sig från dess annonserade värde." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Mottagen MAC är felaktig" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Mottagna filen är skadad" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Filöverföringens initiering misslyckades." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Filöverföringens autentisering misslyckades." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Slut på minne" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Övriga Kontakter" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Inställningen för mellanserver för gruppchatt är felaktig:\n\n\t%s\n\nUppdatera ditt konto." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Kunde inte hitta gruppchattservern!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Detta meddelande levererades inte till chattrummet ”%s”" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Fel när rumslistan hämtades" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Fel vid anslutning till chattrummet" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "Misslyckades att bjuda in %s" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "Meddelande mottaget med okänt innehåll från %s" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Kunde inte skapa en ström" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Anslutningen gick ut" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "Begäran gick ut" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Anropet kunde inte besvaras" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Kan inte etablera ett samtal" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Krypteringsinställningarna hos motparten är inkompatibla med våra." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "Användare %s avvisade samtalet" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Samtalet avvisades" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Fel när audioströmmen skapades" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Fel när videoströmmen skapades" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Anslut till konferenssamtal" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Konferenssamtal stödjs inte av denna servern" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Felaktigt telefonnummer" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Audiotesttjänsten är inte tillgänglig." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Användare ej tillgänglig" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s vill inte bli störd" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "Användare %s är inte tillgänglig" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Ej stödd mediatyp" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Mediafel" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Fel vid läsning från strömmen" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "du är redan är inloggad på en annan plats" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "användaren inaktiverad" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "användaren flyttad" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Blockerad" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Personligt" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Team" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Företag" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Publikt" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Okänt" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Ospecificerad" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Personer i mitt företag" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Personer i domäner kopplade till mitt företag" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Personer i offentliga domäner" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "Personer i %s" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Lägg till en ny domän …" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Online hjälp …" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Anslut grupper" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Inaktiv" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Upptagen" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Strax tillbaka" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "På lunch" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "I ett telefonsamtal" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "I en konferens" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "I ett möte" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Enbart brådskade ärenden får avbryta" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Presenterar" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Närvaroprenumerationen misslyckades!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "En eller flera kompisar kommer därför att permanent visas som frånkopplade.\n\nKontrollera att det inte finns några trasiga SIP URI:er i din kontaktlista." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "UCS-initiering misslyckades!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Kunde inte hitta en Exchange-server med standard E-postinställningar. Därför kommer kontaktlistan inte att fungera.\n\nDu behöver ange E-postinställningar i kontoinställningen." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Kunde inte hitta en Exchange-server med E-postinställningarna som gavs i kontoinställningen. Därför kommer kontaktlistan inte att fungera.\n\nRätta dina E-postinställningar.<" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Ditt meddelande eller en inbjudan har inte levererats, möjligen eftersom den innehåller en hyperlänk eller annat innehåll som systemadministratören har blockerat." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Detta meddelande har inte levererats till %s eftersom tjänsten tillfälligt inte är tillgänglig." #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Detta meddelande har inte levereras till %s eftersom en eller flera mottagare inte vill bli störda." #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Detta meddelande har inte levererats till %s eftersom en eller flera mottagare inte stödjer denna typ av meddelande" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Detta meddelande har inte levereras till %s eftersom en eller flera mottagare är inte tillgängliga (Offline)" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Visa namn" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "Befattning" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Stad" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Stat/Region" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Kontor" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Land" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "Arbetstelefon" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "E-postadress" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Plats" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Initialer" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Enhet" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "Du" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Domän" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Lägg till ny domän" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Lägg till" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "Avbryt" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Kopiera till" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Lås" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Lås upp" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Visa presentationen" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Mötespostinformation" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Användare" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Bjud in" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Privat" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Logg" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Beskrivning" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Konversationsämne: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Meddelande" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Ansluter" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Lösenord krävs" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "anvandare@foretag.se" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Telefonnummer" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Ring ett telefonnummer" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Ring" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_Avbryt" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Mötesplats" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Alternativt" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "Organisatörens e-post" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "Mötes-ID" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Gå med i konferens" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Gå med i en schemalagd konferens" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Ange mötesplatssträngen du tog emot i inbjudan.\n\nGiltig plats skall vara någonting som\nmeet:sip:person@foretag.se;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:person@foretag.se;gruu;opaque=app:conf:focus:id:abcdef1234\neller\nhttps://meet.foretag.se/person/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Gå med" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Publicering av kalenderinformation har inaktiverats" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "Om SIPe plugin …" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Kontaktsökning …" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Ring ett telefonnummer …" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Testring" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Gå med i en schemalagd konferens …" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Återpublicera kalender" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Återställ status" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Server[:port]\n(lämna tomt för automatiskt upptäckande)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Anslutningstyp" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Automatiskt" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Användaragent" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Autenticeringsmetod" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Använd ”Single Sign-On”" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Publicera inte min kalenderinformation" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Visa profilbilder från webben\n(potentiellt farligt)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "E-post tjänstens URL\n(lämna tomt för automatiskt upptäckande)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "E-post adress\n(om annan än Användarnamn)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "E-post inloggningsnamn\n (om annan än Inloggningsnamn)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "E-post lösenord\n(om annan än lösenord)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Mellanserver för gruppchatt\n foretag.se eller person@foretag.se\n(lämna tomt för att bestämma från Username)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Fjärrskrivbordsklient" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Mediakryptering" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Följ serverpolicyn" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Alltid" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "Optionell" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Avstängd" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Inloggningsnamn\n user eller DOMÄN\\ \\user eller\n user@company.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Användarnamn" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "Namn" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-post" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Förnamn" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Efternamn" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Sök" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Sök efter kontakt" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Ange information om personen som du vill hitta. Tomma fält ignoreras." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Sök" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Läsfel" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Servern har kopplat ifrån" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Det gick inte att ansluta" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "Det gick inte att skapa SSL-kontext" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Kunde inte skapa en socket" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Skrivfel" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "OK" ================================================ FILE: po/ta.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Navaneethasankar K, 2018 # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Tamil (http://www.transifex.com/stefanb/pidgin-sipe/language/ta/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ta\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "உள்நுழைவு தோல்வியடைந்தது" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "வழங்கியால் நீங்கள் மறுக்கப்பட்டீர்கள்: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "காரணம் தரப்படவில்லை" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "ஏற்க" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "மறு" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "கைபேசி" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "நிலை" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "நாட்காட்டி" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "குறிப்பு" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "அணுகல் நிலை" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "" msgstr[1] "" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr "" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "தொடர்புகள் ஏதும் இல்லை" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "தேடல் முடிவுகளை காட்ட முடியவில்லை" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "தொடர்புகளுக்கான தேடல் தோல்வியடைந்தது" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "தவறான தொடர்புகளுக்கான தேடல்" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "%s ஐ தலைவராக்கு" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "%s ல் இருந்து நீக்கு" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "%s க்கு அழை" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "புதிய அரட்டை" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "பணி" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "வீடு" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "மற்றவை" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "தனிப்பயன்1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "மின்னஞ்சல் அனுப்பு..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "வெட்டி" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "தற்காலிகம்" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "பிஸி" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "அலுவலகத்தில் இல்லை" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "தகவல் இல்லை" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "தற்போது %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "அடுத்த 8 மணி நேரத்திற்கு வேலை நேரம் இல்லை" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s அடுத்த 8 மணி நேரத்திற்கு" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "வேலை செய்யவில்லை" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s %.2d:%.2d வரை" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%.2d:%.2dல்%s.%s " #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "%sக்கு சான்றிதழ் கோரிக்கை தோல்வியடைந்தது" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "%sக்கு இணைய சீட்டு கோரிக்கை தோல்வியடைந்தது" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "அரட்டை #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "கூட்டாலோசனை சேர்க்கை தோல்வியடைந்தது" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "எண்" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "கூட்டாலோசனை அடையாள எண்" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "சந்திப்பு உரலி" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "அமைப்பாளர்" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "வேறு தொடர்பு எண்கள்" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "உதவி" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "கேட்கும் கொள்குழி (சாக்கெட்) ஐ உருவாக்க முடியவில்லை!" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "நிறுவனம்" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "நாடு" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "செய்தி" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "இணைத்தல்" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "தானாக" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "பயனர் உதவியாளர்" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "பெயர்" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "மின் அஞ்சல்" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "தேடுக" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_தேடுக" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "இணைக்க முடியவில்லை" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/te.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Telugu (http://www.transifex.com/stefanb/pidgin-sipe/language/te/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: te\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "" msgstr[1] "" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr "" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: po/tr.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # onur , 2013,2015-2018 # onur , 2012 # Stefan Becker , 2013 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-11-06 20:57+0000\n" "Last-Translator: onur \n" "Language-Team: Turkish (http://www.transifex.com/stefanb/pidgin-sipe/language/tr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: tr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "Sunucuda kimlik doğrulaması yapılamadı" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "%s'den sertifika istenemiyor " #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "Sertifika sağlama servisi için URI belirtilmedi" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "Kimlik doğrulaması başarısız oldu" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "Seçilen kimlik doğrulama düzeni uygun değildir" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "Sunucu tarafından reddedildiniz: %s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "bir neden bildirilmedi" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "Bulunamadı: %s. Lütfen yöneticinize başvurun" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP, hedef URI için etkin değil veya mevcut değil" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "Servis kullanılamıyor: %s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "Hatalı bir ileti alındı" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "Geçersiz ileti imzası alındı" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s sunum yapmak istiyor" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "Kabul et" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "Reddet" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "Uygulama paylaşım hatası" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "Bilinmeyen bir uzak masaüstü istemcisi tanımlanmış." #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "Uygulama paylaşımına bağlanılamıyor." #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "RDP sunucusu oluşturulamadı." #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "RDP sunucusu başlatılamadı." #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "RDP sunucusu başlatılamadı." #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "Masaüstü %s ile paylaşılıyor" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "Sunumu durdur" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "Uygulama paylaşımı başlatılamadı" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "Tüm masaüstü" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "Paylaşılacak ekran" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "Cep" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "Durum" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "Takvim" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "Toplantı yeri" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "Toplantı konusu" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "\"Ofis dışında\" notu" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "Not" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "Erişim düzeyi" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "%d kişi bulundu%s:\n%d kişi bulundu%s:" msgstr[1] "%d kişi bulundu%s:\n%d kişi bulundu%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr "(aramanız daha fazla sonuç verdi)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "Aranan kişi bulunamadı" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "Arama sonuçları görüntülenemiyor" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "Kişi araması başarısız oldu" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "Hatalı kişi arama sorgusu" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "'%s' adlı kişiyi lider yap" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "'%s'den çıkar" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "'%s'e davet et" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "Yeni sohbet" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "İş" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "Ev" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "Diğer" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "Özel1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "E-posta gönder..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "Masaüstü kontrolünü devral" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "Masaüstü kontrolünü devret" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "Masaüstümü paylaş" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "Serbest" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "Kesin değil" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "Meşgul" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "Ofis dışında" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "Veri yok" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "Şu anda %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "Gelecek 8 saat boyunca çalışma saatleri dışında" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "Gelecek 8 saat boyunca %s" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "Çalışmıyor" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%.2d:%.2d'e kadar %s" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s. %.2d:%.2d çalışma saatleri dışında" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s. %.2d:%.2d itibarıyla %s" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "%s için sertifika isteği başarısız oldu" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "%s için web bileti isteği başarısız oldu" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "Sohbet #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "Konferansa katılım yapılamadı" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "Bu sayfada konferans için bir URI bulunamadı:\n\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "\"%s\" geçerli bir konferans URI değil" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "Girilen konferans bilgisi yetersiz" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\nBu program ses desteği olmadan derlenmiş olduğundan, eğer kabul ederseniz, diğer kişilerle sadece mesajlaşarak iletişim kurabileceksiniz." #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "seni %s konferans görüşmesine davet etmek istiyor" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "Bu görüşme artık kilitli değil. Başkaları da katılabilir. " #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "Bu görüşme kilitli. Görüşme kilitli olduğu sürece başka kimse katılamaz." #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "Bu görüşmeyle bağlantınız kesildi." #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "Arama bilgisi" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "Numara" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "Konferans kimliği" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "Toplantı bağlantısı" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "Düzenleyen" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "Alternatif arama numaraları" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "Birçok ürün tarafından kullanılan SIP/SIMPLE'ın genişletilmiş sürümünü uygulayan bir üçüncü taraf eklentisi" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "Kişisel Web Sayfası" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "Destek" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "Yardım Forumu" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "Sorun Bildir" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "Hata Takibi" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "Çeviriler" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "Lisans" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "Lütfen burada pratik web arayüzünü kullanarak SIPE'ı anadilinize çevirmemize yardımcı olun" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr "pratik web arayüzünü kullanarak" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "Yazarlar" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "Orijinal metinler İngilizce (en): SIPE geliştiricileri" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "SIP Exchange kullanıcı adı geçersiz karakterler içeriyor" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "Kullanıcı adı geçerli bir SIP URI olmalıdır\nÖrnek: kullanıcı@firma.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "Tek yerden oturum açma etkin değilken şifre gerekmektedir" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "E-posta adresi girilmişse geçerli olmalıdır\nÖrnek: kullanıcı@firma.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "SIP Exchange kullanıcı adı boşluk içeriyor" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "Eğer verilmişse, e-posta servis URLsi geçerli olmalıdır\nÖrnek: https://exchange.corp.com/EWS/Exchange.asmx\nÖrnek: https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "Konum:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "Soketi okurken hata oluştu" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "Alınan şifreleme anahtarının boyutu hatalı." #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "Alınan karma anahtarının boyutu hatalı." #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "Dinleme soketi oluşturulamadı" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "Hata oluştu" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "Veri akışını gerçekleştirirken hata oluştu" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "Sokete yazarken hata oluştu" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "Dosya boyutu belirtilenden farklı." #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "Alınan MAC bozuk durumda" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "Alınan dosya bozuk durumda" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "Dosya aktarımı başlatılamadı." #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "Dosya aktarımı kimlik doğrulaması başarısız oldu." #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "Bellek yetersiz" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "Diğer Kişiler" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "Grup Sohbet Proxy ayarı hatalı:\n\n»%s\n\nLütfen hesabınızı güncelleyin." #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "Grup Sohbet sunucusu bulunamadı!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "Bu mesaj '%s' sohbet odasına iletilemedi" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "Oda listesi alınırken hata oluştu" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "Sohbet odasına katılırken hata oluştu" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "%s davet edilemedi" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "%s adresinden tanınmayan içerikli bir mesaj alındı" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "Akış oluşturulamadı" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "Bağlantı zaman aşımına uğradı" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "İstem zaman aşımına uğradı" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "Çağrı cevaplanamadı" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "Çağrı yapılamıyor" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "Karşı tarafın şifreleme ayarları bizimkiyle uyumsuz." #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "%s adlı kullanıcı çağrıyı reddetti" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "Çağrı reddedildi" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "Ses akışını gerçekleştirirken hata oluştu" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "Video akışını gerçekleştirirken hata oluştu" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "Konferans çağrısına katıl" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "Bu sunucuda konferans görüşmeleri desteklenmemektedir." #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "Geçersiz telefon numarası" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "Ses Test Servisi mevcut değil." #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "Kullanıcıya ulaşılamıyor" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s rahatsız edilmek istemiyor" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "%s adlı kullanıcıya ulaşılamıyor" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "Desteklenmeyen ortam türü" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "Ortam hatası" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "Akışı okurken hata oluştu" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "başka bir yerden oturum açmış durumdasınız" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "kullanıcı devre dışı" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "kullanıcı taşınmış" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "Bloke edilmiş" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "Kişisel" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "Takım" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "Şirket" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "Genel" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "Bilinmeyen" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "Belirtme" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "Şirketimdeki kişiler" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "Şirketime bağlı alanlardaki kişiler" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "Genel alanlardaki kişiler" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "%s konumundaki kişiler" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "Yeni alan ekle..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "Çevrimiçi yardım..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "Erişim grupları" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "Etkin değil" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "Meşgul-Yerinde değil" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "Hemen dönecek" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "Yemekte" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "Telefonda" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "Konferansta" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "Toplantıda" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "Sadece acil durumlarda" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "Sunum yapılıyor" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "Durum bildiriminde hata oluştu!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "Bu yüzden bir veya daha fazla arkadaş sürekli olarak çevrimdışı görünecektir.\nLütfen kişi listenizde hatalı SIP URI'leri olmadığından emin olun." #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "UCS başlatılamadı." #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "Varsayılan E-posta ayarlarıyla bir Exchange sunucusu bulunamadı. Bu nedenle kişi listesi çalışmayacaktır.\n\nHesap detaylarında E-posta ayarlarını girmeniz gerekmektedir." #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "Hesap ayarlarında girdiğiniz E-posta ayarlarıyla bir Exchange sunucusu bulunamadı. Bu nedenle kişi listesi çalışmayacaktır.\n\nLütfen E-posta ayarlarınızı düzeltin." #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "Mesajınız veya davetiniz iletilemedi. Bunun nedeni sistem yöneticisinin bloke ettigi bir bağlantı veya içerik olabilir." #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "Bu mesaj %s adlı kişiye iletilemedi çünkü servis kullanılamıyor" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "Bu mesaj %s adlı kişiye iletilemedi çünkü bir veya daha fazla alıcı rahatsız edilmek istemiyor" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "Bu mesaj %s adlı kişiye iletilemedi çünkü bir veya daha fazla alıcı bu mesaj türünü desteklemiyor" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "Bu mesaj %s adlı kişiye iletilemedi çünkü bir veya daha fazla alıcı çevrim dışı" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "Görüntü adı" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "İş unvanı" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "Şehir" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "Eyalet" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "Ofis" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "Ülke" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "İş telefonu" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "E-posta adresi" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "Yer" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "Takma ad " #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "Cihaz" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "sen" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "Alan" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "Yeni alan ekle" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "Ekle" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "İptal et" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "Kopyala" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "Kilitle" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "Kilidi kaldır" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "Sunum göster." #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "Toplantı girişi bilgileri" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "Kullanıcılar" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "Davet et" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "Özel" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "Sistem günlüğü" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "Açıklama" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "Görüşme konusu: %s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "Mesaj" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "Bağlanıyor" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "Şifre gerekiyor" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "kullanıcı@firma.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "Telefon numarası" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "Bir telefon numarası ara" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "_Ara" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "_İptal et" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "Toplantı yeri" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "Alternatif olarak" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "Düzenleyenin e-postası" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "Toplantı kimliği" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "Konferansa katıl" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "Planlanmış konferansa katıl" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "Davetiyede almış olduğunuz toplantı yeri dizinini giriniz.\n\nGeçerli toplantı yeri şu şekilde olacaktır:\nmeet:sip:kişi@firma.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nveya\nhttps://meet.firma.com/kişi/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "_Katıl" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "Takvim bilgisinin yayımlanması durdurulmuştur" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "SIPE eklentisi hakkında..." #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "Kişi araması..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "Bir telefon numarası ara..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "Test çağrısı" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "Planlanmış konferansa katıl..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "Takvimi tekrar yayımla" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "Durumu yenile" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "Sunucu[:Bağlantı noktası]\n(otomatik-bulma için boş bırakın)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "Bağlantı türü" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "Otomatik" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "Kullanıcı Aracısı" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "Kimlik doğrulama düzeni" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "Tek yerden oturum açmayı kullan" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "Takvim bilgimi yayımlama" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "Ağdan profil resimlerini göster\n(güvenlik açığı oluşturabilir)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "E-posta hizmetleri URL'si\n(otomatik-bulma için boş bırakın)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "E-posta adresi\n(Kullanıcı Adı ile aynı değilse)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "E-posta girişi\n(Giriş Adı ile aynı değilse)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "E-posta şifresi\n(Şifre ile aynı değilse)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "Grup Sohbet Proxy'si\nfirma.com veya kullanıcı@firma.com\n(Kullanıcı Adı üzerinden belirlemek için boş bırakın)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "Uzak masaüstü istemcisi" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "Ortam şifrelemesi" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "Sunucu ilkerine uy" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "Her zaman" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "İsteğe bağlı" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "Devre dışı" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "Giriş Adı\nkullanıcı veya ALAN\\kullanıcı veya\nkullanıcı@firma.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "Kullanıcı adı" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "İsim" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "E-posta" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "Ad" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "Soyad" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP Kimliği" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "Ara" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "Bir kişiyi ara" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "Bulmak istediğiniz kişiyle ilgili bilgileri girin. Boş alanlar göz ardı edilecektir." #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "_Ara" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "Okuma hatası" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "Sunucu bağlantısını kesti" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "Bağlanamadı" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "SSL ortamı oluşturulamadı" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "Soket oluşturulamadı" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "Yazma hatası" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "Tamam" ================================================ FILE: po/transifex-pot-fixup.pl ================================================ #!/usr/bin/perl -w # # Fix up pidgin-sipe.pot after an update to make it acceptable for Transifex # # Transifex update procedure: # # $ cd po # $ intltool-update --pot -g pidgin-sipe # update POT file # $ ./transifex-pot-fixup.pl # this script # $ cd .. # $ tx push -s # update POT file on Transifex # # [optional: update the languages you know on Transifex] # # $ tx pull -s # fetch updated translations # $ git add -u po/*.po po/*.pot # add files to next commit # $ git commit -e # use 5.008; use strict; use warnings; open(my $fh, "+<", "pidgin-sipe.pot") or die "$0: can't open POT file: $!\n"; my $date; { my(undef, $min, $hour, $mday, $mon, $year) = gmtime(time()); $date = sprintf("%4d-%02d-%02d %02d:%02d+0000", $year + 1900, $mon + 1, $mday, $hour, $min); } # Must be 19 lines (same as header created by intltool-update) my @lines = ( <<"END_OF_HEADER" # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # msgid "" msgstr "" "Project-Id-Version: pidgin sipe\\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\\n" "POT-Creation-Date: 2010-11-30 23:36+0200\\n" "PO-Revision-Date: $date\\n" "Last-Translator: Stefan Becker \\n" "Language-Team: English (http://www.transifex.com/stefanb/pidgin-sipe/language/en/)\\n" "MIME-Version: 1.0\\n" "Content-Type: text/plain; charset=UTF-8\\n" "Content-Transfer-Encoding: 8bit\\n" "Language: en\\n" "Plural-Forms: nplurals=2; plural=(n != 1);\\n" END_OF_HEADER ); while (<$fh>) { # skip header next if $. < 20; push(@lines, $_); } # Update pot file seek($fh, 0, 0) or die "$0: can't rewind POT file: $!\n"; print $fh @lines; close($fh) or die "$0: can't write to POT file: $!\n"; # That's all folks exit 0; ================================================ FILE: po/zh_CN.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Kyle Wang, 2014-2018 # Stefan Becker , 2011 # Tommy He , 2015 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-11-12 09:58+0000\n" "Last-Translator: Kyle Wang\n" "Language-Team: Chinese (China) (http://www.transifex.com/stefanb/pidgin-sipe/language/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "到服务器的认证失败" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "无法从 %s 请求证书" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "未提供证书置备服务的URI" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "认证失败" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "选择了不兼容的认证方案" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "您已被服务器拒绝:%s" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "未给出原因" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "没找到:%s。请和您的管理员联系" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "目标 URI 没有启用 SIP 或不存在" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "服务不可用:%s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "收到损坏的消息" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "收到无效消息签名" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "%s 想要开始演示" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "接受" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "拒绝" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "程序共享错误" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "未知的远程桌面客户端已配置。" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "无法连接程序共享" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "无法创建RDP服务器。" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "无法初始化RDP服务器。" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "无法启动RDP服务器。" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "与%s共享桌面" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "停止演示" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "无法初始化程序共享" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "整个桌面" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "要共享的显示器" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "手机" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "状态" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "日历" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "会议场所" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "会议内容" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "不在办公室备注" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "备注" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "访问级别" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "找到 %d 个联系人%s:" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (与您的查询越匹配)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "未找到联系人" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "无法显示搜索结果" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "联系人搜索失败" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "无效的联系人搜索请求" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "设置为“%s”的领导者" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "从“%s”中删除" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "邀请到“%s”" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "新建聊天" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "工作" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "家庭" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "其他" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "自定义 1" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "发送邮件..." #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "获取桌面控制" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "授予桌面控制" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "共享我的桌面" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "空闲" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "待定" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "忙碌" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "不在办公室" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "无数据" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "目前为 %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "未来 8 小时为工作以外时间" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "未来 8 小时 %s" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "不工作" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s 直到 %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s。%.2d:%.2d 为工作以外时间" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%1$s。%3$.2d:%4$.2d 为 %2$s" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "向 %s 请求证书失败" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "向 %s 请求票据凭证失败" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "聊天 #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "加入会议失败" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "此页未发现会议 URI:\n\n%s" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "“%s”不是合法的会议URI" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "未提供完整的会议信息" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "\n\n因本客户端编译时未加入语音通话支持,如果您接受,您将只能通过即时消息会话与其他与会者进行联系。" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "想邀请您加入电话会议 %s" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "此会议不再锁定。其他参与者可以加入。" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "此会议被锁定。被锁定时其他人不能加入会议。" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "您已被从会议中断开。" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "拨入信息" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "号码" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "会议 ID" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "会议连接" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "组织者" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "备选拨入号码" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "实现用于多个产品的 SIP/SIMPLE 扩展版本的第三方插件" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "主页" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "支持" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "帮助论坛" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "报告问题" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "缺陷追踪" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "翻译" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "许可证" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "请到这里帮助我们将 SIPE 翻译为您的母语 " #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " 使用方便的 web 界面" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "作者" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "中文化 (zh_CN): 神州散人,Kyle Wang" #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "SIP Exchange 用户名包含无效字符" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "用户名必须是有效的 SIP URI\n例如:user@company.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "禁用单点登录时,需要输入密码" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "如果提供了电子邮件地址那么它应该是有效的\n例如:user@company.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "SIP Exchange 用户名包含空格" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "如果提供电子邮件服务 URL 那么它应该是有效的\n例如:https://exchange.corp.com/EWS/Exchange.asmx\n例如:https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "位置:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "套接字读取失败" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "收到的加密密钥尺寸错误。" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "收到的散列密钥尺寸错误。" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "无法创建侦听套接字" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "发生了错误" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "创建数据流错误" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "套接字写入失败" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "文件尺寸与告知的值不同。" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "收到的 MAC 已损坏" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "收到的文件已损坏" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "文件传输初始化失败。" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "文件传输认证失败。" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "内存不足" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "其他联系人" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "群组聊天代理设置错误:\n\n\t%s\n\n请更新您的帐号" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "未找到群组聊天服务器!" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "此消息没有被发送到聊天室“%s”" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "获取聊天室列表发生错误" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "加入聊天室时发生错误" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "邀请 %s 失败" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "从 %s 收到含有无法识别的内容的消息" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "无法创建流" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "连接超时" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "请求超时" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "呼叫无应答" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "无法建立通话" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "节点的加密设置和我们的不兼容。" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "用户 %s 拒绝了通话" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "通话被拒绝" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "创建音频流错误" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "创建视频流错误" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "加入电话会议" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "此服务器不支持电话会议。" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "无效的电话号码" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "音频测试服务不可用。" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "用户不可用" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s 不想被打扰" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "用户 %s 不可用" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "不支持的媒体类型" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "媒体错误" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "流读取错误" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "您已经在另一地点登入" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "用户已禁止" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "用户已移动" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "被屏蔽" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "个人" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "团队" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "公司" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "公共" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "未知" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "未指定" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "我公司里的人" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "连接至我公司的域里的人" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "公共域里的人" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "在 %s 的人" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "新增域..." #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "在线帮助..." #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "访问组" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "不活动" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "忙碌-发呆" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "马上回来" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "外出就餐" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "接电话中" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "在会议中" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "在会谈中" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "非紧急事件勿扰" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "正在演示" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "在线情况订阅错误!" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "有些好友将一直显示为离线状态。\n\n请检查您的联系人列表中有无损坏的SIP URI。" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "UCS初始化失败!" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "未找到默认电子邮件设置的 Exchange 服务器,联系人列表将无法使用。\n\n您需要在帐户设置中提供电子邮件设置。" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "未找到帐户设置中电子邮件的 Exchange 服务器,联系人列表将无法使用。\n\n请更正您的电子邮件设置。" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "您的消息或邀请没有被发送,原因可能是它包含了被系统管理员屏蔽的链接或其他内容。" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "此消息没有被发送到 %s,因为服务不可用" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "此消息没有被发送到 %s,因为有的收信人不想被打扰" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "此消息没有被发送到 %s,因为有的收信人不支持此消息类型" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "此消息没有被发送到 %s,因为有的收信人不在线" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "显示名称" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "职称" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "城市" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "省份" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "办公室" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "国家" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "工作电话" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "电子邮件地址" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "站点" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "别名" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "设备" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "您" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "域" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "新增域" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "添加" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "取消" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "复制到" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "锁定" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "解锁" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "显示演示" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "会议信息" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "_URI:" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "用户" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "邀请" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "私人" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "日志" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "描述" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "会话主题:%s" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "消息" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "正在连接" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "需要提供密码" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "user@company.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "电话号码" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "拨打电话号码" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "拨号(_C)" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "取消(_C)" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "会议地点" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "备选" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "组织者邮件" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "会议 ID" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "加入会议" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "加入预定会议" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "输入您收到的邀请中的会议地点字符串。\n\n合法的地址可能为\nmeet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\nconf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n或者\nhttps://meet.company.com/someone/abcdef1234" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "加入(_J)" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "已禁用发布日历信息" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "关于 SIPE 插件" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "联系人搜索..." #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "拨打电话号码..." #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "测试呼叫" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "加入预定会议..." #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "重新发布日历" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "复位状态" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "服务器[:端口]\n(留空使用自动发现)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "连接类型" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "自动" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "用户代理" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "认证方案" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "NTLM" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "Kerberos" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "TLS-DSK" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "使用单点登录" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "不发布我的日历信息" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "显示来自网络的资料图片\n(有潜在危险)" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "电子邮件服务 URL\n(留空使用自动发现)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "电子邮件地址\n(如果与用户名不同)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "电子邮件登录名\n(如果与登录名不同)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "电子邮件密码\n(如果与密码不同)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "群组聊天代理\ncompany.com 或 user@company.com\n(留空表示根据用户名决定)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "远程桌面客户端" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "媒体加密" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "遵从服务器策略" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "始终" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "可选" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "禁用" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "登录\n 用户 或 域\\用户 或\n user@company.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "用户名" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "姓名" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "电子邮件" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "姓" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "名" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "SIP ID" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "搜索" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "搜索联系人" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "输入您要查找的人的信息。空字段将被忽略。" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "搜索(_S)" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "读取错误" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "服务器已断开连接" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "无法连接" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "无法创建 SSL 环境" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "无法创建套接字" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "写入错误" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "确定" ================================================ FILE: po/zh_TW.po ================================================ # (English) English User Interface strings for pidgin-sipe. # Copyright (C) 2008-2018 SIPE Project # This file is distributed under the same license as the pidgin-sipe package. # # # # Translators: # Stefan Becker , 2011 msgid "" msgstr "" "Project-Id-Version: pidgin sipe\n" "Report-Msgid-Bugs-To: https://sourceforge.net/p/sipe/bugs/\n" "POT-Creation-Date: 2010-11-30 23:36+0200\n" "PO-Revision-Date: 2018-10-18 16:09+0000\n" "Last-Translator: Stefan Becker \n" "Language-Team: Chinese (Taiwan) (http://www.transifex.com/stefanb/pidgin-sipe/language/zh_TW/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: zh_TW\n" "Plural-Forms: nplurals=1; plural=0;\n" #: ../src/core/sip-transport.c:241 msgid "Failed to authenticate to server" msgstr "由伺服器認證時失敗" #: ../src/core/sip-transport.c:299 #, c-format msgid "Can't request certificate from %s" msgstr "" #: ../src/core/sip-transport.c:309 msgid "No URI for certificate provisioning service provided" msgstr "" #: ../src/core/sip-transport.c:1225 msgid "Authentication failed" msgstr "認證失敗" #: ../src/core/sip-transport.c:1282 msgid "Incompatible authentication scheme chosen" msgstr "選擇了不相容的認證方案" #: ../src/core/sip-transport.c:1299 ../src/core/sipe-notify.c:1159 #, c-format msgid "You have been rejected by the server: %s" msgstr "您已被伺服器 %s 拒絕" #: ../src/core/sip-transport.c:1300 ../src/core/sip-transport.c:1316 #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #: ../src/core/sipe-conf.c:267 ../src/core/sipe-notify.c:1159 msgid "no reason given" msgstr "沒有給予原因" #: ../src/core/sip-transport.c:1315 #, c-format msgid "Not found: %s. Please contact your Administrator" msgstr "找不到:%s。請與您的管理員聯絡" #: ../src/core/sip-transport.c:1317 msgid "SIP is either not enabled for the destination URI or it does not exist" msgstr "SIP 若非未對目的 URI 啟用或是它並不存在" #: ../src/core/sip-transport.c:1340 ../src/core/sip-transport.c:1364 #, c-format msgid "Service unavailable: %s" msgstr "不提供該項服務︰%s" #: ../src/core/sip-transport.c:1717 msgid "Corrupted message received" msgstr "" #: ../src/core/sip-transport.c:1741 msgid "Invalid message signature received" msgstr "收到的訊息簽章無效" #: ../src/core/sipe-appshare.c:494 #, c-format msgid "%s wants to start presenting" msgstr "" #: ../src/core/sipe-appshare.c:498 ../src/core/sipe-conf.c:1102 msgid "Accept" msgstr "" #: ../src/core/sipe-appshare.c:499 ../src/core/sipe-conf.c:1103 msgid "Decline" msgstr "" #: ../src/core/sipe-appshare.c:537 ../src/core/sipe-appshare.c:635 #: ../src/core/sipe-appshare.c:780 ../src/core/sipe-appshare.c:901 msgid "Application sharing error" msgstr "" #: ../src/core/sipe-appshare.c:538 msgid "Unknown remote desktop client configured." msgstr "" #: ../src/core/sipe-appshare.c:636 msgid "Couldn't connect application sharing" msgstr "" #: ../src/core/sipe-appshare.c:753 msgid "Could not create RDP server." msgstr "" #: ../src/core/sipe-appshare.c:770 msgid "Could not initialize RDP server." msgstr "" #: ../src/core/sipe-appshare.c:772 msgid "Could not start RDP server." msgstr "" #: ../src/core/sipe-appshare.c:863 #, c-format msgid "Sharing desktop with %s" msgstr "" #: ../src/core/sipe-appshare.c:867 msgid "Stop presenting" msgstr "" #: ../src/core/sipe-appshare.c:902 msgid "Couldn't initialize application sharing" msgstr "" #: ../src/core/sipe-appshare.c:960 msgid "Whole desktop" msgstr "" #: ../src/core/sipe-appshare.c:973 msgid "Monitor to share" msgstr "" #: ../src/core/sipe-buddy.c:543 ../src/core/sipe-buddy.c:2334 msgid "Mobile" msgstr "" #: ../src/core/sipe-buddy.c:831 msgid "Status" msgstr "狀態" #: ../src/core/sipe-buddy.c:834 msgid "Calendar" msgstr "行事曆" #: ../src/core/sipe-buddy.c:839 msgid "Meeting in" msgstr "會議舉行於" #: ../src/core/sipe-buddy.c:843 msgid "Meeting about" msgstr "會議關於" #: ../src/core/sipe-buddy.c:848 msgid "Out of office note" msgstr "不在辦公室註記" #: ../src/core/sipe-buddy.c:848 msgid "Note" msgstr "註記" #: ../src/core/sipe-buddy.c:853 ../src/core/sipe-buddy.c:2407 #: ../src/purple/purple-buddy.c:634 msgid "Access level" msgstr "存取等級" #: ../src/core/sipe-buddy.c:1076 #, c-format msgid "Found %d contact%s:" msgid_plural "Found %d contacts%s:" msgstr[0] "找到 %d 位聯絡人%s︰" #: ../src/core/sipe-buddy.c:1078 msgid " (more matched your query)" msgstr " (更多符合您查詢的項目)" #: ../src/core/sipe-buddy.c:1124 ../src/core/sipe-buddy.c:1262 #: ../src/core/sipe-ucs.c:318 msgid "No contacts found" msgstr "" #: ../src/core/sipe-buddy.c:1137 ../src/core/sipe-buddy.c:1275 #: ../src/core/sipe-ucs.c:282 msgid "Unable to display the search results" msgstr "無法顯示搜尋結果" #: ../src/core/sipe-buddy.c:1240 ../src/core/sipe-buddy.c:1252 #: ../src/core/sipe-ucs.c:395 msgid "Contact search failed" msgstr "" #: ../src/core/sipe-buddy.c:1415 ../src/core/sipe-ucs.c:399 msgid "Invalid contact search query" msgstr "" #: ../src/core/sipe-buddy.c:2271 #, c-format msgid "Make leader of '%s'" msgstr "成為「%s」的召集人" #: ../src/core/sipe-buddy.c:2284 #, c-format msgid "Remove from '%s'" msgstr "從「%s」移除" #: ../src/core/sipe-buddy.c:2298 #, c-format msgid "Invite to '%s'" msgstr "邀請參加「%s」" #: ../src/core/sipe-buddy.c:2314 msgid "New chat" msgstr "新的聊天室" #: ../src/core/sipe-buddy.c:2327 msgid "Work" msgstr "" #: ../src/core/sipe-buddy.c:2342 msgid "Home" msgstr "首頁" #: ../src/core/sipe-buddy.c:2350 msgid "Other" msgstr "" #: ../src/core/sipe-buddy.c:2358 msgid "Custom1" msgstr "" #: ../src/core/sipe-buddy.c:2368 msgid "Send email..." msgstr "發送電子郵件…" #: ../src/core/sipe-buddy.c:2385 msgid "Take desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2390 msgid "Give desktop control" msgstr "" #: ../src/core/sipe-buddy.c:2396 ../src/purple/purple-chat.c:370 msgid "Share my desktop" msgstr "" #: ../src/core/sipe-cal.c:907 msgid "Free" msgstr "免費" #: ../src/core/sipe-cal.c:908 msgid "Tentative" msgstr "臨時" #. SIPE_ACTIVITY_BUSY #: ../src/core/sipe-cal.c:909 ../src/core/sipe-status.c:57 msgid "Busy" msgstr "忙碌" #. SIPE_ACTIVITY_OOF #: ../src/core/sipe-cal.c:910 ../src/core/sipe-status.c:68 msgid "Out of office" msgstr "不在辦公室" #: ../src/core/sipe-cal.c:911 msgid "No data" msgstr "沒有資料" #: ../src/core/sipe-cal.c:1019 ../src/core/sipe-cal.c:1046 #, c-format msgid "Currently %s" msgstr "目前 %s" #: ../src/core/sipe-cal.c:1025 msgid "Outside of working hours for next 8 hours" msgstr "在下一個 8 小時工作時數之外" #: ../src/core/sipe-cal.c:1027 #, c-format msgid "%s for next 8 hours" msgstr "%s 用於下一個 8 小時" #: ../src/core/sipe-cal.c:1036 msgid "Not working" msgstr "不在工作中" #: ../src/core/sipe-cal.c:1040 #, c-format msgid "%s until %.2d:%.2d" msgstr "%s 直到 %.2d:%.2d" #: ../src/core/sipe-cal.c:1048 #, c-format msgid "%s. Outside of working hours at %.2d:%.2d" msgstr "%s。於 %.2d:%.2d 的工作時數之外" #: ../src/core/sipe-cal.c:1053 #, c-format msgid "%s. %s at %.2d:%.2d" msgstr "%s。%s 於 %.2d:%.2d" #: ../src/core/sipe-certificate.c:233 ../src/core/sipe-certificate.c:277 #, c-format msgid "Certificate request to %s failed" msgstr "" #: ../src/core/sipe-certificate.c:284 #, c-format msgid "Web ticket request to %s failed" msgstr "" #. Generate next ID #: ../src/core/sipe-chat.c:251 #, c-format msgid "Chat #%d" msgstr "聊天室 #%d" #: ../src/core/sipe-conf.c:266 ../src/core/sipe-conf.c:420 #: ../src/core/sipe-conf.c:462 ../src/core/sipe-conf.c:514 msgid "Failed to join the conference" msgstr "" #: ../src/core/sipe-conf.c:416 #, c-format msgid "" "Can't find a conference URI on this page:\n" "\n" "%s" msgstr "" #: ../src/core/sipe-conf.c:459 #, c-format msgid "\"%s\" is not a valid conference URI" msgstr "" #: ../src/core/sipe-conf.c:515 msgid "Incomplete conference information provided" msgstr "" #: ../src/core/sipe-conf.c:1122 msgid "" "\n" "\n" "As this client was not compiled with voice call support, if you accept, you will be able to contact the other participants only via IM session." msgstr "" #: ../src/core/sipe-conf.c:1127 #, c-format msgid "wants to invite you to a conference call%s" msgstr "" #: ../src/core/sipe-conf.c:1469 msgid "" "This conference is no longer locked. Additional participants can now join." msgstr "這個會議已不再鎖定。額外參與者現在可以加入。" #: ../src/core/sipe-conf.c:1473 msgid "" "This conference is locked. Nobody else can join the conference while it is " "locked." msgstr "這個會議已被鎖定。當它被鎖定時,其他任何人都不可以加入會議。" #: ../src/core/sipe-conf.c:1506 msgid "You have been disconnected from this conference." msgstr "您已經從這個會議結束連線。" #: ../src/core/sipe-conf.c:1661 msgid "Dial-in info" msgstr "" #: ../src/core/sipe-conf.c:1662 msgid "Number" msgstr "" #: ../src/core/sipe-conf.c:1664 msgid "Conference ID" msgstr "" #: ../src/core/sipe-conf.c:1666 msgid "Meeting link" msgstr "" #: ../src/core/sipe-conf.c:1668 msgid "Organizer" msgstr "" #: ../src/core/sipe-conf.c:1670 msgid "Alternative dial-in numbers" msgstr "" #. The next 13 texts make up the SIPE about note text #. About note, part 1/13: introduction #: ../src/core/sipe-core.c:236 msgid "" "A third-party plugin implementing extended version of SIP/SIMPLE used by " "various products" msgstr "一個協力廠商的外掛程式,實作了由不同產品所使用的 SIP/SIMPLE 進階版本" #. About note, part 2/13: home page URL (label) #: ../src/core/sipe-core.c:238 msgid "Home Page" msgstr "" #. About note, part 3/13: support forum URL (label) #: ../src/core/sipe-core.c:240 msgid "Support" msgstr "" #. About note, part 4/13: support forum name (hyperlink text) #: ../src/core/sipe-core.c:242 msgid "Help Forum" msgstr "說明論壇" #. About note, part 5/13: bug tracker URL (label) #: ../src/core/sipe-core.c:244 msgid "Report Problems" msgstr "報告問題" #. About note, part 6/13: bug tracker URL (hyperlink text) #: ../src/core/sipe-core.c:246 msgid "Bug Tracker" msgstr "錯誤追蹤者" #. About note, part 7/13: translation service URL (label) #: ../src/core/sipe-core.c:248 msgid "Translations" msgstr "翻譯" #. About note, part 8/13: license type (label) #: ../src/core/sipe-core.c:250 msgid "License" msgstr "授權" #. About note, part 9/13: (REMOVED) #. About note, part 10/13: translation request, text before Transifex.com URL #. append a space if text is not empty #: ../src/core/sipe-core.c:254 msgid "Please help us to translate SIPE to your native language here at " msgstr "請幫助我們將 SIPE 翻譯為您的原生語言,並在" #. About note, part 11/13: translation request, text after Transifex.com URL #. start with a space if text is not empty #: ../src/core/sipe-core.c:257 msgid " using convenient web interface" msgstr " 使用方便的網頁介面" #. About note, part 12/13: author list (header) #: ../src/core/sipe-core.c:259 msgid "Authors" msgstr "作者" #. About note, part 13/13: Localization credit #. PLEASE NOTE: do *NOT* simply translate the english original #. but write something similar to the following sentence: #. "Localization for (): " #: ../src/core/sipe-core.c:264 msgid "Original texts in English (en): SIPE developers" msgstr "傳統漢語本地化 (zh_TW):趙惟倫 " #: ../src/core/sipe-core.c:288 msgid "SIP Exchange user name contains invalid characters" msgstr "SIP Exchange 的使用者名稱不可含有無效字元" #: ../src/core/sipe-core.c:296 ../src/purple/purple-buddy.c:486 #: ../src/telepathy/telepathy-protocol.c:89 #: ../src/telepathy/telepathy-protocol.c:197 #, c-format msgid "" "User name should be a valid SIP URI\n" "Example: user@company.com" msgstr "使用者名稱應該是有效的 SIP URI\n範例:user@company.com" #: ../src/core/sipe-core.c:303 msgid "Password is required when Single Sign-On is not enabled" msgstr "" #: ../src/core/sipe-core.c:313 msgid "" "Email address should be valid if provided\n" "Example: user@company.com" msgstr "如果提供電子郵件地址應該是有效的\n範例:user@company.com" #: ../src/core/sipe-core.c:322 msgid "SIP Exchange user name contains whitespace" msgstr "SIP Exchange 的使用者名稱不可含有空白字元" #: ../src/core/sipe-core.c:336 msgid "" "Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf" msgstr "如果提供電子郵件服務網址應該是有效的\n範例:https://exchange.corp.com/EWS/Exchange.asmx\n範例:https://domino.corp.com/maildatabase.nsf" #. Translators: (!) should be as in localized Lotus Notes to be able to #. extract meeting location #: ../src/core/sipe-domino.c:260 ../src/core/sipe-domino.c:261 msgid "Location:" msgstr "位置:" #: ../src/core/sipe-ft.c:264 ../src/core/sipe-ft-tftp.c:109 #: ../src/core/sipe-ft-tftp.c:393 ../src/core/sipe-ft-tftp.c:422 #: ../src/core/sipe-ft-tftp.c:481 msgid "Socket read failed" msgstr "通訊端讀取失敗" #: ../src/core/sipe-ft.c:412 msgid "Received encryption key has wrong size." msgstr "接收到的加密金鑰大小有誤。" #: ../src/core/sipe-ft.c:427 msgid "Received hash key has wrong size." msgstr "接收到的雜湊鍵大小有誤。" #: ../src/core/sipe-ft.c:447 msgid "Could not create listen socket" msgstr "無法建立監聽通訊端" #: ../src/core/sipe-ft-lync.c:762 ../src/core/sipe-media.c:1485 #: ../src/core/sipe-media.c:1496 ../src/core/sipe-media.c:1576 #: ../src/core/sipe-media.c:2096 msgid "Error occurred" msgstr "" #: ../src/core/sipe-ft-lync.c:763 msgid "Error creating data stream" msgstr "" #: ../src/core/sipe-ft-tftp.c:115 ../src/core/sipe-ft-tftp.c:525 #: ../src/core/sipe-ft-tftp.c:534 msgid "Socket write failed" msgstr "通訊端寫入失敗" #: ../src/core/sipe-ft-tftp.c:208 msgid "File size is different from the advertised value." msgstr "檔案大小與宣稱的數值不同。" #: ../src/core/sipe-ft-tftp.c:247 msgid "Received MAC is corrupted" msgstr "接收到的 MAC 已損壞" #: ../src/core/sipe-ft-tftp.c:258 msgid "Received file is corrupted" msgstr "接收到的檔案已損壞" #: ../src/core/sipe-ft-tftp.c:287 msgid "File transfer initialization failed." msgstr "檔案傳送初始化失敗。" #: ../src/core/sipe-ft-tftp.c:318 msgid "File transfer authentication failed." msgstr "檔案傳送認證失敗。" #: ../src/core/sipe-ft-tftp.c:414 ../src/core/sipe-ft-tftp.c:432 #: ../src/core/sipe-ft-tftp.c:495 msgid "Out of memory" msgstr "記憶體不足" #: ../src/core/sipe-group.c:168 ../src/core/sipe-notify.c:1173 #: ../src/core/sipe-notify.c:1207 ../src/core/sipe-notify.c:1311 #: ../src/purple/purple-search.c:128 msgid "Other Contacts" msgstr "其他聯絡人" #: ../src/core/sipe-groupchat.c:329 #, c-format msgid "" "Group Chat Proxy setting is incorrect:\n" "\n" "\t%s\n" "\n" "Please update your Account." msgstr "群組聊天室代理伺服器設不正確:\n\n\t%s\n\n請更新您的帳戶。" #: ../src/core/sipe-groupchat.c:332 msgid "Couldn't find Group Chat server!" msgstr "無法找到" #: ../src/core/sipe-groupchat.c:508 #, c-format msgid "This message was not delivered to chat room '%s'" msgstr "這個訊息未被投遞到聊天室 '%s'" #: ../src/core/sipe-groupchat.c:616 msgid "Error retrieving room list" msgstr "擷取聊天室清單時發生錯誤" #: ../src/core/sipe-groupchat.c:720 msgid "Error joining chat room" msgstr "加入聊天室時發生錯誤" #. generate one error and remove all unprocessed messages #: ../src/core/sipe-im.c:197 #, c-format msgid "Failed to invite %s" msgstr "邀請 %s 時失敗" #: ../src/core/sipe-incoming.c:763 #, c-format msgid "Received a message with unrecognized contents from %s" msgstr "從 %s 接收到內容無法辨識的訊息" #: ../src/core/sipe-media.c:870 msgid "Couldn't create stream" msgstr "" #: ../src/core/sipe-media.c:871 msgid "Connection timed out" msgstr "" #: ../src/core/sipe-media.c:915 msgid "Request timed out" msgstr "" #: ../src/core/sipe-media.c:916 msgid "Call could not be answered" msgstr "" #: ../src/core/sipe-media.c:1007 ../src/core/sipe-media.c:1644 #: ../src/core/sipe-media.c:1654 ../src/core/sipe-media.c:2074 #: ../src/core/sipe-media.c:2097 msgid "Unable to establish a call" msgstr "無法建立呼叫" #: ../src/core/sipe-media.c:1008 ../src/core/sipe-media.c:2075 msgid "Encryption settings of peer are incompatible with ours." msgstr "對方的加密設定值與我們的不相容。" #: ../src/core/sipe-media.c:1103 #, c-format msgid "User %s rejected call" msgstr "使用者 %s 已拒絕呼叫" #: ../src/core/sipe-media.c:1104 msgid "Call rejected" msgstr "呼叫被拒絕" #: ../src/core/sipe-media.c:1486 ../src/core/sipe-media.c:1577 msgid "Error creating audio stream" msgstr "建立音訊串流時發生錯誤" #: ../src/core/sipe-media.c:1497 msgid "Error creating video stream" msgstr "建立視訊串流時發生錯誤" #: ../src/core/sipe-media.c:1542 ../src/purple/purple-chat.c:353 msgid "Join conference call" msgstr "加入會議談話" #: ../src/core/sipe-media.c:1543 msgid "Conference calls are not supported on this server." msgstr "" #: ../src/core/sipe-media.c:1645 msgid "Invalid phone number" msgstr "" #: ../src/core/sipe-media.c:1655 msgid "Audio Test Service is not available." msgstr "" #: ../src/core/sipe-media.c:2040 msgid "User unavailable" msgstr "使用者無法聯繫" #: ../src/core/sipe-media.c:2043 #, c-format msgid "%s does not want to be disturbed" msgstr "%s 不想被打擾" #: ../src/core/sipe-media.c:2045 #, c-format msgid "User %s is not available" msgstr "使用者 %s 無法聯繫" #: ../src/core/sipe-media.c:2057 msgid "Unsupported media type" msgstr "" #: ../src/core/sipe-media.c:2421 msgid "Media error" msgstr "" #: ../src/core/sipe-media.c:2422 msgid "Error while reading from stream" msgstr "" #. reason = g_strdup(_("User logged out")); // [MS-OCER] #: ../src/core/sipe-notify.c:1151 msgid "you are already signed in at another location" msgstr "您已經在另一個地點登入了" #: ../src/core/sipe-notify.c:1153 msgid "user disabled" msgstr "使用者已停用" #: ../src/core/sipe-notify.c:1155 msgid "user moved" msgstr "使用者已移動" #: ../src/core/sipe-ocs2007.c:462 ../src/purple/purple-buddy.c:641 #: ../src/purple/purple-buddy.c:648 msgid "Blocked" msgstr "已阻斷" #: ../src/core/sipe-ocs2007.c:463 ../src/purple/purple-buddy.c:637 #: ../src/purple/purple-buddy.c:644 msgid "Personal" msgstr "個人" #. index 0 #: ../src/core/sipe-ocs2007.c:464 ../src/purple/purple-buddy.c:638 #: ../src/purple/purple-buddy.c:645 msgid "Team" msgstr "小組" #. SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY #. SIPE_BUDDY_INFO_COMPANY #: ../src/core/sipe-ocs2007.c:465 ../src/purple/purple-buddy.c:76 #: ../src/purple/purple-buddy.c:639 ../src/purple/purple-buddy.c:646 #: ../src/purple/purple-search.c:66 ../src/purple/purple-search.c:229 msgid "Company" msgstr "公司" #: ../src/core/sipe-ocs2007.c:466 ../src/purple/purple-buddy.c:640 #: ../src/purple/purple-buddy.c:647 msgid "Public" msgstr "公用" #: ../src/core/sipe-ocs2007.c:468 msgid "Unknown" msgstr "不明" #. Translators: remove (clear) previously assigned access level #: ../src/core/sipe-ocs2007.c:2590 msgid "Unspecify" msgstr "未定義" #: ../src/core/sipe-ocs2007.c:2609 msgid "People in my company" msgstr "公司同事" #: ../src/core/sipe-ocs2007.c:2619 msgid "People in domains connected with my company" msgstr "與公司有連結的認證域名中的人" #: ../src/core/sipe-ocs2007.c:2628 msgid "People in public domains" msgstr "位於公共認證域名中的人" #: ../src/core/sipe-ocs2007.c:2638 #, c-format msgid "People at %s" msgstr "位於 %s 中的人" #: ../src/core/sipe-ocs2007.c:2663 msgid "Add new domain..." msgstr "加入新的認證域名…" #: ../src/core/sipe-ocs2007.c:2692 msgid "Online help..." msgstr "線上說明…" #: ../src/core/sipe-ocs2007.c:2700 msgid "Access groups" msgstr "存取群組" #. * This has nothing to do with Availability numbers, like 3500 (online). #. * Just a mapping of Communicator Activities to tokens/translations #. @TODO: NULL means "default translation from Pidgin"? #. * What about other backends? #. SIPE_ACTIVITY_UNSET #. SIPE_ACTIVITY_AVAILABLE #. SIPE_ACTIVITY_ONLINE #. SIPE_ACTIVITY_INACTIVE #: ../src/core/sipe-status.c:56 msgid "Inactive" msgstr "非作用中" #. SIPE_ACTIVITY_BUSYIDLE #: ../src/core/sipe-status.c:58 msgid "Busy-Idle" msgstr "忙碌-空閒" #. SIPE_ACTIVITY_DND #. SIPE_ACTIVITY_BRB #: ../src/core/sipe-status.c:60 msgid "Be right back" msgstr "馬上回來" #. SIPE_ACTIVITY_AWAY #. SIPE_ACTIVITY_LUNCH #: ../src/core/sipe-status.c:62 msgid "Out to lunch" msgstr "外出午餐" #. SIPE_ACTIVITY_INVISIBLE #. SIPE_ACTIVITY_OFFLINE #. SIPE_ACTIVITY_ON_PHONE #: ../src/core/sipe-status.c:65 msgid "In a call" msgstr "電話中" #. SIPE_ACTIVITY_IN_CONF #: ../src/core/sipe-status.c:66 msgid "In a conference" msgstr "會議中" #. SIPE_ACTIVITY_IN_MEETING #: ../src/core/sipe-status.c:67 msgid "In a meeting" msgstr "談話中" #. SIPE_ACTIVITY_URGENT_ONLY #: ../src/core/sipe-status.c:69 msgid "Urgent interruptions only" msgstr "非急勿擾" #. SIPE_ACTIVITY_IN_PRES #: ../src/core/sipe-status.c:70 msgid "Presenting" msgstr "" #: ../src/core/sipe-subscriptions.c:193 msgid "Presence subscription failed!" msgstr "" #: ../src/core/sipe-subscriptions.c:194 msgid "" "One or more buddies will therefore permanently show as offline.\n" "\n" "Please check that there are no corrupted SIP URIs in your contacts list." msgstr "" #: ../src/core/sipe-ucs.c:678 msgid "UCS initialization failed!" msgstr "" #: ../src/core/sipe-ucs.c:680 msgid "" "Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n" "\n" "You'll need to provide Email settings in the account setup." msgstr "" #: ../src/core/sipe-ucs.c:681 msgid "" "Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n" "\n" "Please correct your Email settings." msgstr "" #. Service unavailable; Server Internal Error; Server Time-out #. Not acceptable all. #. Message contents not allowed by policy #: ../src/core/sipe-user.c:78 msgid "" "Your message or invitation was not delivered, possibly because it contains a" " hyperlink or other content that the system administrator has blocked." msgstr "您的訊息或邀請未被投遞,可能因為它含有超連結或其他內容,所以被系統管理員阻斷。" #: ../src/core/sipe-user.c:82 #, c-format msgid "" "This message was not delivered to %s because the service is not available" msgstr "這個訊息未被投遞到 %s,因為服務無法使用" #. Busy Here #: ../src/core/sipe-user.c:84 #, c-format msgid "" "This message was not delivered to %s because one or more recipients do not " "want to be disturbed" msgstr "這個訊息未被投遞到 %s,因為一或多位收訊者不想被打擾" #. Unsupported media type #: ../src/core/sipe-user.c:86 #, c-format msgid "" "This message was not delivered to %s because one or more recipients don't " "support this type of message" msgstr "這個訊息未被投遞到 %s,因為一或多位收訊者不支援這個訊息型態" #: ../src/core/sipe-user.c:88 #, c-format msgid "" "This message was not delivered to %s because one or more recipients are " "offline" msgstr "這個訊息未被投遞到 %s,因為一或多位收訊者在離線狀態" #. SIPE_BUDDY_INFO_DISPLAY_NAME #: ../src/purple/purple-buddy.c:67 msgid "Display name" msgstr "顯示名稱" #. SIPE_BUDDY_INFO_JOB_TITLE #: ../src/purple/purple-buddy.c:68 msgid "Job title" msgstr "職稱" #. SIPE_BUDDY_INFO_CITY #: ../src/purple/purple-buddy.c:69 msgid "City" msgstr "城市" #. SIPE_BUDDY_INFO_STATE #: ../src/purple/purple-buddy.c:70 msgid "State" msgstr "省/州" #. SIPE_BUDDY_INFO_OFFICE #: ../src/purple/purple-buddy.c:71 msgid "Office" msgstr "辦公室" #. SIPE_BUDDY_INFO_DEPARTMENT #. SIPE_BUDDY_INFO_COUNTRY #: ../src/purple/purple-buddy.c:73 ../src/purple/purple-search.c:69 #: ../src/purple/purple-search.c:231 msgid "Country" msgstr "國家/地區" #. SIPE_BUDDY_INFO_WORK_PHONE #: ../src/purple/purple-buddy.c:74 msgid "Business phone" msgstr "業務電話" #. SIPE_BUDDY_INFO_EMAIL #: ../src/purple/purple-buddy.c:77 msgid "Email address" msgstr "電子郵件地址" #. SIPE_BUDDY_INFO_SITE #: ../src/purple/purple-buddy.c:78 msgid "Site" msgstr "站臺" #. SIPE_BUDDY_INFO_ZIPCODE #. SIPE_BUDDY_INFO_STREET #. SIPE_BUDDY_INFO_MOBILE_PHONE #. SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY #. SIPE_BUDDY_INFO_HOME_PHONE #. SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY #. SIPE_BUDDY_INFO_OTHER_PHONE #. SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY #. SIPE_BUDDY_INFO_CUSTOM1_PHONE #. SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY #. SIPE_BUDDY_INFO_ALIAS #: ../src/purple/purple-buddy.c:89 msgid "Alias" msgstr "別名" #. SIPE_BUDDY_INFO_DEVICE #: ../src/purple/purple-buddy.c:90 msgid "Device" msgstr "裝置" #: ../src/purple/purple-buddy.c:260 ../src/purple/purple-buddy.c:278 msgid "you" msgstr "您" #: ../src/purple/purple-buddy.c:585 msgid "" "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" msgstr "https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels" #: ../src/purple/purple-buddy.c:627 msgid "Domain" msgstr "認證域名" #: ../src/purple/purple-buddy.c:656 ../src/purple/purple-buddy.c:657 msgid "Add new domain" msgstr "加入新的認證域名" #: ../src/purple/purple-buddy.c:658 msgid "Add" msgstr "加入" #: ../src/purple/purple-buddy.c:659 ../src/purple/purple-user.c:141 msgid "Cancel" msgstr "取消" #: ../src/purple/purple-buddy.c:839 msgid "Copy to" msgstr "複製到" #: ../src/purple/purple-chat.c:330 msgid "Lock" msgstr "鎖定" #: ../src/purple/purple-chat.c:335 msgid "Unlock" msgstr "解除鎖定" #: ../src/purple/purple-chat.c:363 msgid "Show presentation" msgstr "" #: ../src/purple/purple-chat.c:378 msgid "Meeting entry info" msgstr "" #: ../src/purple/purple-groupchat.c:56 msgid "_URI:" msgstr "網址(_U):" #: ../src/purple/purple-groupchat.c:160 msgid "Users" msgstr "使用者" #: ../src/purple/purple-groupchat.c:163 msgid "Invite" msgstr "邀請" #: ../src/purple/purple-groupchat.c:166 msgid "Private" msgstr "私人" #: ../src/purple/purple-groupchat.c:169 msgid "Log" msgstr "記錄" #: ../src/purple/purple-groupchat.c:172 msgid "Description" msgstr "描述" #: ../src/purple/purple-im.c:86 #, c-format msgid "Conversation subject: %s" msgstr "" #. Macro to reduce code repetition #. Translators: noun #: ../src/purple/purple-plugin-common.c:193 msgid "Message" msgstr "訊息" #: ../src/purple/purple-plugin-common.c:440 msgid "Connecting" msgstr "連線中" #: ../src/purple/purple-plugin-common.c:470 msgid "Password required" msgstr "" #: ../src/purple/purple-plugin-common.c:629 msgid "user@company.com" msgstr "user@company.com" #: ../src/purple/purple-plugin-common.c:748 msgid "Phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:752 #: ../src/purple/purple-plugin-common.c:753 msgid "Call a phone number" msgstr "" #: ../src/purple/purple-plugin-common.c:756 msgid "_Call" msgstr "" #: ../src/purple/purple-plugin-common.c:757 #: ../src/purple/purple-plugin-common.c:805 ../src/purple/purple-search.c:240 msgid "_Cancel" msgstr "取消(_C)" #: ../src/purple/purple-plugin-common.c:784 msgid "Meeting location" msgstr "" #: ../src/purple/purple-plugin-common.c:786 msgid "Alternatively" msgstr "" #: ../src/purple/purple-plugin-common.c:788 msgid "Organizer email" msgstr "" #: ../src/purple/purple-plugin-common.c:790 msgid "Meeting ID" msgstr "" #: ../src/purple/purple-plugin-common.c:794 msgid "Join conference" msgstr "" #: ../src/purple/purple-plugin-common.c:795 msgid "Join scheduled conference" msgstr "" #: ../src/purple/purple-plugin-common.c:796 msgid "" "Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234" msgstr "" #: ../src/purple/purple-plugin-common.c:804 msgid "_Join" msgstr "" #: ../src/purple/purple-plugin-common.c:819 #: ../src/purple/purple-plugin-common.c:837 msgid "Publishing of calendar information has been disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:856 msgid "About SIPE plugin..." msgstr "關於 SIPE 外掛程式…" #: ../src/purple/purple-plugin-common.c:859 msgid "Contact search..." msgstr "聯絡人搜尋…" #: ../src/purple/purple-plugin-common.c:863 msgid "Call a phone number..." msgstr "" #: ../src/purple/purple-plugin-common.c:866 msgid "Test call" msgstr "" #: ../src/purple/purple-plugin-common.c:870 msgid "Join scheduled conference..." msgstr "" #: ../src/purple/purple-plugin-common.c:873 msgid "Republish Calendar" msgstr "重新發布行事曆" #: ../src/purple/purple-plugin-common.c:876 msgid "Reset status" msgstr "重置狀態" #. * #. * When adding new string settings please make sure to keep these #. * in sync: #. * #. * api/sipe-backend.h #. * purple-settings.c:setting_name[] #: ../src/purple/purple-plugin-common.c:894 msgid "" "Server[:Port]\n" "(leave empty for auto-discovery)" msgstr "伺服器[:通訊埠]\n(留空表示自動搜索)" #: ../src/purple/purple-plugin-common.c:897 msgid "Connection type" msgstr "連線類型" #: ../src/purple/purple-plugin-common.c:898 #: ../src/purple/purple-plugin-common.c:910 msgid "Auto" msgstr "自動" #: ../src/purple/purple-plugin-common.c:899 msgid "SSL/TLS" msgstr "SSL/TLS" #: ../src/purple/purple-plugin-common.c:900 msgid "TCP" msgstr "TCP" #. option = purple_account_option_bool_new(_("Publish status (note: everyone #. may watch you)"), "doservice", TRUE); #. sipe_prpl_info.protocol_options = #. g_list_append(sipe_prpl_info.protocol_options, option); #: ../src/purple/purple-plugin-common.c:906 msgid "User Agent" msgstr "使用者代理" #: ../src/purple/purple-plugin-common.c:909 msgid "Authentication scheme" msgstr "" #: ../src/purple/purple-plugin-common.c:911 msgid "NTLM" msgstr "" #: ../src/purple/purple-plugin-common.c:913 msgid "Kerberos" msgstr "" #: ../src/purple/purple-plugin-common.c:915 msgid "TLS-DSK" msgstr "" #. * When the user selects Single Sign-On then SIPE will ignore the #. * settings for "login name" and "password". Instead it will use the #. * default credentials provided by the OS. #. * #. * NOTE: the default must be *OFF*, i.e. it is up to the user to tell #. * SIPE that it is OK to use Single Sign-On or not. #. * #. * Configurations that are known to support Single Sign-On: #. * #. * - Windows, host joined to domain, SIPE with SSPI: NTLM #. * - Windows, host joined to domain, SIPE with SSPI: Kerberos #. * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos #: ../src/purple/purple-plugin-common.c:933 msgid "Use Single Sign-On" msgstr "使用單一登入" #. * Example (Exchange): https://server.company.com/EWS/Exchange.asmx #. * Example (Domino) : https://[domino_server]/[mail_database_name].nsf #: ../src/purple/purple-plugin-common.c:940 msgid "Don't publish my calendar information" msgstr "" #: ../src/purple/purple-plugin-common.c:943 msgid "" "Show profile pictures from web\n" "(potentially dangerous)" msgstr "" #: ../src/purple/purple-plugin-common.c:946 msgid "" "Email services URL\n" "(leave empty for auto-discovery)" msgstr "電子郵件服務網址\n(留空表示自動搜索)" #: ../src/purple/purple-plugin-common.c:949 msgid "" "Email address\n" "(if different from Username)" msgstr "電子郵件地址\n(如果與使用者名稱不同)" #. * Example (Exchange): DOMAIN\user or user@company.com #. * Example (Domino) : email_address #: ../src/purple/purple-plugin-common.c:955 msgid "" "Email login\n" "(if different from Login)" msgstr "電子郵件帳號\n(如果與本帳號不同)" #: ../src/purple/purple-plugin-common.c:958 msgid "" "Email password\n" "(if different from Password)" msgstr "電子郵件密碼\n(如果與本密碼不同)" #. * Example (federated domain): company.com (i.e. ocschat@company.com) #. * Example (non-default user): user@company.com #: ../src/purple/purple-plugin-common.c:965 msgid "" "Group Chat Proxy\n" " company.com or user@company.com\n" "(leave empty to determine from Username)" msgstr "群組聊天室代理伺服器\n company.com 或 user@company.com\n(留空表示由使用者名稱決定)" #: ../src/purple/purple-plugin-common.c:969 msgid "Remote desktop client" msgstr "" #: ../src/purple/purple-plugin-common.c:974 msgid "Media encryption" msgstr "" #: ../src/purple/purple-plugin-common.c:975 msgid "Obey server policy" msgstr "" #: ../src/purple/purple-plugin-common.c:976 msgid "Always" msgstr "" #: ../src/purple/purple-plugin-common.c:977 msgid "Optional" msgstr "" #: ../src/purple/purple-plugin-common.c:978 msgid "Disabled" msgstr "" #: ../src/purple/purple-plugin-common.c:988 msgid "" "Login\n" " user or DOMAIN\\user or\n" " user@company.com" msgstr "帳號\n 使用者 或 認證域名\\使用者 或\n user@company.com" #: ../src/purple/purple-search.c:60 msgid "User name" msgstr "使用者名稱" #: ../src/purple/purple-search.c:63 msgid "Name" msgstr "名稱" #: ../src/purple/purple-search.c:72 ../src/purple/purple-search.c:225 msgid "Email" msgstr "電子郵件" #: ../src/purple/purple-search.c:221 msgid "First name" msgstr "名字" #: ../src/purple/purple-search.c:223 msgid "Last name" msgstr "姓氏" #: ../src/purple/purple-search.c:227 msgid "SIP ID" msgstr "" #: ../src/purple/purple-search.c:235 msgid "Search" msgstr "搜尋" #: ../src/purple/purple-search.c:236 msgid "Search for a contact" msgstr "搜尋聯絡人" #: ../src/purple/purple-search.c:237 msgid "" "Enter the information for the person you wish to find. Empty fields will be " "ignored." msgstr "輸入想要尋找的人員資訊。將忽略空白欄位。" #: ../src/purple/purple-search.c:239 msgid "_Search" msgstr "搜尋(_S)" #: ../src/purple/purple-transport.c:153 msgid "Read error" msgstr "讀取錯誤" #: ../src/purple/purple-transport.c:157 #: ../src/telepathy/telepathy-transport.c:96 msgid "Server has disconnected" msgstr "伺服器已斷線" #: ../src/purple/purple-transport.c:285 msgid "Could not connect" msgstr "無法連線" #: ../src/purple/purple-transport.c:355 msgid "Could not create SSL context" msgstr "無法建立 SSL 內容" #: ../src/purple/purple-transport.c:377 msgid "Could not create socket" msgstr "無法建立通訊端" #: ../src/purple/purple-transport.c:473 msgid "Write error" msgstr "寫入錯誤" #: ../src/purple/purple-user.c:140 msgid "OK" msgstr "" ================================================ FILE: siplcs.vcxproj ================================================ Debug Unicode Win32 Debug Win32 Release Unicode Win32 Release Win32 siplcs {B86C74FF-7A76-46EA-860C-9AEDD070CEAB} libsipe DynamicLibrary false Unicode DynamicLibrary false Unicode Application MultiByte true Application MultiByte <_ProjectFileVersion>10.0.40219.1 $(SolutionDir)$(Configuration)/plugins\ $(Configuration)\ $(SolutionDir)$(Configuration)/Plugins\ $(Configuration)\ $(SolutionDir)$(Configuration)/Plugins\ $(SolutionDir)$(Configuration)/Obj/$(ProjectName)\ $(SolutionDir)$(Configuration)/Plugins\ $(SolutionDir)$(Configuration)/Obj/$(ProjectName)\ Disabled $(SolutionDir)/../include;$(ProjectDir)/src/api;%(AdditionalIncludeDirectories) true EnableFastChecks MultiThreadedDebugDLL Level3 EditAndContinue _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true MachineX86 MaxSpeed true MultiThreadedDLL true Level3 ProgramDatabase true true true MachineX86 Full $(SolutionDir)/../include;$(ProjectDir)/src/api;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;PACKAGE_NAME="pidgin-sipe";PACKAGE_VERSION="1.25.0";PACKAGE_URL="http://sipe.sourceforge.net/";PACKAGE_BUGREPORT="https://sourceforge.net/p/sipe/bugs/";SIPE_VERSION="1.25.0";_CRT_SECURE_NO_WARNINGS;HAVE_NSS;HAVE_GMIME;HAVE_VV;ENABLE_NLS;MIRANDA;HAVE_GSSAPI_GSSAPI_H;HAVE_SSPI;SECURITY_WIN32;SIPE_TRANSLATIONS_URL="https://www.transifex.com/stefanb/pidgin-sipe/";%(PreprocessorDefinitions) MultiThreadedDLL Level3 ProgramDatabase glib-2.0.lib;gobject-2.0.lib;libxml2.lib;Ws2_32.lib;nss3.lib;smime3.lib;iconv.lib;intl.lib;gmime-2.6.lib;nspr4.lib;%(AdditionalDependencies) %(IgnoreSpecificDefaultLibraries) true MachineX86 Disabled $(SolutionDir)/../include;$(ProjectDir)/src/core;$(ProjectDir)/src/api;$(ProjectDir)/src/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;PACKAGE_NAME="pidgin-sipe";PACKAGE_VERSION="1.25.0";PACKAGE_URL="http://sipe.sourceforge.net/";SIPE_TRANSLATIONS_URL="https://www.transifex.com/stefanb/pidgin-sipe/";PACKAGE_BUGREPORT="https://sourceforge.net/p/sipe/bugs/";SIPE_VERSION="1.25.0";_CRT_SECURE_NO_WARNINGS;HAVE_NSS;HAVE_GMIME;HAVE_VV;ENABLE_NLS;MIRANDA;HAVE_GSSAPI_GSSAPI_H;SECURITY_WIN32;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDLL $(TargetDir)$(TargetName).pdb Level3 EditAndContinue glib-2.0.lib;gobject-2.0.lib;libxml2.lib;Ws2_32.lib;nss3.lib;smime3.lib;nspr4.lib;gmime-2.6-debug.lib;iconv.lib;intl.lib;%(AdditionalDependencies) true MachineX86 %(IgnoreSpecificDefaultLibraries) ================================================ FILE: siplcs.vcxproj.filters ================================================  {0f7082ea-15e2-4067-b9ba-b9b12a41ef5d} {0e9e11e0-1f11-49f8-8f6c-403264973d12} {1985067e-9fcc-4a0e-a02a-79ae17f356cf} core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda miranda core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core core api api api api api miranda miranda miranda core core core core miranda ================================================ FILE: src/Makefile.am ================================================ SUBDIRS = core api if SIPE_INCLUDE_PURPLE SUBDIRS += purple endif if SIPE_INCLUDE_TELEPATHY SUBDIRS += telepathy endif EXTRA_DIST = \ adium \ miranda \ Makefile.mingw MAINTAINERCLEANFILES = \ Makefile.in ================================================ FILE: src/Makefile.mingw ================================================ OLD_PIDGIN_TREE_TOP := $(PIDGIN_TREE_TOP) PIDGIN_TREE_TOP := ../$(OLD_PIDGIN_TREE_TOP) include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak .PHONY: all clean install all: $(MAKE) -C core -f $(MINGW_MAKEFILE) clean: $(MAKE) -C core -f $(MINGW_MAKEFILE) clean rmbak: rm -f *~ $(MAKE) -C core -f $(MINGW_MAKEFILE) rmbak install: all $(MAKE) -C core -f $(MINGW_MAKEFILE) install tests: all $(MAKE) -C core -f $(MINGW_MAKEFILE) tests ================================================ FILE: src/adium/DCPurpleSIPEJoinChatViewController.h ================================================ // // DCPurpleSIPEJoinChatViewController.h // SIPEAdiumPlugin // // Created by Michael Lamb on 02/10/12. // Copyright 2012 Michael Lamb. All rights reserved. // #import #import @class AICompletingTextField; @interface DCPurpleSIPEJoinChatViewController : DCJoinChatViewController { IBOutlet NSComboBox *combo_rooms; IBOutlet NSProgressIndicator *progress_fetch; NSTimer *timer; NSMutableDictionary *room_dict; struct _PurpleRoomlist *room_list; } @end ================================================ FILE: src/adium/DCPurpleSIPEJoinChatViewController.m ================================================ // // DCPurpleSIPEJoinChatViewController.m // SIPEAdiumPlugin // // Copyright (C) 2015 SIPE Project // // Created by Michael Lamb on 02/10/12. // Copyright 2012 Michael Lamb. All rights reserved. // #import "DCPurpleSIPEJoinChatViewController.h" #import #import #import #import #import #import "roomlist.h" @implementation DCPurpleSIPEJoinChatViewController - (id)init { self = [super init]; if (self) { room_dict = [[NSMutableDictionary alloc] init]; combo_rooms.usesDataSource = YES; combo_rooms.completes = YES; combo_rooms.dataSource = self; } return self; } - (void)dealloc { [timer invalidate]; timer = nil; if (room_list != NULL) { purple_roomlist_unref(room_list); room_list = NULL; } [room_dict release]; [super dealloc]; } - (void)configureForAccount:(AIAccount *)inAccount { [super configureForAccount:inAccount]; if ( delegate ) { [(DCJoinChatWindowController *)delegate setJoinChatEnabled:YES]; } // get room list if (room_list == NULL) { // we want to run that code only once (configureForAccount is called twice actually) CBPurpleAccount *pinAccount = (CBPurpleAccount*)inAccount; room_list = purple_roomlist_get_list(pinAccount.purpleAccount->gc); if (room_list) { purple_roomlist_ref(room_list); [progress_fetch startAnimation:self]; // start a timer to control when the fetching is done timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(checkForRoomlistFetchCompletion:) userInfo:nil repeats:YES]; } else { [progress_fetch setHidden:YES]; AILog(@"(DCPurpleSIPEJoinChatViewController) Can't fetch room list."); } } } - (void)joinChatWithAccount:(AIAccount *)inAccount { NSString *uri = nil; NSInteger idx = [combo_rooms indexOfSelectedItem]; if (idx >= 0 && [room_dict count]) { // get selected entry NSString *key = [[room_dict allKeys] objectAtIndex:idx]; if (key) uri = [room_dict valueForKey:key]; } else uri = [combo_rooms stringValue]; if (uri && [uri length]) { NSRange res = [uri rangeOfString:@"ma-chan://" options:NSCaseInsensitiveSearch]; if (res.location != 0) { NSAlert *alert = [[NSAlert alloc] init]; [alert setMessageText:@"Invalid room URI"]; [alert setInformativeText:[combo_rooms toolTip]]; [alert addButtonWithTitle:@"Ok"]; [alert runModal]; [alert release]; } else { [self doJoinChatWithName:[NSString stringWithFormat:@"%@",uri] onAccount:inAccount chatCreationInfo:[NSDictionary dictionaryWithObjectsAndKeys: uri, @"uri", nil] invitingContacts:nil withInvitationMessage:nil]; } } else { AILog(@"(DCPurpleSIPEJoinChatViewController) No URI specified."); } // TODO: allow creation of OCS "Conference" (different from group-chat) // Add a text field (UI should have radio buttons to enable/disable // create a PurpleBuddy* based off of the username entered in the text field // then call: // sipe_core_buddy_new_chat(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, purple_buddy_get_name(buddy)); // which should kick off the Adium code to open a chat window. } - (void)checkForRoomlistFetchCompletion:(NSTimer*) aTimer { if (room_list && purple_roomlist_get_in_progress(room_list) == FALSE) { [progress_fetch stopAnimation:self]; [progress_fetch setHidden:YES]; [timer invalidate]; timer = nil; // finally copy the list into our dict for (GList *rooms = room_list->rooms; rooms != NULL; rooms = rooms->next) { PurpleRoomlistRoom *room = rooms->data; gchar *roomName = room->name; gchar *uriStr = room->fields->data; if (roomName != NULL && uriStr != NULL) { NSString *nameStr = [NSString stringWithUTF8String:roomName]; if ([room_dict objectForKey:nameStr] == nil) { [room_dict setObject:[NSString stringWithUTF8String:uriStr] forKey:nameStr]; } } } purple_roomlist_unref(room_list); room_list = NULL; } } #pragma mark NSComboBoxDataSource - (NSInteger) numberOfItemsInComboBox:(NSComboBox*) aComboBox { return [room_dict count]; } - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index { NSArray* keys = [room_dict allKeys]; return [keys objectAtIndex:index]; } // String completion - (NSString *)comboBox:(NSComboBox *)aComboBox completedString:(NSString *)uncompletedString { if ([room_dict count] == 0 || uncompletedString == nil) { return @""; } NSArray *keys = [room_dict allKeys]; for (NSString *key in keys) { NSRange res = [key rangeOfString:uncompletedString options:NSCaseInsensitiveSearch]; if (res.location != NSNotFound && res.location == 0) { return key; } } return @""; } - (NSUInteger)comboBox:(NSComboBox *)aComboBox indexOfItemWithStringValue:(NSString *)aString { if ([room_dict count] == 0) { return NSNotFound; } NSArray *keys = [room_dict allKeys]; return [keys indexOfObjectIdenticalTo:aString]; } #pragma mark - (NSString *)nibName { return @"DCPurpleSIPEJoinChatView"; } @end ================================================ FILE: src/adium/ESPurpleSIPEAccount.h ================================================ // // ESSIPEAccount.h // SIPEAdiumPlugin // // Created by Matt Meissner on 10/30/09. // Modified by Michael Lamb on 2/27/13 // Copyright 2013 Michael Lamb/Harris Kauffman. All rights reserved. // #import #define KEY_SIPE_WINDOWS_LOGIN @"SIPE:Windows Login" #define KEY_SIPE_CONNECT_HOST @"SIPE:Connect Host" #define KEY_SIPE_PASSWORD @"SIPE:Password" // TODO: Do we need to keep this key? PurpleAccount should store this for us #define KEY_SIPE_CONNECTION_TYPE @"SIPE:Connection Type" #define KEY_SIPE_EMAIL @"SIPE:Email" #define KEY_SIPE_EMAIL_LOGIN @"SIPE:Email Login" #define KEY_SIPE_EMAIL_URL @"SIPE:Email URL" #define KEY_SIPE_EMAIL_PASSWORD @"SIPE:Email Password" #define KEY_SIPE_GROUP_CHAT_PROXY @"SIPE:Group Chat Proxy" #define KEY_SIPE_USER_AGENT @"SIPE:User Agent" #define KEY_SIPE_SINGLE_SIGN_ON @"SIPE:Single Sign On" #define KEY_SIPE_DONT_PUBLISH @"SIPE:Dont Publish" #define KEY_SIPE_AUTH_SCHEME @"SIPE:Authentication Scheme" #define KEY_SIPE_AUTODISCOVER @"SIPE:Autodiscover" #define KEY_SIPE_BEAST_DISABLE @"SIPE:BEAST Disable" #define KEY_SIPE_ALLOW_WEB_PHOTO @"SIPE:Allow Web Photo" #define PURPLE_SSL_CDSA_BEAST_TLS_WORKAROUND "ssl_cdsa_beast_tls_workaround" @interface ESPurpleSIPEAccount : CBPurpleAccount { NSDictionary *adium_to_sipe_status; NSDictionary *sipe_to_adium_status; } @end ================================================ FILE: src/adium/ESPurpleSIPEAccount.m ================================================ // // ESSIPEAccount.m // SIPEAdiumPlugin // // Copyright (C) 2015 SIPE Project // // Created by Matt Meissner on 10/30/09. // Modified by Michael Lamb on 2/27/13 // Copyright 2013 Michael Lamb/Harris Kauffman. All rights reserved. // #import #import #import #import #import #import "AIContactController.h" #import "ESPurpleSIPEAccount.h" #import "ESSIPEService.h" #include "sipe-core.h" #include "sipe-backend.h" #include "purple-private.h" @class AICoreComponentLoader; @implementation ESPurpleSIPEAccount - (void)initAccount { [super initAccount]; sipe_to_adium_status = [[NSDictionary alloc] initWithObjectsAndKeys: STATUS_NAME_AVAILABLE, @"available", //SIPE_ACTIVITY_AVAILABLE STATUS_NAME_AVAILABLE, @"online", //SIPE_ACTIVITY_ONLINE STATUS_NAME_AWAY, @"idle", //SIPE_ACTIVITY_INACTIVE STATUS_NAME_BUSY, @"busy", //SIPE_ACTIVITY_BUSY STATUS_NAME_BUSY, @"busyidle", //SIPE_ACTIVITY_BUSYIDLE STATUS_NAME_DND, @"do-not-disturb", //SIPE_ACTIVITY_DND STATUS_NAME_BRB, @"be-right-back", //SIPE_ACTIVITY_BRB STATUS_NAME_AWAY, @"away", //SIPE_ACTIVITY_AWAY STATUS_NAME_LUNCH, @"out-to-lunch", //SIPE_ACTIVITY_LUNCH STATUS_NAME_INVISIBLE, @"invisible", //SIPE_ACTIVITY_INVISIBLE STATUS_NAME_OFFLINE, @"offline", //SIPE_ACTIVITY_OFFLINE STATUS_NAME_PHONE, @"on-the-phone", //SIPE_ACTIVITY_ON_PHONE STATUS_NAME_NOT_AT_DESK, @"in-a-conference", //SIPE_ACTIVITY_IN_CONF STATUS_NAME_NOT_AT_DESK, @"in-a-meeting", //SIPE_ACTIVITY_IN_MEETING STATUS_NAME_NOT_IN_OFFICE, @"out-of-office", //SIPE_ACTIVITY_OOF STATUS_NAME_AWAY_FRIENDS_ONLY, @"urgent-interruptions-only", //SIPE_ACTIVITY_URGENT_ONLY nil ]; adium_to_sipe_status = [[NSDictionary alloc] initWithObjectsAndKeys: @"available", STATUS_NAME_AVAILABLE, //SIPE_ACTIVITY_AVAILABLE @"busy", STATUS_NAME_BUSY, //SIPE_ACTIVITY_BUSY @"do-not-disturb", STATUS_NAME_DND, //SIPE_ACTIVITY_DND @"be-right-back", STATUS_NAME_BRB, //SIPE_ACTIVITY_BRB @"away", STATUS_NAME_AWAY, //SIPE_ACTIVITY_AWAY @"out-to-lunch", STATUS_NAME_LUNCH, //SIPE_ACTIVITY_LUNCH @"invisible", STATUS_NAME_INVISIBLE, //SIPE_ACTIVITY_INVISIBLE @"offline", STATUS_NAME_OFFLINE, //SIPE_ACTIVITY_OFFLINE @"on-the-phone", STATUS_NAME_PHONE, //SIPE_ACTIVITY_ON_PHONE @"in-a-meeting", STATUS_NAME_NOT_AT_DESK, //SIPE_ACTIVITY_IN_MEETING @"out-of-office", STATUS_NAME_NOT_IN_OFFICE, //SIPE_ACTIVITY_OOF @"urgent-interruptions-only", STATUS_NAME_AWAY_FRIENDS_ONLY, //SIPE_ACTIVITY_URGENT_ONLY nil ]; } - (void)dealloc { [adium_to_sipe_status release]; [sipe_to_adium_status release]; [super dealloc]; } - (const char*)protocolPlugin { return "prpl-sipe"; } - (const char *)purpleAccountName { NSString *completeUserName = [NSString stringWithUTF8String:[super purpleAccountName]]; NSString *windowsLogin =[self preferenceForKey:KEY_SIPE_WINDOWS_LOGIN group:GROUP_ACCOUNT_STATUS]; if ( ![windowsLogin isEqualToString:@""] ) { completeUserName = [NSString stringWithFormat:@"%@,%@", completeUserName, windowsLogin]; } return [completeUserName UTF8String]; } #pragma mark Account Configuration - (void)configurePurpleAccount { // Account preferences AILog(@"(ESPurpleSIPEAccount) Configuring account: %s\n", self.purpleAccountName); NSArray *myArray = [NSArray arrayWithObjects:@"auto", @"tls", @"tcp", nil]; NSDictionary *keys_to_account = [NSDictionary dictionaryWithObjectsAndKeys: @"server", KEY_SIPE_CONNECT_HOST, @"password", KEY_SIPE_PASSWORD, @"transport", KEY_SIPE_CONNECTION_TYPE, @"email", KEY_SIPE_EMAIL, @"email_login", KEY_SIPE_EMAIL_LOGIN, @"email_url", KEY_SIPE_EMAIL_URL, @"email_password", KEY_SIPE_EMAIL_PASSWORD, @"groupchat_user", KEY_SIPE_GROUP_CHAT_PROXY, @"useragent", KEY_SIPE_USER_AGENT, @"sso", KEY_SIPE_SINGLE_SIGN_ON, @"dont-publish", KEY_SIPE_DONT_PUBLISH, @"authentication", KEY_SIPE_AUTH_SCHEME, @"ssl_cdsa_beast_tls_workaround", KEY_SIPE_BEAST_DISABLE, @"allow-web-photo", KEY_SIPE_ALLOW_WEB_PHOTO, nil ]; for (NSString* key in keys_to_account) { NSString *prpl_key = [keys_to_account objectForKey:key]; id value = [self preferenceForKey:key group:GROUP_ACCOUNT_STATUS]; if ([value isKindOfClass:[NSString class]]) { if ([key isEqualToString:KEY_SIPE_CONNECT_HOST]) { if ([value isEqualToString:@""]) { /* * We're using auto-discover, i.e. we can only * determine the real server name when we have a * valid network connection. * * Unfortunately Adiums' reachability feature * requires us to specify a host even when no * network is available: * * * must be a valid DNS name * (can't use [[NSHost currentHost] ...]) * * must only be reachable via network * (can't use @"localhost") * * Hard-code a well-known host name instead. As * this is for Adium we use the obvious choice. * * NOTE: this will fail for Intranet-only users. * * See also: https://sourceforge.net/p/sipe/bugs/262 */ [self setPreference:@"adium.im" forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS]; } else { // If the user entered server:port only give the server portion to adium // otherwise the DNS lookup will fail the reachability test NSArray *server = [value componentsSeparatedByString:@":"]; [self setPreference:[server objectAtIndex:0] forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS]; } } purple_account_set_string(account, [prpl_key UTF8String], [value UTF8String]); } else if ([value isKindOfClass:[NSNumber class]]) { if ([key isEqualToString:KEY_SIPE_CONNECTION_TYPE]) { NSString *tmp = [myArray objectAtIndex:(NSUInteger)value]; purple_account_set_string(account, [prpl_key UTF8String], [tmp UTF8String]); } else { purple_account_set_bool(account, [prpl_key UTF8String], [value boolValue]); } } else { AILog(@"(ESPurpleSIPEAccount) Unknown class %@ for key %@", [value class], key); } } // Adium doesn't honor our "optional" password on account creation and will prompt if the password field is left blank, so we must force it to think there is one, but only if there isn't already a password saved if ( [[self preferenceForKey:KEY_SIPE_SINGLE_SIGN_ON group:GROUP_ACCOUNT_STATUS] boolValue] && [[self preferenceForKey:KEY_SIPE_PASSWORD group:GROUP_ACCOUNT_STATUS] isEqualToString:@""] ) { [self setPasswordTemporarily:@"placeholder"]; } } #pragma mark File transfer - (BOOL)canSendFolders { return NO; } - (void)beginSendOfFileTransfer:(ESFileTransfer *)fileTransfer { [super _beginSendOfFileTransfer:fileTransfer]; } - (void)acceptFileTransferRequest:(ESFileTransfer *)fileTransfer { [super acceptFileTransferRequest:fileTransfer]; } - (void)rejectFileReceiveRequest:(ESFileTransfer *)fileTransfer { [super rejectFileReceiveRequest:fileTransfer]; } - (void)cancelFileTransfer:(ESFileTransfer *)fileTransfer { [super cancelFileTransfer:fileTransfer]; } #pragma mark Status Messages /*! * @brief Status name to use for a Purple buddy */ - (NSString *)statusNameForPurpleBuddy:(PurpleBuddy *)buddy { NSString *statusName; PurplePresence *presence = purple_buddy_get_presence(buddy); PurpleStatus *status = purple_presence_get_active_status(presence); NSString *purpleStatusID = [NSString stringWithUTF8String:purple_status_get_id(status)]; if (!purpleStatusID) return nil; if (sipe_to_adium_status[purpleStatusID]) statusName = sipe_to_adium_status[purpleStatusID]; else { AILog(@"(ESPurpleSIPEAccount) Unknown purpleStatusID in statusNameForPurpleBuddy: %@", purpleStatusID); statusName = STATUS_NAME_OFFLINE; } return statusName; } /*! * @brief Maps purple status IDs to Adium statuses */ - (const char *)purpleStatusIDForStatus:(AIStatus *)statusState arguments:(NSMutableDictionary *)arguments { const gchar *statusID; NSString *statusName = statusState.statusName; if ( adium_to_sipe_status[statusName] ) statusID = [adium_to_sipe_status[statusName] UTF8String]; else { AILog(@"(ESPurpleSIPEAccount): Unknown statusName in purpleStatusIDForStatus: %@", statusName); statusID = [super purpleStatusIDForStatus:statusState arguments:arguments]; } return statusID; } // Improve the formatting of displayed names - (AIListContact *)contactWithUID:(NSString *)sourceUID { // give the inherited implementation a whack at finding a contact AIListContact *contact = [super contactWithUID:sourceUID]; NSRange sipURI = [sourceUID rangeOfString:@"sip:"]; if (sipURI.location != NSNotFound) { // sourceUID is of the form "sip:@". // strip out the sip: part, and try find a contact with the nice display name NSString *displayName = [sourceUID substringFromIndex:sipURI.location + sipURI.length]; // check to see if we have a contact already with the non-sip'ified username if([adium.contactController existingContactWithService:service account:self UID:displayName]) { // if we do, lets return it! contact = [adium.contactController existingContactWithService:service account:self UID:displayName]; } else { // otherwise, return the contact from super, setting formattedUID with "sip:" chopped [contact setFormattedUID:displayName notify:NotifyNow]; } } return contact; } // generate group chat's creation dictionary from purple conversation, e.g. for bookmarking - (NSDictionary *)extractChatCreationDictionaryFromConversation:(PurpleConversation *)conv { NSDictionary *dict = nil; struct sipe_core_public *sipe_public = PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC; // called during purple_serv_got_joined_chat()? struct sipe_chat_session *session = sipe_public->backend_private->adium_chat_session; // Adium might never call this method after creating the chat. Just in case... if (!session) session = sipe_purple_chat_get_session(conv); if (session) { const gchar *uri = sipe_core_chat_id(sipe_public, session); dict = [NSDictionary dictionaryWithObjectsAndKeys: [NSString stringWithUTF8String:uri], @"uri", nil ]; } else { AILog(@"(ESPurpleSIPEAccount) Can't determine chat session in extractChatCreationDictionaryFromConversation:"); } return dict; } @end ================================================ FILE: src/adium/ESSIPEAccountViewController.h ================================================ // // ESSIPEAccountViewController.h // SIPEAdiumPlugin // // Created by Matt Meissner on 10/30/09. // Modified by Michael Lamb on 2/27/13 // Copyright 2013 Michael Lamb/Harris Kauffman. All rights reserved. // #import #import @interface ESSIPEAccountViewController : PurpleAccountViewController { IBOutlet NSTextField *textField_windowsLogin; IBOutlet NSTextField *textField_server; IBOutlet NSTextField *textField_userAgent; IBOutlet NSTextField *textField_emailURL; IBOutlet NSTextField *textField_email; IBOutlet NSTextField *textField_emailLogin; IBOutlet NSTextField *textField_emailPassword; IBOutlet NSTextField *textField_groupchatUser; IBOutlet NSButton *checkBox_autoDiscover; IBOutlet NSButton *checkBox_singleSignOn; IBOutlet NSButton *checkbox_dontPublish; IBOutlet NSButton *checkbox_beastDisable; IBOutlet NSButton *checkbox_allowWebPhoto; IBOutlet NSPopUpButton *popup_connectionType; IBOutlet NSPopUpButton *popup_authenticationScheme; NSDictionary *sipe_key_to_gui; } @end ================================================ FILE: src/adium/ESSIPEAccountViewController.m ================================================ // // ESSIPEAccountViewController.m // SIPEAdiumPlugin // // Created by Matt Meissner on 10/30/09. // Modified by Michael Lamb on 2/27/13 // Copyright 2013 Michael Lamb/Harris Kauffman. All rights reserved. // #import #import #import "ESSIPEAccountViewController.h" #include "prpl.h" #include "ESPurpleSIPEAccount.h" // Gotta define these here, because they're not yet in the 10.9 SDK. :( #define NSAppKitVersionNumber10_8 1187 #define NSAppKitVersionNumber10_8_5 1187.4 #define NSAppKitVersionNumber10_9 1265 @implementation ESSIPEAccountViewController - (id)init { self = [super init]; if (self) { sipe_key_to_gui = [[NSDictionary alloc] initWithObjectsAndKeys: textField_windowsLogin, KEY_SIPE_WINDOWS_LOGIN, textField_password, KEY_SIPE_PASSWORD, textField_server, KEY_SIPE_CONNECT_HOST, popup_connectionType, KEY_SIPE_CONNECTION_TYPE, popup_authenticationScheme, KEY_SIPE_AUTH_SCHEME, textField_userAgent, KEY_SIPE_USER_AGENT, checkBox_singleSignOn, KEY_SIPE_SINGLE_SIGN_ON, checkbox_beastDisable, KEY_SIPE_BEAST_DISABLE, textField_groupchatUser, KEY_SIPE_GROUP_CHAT_PROXY, textField_emailURL, KEY_SIPE_EMAIL_URL, textField_email, KEY_SIPE_EMAIL, textField_emailLogin, KEY_SIPE_EMAIL_LOGIN, textField_emailPassword, KEY_SIPE_EMAIL_PASSWORD, checkbox_dontPublish, KEY_SIPE_DONT_PUBLISH, checkbox_allowWebPhoto, KEY_SIPE_ALLOW_WEB_PHOTO, nil ]; } return self; } - (void)dealloc { [sipe_key_to_gui release]; [super dealloc]; } - (NSString *)nibName{ return @"ESSIPEAccountView"; } #pragma mark Configuration methods - (void)configureForAccount:(AIAccount *)inAccount { [super configureForAccount:inAccount]; // BEAST mitigation for Mavericks and 10.8.5 users (with Security Update 2014-001) if (NSAppKitVersionNumber < NSAppKitVersionNumber10_8_5) { // We are not running on an OS with BEAST mitigations - Don't display this as a configuration option [checkbox_beastDisable setHidden:YES]; } // Only need 1 hash for both connection & auth since there are no overlapping keys NSDictionary *conn_auth_dict = [NSDictionary dictionaryWithObjectsAndKeys: @"NTLM",@"ntlm", @"Kerberos",@"krb5", @"TLS-DSK",@"tls-dsk", @"Auto",@"auto", @"SSL/TLS",@"tls", @"TCP",@"tcp", nil]; for (NSString* key in sipe_key_to_gui) { id value = [sipe_key_to_gui objectForKey:key]; if ([value isKindOfClass:[NSTextField class]]) { NSString *tmp = [account preferenceForKey:key group:GROUP_ACCOUNT_STATUS]; [value setStringValue:(tmp ? tmp : @"")]; } else if ([value isKindOfClass:[NSPopUpButton class]]) { // NSPopUpButton *MUST* appear before NSButton in the if/else // because NSPopUpButton is a NSButton... NSString *tmp_key = [account preferenceForKey:key group:GROUP_ACCOUNT_STATUS]; NSString *tmp = @"auto"; if ([conn_auth_dict objectForKey:tmp_key]) tmp = [conn_auth_dict objectForKey:tmp_key]; [value selectItemWithTitle:tmp]; } else if ([value isKindOfClass:[NSButton class]]) { [value setState:[[account preferenceForKey:key group:GROUP_ACCOUNT_STATUS] boolValue]]; } else { AILog(@"(ESSIPEAccountViewController) Unknown class %@ for key %@", [value class], key); } } } - (void)saveConfiguration { [super saveConfiguration]; // Only need 1 hash for both connection & auth since there are no overlapping keys NSDictionary *conn_auth_dict = [NSDictionary dictionaryWithObjectsAndKeys: @"ntlm",@"NTLM", @"krb5",@"Kerberos", @"tls-dsk",@"TLS-DSK", @"auto",@"Auto", @"tls",@"SSL/TLS", @"tcp",@"TCP", nil]; for (NSString* key in sipe_key_to_gui) { id value = [sipe_key_to_gui objectForKey:key]; if ([value isKindOfClass:[NSTextField class]]) { [account setPreference:[[value stringValue] length] ? [value stringValue] : @"" forKey:key group:GROUP_ACCOUNT_STATUS]; } else if ([value isKindOfClass:[NSPopUpButton class]]) { // NSPopUpButton *MUST* appear before NSButton in the if/else // because NSPopUpButton is a NSButton... NSString *tmp = [conn_auth_dict objectForKey:[[value selectedItem] title]]; [account setPreference:tmp forKey:key group:GROUP_ACCOUNT_STATUS]; } else if ([value isKindOfClass:[NSButton class]]) { [account setPreference:[NSNumber numberWithBool:[value state]] forKey:key group:GROUP_ACCOUNT_STATUS]; } else { AILog(@"(ESSIPEAccountViewController) Unknown class %@ for key %@", [value class], key); } } } @end ================================================ FILE: src/adium/ESSIPELibpurpleServicePlugin.h ================================================ // // ESSIPELibpurpleServicePlugin.h // SIPEAdiumPlugin // // Created by Matt Meissner on 10/30/09. // Modified by Michael Lamb on 2/27/13 // Copyright 2013 Michael Lamb/Harris Kauffman. All rights reserved. // #import #import #import "ESSIPEService.h" @interface ESSIPELibpurpleServicePlugin : AIPlugin { ESSIPEService *SIPEService; } @end ================================================ FILE: src/adium/ESSIPELibpurpleServicePlugin.m ================================================ // // ESSIPELibpurpleServicePlugin.m // SIPEAdiumPlugin // // Copyright (C) 2015 SIPE Project // // Created by Matt Meissner on 10/30/09. // Modified by Michael Lamb on 2/27/13 // Copyright 2013 Michael Lamb/Harris Kauffman. All rights reserved. // #import "ESSIPEService.h" #import "ESSIPELibpurpleServicePlugin.h" // C declarations extern void purple_init_sipe_plugin(void); @implementation ESSIPELibpurpleServicePlugin # pragma mark Plugin Load/Install - (void)installLibpurplePlugin { } - (void)loadLibpurplePlugin { } - (void)installPlugin { purple_init_sipe_plugin(); [ESSIPEService registerService]; } #pragma mark Plugin Metadata - (NSString *)libpurplePluginPath { return [[NSBundle bundleForClass:[self class]] resourcePath]; } - (NSString*) pluginAuthor { return @"Harris Kauffman, Michael Lamb"; } - (NSString*) pluginVersion { return @PACKAGE_VERSION; } - (NSString*) pluginDescription { return @"Allows Adium to connect to Office Communicator accounts"; } - (NSString*) pluginWebsite { return @PACKAGE_URL; } @end ================================================ FILE: src/adium/ESSIPEService.h ================================================ // // ESSIPEService.h // SIPEAdiumPlugin // // Created by Matt Meissner on 10/30/09. // Modified by Michael Lamb on 2/27/13 // Copyright 2013 Michael Lamb/Harris Kauffman. All rights reserved. // //#import #import @interface ESSIPEService : PurpleService { } @end ================================================ FILE: src/adium/ESSIPEService.m ================================================ // // ESSIPEService.m // SIPEAdiumPlugin // // Created by Matt Meissner on 10/30/09. // Modified by Michael Lamb on 2/27/13 // Copyright 2013 Michael Lamb/Harris Kauffman. All rights reserved. // Copyright (C) 2014 SIPE Project // #import #import #import #import #import "DCPurpleSIPEJoinChatViewController.h" #import "ESSIPEAccountViewController.h" #import "ESPurpleSIPEAccount.h" #import "ESSIPEService.h" @implementation ESSIPEService #pragma mark Account/Chat Creation - (Class)accountClass { return [ESPurpleSIPEAccount class]; } - (AIAccountViewController *)accountViewController{ return [ESSIPEAccountViewController accountViewController]; } - (DCJoinChatViewController *)joinChatView{ return [DCPurpleSIPEJoinChatViewController joinChatView]; } - (BOOL)canCreateGroupChats{ return YES; } #pragma mark Service Description Metadata - (NSString *)serviceCodeUniqueID{ return @"libpurple-SIPE"; } - (NSString *)serviceID{ return @"SIPE"; } - (NSString *)serviceClass{ return @"SIPE"; } - (NSString *)shortDescription{ return @"OCS"; } - (NSString *)longDescription{ return @"Office Communicator"; } - (BOOL)caseSensitive{ return NO; } - (AIServiceImportance)serviceImportance{ return AIServiceSecondary; } // Some auth schemes may not need a password - (BOOL)requiresPassword{ return NO; } - (NSImage *)defaultServiceIconOfType:(AIServiceIconType)iconType { NSImage *baseImage = [NSImage imageNamed:@"sipe" forClass:[self class]]; if ((iconType == AIServiceIconSmall) || (iconType == AIServiceIconList)) { [baseImage setSize:NSMakeSize(16, 16)]; } return baseImage; } #pragma mark Service Properties - (NSCharacterSet *)allowedCharacters { NSMutableCharacterSet *allowedCharacters = [[NSCharacterSet alphanumericCharacterSet] mutableCopy]; NSCharacterSet *returnSet; // // NOTE: needs to be in sync with sipe-utils.c:escape_uri_part() // // @ - XXX@YYY // : - sip:XXX@YYY // ._-~ - unreserved, see RFC 3986 Appendix A // [allowedCharacters addCharactersInString:@"@:._-~"]; returnSet = [allowedCharacters immutableCopy]; [allowedCharacters release]; return [returnSet autorelease]; } #pragma mark Statuses - (void)registerStatuses{ NSNumber *awayStatus, *availableStatus, *invisibleStatus, *offlineStatus; awayStatus = [NSNumber numberWithInt:AIAwayStatusType]; availableStatus = [NSNumber numberWithInt:AIAvailableStatusType]; invisibleStatus = [NSNumber numberWithInt:AIInvisibleStatusType]; offlineStatus = [NSNumber numberWithInt:AIOfflineStatusType]; NSDictionary *statuses = [NSDictionary dictionaryWithObjectsAndKeys: availableStatus, STATUS_NAME_AVAILABLE, awayStatus, STATUS_NAME_AWAY, awayStatus, STATUS_NAME_BUSY, invisibleStatus, STATUS_NAME_INVISIBLE, awayStatus, STATUS_NAME_BRB, awayStatus, STATUS_NAME_DND, awayStatus, STATUS_NAME_LUNCH, offlineStatus, STATUS_NAME_OFFLINE, awayStatus, STATUS_NAME_PHONE, awayStatus, STATUS_NAME_NOT_AT_DESK, awayStatus, STATUS_NAME_NOT_IN_OFFICE, awayStatus, STATUS_NAME_AWAY_FRIENDS_ONLY, nil ]; for (NSString* key in statuses) { AIStatusType value = [[statuses objectForKey:key] intValue]; [adium.statusController registerStatus:key withDescription:[adium.statusController localizedDescriptionForCoreStatusName:key] ofType:value forService:self ]; } } @end ================================================ FILE: src/adium/English.lproj/DCPurpleSIPEJoinChatView.xib ================================================ 1080 14B25 6254 1343.16 755.00 com.apple.InterfaceBuilder.CocoaPlugin 6254 IBNSLayoutConstraint NSComboBox NSComboBoxCell NSCustomObject NSCustomView NSProgressIndicator NSTextField NSTextFieldCell com.apple.InterfaceBuilder.CocoaPlugin PluginDependencyRecalculationVersion DCPurpleSIPEJoinChatViewController FirstResponder NSApplication 268 268 {{461, 25}, {16, 16}} _NS:1432 {750, 750} 20750 100 268 {{99, 19}, {364, 26}} _NS:9 YES 342884417 272630784 YES 13 1044 _NS:9 YES 6 System textBackgroundColor 3 MQA 6 System controlTextColor 3 MAA 5 YES Item 1 Item 2 Item 3 274 {13, 63} _NS:29 YES NO YES tableViewAction: 0 10 10 3.4028234663852886e+38 67108928 2048 YES 11 3100 6 System headerColor 6 System headerTextColor 337641536 268437504 Item 3 6 System controlBackgroundColor 3 MC42NjY2NjY2NjY3AA 3 YES 3 2 6 System gridColor 3 MC41AA 19 tableViewAction: -765427712 1 -1 0 YES 0 1 NO 1 268 {{18, 25}, {74, 17}} YES 67108864 71303168 Chat Room 6 System controlColor NO 1 {480, 62} NSView view 233 combo_rooms 263 progress_fetch 264 0 -2 File's Owner -1 First Responder -3 Application 1 10 0 10 1 1 0.0 1000 6 24 2 NO 4 0 4 1 1 0.0 1000 6 24 2 NO 6 0 6 1 1 3 1000 3 9 3 NO 6 0 6 1 1 20 1000 8 23 3 NO 5 0 5 1 1 20 1000 8 23 3 NO 3 0 3 1 1 20 1000 0 29 3 NO View 135 225 138 245 246 7 0 0 1 1 361 1000 3 9 1 NO 247 251 255 256 259 261 262 com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin ToolTip ToolTip You can enter a channel URI in form of "ma-chan://..." directly or select a room from the list. com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin 264 DCJoinChatViewController NSObject view NSView view view NSView IBProjectSource ../../../../adium/Frameworks/Adium Framework/Source/DCJoinChatViewController.h DCPurpleSIPEJoinChatViewController DCJoinChatViewController NSComboBox NSProgressIndicator combo_rooms NSComboBox progress_fetch NSProgressIndicator IBProjectSource ../DCPurpleSIPEJoinChatViewController.h NSObject selectServiceType: id selectServiceType: selectServiceType: id IBProjectSource ../../../../adium/Source/AIServiceMenu.h DCJoinChatViewController NSObject view NSView view view NSView IBFrameworkSource Adium.framework/Headers/DCJoinChatViewController.h NSActionCell NSCell IBFrameworkSource AppKit.framework/Headers/NSActionCell.h NSApplication NSResponder IBFrameworkSource AppKit.framework/Headers/NSApplication.h NSCell NSObject IBFrameworkSource AppKit.framework/Headers/NSCell.h NSComboBox NSTextField IBFrameworkSource AppKit.framework/Headers/NSComboBox.h NSComboBoxCell NSTextFieldCell IBFrameworkSource AppKit.framework/Headers/NSComboBoxCell.h NSControl NSView IBFrameworkSource AppKit.framework/Headers/NSControl.h NSFormatter NSObject IBFrameworkSource Foundation.framework/Headers/NSFormatter.h NSLayoutConstraint NSObject IBFrameworkSource AppKit.framework/Headers/NSLayoutConstraint.h NSMenu NSObject IBFrameworkSource AppKit.framework/Headers/NSMenu.h NSObject selectServiceType: id selectServiceType: selectServiceType: id IBFrameworkSource Adium.framework/Headers/AIServiceMenu.h NSProgressIndicator NSView IBFrameworkSource AppKit.framework/Headers/NSProgressIndicator.h NSResponder NSObject IBFrameworkSource AppKit.framework/Headers/NSResponder.h NSTextField NSControl IBFrameworkSource AppKit.framework/Headers/NSTextField.h NSTextFieldCell NSActionCell IBFrameworkSource AppKit.framework/Headers/NSTextFieldCell.h NSView NSResponder IBFrameworkSource AppKit.framework/Headers/NSView.h 0 IBCocoaFramework YES com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 YES 3 YES ================================================ FILE: src/adium/English.lproj/ESSIPEAccountView.xib ================================================ ================================================ FILE: src/adium/English.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: src/adium/Info.plist ================================================ CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleName ${PRODUCT_NAME} CFBundleIconFile CFBundleIdentifier net.sourceforge.sipe.${PRODUCT_NAME:rfc1034Identifier} CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType BNDL CFBundleSignature AdIM AIMinimumAdiumVersionRequirement 1.5 CFBundleShortVersionString PACKAGE_STRING CFBundleVersion PACKAGE_VERSION NSPrincipalClass ESSIPELibpurpleServicePlugin ================================================ FILE: src/adium/PurpleDefaultsSIPE.plist ================================================ SIPE:Authentication Scheme auto SIPE:Single Sign On SIPE:Dont Publish SIPE:Allow Web Photo SIPE:BEAST Disable SIPE:Connection Type auto Connect Host localhost ================================================ FILE: src/adium/README.Adium ================================================ Compiling SIPE plugin for Adium =============================== To compile the Adium plugin you will need Xcode and the source code from the following URLs: - Adium 1.5+: http://trac.adium.im/wiki/GettingNewestAdiumSource - OpenSSL 0.9.8za: https://www.openssl.org/source/old/0.9.x/openssl-0.9.8za.tar.gz - SIPE: http://sourceforge.net/projects/sipe/files/sipe/ 1. Get Adium ------------ NOTE: you only have to do this step once. If you don't change the Adium source code, then you can reuse the results from this step in future SIPEAdiumPlugin builds. Follow the instructions at their URL above. The directory needs to be called "adium" and must be located at the same level as the SIPE source code directory. 2. Get OpenSSL -------------- NOTE: you only have to do this step once. If you don't change the OpenSSL source code, then you can reuse the results from this step in future SIPEAdiumPlugin builds. Execute the following commands in the Terminal application to download and unpack the source code: $ cd my_working_directory $ curl -o openssl-0.9.8za.tar.gz https://www.openssl.org/source/old/0.9.x/openssl-0.9.8za.tar.gz $ tar -xf openssl-0.9.8za.tar.gz The directory needs to be called "openssl-0.9.8za" and must be located at the same level as the SIPE source code directory. 3. Build the SIPEAdiumPlugin Xcode project ------------------------------------------ In Xcode go to the menu File -> Open..., browse to the location of your SIPE source tree, go into the src/adium directory, select SIPEAdiumPlugin.xcodeproj and press "Open". NOTE: please always make sure that the correct scheme has been selected by selecting the menu Product -> Scheme -> SIPEAdiumPlugin. Otherwise you will get cryptic build failures. Now you can just select Product -> Build and after a short while you should get a SIPEAdiumPlugin binary that you can install into your Adium application. 4. Generate & verify the release archive ---------------------------------------- The build copies the files into your Adium plugin directory. Use the following commands to generate the release archive: $ cd ~/Library/Application\ Support/Adium\ 2.0/PlugIns $ zip -r ~/SIPEAdiumPlugin.zip SIPEAdiumPlugin.AdiumLibpurplePlugin/ You need to have a bash 4 to run the verification script, e.g. on a Linux box $ src/adium/check_release.sh ~/SIPEAdiumPlugin.zip 5. Build SIPEAdiumPlugin for an older Max OS X release ------------------------------------------------------ If your Xcode does not have the SDK for the older Mac OS X release then please see https://github.com/devernay/xcodelegacy how to extract & install SDKs for - OS X 10.9 & 10.10 from the Xcode 6.4 installation package - OS X 10.11 from the Xcode 7.3.1 installation package Installing all SDKs in one go seems to break Xcode builds, so please make sure to install each SDK separately with $ sudo ./XcodeLegacy.sh -osxXXXX install Xcode no longer seems to apply project build settings top-down. To make changes please - select the SIPEAdiumPlugin or Adium project - select the "Build Settings" tab - change from "Basic" to "All" (on the left) - change from "Combined" to "Levels" (in the middle) - find the line with the setting you want to change (use search field) - select that line - change the setting at the right-most(!) place in that line - you will notice that all places to the left change automatically Settings to change SDK to | Base SDK | Implicitly Link Objective-C | Other build for | | Runtime Support (Adium only) | -----------|------------|------------------------------|------------------- 10.11 | OS X 10.11 | | 10.10 | OS X 10.10 | | Adium <= 1.5.10.3 10.9 | OS X 10.9 | No | Adium <= 1.5.10.3 6. Alternative build approach ----------------------------- The default build approach also builds Adium which is actually unnecessary to build the plugin successfully. All what is needed are the compiled frameworks from an Adium build. This section describes the modifications in the sections above to build the plugin without Adium. NOTE: this approach can't run the plugin, hence you won't be able to debug it. To execute the plugin you will need to run Adium by hand, e.g. from your normal Adium installation on the desktop. Section 1: - after getting the Adium source code open Xcode and go to the menu File -> Open... - browse to the location of your Adium source tree, select Adium.xcodeproj and press "Open" - optional: go to the menu Product -> Clean, if you want to clean up the artifacts from an earlier build to run a clean build from scratch - go to the menu Product -> Build to run the Adium build - after it is done go to the menu File -> Close Project As long as you don't change the Adium source you can re-use the output from this Adium build for all future plugin builds. Section 3: - after opening the project select the Adium sub-project - select "Delete" from the context menu - press "Remove Reference" in the dialog - go to the menu Product -> Build to run the plugin build This build is considerably faster and can help to speed up the development cycle. NOTE: do *NEVER* check-in the modified SIPEAdiumPlugin.xcodeproj directory from this approach. All real changes to the build must be done using the default build approach! To revert to the default build approach: - go to the menu Product -> Clean to remove the build artifacts of the plugin build - go to the menu File -> Close Project to close the project - execute the following git commands to revert the plugin source tree: $ git clean -xfd $ git checkout HEAD -- src/adium/SIPEAdiumPlugin.xcodeproj/ Now you can perform the default build again as described in section 3. ================================================ FILE: src/adium/SIPEAdiumPlugin.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 1C3F91AC12C1F531000AA829 /* libpidgin-sipe.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C7056D312C1E5820004E43B /* libpidgin-sipe.a */; }; 1C822BED12F8E87500CC4AEA /* sipe-im.c in Sources */ = {isa = PBXBuildFile; fileRef = 1C822BEB12F8E87500CC4AEA /* sipe-im.c */; }; 1CD71E3413C5380B0079DE64 /* sipe-ft-tftp.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CD71E3313C5380B0079DE64 /* sipe-ft-tftp.c */; }; 1CD71E3B13C538340079DE64 /* sipe-group.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CD71E3A13C538340079DE64 /* sipe-group.c */; }; 1CDEE46112C35DAD00790CAF /* ESSIPEAccountViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CDEE46012C35DAD00790CAF /* ESSIPEAccountViewController.m */; }; 1CE49FB914A17CF000663393 /* sipe-svc.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE49FB114A17CF000663393 /* sipe-svc.c */; }; 1CE49FBA14A17CF000663393 /* sipe-ocs2007.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE49FB214A17CF000663393 /* sipe-ocs2007.c */; }; 1CE49FBB14A17CF000663393 /* sipe-ocs2005.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE49FB314A17CF000663393 /* sipe-ocs2005.c */; }; 1CE49FEA14A17EF000663393 /* sipe-status.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE49FE914A17EF000663393 /* sipe-status.c */; }; 1CE49FF314A17F4D00663393 /* sip-soap.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE49FF014A17F4D00663393 /* sip-soap.c */; }; 1CE49FF414A17F4D00663393 /* sipe-notify.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE49FF114A17F4D00663393 /* sipe-notify.c */; }; 1CE49FF514A17F4D00663393 /* sipe-webticket.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE49FF214A17F4D00663393 /* sipe-webticket.c */; }; 1CE4A00714A17FD100663393 /* sip-sec-digest.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE4A00114A17FD100663393 /* sip-sec-digest.c */; }; 1CE4A00814A17FD100663393 /* sip-sec-tls-dsk.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE4A00014A17FD100663393 /* sip-sec-tls-dsk.c */; }; 1CE4A00A14A17FD100663393 /* sipe-certificate.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE4A00214A17FD100663393 /* sipe-certificate.c */; }; 1CE4A00D14A17FD100663393 /* sipe-tls.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE4A00514A17FD100663393 /* sipe-tls.c */; }; 1CE4A01E14A180E100663393 /* purple-search.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE4A01C14A180E100663393 /* purple-search.c */; }; 1CE4A01F14A180E100663393 /* purple-status.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CE4A01D14A180E100663393 /* purple-status.c */; }; 1CF260F912C2DFA00045B6CC /* purple-buddy.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF260F112C2DFA00045B6CC /* purple-buddy.c */; }; 1CF260FA12C2DFA00045B6CC /* purple-chat.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF260F212C2DFA00045B6CC /* purple-chat.c */; }; 1CF260FB12C2DFA00045B6CC /* purple-ft.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF260F312C2DFA00045B6CC /* purple-ft.c */; }; 1CF260FC12C2DFA00045B6CC /* purple-groupchat.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF260F412C2DFA00045B6CC /* purple-groupchat.c */; }; 1CF260FD12C2DFA00045B6CC /* purple-im.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF260F512C2DFA00045B6CC /* purple-im.c */; }; 1CF260FF12C2DFA00045B6CC /* purple-notify.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF260F712C2DFA00045B6CC /* purple-notify.c */; }; 1CF2610012C2DFA00045B6CC /* purple-user.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF260F812C2DFA00045B6CC /* purple-user.c */; }; 1CF2611812C2E1AA0045B6CC /* sipe-groupchat.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF2610C12C2E1AA0045B6CC /* sipe-groupchat.c */; }; 1CF2611912C2E1AA0045B6CC /* sipe-incoming.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF2610D12C2E1AA0045B6CC /* sipe-incoming.c */; }; 1CF2611B12C2E1AA0045B6CC /* sipe-ucs.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF2610F12C2E1AA0045B6CC /* sipe-ucs.c */; }; 1CF2611C12C2E1AA0045B6CC /* sipe-subscriptions.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF2611012C2E1AA0045B6CC /* sipe-subscriptions.c */; }; 1CF2611D12C2E1AA0045B6CC /* sipe-user.c in Sources */ = {isa = PBXBuildFile; fileRef = 1CF2611112C2E1AA0045B6CC /* sipe-user.c */; }; 1CF2612C12C2E73F0045B6CC /* ESPurpleSIPEAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = C99F358E109AE68400E79CB2 /* ESPurpleSIPEAccount.m */; }; 1CF2612D12C2E7420045B6CC /* ESSIPEService.m in Sources */ = {isa = PBXBuildFile; fileRef = C948843C109B2F8100ABFAF7 /* ESSIPEService.m */; }; 1CF2612E12C2E7430045B6CC /* ESSIPELibpurpleServicePlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = C9488445109B352000ABFAF7 /* ESSIPELibpurpleServicePlugin.m */; }; 5AAF284A184E4172007481FA /* sipe-cert-crypto-openssl.c in Sources */ = {isa = PBXBuildFile; fileRef = B82FA4181847524B00AE581B /* sipe-cert-crypto-openssl.c */; }; 5AAF284B184E4172007481FA /* sipe-crypt-openssl.c in Sources */ = {isa = PBXBuildFile; fileRef = B82FA4191847524B00AE581B /* sipe-crypt-openssl.c */; }; 5AAF284C184E4172007481FA /* sipe-digest-openssl.c in Sources */ = {isa = PBXBuildFile; fileRef = B82FA41A1847524B00AE581B /* sipe-digest-openssl.c */; }; 5AAF284D184E4172007481FA /* sipe-http-request.c in Sources */ = {isa = PBXBuildFile; fileRef = 7B379591176A2DDF001A02FD /* sipe-http-request.c */; }; 5AAF284E184E4172007481FA /* sipe-http-transport.c in Sources */ = {isa = PBXBuildFile; fileRef = 7B379592176A2DDF001A02FD /* sipe-http-transport.c */; }; 5AAF284F184E4172007481FA /* sipe-http.c in Sources */ = {isa = PBXBuildFile; fileRef = 7B379593176A2DDF001A02FD /* sipe-http.c */; }; 5AAF2850184E4172007481FA /* sip-sec-negotiate.c in Sources */ = {isa = PBXBuildFile; fileRef = 7B78059916DFF8D200B844A6 /* sip-sec-negotiate.c */; }; 5AAF2851184E4172007481FA /* sip-sec-basic.c in Sources */ = {isa = PBXBuildFile; fileRef = 7B78059D16DFF8D200B844A6 /* sip-sec-basic.c */; }; 7B50EFAB16DB326100F897D1 /* PurpleDefaultsSIPE.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7B50EFAA16DB326100F897D1 /* PurpleDefaultsSIPE.plist */; }; 7B78055816DEC73400B844A6 /* ESSIPEAccountView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7B78055716DEC73400B844A6 /* ESSIPEAccountView.xib */; }; 7BBE151C184D4BEA0000FFC9 /* DCPurpleSIPEJoinChatViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BBE151A184D4BEA0000FFC9 /* DCPurpleSIPEJoinChatViewController.h */; }; 7BBE151D184D4BEA0000FFC9 /* DCPurpleSIPEJoinChatViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BBE151B184D4BEA0000FFC9 /* DCPurpleSIPEJoinChatViewController.m */; }; 7BBE1529184D4BF50000FFC9 /* DCPurpleSIPEJoinChatView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7BBE1527184D4BF50000FFC9 /* DCPurpleSIPEJoinChatView.xib */; }; 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; }; 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; B13FAB5F119D5155001CE037 /* purple-connection.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FAB4F119D5155001CE037 /* purple-connection.c */; }; B13FAB61119D5155001CE037 /* purple-debug.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FAB51119D5155001CE037 /* purple-debug.c */; }; B13FAB63119D5155001CE037 /* purple-dnsquery.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FAB53119D5155001CE037 /* purple-dnsquery.c */; }; B13FAB64119D5155001CE037 /* purple-markup.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FAB54119D5155001CE037 /* purple-markup.c */; }; B13FAB66119D5155001CE037 /* purple-mime.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FAB56119D5155001CE037 /* purple-mime.c */; }; B13FAB67119D5155001CE037 /* purple-network.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FAB57119D5155001CE037 /* purple-network.c */; }; B13FAB68119D5155001CE037 /* purple-plugin.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FAB58119D5155001CE037 /* purple-plugin.c */; }; B13FAB6A119D5155001CE037 /* purple-schedule.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FAB5A119D5155001CE037 /* purple-schedule.c */; }; B13FAB6B119D5155001CE037 /* purple-setting.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FAB5B119D5155001CE037 /* purple-setting.c */; }; B13FAB6C119D5155001CE037 /* purple-transport.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FAB5C119D5155001CE037 /* purple-transport.c */; }; B13FABDF119D585A001CE037 /* sip-csta.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABA3119D585A001CE037 /* sip-csta.c */; }; B13FABE1119D585A001CE037 /* sip-sec-gssapi.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABA5119D585A001CE037 /* sip-sec-gssapi.c */; }; B13FABE5119D585A001CE037 /* sip-sec-ntlm.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABA9119D585A001CE037 /* sip-sec-ntlm.c */; }; B13FABE9119D585A001CE037 /* sip-sec.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABAD119D585A001CE037 /* sip-sec.c */; }; B13FABEB119D585A001CE037 /* sip-transport.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABAF119D585A001CE037 /* sip-transport.c */; }; B13FABED119D585A001CE037 /* sipe-buddy.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABB1119D585A001CE037 /* sipe-buddy.c */; }; B13FABEF119D585A001CE037 /* sipe-cal.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABB3119D585A001CE037 /* sipe-cal.c */; }; B13FABF1119D585A001CE037 /* sipe-chat.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABB5119D585A001CE037 /* sipe-chat.c */; }; B13FABF3119D585A001CE037 /* sipe-conf.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABB7119D585A001CE037 /* sipe-conf.c */; }; B13FABF6119D585A001CE037 /* sipe-core.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABBA119D585A001CE037 /* sipe-core.c */; }; B13FABF8119D585A001CE037 /* sipe-dialog.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABBC119D585A001CE037 /* sipe-dialog.c */; }; B13FABFD119D585A001CE037 /* sipe-ews.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABC1119D585A001CE037 /* sipe-ews.c */; }; B13FABFE119D585A001CE037 /* sipe-ews-autodiscover.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABC2119D585A001CE037 /* sipe-ews-autodiscover.c */; }; B13FABFF119D585A001CE037 /* sipe-ft.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABC3119D585A001CE037 /* sipe-ft.c */; }; B13FAC00119D585A001CE037 /* sipe-lync-autodiscover.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABC4119D585A001CE037 /* sipe-lync-autodiscover.c */; }; B13FAC04119D585A001CE037 /* sipe-schedule.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABC8119D585A001CE037 /* sipe-schedule.c */; }; B13FAC06119D585A001CE037 /* sipe-session.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABCA119D585A001CE037 /* sipe-session.c */; }; B13FAC08119D585A001CE037 /* sipe-sign.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABCC119D585A001CE037 /* sipe-sign.c */; }; B13FAC0A119D585A001CE037 /* sipe-utils.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABCE119D585A001CE037 /* sipe-utils.c */; }; B13FAC0F119D585A001CE037 /* sipe-xml.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABD3119D585A001CE037 /* sipe-xml.c */; }; B13FAC13119D585A001CE037 /* sipmsg.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABD7119D585A001CE037 /* sipmsg.c */; }; B13FAC15119D585A001CE037 /* uuid.c in Sources */ = {isa = PBXBuildFile; fileRef = B13FABD9119D585A001CE037 /* uuid.c */; }; B89F38821BDEB0D30017B509 /* sipe-ft-lync.c in Sources */ = {isa = PBXBuildFile; fileRef = B89F38811BDEB0D30017B509 /* sipe-ft-lync.c */; }; B89F388D1BDEB0F00017B509 /* sipe-mime-common.c in Sources */ = {isa = PBXBuildFile; fileRef = B89F388C1BDEB0F00017B509 /* sipe-mime-common.c */; }; B89F388F1BDEB10B0017B509 /* purple-plugin-common.c in Sources */ = {isa = PBXBuildFile; fileRef = B89F388E1BDEB10B0017B509 /* purple-plugin-common.c */; }; B8E5616722D21E8E00580386 /* sipe-rtf.l in Sources */ = {isa = PBXBuildFile; fileRef = B8E5616622D21E8E00580386 /* sipe-rtf.l */; }; C9FDAE7F109ADC54004EEEAF /* Kerberos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9FDAE7E109ADC54004EEEAF /* Kerberos.framework */; }; C9FDAEF6109ADF97004EEEAF /* sipe.png in Resources */ = {isa = PBXBuildFile; fileRef = C9FDAEF4109ADF97004EEEAF /* sipe.png */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ B8E5616822D21F3A00580386 /* PBXBuildRule */ = { isa = PBXBuildRule; compilerSpec = com.apple.compilers.proxy.script; fileType = sourcecode.lex; isEditable = 1; name = "Project Lex rule"; outputFiles = ( "$(DERIVED_FILE_DIR)/${INPUT_FILE_BASE}.c", ); script = "# $(LEX) is actually flex\ncd ${DERIVED_FILE_DIR} && ${LEX} ${INPUT_FILE_PATH}"; }; /* End PBXBuildRule section */ /* Begin PBXContainerItemProxy section */ 7BB8CBC1183AA605004351FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */; proxyType = 2; remoteGlobalIDString = 34BD9DA9053146CC000AB133; remoteInfo = Adium; }; 7BB8CBC3183AA605004351FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */; proxyType = 2; remoteGlobalIDString = 34BD9DE105314751000AB133; remoteInfo = Adium.Framework; }; 7BB8CBC5183AA605004351FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3485D68009EB416300232CC4; remoteInfo = AdiumLibpurple; }; 7BB8CBC7183AA605004351FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */; proxyType = 2; remoteGlobalIDString = 6334FBFC0F9C11DC003C77A9; remoteInfo = AIUtilities.framework; }; 7BB8CBC9183AA605004351FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */; proxyType = 2; remoteGlobalIDString = 349C2EFA0867AC97000BF883; remoteInfo = AdiumApplescriptRunner; }; 7BB8CBCB183AA605004351FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */; proxyType = 2; remoteGlobalIDString = 312ED3CA0C7E875B00A6BDA9; remoteInfo = "Unit tests"; }; 7BB8CBCD183AA605004351FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */; proxyType = 2; remoteGlobalIDString = 633D4FA40F9D3073004F491E; remoteInfo = "Spotlight Importer"; }; 7BB8CC2A183AC198004351FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */; proxyType = 1; remoteGlobalIDString = 34BD9CD1053146CC000AB133; remoteInfo = Adium; }; C92A79F110B25023009B64B9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 089C1669FE841209C02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = C92A796810B24BAD009B64B9; remoteInfo = "pidgin-sipe"; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 7B5DD2EA16DB1DFD00B3D188 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 1C7056D312C1E5820004E43B /* libpidgin-sipe.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libpidgin-sipe.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 1C822BEB12F8E87500CC4AEA /* sipe-im.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-im.c"; sourceTree = ""; }; 1CD71E3313C5380B0079DE64 /* sipe-ft-tftp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-ft-tftp.c"; sourceTree = ""; }; 1CD71E3A13C538340079DE64 /* sipe-group.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-group.c"; sourceTree = ""; }; 1CDEE45F12C35DAD00790CAF /* ESSIPEAccountViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESSIPEAccountViewController.h; sourceTree = ""; }; 1CDEE46012C35DAD00790CAF /* ESSIPEAccountViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ESSIPEAccountViewController.m; sourceTree = ""; }; 1CE49FB114A17CF000663393 /* sipe-svc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-svc.c"; sourceTree = ""; }; 1CE49FB214A17CF000663393 /* sipe-ocs2007.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-ocs2007.c"; sourceTree = ""; }; 1CE49FB314A17CF000663393 /* sipe-ocs2005.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-ocs2005.c"; sourceTree = ""; }; 1CE49FE914A17EF000663393 /* sipe-status.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-status.c"; sourceTree = ""; }; 1CE49FF014A17F4D00663393 /* sip-soap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sip-soap.c"; sourceTree = ""; }; 1CE49FF114A17F4D00663393 /* sipe-notify.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-notify.c"; sourceTree = ""; }; 1CE49FF214A17F4D00663393 /* sipe-webticket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-webticket.c"; sourceTree = ""; }; 1CE4A00014A17FD100663393 /* sip-sec-tls-dsk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sip-sec-tls-dsk.c"; sourceTree = ""; }; 1CE4A00114A17FD100663393 /* sip-sec-digest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sip-sec-digest.c"; sourceTree = ""; }; 1CE4A00214A17FD100663393 /* sipe-certificate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-certificate.c"; sourceTree = ""; }; 1CE4A00514A17FD100663393 /* sipe-tls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-tls.c"; sourceTree = ""; }; 1CE4A01C14A180E100663393 /* purple-search.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-search.c"; sourceTree = ""; }; 1CE4A01D14A180E100663393 /* purple-status.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-status.c"; sourceTree = ""; }; 1CF260F112C2DFA00045B6CC /* purple-buddy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-buddy.c"; sourceTree = ""; }; 1CF260F212C2DFA00045B6CC /* purple-chat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-chat.c"; sourceTree = ""; }; 1CF260F312C2DFA00045B6CC /* purple-ft.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-ft.c"; sourceTree = ""; }; 1CF260F412C2DFA00045B6CC /* purple-groupchat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-groupchat.c"; sourceTree = ""; }; 1CF260F512C2DFA00045B6CC /* purple-im.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-im.c"; sourceTree = ""; }; 1CF260F712C2DFA00045B6CC /* purple-notify.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-notify.c"; sourceTree = ""; }; 1CF260F812C2DFA00045B6CC /* purple-user.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-user.c"; sourceTree = ""; }; 1CF2610C12C2E1AA0045B6CC /* sipe-groupchat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-groupchat.c"; sourceTree = ""; }; 1CF2610D12C2E1AA0045B6CC /* sipe-incoming.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-incoming.c"; sourceTree = ""; }; 1CF2610F12C2E1AA0045B6CC /* sipe-ucs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-ucs.c"; sourceTree = ""; }; 1CF2611012C2E1AA0045B6CC /* sipe-subscriptions.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-subscriptions.c"; sourceTree = ""; }; 1CF2611112C2E1AA0045B6CC /* sipe-user.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-user.c"; sourceTree = ""; }; 32DBCF630370AF2F00C91783 /* SIPEAdiumPlugin_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SIPEAdiumPlugin_Prefix.pch; sourceTree = ""; }; 7B379591176A2DDF001A02FD /* sipe-http-request.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-http-request.c"; sourceTree = ""; }; 7B379592176A2DDF001A02FD /* sipe-http-transport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-http-transport.c"; sourceTree = ""; }; 7B379593176A2DDF001A02FD /* sipe-http.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-http.c"; sourceTree = ""; }; 7B50EFAA16DB326100F897D1 /* PurpleDefaultsSIPE.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = PurpleDefaultsSIPE.plist; sourceTree = ""; }; 7B78055716DEC73400B844A6 /* ESSIPEAccountView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ESSIPEAccountView.xib; path = English.lproj/ESSIPEAccountView.xib; sourceTree = ""; }; 7B78059916DFF8D200B844A6 /* sip-sec-negotiate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sip-sec-negotiate.c"; sourceTree = ""; }; 7B78059D16DFF8D200B844A6 /* sip-sec-basic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sip-sec-basic.c"; sourceTree = ""; }; 7BB8CB91183AA0BD004351FF /* Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = ""; }; 7BB8CB92183AA0BD004351FF /* Debug-Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Debug-Release.xcconfig"; sourceTree = ""; }; 7BB8CB93183AA0BD004351FF /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; 7BB8CB94183AA0BD004351FF /* libpidgin-sipe.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "libpidgin-sipe.xcconfig"; sourceTree = ""; }; 7BB8CB96183AA0BD004351FF /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 7BB8CB97183AA0BD004351FF /* SIPEAdiumPlugin.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = SIPEAdiumPlugin.xcconfig; sourceTree = ""; }; 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Adium.xcodeproj; path = ../../../adium/Adium.xcodeproj; sourceTree = ""; }; 7BBE151A184D4BEA0000FFC9 /* DCPurpleSIPEJoinChatViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DCPurpleSIPEJoinChatViewController.h; sourceTree = ""; }; 7BBE151B184D4BEA0000FFC9 /* DCPurpleSIPEJoinChatViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DCPurpleSIPEJoinChatViewController.m; sourceTree = ""; }; 7BBE1528184D4BF50000FFC9 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/DCPurpleSIPEJoinChatView.xib; sourceTree = ""; }; 8D5B49B6048680CD000E48DA /* SIPEAdiumPlugin.AdiumLibpurplePlugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SIPEAdiumPlugin.AdiumLibpurplePlugin; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; B13FAB4F119D5155001CE037 /* purple-connection.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-connection.c"; sourceTree = ""; }; B13FAB51119D5155001CE037 /* purple-debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-debug.c"; sourceTree = ""; }; B13FAB53119D5155001CE037 /* purple-dnsquery.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-dnsquery.c"; sourceTree = ""; }; B13FAB54119D5155001CE037 /* purple-markup.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-markup.c"; sourceTree = ""; }; B13FAB56119D5155001CE037 /* purple-mime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-mime.c"; sourceTree = ""; }; B13FAB57119D5155001CE037 /* purple-network.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-network.c"; sourceTree = ""; }; B13FAB58119D5155001CE037 /* purple-plugin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-plugin.c"; sourceTree = ""; }; B13FAB5A119D5155001CE037 /* purple-schedule.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-schedule.c"; sourceTree = ""; }; B13FAB5B119D5155001CE037 /* purple-setting.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-setting.c"; sourceTree = ""; }; B13FAB5C119D5155001CE037 /* purple-transport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-transport.c"; sourceTree = ""; }; B13FABA3119D585A001CE037 /* sip-csta.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sip-csta.c"; sourceTree = ""; }; B13FABA5119D585A001CE037 /* sip-sec-gssapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sip-sec-gssapi.c"; sourceTree = ""; }; B13FABA9119D585A001CE037 /* sip-sec-ntlm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sip-sec-ntlm.c"; sourceTree = ""; }; B13FABAD119D585A001CE037 /* sip-sec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sip-sec.c"; sourceTree = ""; }; B13FABAF119D585A001CE037 /* sip-transport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sip-transport.c"; sourceTree = ""; }; B13FABB1119D585A001CE037 /* sipe-buddy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-buddy.c"; sourceTree = ""; }; B13FABB3119D585A001CE037 /* sipe-cal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-cal.c"; sourceTree = ""; }; B13FABB5119D585A001CE037 /* sipe-chat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-chat.c"; sourceTree = ""; }; B13FABB7119D585A001CE037 /* sipe-conf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-conf.c"; sourceTree = ""; }; B13FABBA119D585A001CE037 /* sipe-core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-core.c"; sourceTree = ""; }; B13FABBC119D585A001CE037 /* sipe-dialog.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-dialog.c"; sourceTree = ""; }; B13FABC1119D585A001CE037 /* sipe-ews.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-ews.c"; sourceTree = ""; }; B13FABC2119D585A001CE037 /* sipe-ews-autodiscover.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-ews-autodiscover.c"; sourceTree = ""; }; B13FABC3119D585A001CE037 /* sipe-ft.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-ft.c"; sourceTree = ""; }; B13FABC4119D585A001CE037 /* sipe-lync-autodiscover.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-lync-autodiscover.c"; sourceTree = ""; }; B13FABC8119D585A001CE037 /* sipe-schedule.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-schedule.c"; sourceTree = ""; }; B13FABCA119D585A001CE037 /* sipe-session.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-session.c"; sourceTree = ""; }; B13FABCC119D585A001CE037 /* sipe-sign.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-sign.c"; sourceTree = ""; }; B13FABCE119D585A001CE037 /* sipe-utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-utils.c"; sourceTree = ""; }; B13FABD3119D585A001CE037 /* sipe-xml.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-xml.c"; sourceTree = ""; }; B13FABD7119D585A001CE037 /* sipmsg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sipmsg.c; sourceTree = ""; }; B13FABD9119D585A001CE037 /* uuid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = uuid.c; sourceTree = ""; }; B82FA4181847524B00AE581B /* sipe-cert-crypto-openssl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-cert-crypto-openssl.c"; sourceTree = ""; }; B82FA4191847524B00AE581B /* sipe-crypt-openssl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-crypt-openssl.c"; sourceTree = ""; }; B82FA41A1847524B00AE581B /* sipe-digest-openssl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-digest-openssl.c"; sourceTree = ""; }; B89F38811BDEB0D30017B509 /* sipe-ft-lync.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-ft-lync.c"; sourceTree = ""; }; B89F388C1BDEB0F00017B509 /* sipe-mime-common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sipe-mime-common.c"; sourceTree = ""; }; B89F388E1BDEB10B0017B509 /* purple-plugin-common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "purple-plugin-common.c"; sourceTree = ""; }; B8E5616622D21E8E00580386 /* sipe-rtf.l */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.lex; path = "sipe-rtf.l"; sourceTree = ""; }; C948843B109B2F8100ABFAF7 /* ESSIPEService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESSIPEService.h; sourceTree = ""; }; C948843C109B2F8100ABFAF7 /* ESSIPEService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ESSIPEService.m; sourceTree = ""; }; C9488444109B352000ABFAF7 /* ESSIPELibpurpleServicePlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESSIPELibpurpleServicePlugin.h; sourceTree = ""; }; C9488445109B352000ABFAF7 /* ESSIPELibpurpleServicePlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ESSIPELibpurpleServicePlugin.m; sourceTree = ""; }; C99F358D109AE68400E79CB2 /* ESPurpleSIPEAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESPurpleSIPEAccount.h; sourceTree = ""; }; C99F358E109AE68400E79CB2 /* ESPurpleSIPEAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ESPurpleSIPEAccount.m; sourceTree = ""; }; C9FDAE7E109ADC54004EEEAF /* Kerberos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Kerberos.framework; path = System/Library/Frameworks/Kerberos.framework; sourceTree = SDKROOT; }; C9FDAEF4109ADF97004EEEAF /* sipe.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = sipe.png; path = ../../pixmaps/48/sipe.png; sourceTree = SOURCE_ROOT; }; D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 8D5B49B3048680CD000E48DA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */, C9FDAE7F109ADC54004EEEAF /* Kerberos.framework in Frameworks */, 1C3F91AC12C1F531000AA829 /* libpidgin-sipe.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; C92A796710B24BAD009B64B9 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 089C166AFE841209C02AAC07 /* SIPEAdiumPlugin */ = { isa = PBXGroup; children = ( 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */, 08FB77AFFE84173DC02AAC07 /* Classes */, C92A79B110B24C50009B64B9 /* pidgin-sipe */, 32C88E010371C26100C91783 /* Other Sources */, 089C167CFE841241C02AAC07 /* Resources */, 089C1671FE841209C02AAC07 /* Frameworks and Libraries */, 19C28FB8FE9D52D311CA2CBB /* Products */, ); name = SIPEAdiumPlugin; sourceTree = ""; }; 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = { isa = PBXGroup; children = ( 7B1E80001665C79300E0C654 /* Other Libraries */, 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */, 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */, ); name = "Frameworks and Libraries"; sourceTree = ""; }; 089C167CFE841241C02AAC07 /* Resources */ = { isa = PBXGroup; children = ( 7BBE1527184D4BF50000FFC9 /* DCPurpleSIPEJoinChatView.xib */, 7B78055716DEC73400B844A6 /* ESSIPEAccountView.xib */, 7B50EFAA16DB326100F897D1 /* PurpleDefaultsSIPE.plist */, C9FDAEF4109ADF97004EEEAF /* sipe.png */, 8D5B49B7048680CD000E48DA /* Info.plist */, 089C167DFE841241C02AAC07 /* InfoPlist.strings */, ); name = Resources; sourceTree = ""; }; 08FB77AFFE84173DC02AAC07 /* Classes */ = { isa = PBXGroup; children = ( 7BBE151A184D4BEA0000FFC9 /* DCPurpleSIPEJoinChatViewController.h */, 7BBE151B184D4BEA0000FFC9 /* DCPurpleSIPEJoinChatViewController.m */, C99F358D109AE68400E79CB2 /* ESPurpleSIPEAccount.h */, C99F358E109AE68400E79CB2 /* ESPurpleSIPEAccount.m */, C9488444109B352000ABFAF7 /* ESSIPELibpurpleServicePlugin.h */, C9488445109B352000ABFAF7 /* ESSIPELibpurpleServicePlugin.m */, C948843B109B2F8100ABFAF7 /* ESSIPEService.h */, C948843C109B2F8100ABFAF7 /* ESSIPEService.m */, 1CDEE45F12C35DAD00790CAF /* ESSIPEAccountViewController.h */, 1CDEE46012C35DAD00790CAF /* ESSIPEAccountViewController.m */, ); name = Classes; sourceTree = ""; }; 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */, C9FDAE7E109ADC54004EEEAF /* Kerberos.framework */, ); name = "Linked Frameworks"; sourceTree = ""; }; 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = { isa = PBXGroup; children = ( 089C167FFE841241C02AAC07 /* AppKit.framework */, D2F7E65807B2D6F200F64583 /* CoreData.framework */, 089C1672FE841209C02AAC07 /* Foundation.framework */, ); name = "Other Frameworks"; sourceTree = ""; }; 19C28FB8FE9D52D311CA2CBB /* Products */ = { isa = PBXGroup; children = ( 8D5B49B6048680CD000E48DA /* SIPEAdiumPlugin.AdiumLibpurplePlugin */, 1C7056D312C1E5820004E43B /* libpidgin-sipe.a */, ); name = Products; sourceTree = ""; }; 32C88E010371C26100C91783 /* Other Sources */ = { isa = PBXGroup; children = ( 7BB8CB90183AA07B004351FF /* xcconfigs */, 32DBCF630370AF2F00C91783 /* SIPEAdiumPlugin_Prefix.pch */, ); name = "Other Sources"; sourceTree = ""; }; 7B1E80001665C79300E0C654 /* Other Libraries */ = { isa = PBXGroup; children = ( ); name = "Other Libraries"; sourceTree = ""; }; 7BB8CB90183AA07B004351FF /* xcconfigs */ = { isa = PBXGroup; children = ( 7BB8CB91183AA0BD004351FF /* Base.xcconfig */, 7BB8CB92183AA0BD004351FF /* Debug-Release.xcconfig */, 7BB8CB93183AA0BD004351FF /* Debug.xcconfig */, 7BB8CB94183AA0BD004351FF /* libpidgin-sipe.xcconfig */, 7BB8CB96183AA0BD004351FF /* Release.xcconfig */, 7BB8CB97183AA0BD004351FF /* SIPEAdiumPlugin.xcconfig */, ); path = xcconfigs; sourceTree = ""; }; 7BB8CBB4183AA600004351FF /* Products */ = { isa = PBXGroup; children = ( 7BB8CBC2183AA605004351FF /* Adium.app */, 7BB8CBC4183AA605004351FF /* Adium.framework */, 7BB8CBC6183AA605004351FF /* AdiumLibpurple.framework */, 7BB8CBC8183AA605004351FF /* AIUtilities.framework */, 7BB8CBCA183AA605004351FF /* AdiumApplescriptRunner */, 7BB8CBCC183AA605004351FF /* Unit tests.octest */, 7BB8CBCE183AA605004351FF /* AdiumSpotlightImporter.mdimporter */, ); name = Products; sourceTree = ""; }; B13FAB4D119D5155001CE037 /* purple */ = { isa = PBXGroup; children = ( 1CF260F112C2DFA00045B6CC /* purple-buddy.c */, 1CF260F212C2DFA00045B6CC /* purple-chat.c */, B13FAB4F119D5155001CE037 /* purple-connection.c */, B13FAB51119D5155001CE037 /* purple-debug.c */, B13FAB53119D5155001CE037 /* purple-dnsquery.c */, 1CF260F312C2DFA00045B6CC /* purple-ft.c */, 1CF260F412C2DFA00045B6CC /* purple-groupchat.c */, 1CF260F512C2DFA00045B6CC /* purple-im.c */, B13FAB54119D5155001CE037 /* purple-markup.c */, B13FAB56119D5155001CE037 /* purple-mime.c */, B13FAB57119D5155001CE037 /* purple-network.c */, 1CF260F712C2DFA00045B6CC /* purple-notify.c */, B89F388E1BDEB10B0017B509 /* purple-plugin-common.c */, B13FAB58119D5155001CE037 /* purple-plugin.c */, B13FAB5A119D5155001CE037 /* purple-schedule.c */, 1CE4A01C14A180E100663393 /* purple-search.c */, B13FAB5B119D5155001CE037 /* purple-setting.c */, 1CE4A01D14A180E100663393 /* purple-status.c */, B13FAB5C119D5155001CE037 /* purple-transport.c */, 1CF260F812C2DFA00045B6CC /* purple-user.c */, ); name = purple; path = ../purple; sourceTree = SOURCE_ROOT; }; B13FAB9C119D585A001CE037 /* core */ = { isa = PBXGroup; children = ( B13FABA3119D585A001CE037 /* sip-csta.c */, 7B78059D16DFF8D200B844A6 /* sip-sec-basic.c */, 1CE4A00114A17FD100663393 /* sip-sec-digest.c */, B13FABA5119D585A001CE037 /* sip-sec-gssapi.c */, 7B78059916DFF8D200B844A6 /* sip-sec-negotiate.c */, B13FABA9119D585A001CE037 /* sip-sec-ntlm.c */, 1CE4A00014A17FD100663393 /* sip-sec-tls-dsk.c */, B13FABAD119D585A001CE037 /* sip-sec.c */, 1CE49FF014A17F4D00663393 /* sip-soap.c */, B13FABAF119D585A001CE037 /* sip-transport.c */, B13FABB1119D585A001CE037 /* sipe-buddy.c */, B13FABB3119D585A001CE037 /* sipe-cal.c */, B82FA4181847524B00AE581B /* sipe-cert-crypto-openssl.c */, 1CE4A00214A17FD100663393 /* sipe-certificate.c */, B13FABB5119D585A001CE037 /* sipe-chat.c */, B13FABB7119D585A001CE037 /* sipe-conf.c */, B13FABBA119D585A001CE037 /* sipe-core.c */, B82FA4191847524B00AE581B /* sipe-crypt-openssl.c */, B13FABBC119D585A001CE037 /* sipe-dialog.c */, B82FA41A1847524B00AE581B /* sipe-digest-openssl.c */, B13FABC2119D585A001CE037 /* sipe-ews-autodiscover.c */, B13FABC1119D585A001CE037 /* sipe-ews.c */, B89F38811BDEB0D30017B509 /* sipe-ft-lync.c */, 1CD71E3313C5380B0079DE64 /* sipe-ft-tftp.c */, B13FABC3119D585A001CE037 /* sipe-ft.c */, 1CD71E3A13C538340079DE64 /* sipe-group.c */, 1CF2610C12C2E1AA0045B6CC /* sipe-groupchat.c */, 7B379591176A2DDF001A02FD /* sipe-http-request.c */, 7B379592176A2DDF001A02FD /* sipe-http-transport.c */, 7B379593176A2DDF001A02FD /* sipe-http.c */, 1C822BEB12F8E87500CC4AEA /* sipe-im.c */, 1CF2610D12C2E1AA0045B6CC /* sipe-incoming.c */, B13FABC4119D585A001CE037 /* sipe-lync-autodiscover.c */, B89F388C1BDEB0F00017B509 /* sipe-mime-common.c */, 1CE49FF114A17F4D00663393 /* sipe-notify.c */, 1CE49FB314A17CF000663393 /* sipe-ocs2005.c */, 1CE49FB214A17CF000663393 /* sipe-ocs2007.c */, B8E5616622D21E8E00580386 /* sipe-rtf.l */, B13FABC8119D585A001CE037 /* sipe-schedule.c */, B13FABCA119D585A001CE037 /* sipe-session.c */, B13FABCC119D585A001CE037 /* sipe-sign.c */, 1CE49FE914A17EF000663393 /* sipe-status.c */, 1CF2611012C2E1AA0045B6CC /* sipe-subscriptions.c */, 1CE49FB114A17CF000663393 /* sipe-svc.c */, 1CE4A00514A17FD100663393 /* sipe-tls.c */, 1CF2610F12C2E1AA0045B6CC /* sipe-ucs.c */, 1CF2611112C2E1AA0045B6CC /* sipe-user.c */, B13FABCE119D585A001CE037 /* sipe-utils.c */, 1CE49FF214A17F4D00663393 /* sipe-webticket.c */, B13FABD3119D585A001CE037 /* sipe-xml.c */, B13FABD7119D585A001CE037 /* sipmsg.c */, B13FABD9119D585A001CE037 /* uuid.c */, ); name = core; path = ../core; sourceTree = SOURCE_ROOT; }; C92A79B110B24C50009B64B9 /* pidgin-sipe */ = { isa = PBXGroup; children = ( B13FAB9C119D585A001CE037 /* core */, B13FAB4D119D5155001CE037 /* purple */, ); name = "pidgin-sipe"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 7B1E80021665E5DA00E0C654 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 7BBE151C184D4BEA0000FFC9 /* DCPurpleSIPEJoinChatViewController.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; C92A796510B24BAD009B64B9 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 8D5B49AC048680CD000E48DA /* SIPEAdiumPlugin */ = { isa = PBXNativeTarget; buildConfigurationList = 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "SIPEAdiumPlugin" */; buildPhases = ( B8F496C51F96858B00ACE906 /* Run Script to create symlinks to separate Adium build directory (optional) */, 7B1E80021665E5DA00E0C654 /* Headers */, 8D5B49AF048680CD000E48DA /* Resources */, 8D5B49B1048680CD000E48DA /* Sources */, 8D5B49B3048680CD000E48DA /* Frameworks */, 7B5DD2EA16DB1DFD00B3D188 /* CopyFiles */, ); buildRules = ( B8E5616822D21F3A00580386 /* PBXBuildRule */, ); dependencies = ( 7BB8CC2B183AC198004351FF /* PBXTargetDependency */, C92A79F210B25023009B64B9 /* PBXTargetDependency */, ); name = SIPEAdiumPlugin; productInstallPath = "$(HOME)/Library/Bundles"; productName = SIPEAdiumPlugin; productReference = 8D5B49B6048680CD000E48DA /* SIPEAdiumPlugin.AdiumLibpurplePlugin */; productType = "com.apple.product-type.bundle"; }; C92A796810B24BAD009B64B9 /* pidgin-sipe */ = { isa = PBXNativeTarget; buildConfigurationList = C92A796C10B24BCD009B64B9 /* Build configuration list for PBXNativeTarget "pidgin-sipe" */; buildPhases = ( C92A796510B24BAD009B64B9 /* Headers */, C92A796610B24BAD009B64B9 /* Sources */, C92A796710B24BAD009B64B9 /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "pidgin-sipe"; productName = "pidgin-sipe"; productReference = 1C7056D312C1E5820004E43B /* libpidgin-sipe.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0610; }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "SIPEAdiumPlugin" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( English, Japanese, French, German, ); mainGroup = 089C166AFE841209C02AAC07 /* SIPEAdiumPlugin */; projectDirPath = ""; projectReferences = ( { ProductGroup = 7BB8CBB4183AA600004351FF /* Products */; ProjectRef = 7BB8CBB3183AA600004351FF /* Adium.xcodeproj */; }, ); projectRoot = ""; targets = ( 8D5B49AC048680CD000E48DA /* SIPEAdiumPlugin */, C92A796810B24BAD009B64B9 /* pidgin-sipe */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ 7BB8CBC2183AA605004351FF /* Adium.app */ = { isa = PBXReferenceProxy; fileType = wrapper.application; path = Adium.app; remoteRef = 7BB8CBC1183AA605004351FF /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 7BB8CBC4183AA605004351FF /* Adium.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = Adium.framework; remoteRef = 7BB8CBC3183AA605004351FF /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 7BB8CBC6183AA605004351FF /* AdiumLibpurple.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = AdiumLibpurple.framework; remoteRef = 7BB8CBC5183AA605004351FF /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 7BB8CBC8183AA605004351FF /* AIUtilities.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = AIUtilities.framework; remoteRef = 7BB8CBC7183AA605004351FF /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 7BB8CBCA183AA605004351FF /* AdiumApplescriptRunner */ = { isa = PBXReferenceProxy; fileType = "compiled.mach-o.executable"; path = AdiumApplescriptRunner; remoteRef = 7BB8CBC9183AA605004351FF /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 7BB8CBCC183AA605004351FF /* Unit tests.octest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; path = "Unit tests.octest"; remoteRef = 7BB8CBCB183AA605004351FF /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 7BB8CBCE183AA605004351FF /* AdiumSpotlightImporter.mdimporter */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; path = AdiumSpotlightImporter.mdimporter; remoteRef = 7BB8CBCD183AA605004351FF /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ 8D5B49AF048680CD000E48DA /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */, C9FDAEF6109ADF97004EEEAF /* sipe.png in Resources */, 7B50EFAB16DB326100F897D1 /* PurpleDefaultsSIPE.plist in Resources */, 7BBE1529184D4BF50000FFC9 /* DCPurpleSIPEJoinChatView.xib in Resources */, 7B78055816DEC73400B844A6 /* ESSIPEAccountView.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ B8F496C51F96858B00ACE906 /* Run Script to create symlinks to separate Adium build directory (optional) */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Run Script to create symlinks to separate Adium build directory (optional)"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = ./find_adium_build.sh; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 8D5B49B1048680CD000E48DA /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 1CF2612C12C2E73F0045B6CC /* ESPurpleSIPEAccount.m in Sources */, B8E5616722D21E8E00580386 /* sipe-rtf.l in Sources */, B89F388F1BDEB10B0017B509 /* purple-plugin-common.c in Sources */, 1CF2612D12C2E7420045B6CC /* ESSIPEService.m in Sources */, B89F38821BDEB0D30017B509 /* sipe-ft-lync.c in Sources */, 1CF2612E12C2E7430045B6CC /* ESSIPELibpurpleServicePlugin.m in Sources */, 7BBE151D184D4BEA0000FFC9 /* DCPurpleSIPEJoinChatViewController.m in Sources */, B89F388D1BDEB0F00017B509 /* sipe-mime-common.c in Sources */, 1CDEE46112C35DAD00790CAF /* ESSIPEAccountViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; C92A796610B24BAD009B64B9 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 5AAF284A184E4172007481FA /* sipe-cert-crypto-openssl.c in Sources */, 5AAF284B184E4172007481FA /* sipe-crypt-openssl.c in Sources */, 5AAF284C184E4172007481FA /* sipe-digest-openssl.c in Sources */, 5AAF284D184E4172007481FA /* sipe-http-request.c in Sources */, 5AAF284E184E4172007481FA /* sipe-http-transport.c in Sources */, 5AAF284F184E4172007481FA /* sipe-http.c in Sources */, 5AAF2850184E4172007481FA /* sip-sec-negotiate.c in Sources */, 5AAF2851184E4172007481FA /* sip-sec-basic.c in Sources */, B13FAB5F119D5155001CE037 /* purple-connection.c in Sources */, B13FAB61119D5155001CE037 /* purple-debug.c in Sources */, B13FAB63119D5155001CE037 /* purple-dnsquery.c in Sources */, B13FAB64119D5155001CE037 /* purple-markup.c in Sources */, B13FAB66119D5155001CE037 /* purple-mime.c in Sources */, B13FAB67119D5155001CE037 /* purple-network.c in Sources */, B13FAB68119D5155001CE037 /* purple-plugin.c in Sources */, B13FAB6A119D5155001CE037 /* purple-schedule.c in Sources */, B13FAB6B119D5155001CE037 /* purple-setting.c in Sources */, B13FAB6C119D5155001CE037 /* purple-transport.c in Sources */, B13FABDF119D585A001CE037 /* sip-csta.c in Sources */, B13FABE1119D585A001CE037 /* sip-sec-gssapi.c in Sources */, B13FABE5119D585A001CE037 /* sip-sec-ntlm.c in Sources */, B13FABE9119D585A001CE037 /* sip-sec.c in Sources */, B13FABEB119D585A001CE037 /* sip-transport.c in Sources */, B13FABED119D585A001CE037 /* sipe-buddy.c in Sources */, B13FABEF119D585A001CE037 /* sipe-cal.c in Sources */, B13FABF1119D585A001CE037 /* sipe-chat.c in Sources */, B13FABF3119D585A001CE037 /* sipe-conf.c in Sources */, B13FABF6119D585A001CE037 /* sipe-core.c in Sources */, B13FABF8119D585A001CE037 /* sipe-dialog.c in Sources */, B13FABFD119D585A001CE037 /* sipe-ews.c in Sources */, B13FABFE119D585A001CE037 /* sipe-ews-autodiscover.c in Sources */, B13FABFF119D585A001CE037 /* sipe-ft.c in Sources */, B13FAC00119D585A001CE037 /* sipe-lync-autodiscover.c in Sources */, B13FAC04119D585A001CE037 /* sipe-schedule.c in Sources */, B13FAC06119D585A001CE037 /* sipe-session.c in Sources */, B13FAC08119D585A001CE037 /* sipe-sign.c in Sources */, B13FAC0A119D585A001CE037 /* sipe-utils.c in Sources */, B13FAC0F119D585A001CE037 /* sipe-xml.c in Sources */, B13FAC13119D585A001CE037 /* sipmsg.c in Sources */, B13FAC15119D585A001CE037 /* uuid.c in Sources */, 1CF260F912C2DFA00045B6CC /* purple-buddy.c in Sources */, 1CF260FA12C2DFA00045B6CC /* purple-chat.c in Sources */, 1CF260FB12C2DFA00045B6CC /* purple-ft.c in Sources */, 1CF260FC12C2DFA00045B6CC /* purple-groupchat.c in Sources */, 1CF260FD12C2DFA00045B6CC /* purple-im.c in Sources */, 1CF260FF12C2DFA00045B6CC /* purple-notify.c in Sources */, 1CF2610012C2DFA00045B6CC /* purple-user.c in Sources */, 1CF2611812C2E1AA0045B6CC /* sipe-groupchat.c in Sources */, 1CF2611912C2E1AA0045B6CC /* sipe-incoming.c in Sources */, 1CF2611B12C2E1AA0045B6CC /* sipe-ucs.c in Sources */, 1CF2611C12C2E1AA0045B6CC /* sipe-subscriptions.c in Sources */, 1CF2611D12C2E1AA0045B6CC /* sipe-user.c in Sources */, 1C822BED12F8E87500CC4AEA /* sipe-im.c in Sources */, 1CD71E3413C5380B0079DE64 /* sipe-ft-tftp.c in Sources */, 1CD71E3B13C538340079DE64 /* sipe-group.c in Sources */, 1CE49FB914A17CF000663393 /* sipe-svc.c in Sources */, 1CE49FBA14A17CF000663393 /* sipe-ocs2007.c in Sources */, 1CE49FBB14A17CF000663393 /* sipe-ocs2005.c in Sources */, 1CE49FEA14A17EF000663393 /* sipe-status.c in Sources */, 1CE49FF314A17F4D00663393 /* sip-soap.c in Sources */, 1CE49FF414A17F4D00663393 /* sipe-notify.c in Sources */, 1CE49FF514A17F4D00663393 /* sipe-webticket.c in Sources */, 1CE4A00714A17FD100663393 /* sip-sec-digest.c in Sources */, 1CE4A00814A17FD100663393 /* sip-sec-tls-dsk.c in Sources */, 1CE4A00A14A17FD100663393 /* sipe-certificate.c in Sources */, 1CE4A00D14A17FD100663393 /* sipe-tls.c in Sources */, 1CE4A01E14A180E100663393 /* purple-search.c in Sources */, 1CE4A01F14A180E100663393 /* purple-status.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 7BB8CC2B183AC198004351FF /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Adium; targetProxy = 7BB8CC2A183AC198004351FF /* PBXContainerItemProxy */; }; C92A79F210B25023009B64B9 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C92A796810B24BAD009B64B9 /* pidgin-sipe */; targetProxy = C92A79F110B25023009B64B9 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 089C167DFE841241C02AAC07 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 089C167EFE841241C02AAC07 /* English */, ); name = InfoPlist.strings; sourceTree = SOURCE_ROOT; }; 7BBE1527184D4BF50000FFC9 /* DCPurpleSIPEJoinChatView.xib */ = { isa = PBXVariantGroup; children = ( 7BBE1528184D4BF50000FFC9 /* English */, ); name = DCPurpleSIPEJoinChatView.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7BB8CB97183AA0BD004351FF /* SIPEAdiumPlugin.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; }; name = Debug; }; 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7BB8CB97183AA0BD004351FF /* SIPEAdiumPlugin.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; }; name = Release; }; 1DEB913F08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7BB8CB93183AA0BD004351FF /* Debug.xcconfig */; buildSettings = { ONLY_ACTIVE_ARCH = YES; }; name = Debug; }; 1DEB914008733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7BB8CB96183AA0BD004351FF /* Release.xcconfig */; buildSettings = { }; name = Release; }; 7BB8CBFE183AB83D004351FF /* Debug-Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7BB8CB92183AA0BD004351FF /* Debug-Release.xcconfig */; buildSettings = { }; name = "Debug-Release"; }; 7BB8CBFF183AB83D004351FF /* Debug-Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7BB8CB97183AA0BD004351FF /* SIPEAdiumPlugin.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; }; name = "Debug-Release"; }; 7BB8CC00183AB83D004351FF /* Debug-Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7BB8CB94183AA0BD004351FF /* libpidgin-sipe.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; }; name = "Debug-Release"; }; C92A796A10B24BAD009B64B9 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7BB8CB94183AA0BD004351FF /* libpidgin-sipe.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; }; name = Debug; }; C92A796B10B24BAD009B64B9 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7BB8CB94183AA0BD004351FF /* libpidgin-sipe.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "SIPEAdiumPlugin" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB913B08733D840010E9CD /* Debug */, 1DEB913C08733D840010E9CD /* Release */, 7BB8CBFF183AB83D004351FF /* Debug-Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "SIPEAdiumPlugin" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB913F08733D840010E9CD /* Debug */, 1DEB914008733D840010E9CD /* Release */, 7BB8CBFE183AB83D004351FF /* Debug-Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C92A796C10B24BCD009B64B9 /* Build configuration list for PBXNativeTarget "pidgin-sipe" */ = { isa = XCConfigurationList; buildConfigurations = ( C92A796A10B24BAD009B64B9 /* Debug */, C92A796B10B24BAD009B64B9 /* Release */, 7BB8CC00183AB83D004351FF /* Debug-Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 089C1669FE841209C02AAC07 /* Project object */; } ================================================ FILE: src/adium/SIPEAdiumPlugin.xcodeproj/xcshareddata/xcschemes/SIPEAdiumPlugin.xcscheme ================================================ ================================================ FILE: src/adium/SIPEAdiumPlugin.xcodeproj/xcshareddata/xcschemes/pidgin-sipe.xcscheme ================================================ ================================================ FILE: src/adium/SIPEAdiumPlugin_Prefix.pch ================================================ // // Prefix header for all source files of the 'SIPEAdiumPlugin' target in the 'SIPEAdiumPlugin' project. // #ifdef __OBJC__ #import #endif ================================================ FILE: src/adium/check_release.sh ================================================ #!/bin/bash # # Compare release contents with golden list to ensure release correctness # if [[ -z "$1" ]]; then echo 1>&2 "usage: $0 " exit 1 fi # examine release archive echo "Checking release archive '$1'..." files=$(set -o pipefail; unzip -l "$1" | grep SIPEAdiumPlugin.AdiumLibpurplePlugin/ | awk '{ print $4, $1 }' | grep -v '/ 0') if [[ $? -ne 0 ]]; then echo 1>&2 "ERROR: can't analyze release archive '$1'!" exit 1 fi # the following files *MUST* be in the release archive declare -A golden_list golden_list=( [SIPEAdiumPlugin.AdiumLibpurplePlugin/Contents/Info.plist]=1 [SIPEAdiumPlugin.AdiumLibpurplePlugin/Contents/MacOS/SIPEAdiumPlugin]=1 [SIPEAdiumPlugin.AdiumLibpurplePlugin/Contents/Resources/English.lproj/DCPurpleSIPEJoinChatView.nib]=1 [SIPEAdiumPlugin.AdiumLibpurplePlugin/Contents/Resources/English.lproj/InfoPlist.strings]=1 [SIPEAdiumPlugin.AdiumLibpurplePlugin/Contents/Resources/ESSIPEAccountView.nib]=1 [SIPEAdiumPlugin.AdiumLibpurplePlugin/Contents/Resources/PurpleDefaultsSIPE.plist]=1 [SIPEAdiumPlugin.AdiumLibpurplePlugin/Contents/Resources/sipe.png]=1 ) new_files=() # compare against golden list # @TODO: is there a better way to feed in file list? while read file size; do if [[ -z "${golden_list[${file}]}" ]]; then new_files+=( $file ) elif [[ "${size}" -eq 0 ]]; then echo 1>&2 "ERROR: file '${file}' is empty!" else unset golden_list[${file}] fi done <&2 "Release archive contains superfluous files:" for file in "${new_files[@]}"; do echo -e 1>&2 "\t${file}" done status=1 fi if [[ ${#golden_list[@]} -ne 0 ]]; then echo 1>&2 "Release archive is missing the following files:" for file in "${!golden_list[@]}"; do echo -e 1>&2 "\t${file}" done status=1 fi if [[ $status -eq 0 ]]; then echo "Release archive is OK!" else echo "Release archive is NOT OK!" fi exit $status ================================================ FILE: src/adium/find_adium_build.sh ================================================ #!/bin/sh set -e #set -x # remove old symlink setup rm -rf adium-frameworks # create empty directory to keep linker happy mkdir adium-frameworks # if Adium is built as sub-project -> skip if [[ ( -d "${BUILT_PRODUCTS_DIR}/Adium.framework" ) || ( -d "${BUILT_PRODUCTS_DIR}/AdiumLibPurple.framework" ) || ( -d "${BUILT_PRODUCTS_DIR}/AIUtilities.framework" ) ]]; then echo 1>&2 "Building Adium within SIPEAdiumPlugin - aborting..." exit 0 fi _sipe_build_dir=$(cd "${BUILT_PRODUCTS_DIR}/../../.."; pwd -P) if [[ ! -d "${_sipe_build_dir}" ]]; then echo 1>&2 "can't detect SIPE build directory from '${BUILT_PRODUCTS_DIR}'" exit 1 fi _build_dir=$(cd "${_sipe_build_dir}/.."; pwd -P) if [[ ! -d "${_build_dir}" ]]; then echo 1>&2 "can't detect common build directory from '${_sipe_build_dir}'" exit 1 fi _adium_build_dir=( $(find "${_build_dir}" -maxdepth 1 -type d -name "Adium-*" ) ) if [[ ${#_adium_build_dir[@]} -ne 1 ]]; then echo 1>&2 "can't detect Adium build directory from '${_build_dir}'" exit 1 fi # create symlinks to Adium frameworks _frameworks_dir="${BUILT_PRODUCTS_DIR/#${_sipe_build_dir}/${_adium_build_dir[0]}}" _adium_dirs=( Adium.framework AdiumLibPurple.framework AIUtilities.framework ) ln -s ${_adium_dirs[@]/#/${_frameworks_dir}/} adium-frameworks/ # log result ls -lhtR adium-frameworks exit 0 ================================================ FILE: src/adium/openssl/libcrypto.0.9.8.tbd ================================================ --- archs: [ i386, x86_64 ] platform: macosx install-name: /usr/lib/libcrypto.0.9.8.dylib current-version: 0.9.8 compatibility-version: 0.9.8 exports: - archs: [ i386, x86_64 ] symbols: [ _ACCESS_DESCRIPTION_free, _ACCESS_DESCRIPTION_it, _ACCESS_DESCRIPTION_new, _AES_bi_ige_encrypt, _AES_cbc_encrypt, _AES_cfb128_encrypt, _AES_cfb1_encrypt, _AES_cfb8_encrypt, _AES_cfbr_encrypt_block, _AES_ctr128_encrypt, _AES_decrypt, _AES_ecb_encrypt, _AES_encrypt, _AES_ige_encrypt, _AES_ofb128_encrypt, _AES_options, _AES_set_decrypt_key, _AES_set_encrypt_key, _AES_unwrap_key, _AES_version, _AES_wrap_key, _ASN1_ANY_it, _ASN1_BIT_STRING_asn1_meth, _ASN1_BIT_STRING_free, _ASN1_BIT_STRING_get_bit, _ASN1_BIT_STRING_it, _ASN1_BIT_STRING_name_print, _ASN1_BIT_STRING_new, _ASN1_BIT_STRING_num_asc, _ASN1_BIT_STRING_set, _ASN1_BIT_STRING_set_asc, _ASN1_BIT_STRING_set_bit, _ASN1_BMPSTRING_free, _ASN1_BMPSTRING_it, _ASN1_BMPSTRING_new, _ASN1_BOOLEAN_it, _ASN1_ENUMERATED_free, _ASN1_ENUMERATED_get, _ASN1_ENUMERATED_it, _ASN1_ENUMERATED_new, _ASN1_ENUMERATED_set, _ASN1_ENUMERATED_to_BN, _ASN1_FBOOLEAN_it, _ASN1_GENERALIZEDTIME_check, _ASN1_GENERALIZEDTIME_free, _ASN1_GENERALIZEDTIME_it, _ASN1_GENERALIZEDTIME_new, _ASN1_GENERALIZEDTIME_print, _ASN1_GENERALIZEDTIME_set, _ASN1_GENERALIZEDTIME_set_string, _ASN1_GENERALSTRING_free, _ASN1_GENERALSTRING_it, _ASN1_GENERALSTRING_new, _ASN1_HEADER_free, _ASN1_HEADER_new, _ASN1_IA5STRING_asn1_meth, _ASN1_IA5STRING_free, _ASN1_IA5STRING_it, _ASN1_IA5STRING_new, _ASN1_INTEGER_cmp, _ASN1_INTEGER_dup, _ASN1_INTEGER_free, _ASN1_INTEGER_get, _ASN1_INTEGER_it, _ASN1_INTEGER_new, _ASN1_INTEGER_set, _ASN1_INTEGER_to_BN, _ASN1_NULL_free, _ASN1_NULL_it, _ASN1_NULL_new, _ASN1_OBJECT_create, _ASN1_OBJECT_free, _ASN1_OBJECT_it, _ASN1_OBJECT_new, _ASN1_OCTET_STRING_NDEF_it, _ASN1_OCTET_STRING_cmp, _ASN1_OCTET_STRING_dup, _ASN1_OCTET_STRING_free, _ASN1_OCTET_STRING_it, _ASN1_OCTET_STRING_new, _ASN1_OCTET_STRING_set, _ASN1_PRINTABLESTRING_free, _ASN1_PRINTABLESTRING_it, _ASN1_PRINTABLESTRING_new, _ASN1_PRINTABLE_free, _ASN1_PRINTABLE_it, _ASN1_PRINTABLE_new, _ASN1_PRINTABLE_type, _ASN1_SEQUENCE_it, _ASN1_STRING_TABLE_add, _ASN1_STRING_TABLE_cleanup, _ASN1_STRING_TABLE_get, _ASN1_STRING_cmp, _ASN1_STRING_data, _ASN1_STRING_dup, _ASN1_STRING_encode, _ASN1_STRING_free, _ASN1_STRING_get_default_mask, _ASN1_STRING_length, _ASN1_STRING_length_set, _ASN1_STRING_new, _ASN1_STRING_print, _ASN1_STRING_print_ex, _ASN1_STRING_print_ex_fp, _ASN1_STRING_set, _ASN1_STRING_set0, _ASN1_STRING_set_by_NID, _ASN1_STRING_set_default_mask, _ASN1_STRING_set_default_mask_asc, _ASN1_STRING_to_UTF8, _ASN1_STRING_type, _ASN1_STRING_type_new, _ASN1_T61STRING_free, _ASN1_T61STRING_it, _ASN1_T61STRING_new, _ASN1_TBOOLEAN_it, _ASN1_TIME_check, _ASN1_TIME_free, _ASN1_TIME_it, _ASN1_TIME_new, _ASN1_TIME_print, _ASN1_TIME_set, _ASN1_TIME_to_generalizedtime, _ASN1_TYPE_cmp, _ASN1_TYPE_free, _ASN1_TYPE_get, _ASN1_TYPE_get_int_octetstring, _ASN1_TYPE_get_octetstring, _ASN1_TYPE_new, _ASN1_TYPE_set, _ASN1_TYPE_set1, _ASN1_TYPE_set_int_octetstring, _ASN1_TYPE_set_octetstring, _ASN1_UNIVERSALSTRING_free, _ASN1_UNIVERSALSTRING_it, _ASN1_UNIVERSALSTRING_new, _ASN1_UNIVERSALSTRING_to_string, _ASN1_UTCTIME_check, _ASN1_UTCTIME_cmp_time_t, _ASN1_UTCTIME_free, _ASN1_UTCTIME_it, _ASN1_UTCTIME_new, _ASN1_UTCTIME_print, _ASN1_UTCTIME_set, _ASN1_UTCTIME_set_string, _ASN1_UTF8STRING_free, _ASN1_UTF8STRING_it, _ASN1_UTF8STRING_new, _ASN1_VISIBLESTRING_free, _ASN1_VISIBLESTRING_it, _ASN1_VISIBLESTRING_new, _ASN1_add_oid_module, _ASN1_check_infinite_end, _ASN1_const_check_infinite_end, _ASN1_d2i_bio, _ASN1_d2i_fp, _ASN1_digest, _ASN1_dup, _ASN1_generate_nconf, _ASN1_generate_v3, _ASN1_get_object, _ASN1_i2d_bio, _ASN1_i2d_fp, _ASN1_item_d2i, _ASN1_item_d2i_bio, _ASN1_item_d2i_fp, _ASN1_item_digest, _ASN1_item_dup, _ASN1_item_ex_d2i, _ASN1_item_ex_free, _ASN1_item_ex_i2d, _ASN1_item_ex_new, _ASN1_item_free, _ASN1_item_i2d, _ASN1_item_i2d_bio, _ASN1_item_i2d_fp, _ASN1_item_ndef_i2d, _ASN1_item_new, _ASN1_item_pack, _ASN1_item_sign, _ASN1_item_unpack, _ASN1_item_verify, _ASN1_mbstring_copy, _ASN1_mbstring_ncopy, _ASN1_object_size, _ASN1_pack_string, _ASN1_parse, _ASN1_parse_dump, _ASN1_primitive_free, _ASN1_primitive_new, _ASN1_put_eoc, _ASN1_put_object, _ASN1_seq_pack, _ASN1_seq_unpack, _ASN1_sign, _ASN1_tag2bit, _ASN1_tag2str, _ASN1_template_d2i, _ASN1_template_free, _ASN1_template_i2d, _ASN1_template_new, _ASN1_unpack_string, _ASN1_verify, _ASN1_version, _AUTHORITY_INFO_ACCESS_free, _AUTHORITY_INFO_ACCESS_it, _AUTHORITY_INFO_ACCESS_new, _AUTHORITY_KEYID_free, _AUTHORITY_KEYID_it, _AUTHORITY_KEYID_new, _BASIC_CONSTRAINTS_free, _BASIC_CONSTRAINTS_it, _BASIC_CONSTRAINTS_new, _BF_cbc_encrypt, _BF_cfb64_encrypt, _BF_decrypt, _BF_ecb_encrypt, _BF_encrypt, _BF_ofb64_encrypt, _BF_options, _BF_set_key, _BF_version, _BIGNUM_it, _BIO_ACCEPT_free, _BIO_ACCEPT_new, _BIO_CONNECT_free, _BIO_CONNECT_new, _BIO_accept, _BIO_callback_ctrl, _BIO_clear_flags, _BIO_copy_next_retry, _BIO_ctrl, _BIO_ctrl_get_read_request, _BIO_ctrl_get_write_guarantee, _BIO_ctrl_pending, _BIO_ctrl_reset_read_request, _BIO_ctrl_wpending, _BIO_debug_callback, _BIO_dgram_non_fatal_error, _BIO_dump, _BIO_dump_cb, _BIO_dump_fp, _BIO_dump_indent, _BIO_dump_indent_cb, _BIO_dump_indent_fp, _BIO_dup_chain, _BIO_f_base64, _BIO_f_buffer, _BIO_f_cipher, _BIO_f_md, _BIO_f_nbio_test, _BIO_f_null, _BIO_f_reliable, _BIO_f_zlib, _BIO_fd_non_fatal_error, _BIO_fd_should_retry, _BIO_find_type, _BIO_free, _BIO_free_all, _BIO_get_accept_socket, _BIO_get_callback, _BIO_get_callback_arg, _BIO_get_ex_data, _BIO_get_ex_new_index, _BIO_get_host_ip, _BIO_get_port, _BIO_get_retry_BIO, _BIO_get_retry_reason, _BIO_gethostbyname, _BIO_gets, _BIO_indent, _BIO_int_ctrl, _BIO_method_name, _BIO_method_type, _BIO_new, _BIO_new_accept, _BIO_new_bio_pair, _BIO_new_connect, _BIO_new_dgram, _BIO_new_fd, _BIO_new_file, _BIO_new_fp, _BIO_new_mem_buf, _BIO_new_socket, _BIO_next, _BIO_nread, _BIO_nread0, _BIO_number_read, _BIO_number_written, _BIO_nwrite, _BIO_nwrite0, _BIO_pop, _BIO_printf, _BIO_ptr_ctrl, _BIO_push, _BIO_puts, _BIO_read, _BIO_s_accept, _BIO_s_bio, _BIO_s_connect, _BIO_s_datagram, _BIO_s_fd, _BIO_s_file, _BIO_s_log, _BIO_s_mem, _BIO_s_null, _BIO_s_socket, _BIO_set, _BIO_set_callback, _BIO_set_callback_arg, _BIO_set_cipher, _BIO_set_ex_data, _BIO_set_flags, _BIO_set_tcp_ndelay, _BIO_snprintf, _BIO_sock_cleanup, _BIO_sock_error, _BIO_sock_init, _BIO_sock_non_fatal_error, _BIO_sock_should_retry, _BIO_socket_ioctl, _BIO_socket_nbio, _BIO_test_flags, _BIO_vfree, _BIO_vprintf, _BIO_vsnprintf, _BIO_write, _BN_BLINDING_convert, _BN_BLINDING_convert_ex, _BN_BLINDING_create_param, _BN_BLINDING_free, _BN_BLINDING_get_flags, _BN_BLINDING_get_thread_id, _BN_BLINDING_invert, _BN_BLINDING_invert_ex, _BN_BLINDING_new, _BN_BLINDING_set_flags, _BN_BLINDING_set_thread_id, _BN_BLINDING_update, _BN_CTX_end, _BN_CTX_free, _BN_CTX_get, _BN_CTX_init, _BN_CTX_new, _BN_CTX_start, _BN_GENCB_call, _BN_GF2m_add, _BN_GF2m_arr2poly, _BN_GF2m_mod, _BN_GF2m_mod_arr, _BN_GF2m_mod_div, _BN_GF2m_mod_div_arr, _BN_GF2m_mod_exp, _BN_GF2m_mod_exp_arr, _BN_GF2m_mod_inv, _BN_GF2m_mod_inv_arr, _BN_GF2m_mod_mul, _BN_GF2m_mod_mul_arr, _BN_GF2m_mod_solve_quad, _BN_GF2m_mod_solve_quad_arr, _BN_GF2m_mod_sqr, _BN_GF2m_mod_sqr_arr, _BN_GF2m_mod_sqrt, _BN_GF2m_mod_sqrt_arr, _BN_GF2m_poly2arr, _BN_MONT_CTX_copy, _BN_MONT_CTX_free, _BN_MONT_CTX_init, _BN_MONT_CTX_new, _BN_MONT_CTX_set, _BN_MONT_CTX_set_locked, _BN_RECP_CTX_free, _BN_RECP_CTX_init, _BN_RECP_CTX_new, _BN_RECP_CTX_set, _BN_X931_derive_prime_ex, _BN_X931_generate_Xpq, _BN_X931_generate_prime_ex, _BN_add, _BN_add_word, _BN_bin2bn, _BN_bn2bin, _BN_bn2dec, _BN_bn2hex, _BN_bn2mpi, _BN_bntest_rand, _BN_clear, _BN_clear_bit, _BN_clear_free, _BN_cmp, _BN_consttime_swap, _BN_copy, _BN_dec2bn, _BN_div, _BN_div_recp, _BN_div_word, _BN_dup, _BN_exp, _BN_free, _BN_from_montgomery, _BN_gcd, _BN_generate_prime, _BN_generate_prime_ex, _BN_get0_nist_prime_192, _BN_get0_nist_prime_224, _BN_get0_nist_prime_256, _BN_get0_nist_prime_384, _BN_get0_nist_prime_521, _BN_get_params, _BN_get_word, _BN_hex2bn, _BN_init, _BN_is_bit_set, _BN_is_prime, _BN_is_prime_ex, _BN_is_prime_fasttest, _BN_is_prime_fasttest_ex, _BN_kronecker, _BN_lshift, _BN_lshift1, _BN_mask_bits, _BN_mod_add, _BN_mod_add_quick, _BN_mod_exp, _BN_mod_exp2_mont, _BN_mod_exp_mont, _BN_mod_exp_mont_consttime, _BN_mod_exp_mont_word, _BN_mod_exp_recp, _BN_mod_exp_simple, _BN_mod_inverse, _BN_mod_lshift, _BN_mod_lshift1, _BN_mod_lshift1_quick, _BN_mod_lshift_quick, _BN_mod_mul, _BN_mod_mul_montgomery, _BN_mod_mul_reciprocal, _BN_mod_sqr, _BN_mod_sqrt, _BN_mod_sub, _BN_mod_sub_quick, _BN_mod_word, _BN_mpi2bn, _BN_mul, _BN_mul_word, _BN_new, _BN_nist_mod_192, _BN_nist_mod_224, _BN_nist_mod_256, _BN_nist_mod_384, _BN_nist_mod_521, _BN_nnmod, _BN_num_bits, _BN_num_bits_word, _BN_options, _BN_print, _BN_print_fp, _BN_pseudo_rand, _BN_pseudo_rand_range, _BN_rand, _BN_rand_range, _BN_reciprocal, _BN_rshift, _BN_rshift1, _BN_set_bit, _BN_set_negative, _BN_set_params, _BN_set_word, _BN_sqr, _BN_sub, _BN_sub_word, _BN_swap, _BN_to_ASN1_ENUMERATED, _BN_to_ASN1_INTEGER, _BN_uadd, _BN_ucmp, _BN_usub, _BN_value_one, _BN_version, _BUF_MEM_free, _BUF_MEM_grow, _BUF_MEM_grow_clean, _BUF_MEM_new, _BUF_memdup, _BUF_strdup, _BUF_strlcat, _BUF_strlcpy, _BUF_strndup, _CAST_S_table0, _CAST_S_table1, _CAST_S_table2, _CAST_S_table3, _CAST_S_table4, _CAST_S_table5, _CAST_S_table6, _CAST_S_table7, _CAST_cbc_encrypt, _CAST_cfb64_encrypt, _CAST_decrypt, _CAST_ecb_encrypt, _CAST_encrypt, _CAST_ofb64_encrypt, _CAST_set_key, _CAST_version, _CBIGNUM_it, _CERTIFICATEPOLICIES_free, _CERTIFICATEPOLICIES_it, _CERTIFICATEPOLICIES_new, _COMP_CTX_free, _COMP_CTX_new, _COMP_compress_block, _COMP_expand_block, _COMP_rle, _COMP_zlib, _COMP_zlib_cleanup, _CONF_def_version, _CONF_dump_bio, _CONF_dump_fp, _CONF_free, _CONF_get1_default_config_file, _CONF_get_number, _CONF_get_section, _CONF_get_string, _CONF_imodule_get_flags, _CONF_imodule_get_module, _CONF_imodule_get_name, _CONF_imodule_get_usr_data, _CONF_imodule_get_value, _CONF_imodule_set_flags, _CONF_imodule_set_usr_data, _CONF_load, _CONF_load_bio, _CONF_load_fp, _CONF_module_add, _CONF_module_get_usr_data, _CONF_module_set_usr_data, _CONF_modules_finish, _CONF_modules_free, _CONF_modules_load, _CONF_modules_load_file, _CONF_modules_unload, _CONF_parse_list, _CONF_set_default_method, _CONF_set_nconf, _CONF_version, _CRL_DIST_POINTS_free, _CRL_DIST_POINTS_it, _CRL_DIST_POINTS_new, _CRYPTO_add_lock, _CRYPTO_cleanup_all_ex_data, _CRYPTO_dbg_free, _CRYPTO_dbg_get_options, _CRYPTO_dbg_malloc, _CRYPTO_dbg_pop_info, _CRYPTO_dbg_push_info, _CRYPTO_dbg_realloc, _CRYPTO_dbg_remove_all_info, _CRYPTO_dbg_set_options, _CRYPTO_destroy_dynlockid, _CRYPTO_dup_ex_data, _CRYPTO_ex_data_new_class, _CRYPTO_free, _CRYPTO_free_ex_data, _CRYPTO_free_locked, _CRYPTO_get_add_lock_callback, _CRYPTO_get_dynlock_create_callback, _CRYPTO_get_dynlock_destroy_callback, _CRYPTO_get_dynlock_lock_callback, _CRYPTO_get_dynlock_value, _CRYPTO_get_ex_data, _CRYPTO_get_ex_data_implementation, _CRYPTO_get_ex_new_index, _CRYPTO_get_id_callback, _CRYPTO_get_lock_name, _CRYPTO_get_locked_mem_ex_functions, _CRYPTO_get_locked_mem_functions, _CRYPTO_get_locking_callback, _CRYPTO_get_mem_debug_functions, _CRYPTO_get_mem_debug_options, _CRYPTO_get_mem_ex_functions, _CRYPTO_get_mem_functions, _CRYPTO_get_new_dynlockid, _CRYPTO_get_new_lockid, _CRYPTO_is_mem_check_on, _CRYPTO_lock, _CRYPTO_malloc, _CRYPTO_malloc_debug_init, _CRYPTO_malloc_locked, _CRYPTO_mem_ctrl, _CRYPTO_mem_leaks, _CRYPTO_mem_leaks_cb, _CRYPTO_mem_leaks_fp, _CRYPTO_memcmp, _CRYPTO_new_ex_data, _CRYPTO_num_locks, _CRYPTO_pop_info, _CRYPTO_push_info_, _CRYPTO_realloc, _CRYPTO_realloc_clean, _CRYPTO_remalloc, _CRYPTO_remove_all_info, _CRYPTO_set_add_lock_callback, _CRYPTO_set_dynlock_create_callback, _CRYPTO_set_dynlock_destroy_callback, _CRYPTO_set_dynlock_lock_callback, _CRYPTO_set_ex_data, _CRYPTO_set_ex_data_implementation, _CRYPTO_set_id_callback, _CRYPTO_set_locked_mem_ex_functions, _CRYPTO_set_locked_mem_functions, _CRYPTO_set_locking_callback, _CRYPTO_set_mem_debug_functions, _CRYPTO_set_mem_debug_options, _CRYPTO_set_mem_ex_functions, _CRYPTO_set_mem_functions, _CRYPTO_set_mem_info_functions, _CRYPTO_strdup, _CRYPTO_thread_id, _DES_SPtrans, _DES_cbc_cksum, _DES_cbc_encrypt, _DES_cfb64_encrypt, _DES_cfb_encrypt, _DES_check_key_parity, _DES_crypt, _DES_decrypt3, _DES_ecb3_encrypt, _DES_ecb_encrypt, _DES_ede3_cbc_encrypt, _DES_ede3_cbcm_encrypt, _DES_ede3_cfb64_encrypt, _DES_ede3_cfb_encrypt, _DES_ede3_ofb64_encrypt, _DES_enc_read, _DES_enc_write, _DES_encrypt1, _DES_encrypt2, _DES_encrypt3, _DES_fcrypt, _DES_is_weak_key, _DES_key_sched, _DES_ncbc_encrypt, _DES_ofb64_encrypt, _DES_ofb_encrypt, _DES_options, _DES_pcbc_encrypt, _DES_quad_cksum, _DES_random_key, _DES_read_2passwords, _DES_read_password, _DES_set_key, _DES_set_key_checked, _DES_set_key_unchecked, _DES_set_odd_parity, _DES_string_to_2keys, _DES_string_to_key, _DES_xcbc_encrypt, _DH_OpenSSL, _DH_check, _DH_check_pub_key, _DH_compute_key, _DH_free, _DH_generate_key, _DH_generate_parameters, _DH_generate_parameters_ex, _DH_get_default_method, _DH_get_ex_data, _DH_get_ex_new_index, _DH_new, _DH_new_method, _DH_set_default_method, _DH_set_ex_data, _DH_set_method, _DH_size, _DH_up_ref, _DH_version, _DHparams_it, _DHparams_print, _DHparams_print_fp, _DIRECTORYSTRING_free, _DIRECTORYSTRING_it, _DIRECTORYSTRING_new, _DISPLAYTEXT_free, _DISPLAYTEXT_it, _DISPLAYTEXT_new, _DIST_POINT_NAME_free, _DIST_POINT_NAME_it, _DIST_POINT_NAME_new, _DIST_POINT_free, _DIST_POINT_it, _DIST_POINT_new, _DSAPrivateKey_it, _DSAPublicKey_it, _DSA_OpenSSL, _DSA_SIG_free, _DSA_SIG_it, _DSA_SIG_new, _DSA_do_sign, _DSA_do_verify, _DSA_dup_DH, _DSA_free, _DSA_generate_key, _DSA_generate_parameters, _DSA_generate_parameters_ex, _DSA_get_default_method, _DSA_get_ex_data, _DSA_get_ex_new_index, _DSA_new, _DSA_new_method, _DSA_print, _DSA_print_fp, _DSA_set_default_method, _DSA_set_ex_data, _DSA_set_method, _DSA_sign, _DSA_sign_setup, _DSA_size, _DSA_up_ref, _DSA_verify, _DSA_version, _DSAparams_it, _DSAparams_print, _DSAparams_print_fp, _DSO_METHOD_dl, _DSO_METHOD_dlfcn, _DSO_METHOD_null, _DSO_METHOD_openssl, _DSO_METHOD_vms, _DSO_METHOD_win32, _DSO_bind_func, _DSO_bind_var, _DSO_convert_filename, _DSO_ctrl, _DSO_flags, _DSO_free, _DSO_get_default_method, _DSO_get_filename, _DSO_get_loaded_filename, _DSO_get_method, _DSO_load, _DSO_merge, _DSO_new, _DSO_new_method, _DSO_set_default_method, _DSO_set_filename, _DSO_set_method, _DSO_set_name_converter, _DSO_up_ref, _ECDH_OpenSSL, _ECDH_compute_key, _ECDH_get_default_method, _ECDH_get_ex_data, _ECDH_get_ex_new_index, _ECDH_set_default_method, _ECDH_set_ex_data, _ECDH_set_method, _ECDH_version, _ECDSA_OpenSSL, _ECDSA_SIG_free, _ECDSA_SIG_it, _ECDSA_SIG_new, _ECDSA_do_sign, _ECDSA_do_sign_ex, _ECDSA_do_verify, _ECDSA_get_default_method, _ECDSA_get_ex_data, _ECDSA_get_ex_new_index, _ECDSA_set_default_method, _ECDSA_set_ex_data, _ECDSA_set_method, _ECDSA_sign, _ECDSA_sign_ex, _ECDSA_sign_setup, _ECDSA_size, _ECDSA_verify, _ECDSA_version, _ECPARAMETERS_free, _ECPARAMETERS_it, _ECPARAMETERS_new, _ECPKPARAMETERS_free, _ECPKPARAMETERS_it, _ECPKPARAMETERS_new, _ECPKParameters_print, _ECPKParameters_print_fp, _ECParameters_print, _ECParameters_print_fp, _EC_EX_DATA_clear_free_all_data, _EC_EX_DATA_clear_free_data, _EC_EX_DATA_free_all_data, _EC_EX_DATA_free_data, _EC_EX_DATA_get_data, _EC_EX_DATA_set_data, _EC_GF2m_simple_method, _EC_GFp_mont_method, _EC_GFp_nist_method, _EC_GFp_simple_method, _EC_GROUP_check, _EC_GROUP_check_discriminant, _EC_GROUP_clear_free, _EC_GROUP_cmp, _EC_GROUP_copy, _EC_GROUP_dup, _EC_GROUP_free, _EC_GROUP_get0_generator, _EC_GROUP_get0_seed, _EC_GROUP_get_asn1_flag, _EC_GROUP_get_basis_type, _EC_GROUP_get_cofactor, _EC_GROUP_get_curve_GF2m, _EC_GROUP_get_curve_GFp, _EC_GROUP_get_curve_name, _EC_GROUP_get_degree, _EC_GROUP_get_order, _EC_GROUP_get_pentanomial_basis, _EC_GROUP_get_point_conversion_form, _EC_GROUP_get_seed_len, _EC_GROUP_get_trinomial_basis, _EC_GROUP_have_precompute_mult, _EC_GROUP_method_of, _EC_GROUP_new, _EC_GROUP_new_by_curve_name, _EC_GROUP_new_curve_GF2m, _EC_GROUP_new_curve_GFp, _EC_GROUP_precompute_mult, _EC_GROUP_set_asn1_flag, _EC_GROUP_set_curve_GF2m, _EC_GROUP_set_curve_GFp, _EC_GROUP_set_curve_name, _EC_GROUP_set_generator, _EC_GROUP_set_point_conversion_form, _EC_GROUP_set_seed, _EC_KEY_check_key, _EC_KEY_copy, _EC_KEY_dup, _EC_KEY_free, _EC_KEY_generate_key, _EC_KEY_get0_group, _EC_KEY_get0_private_key, _EC_KEY_get0_public_key, _EC_KEY_get_conv_form, _EC_KEY_get_enc_flags, _EC_KEY_get_key_method_data, _EC_KEY_insert_key_method_data, _EC_KEY_new, _EC_KEY_new_by_curve_name, _EC_KEY_precompute_mult, _EC_KEY_print, _EC_KEY_print_fp, _EC_KEY_set_asn1_flag, _EC_KEY_set_conv_form, _EC_KEY_set_enc_flags, _EC_KEY_set_group, _EC_KEY_set_private_key, _EC_KEY_set_public_key, _EC_KEY_up_ref, _EC_METHOD_get_field_type, _EC_POINT_add, _EC_POINT_bn2point, _EC_POINT_clear_free, _EC_POINT_cmp, _EC_POINT_copy, _EC_POINT_dbl, _EC_POINT_dup, _EC_POINT_free, _EC_POINT_get_Jprojective_coordinates_GFp, _EC_POINT_get_affine_coordinates_GF2m, _EC_POINT_get_affine_coordinates_GFp, _EC_POINT_hex2point, _EC_POINT_invert, _EC_POINT_is_at_infinity, _EC_POINT_is_on_curve, _EC_POINT_make_affine, _EC_POINT_method_of, _EC_POINT_mul, _EC_POINT_new, _EC_POINT_oct2point, _EC_POINT_point2bn, _EC_POINT_point2hex, _EC_POINT_point2oct, _EC_POINT_set_Jprojective_coordinates_GFp, _EC_POINT_set_affine_coordinates_GF2m, _EC_POINT_set_affine_coordinates_GFp, _EC_POINT_set_compressed_coordinates_GF2m, _EC_POINT_set_compressed_coordinates_GFp, _EC_POINT_set_to_infinity, _EC_POINTs_make_affine, _EC_POINTs_mul, _EC_PRIVATEKEY_free, _EC_PRIVATEKEY_it, _EC_PRIVATEKEY_new, _EC_get_builtin_curves, _EDIPARTYNAME_free, _EDIPARTYNAME_it, _EDIPARTYNAME_new, _ENGINE_add, _ENGINE_add_conf_module, _ENGINE_by_id, _ENGINE_cleanup, _ENGINE_cmd_is_executable, _ENGINE_ctrl, _ENGINE_ctrl_cmd, _ENGINE_ctrl_cmd_string, _ENGINE_finish, _ENGINE_free, _ENGINE_get_DH, _ENGINE_get_DSA, _ENGINE_get_ECDH, _ENGINE_get_ECDSA, _ENGINE_get_RAND, _ENGINE_get_RSA, _ENGINE_get_STORE, _ENGINE_get_cipher, _ENGINE_get_cipher_engine, _ENGINE_get_ciphers, _ENGINE_get_cmd_defns, _ENGINE_get_ctrl_function, _ENGINE_get_default_DH, _ENGINE_get_default_DSA, _ENGINE_get_default_ECDH, _ENGINE_get_default_ECDSA, _ENGINE_get_default_RAND, _ENGINE_get_default_RSA, _ENGINE_get_destroy_function, _ENGINE_get_digest, _ENGINE_get_digest_engine, _ENGINE_get_digests, _ENGINE_get_ex_data, _ENGINE_get_ex_new_index, _ENGINE_get_finish_function, _ENGINE_get_first, _ENGINE_get_flags, _ENGINE_get_id, _ENGINE_get_init_function, _ENGINE_get_last, _ENGINE_get_load_privkey_function, _ENGINE_get_load_pubkey_function, _ENGINE_get_name, _ENGINE_get_next, _ENGINE_get_prev, _ENGINE_get_ssl_client_cert_function, _ENGINE_get_static_state, _ENGINE_get_table_flags, _ENGINE_init, _ENGINE_load_builtin_engines, _ENGINE_load_cryptodev, _ENGINE_load_dynamic, _ENGINE_load_openssl, _ENGINE_load_private_key, _ENGINE_load_public_key, _ENGINE_load_ssl_client_cert, _ENGINE_new, _ENGINE_register_DH, _ENGINE_register_DSA, _ENGINE_register_ECDH, _ENGINE_register_ECDSA, _ENGINE_register_RAND, _ENGINE_register_RSA, _ENGINE_register_STORE, _ENGINE_register_all_DH, _ENGINE_register_all_DSA, _ENGINE_register_all_ECDH, _ENGINE_register_all_ECDSA, _ENGINE_register_all_RAND, _ENGINE_register_all_RSA, _ENGINE_register_all_STORE, _ENGINE_register_all_ciphers, _ENGINE_register_all_complete, _ENGINE_register_all_digests, _ENGINE_register_ciphers, _ENGINE_register_complete, _ENGINE_register_digests, _ENGINE_remove, _ENGINE_set_DH, _ENGINE_set_DSA, _ENGINE_set_ECDH, _ENGINE_set_ECDSA, _ENGINE_set_RAND, _ENGINE_set_RSA, _ENGINE_set_STORE, _ENGINE_set_ciphers, _ENGINE_set_cmd_defns, _ENGINE_set_ctrl_function, _ENGINE_set_default, _ENGINE_set_default_DH, _ENGINE_set_default_DSA, _ENGINE_set_default_ECDH, _ENGINE_set_default_ECDSA, _ENGINE_set_default_RAND, _ENGINE_set_default_RSA, _ENGINE_set_default_ciphers, _ENGINE_set_default_digests, _ENGINE_set_default_string, _ENGINE_set_destroy_function, _ENGINE_set_digests, _ENGINE_set_ex_data, _ENGINE_set_finish_function, _ENGINE_set_flags, _ENGINE_set_id, _ENGINE_set_init_function, _ENGINE_set_load_privkey_function, _ENGINE_set_load_pubkey_function, _ENGINE_set_load_ssl_client_cert_function, _ENGINE_set_name, _ENGINE_set_table_flags, _ENGINE_unregister_DH, _ENGINE_unregister_DSA, _ENGINE_unregister_ECDH, _ENGINE_unregister_ECDSA, _ENGINE_unregister_RAND, _ENGINE_unregister_RSA, _ENGINE_unregister_STORE, _ENGINE_unregister_ciphers, _ENGINE_unregister_digests, _ENGINE_up_ref, _ERR_add_error_data, _ERR_clear_error, _ERR_error_string, _ERR_error_string_n, _ERR_free_strings, _ERR_func_error_string, _ERR_get_err_state_table, _ERR_get_error, _ERR_get_error_line, _ERR_get_error_line_data, _ERR_get_implementation, _ERR_get_next_error_library, _ERR_get_state, _ERR_get_string_table, _ERR_lib_error_string, _ERR_load_ASN1_strings, _ERR_load_BIO_strings, _ERR_load_BN_strings, _ERR_load_BUF_strings, _ERR_load_COMP_strings, _ERR_load_CONF_strings, _ERR_load_CRYPTO_strings, _ERR_load_DH_strings, _ERR_load_DSA_strings, _ERR_load_DSO_strings, _ERR_load_ECDH_strings, _ERR_load_ECDSA_strings, _ERR_load_EC_strings, _ERR_load_ENGINE_strings, _ERR_load_ERR_strings, _ERR_load_EVP_strings, _ERR_load_OBJ_strings, _ERR_load_OCSP_strings, _ERR_load_PEM_strings, _ERR_load_PKCS12_strings, _ERR_load_PKCS7_strings, _ERR_load_RAND_strings, _ERR_load_RSA_strings, _ERR_load_STORE_strings, _ERR_load_UI_strings, _ERR_load_X509V3_strings, _ERR_load_X509_strings, _ERR_load_crypto_strings, _ERR_load_strings, _ERR_peek_error, _ERR_peek_error_line, _ERR_peek_error_line_data, _ERR_peek_last_error, _ERR_peek_last_error_line, _ERR_peek_last_error_line_data, _ERR_pop_to_mark, _ERR_print_errors, _ERR_print_errors_cb, _ERR_print_errors_fp, _ERR_put_error, _ERR_reason_error_string, _ERR_release_err_state_table, _ERR_remove_state, _ERR_set_error_data, _ERR_set_implementation, _ERR_set_mark, _ERR_unload_strings, _EVP_BytesToKey, _EVP_CIPHER_CTX_block_size, _EVP_CIPHER_CTX_cipher, _EVP_CIPHER_CTX_cleanup, _EVP_CIPHER_CTX_clear_flags, _EVP_CIPHER_CTX_ctrl, _EVP_CIPHER_CTX_flags, _EVP_CIPHER_CTX_free, _EVP_CIPHER_CTX_get_app_data, _EVP_CIPHER_CTX_init, _EVP_CIPHER_CTX_iv_length, _EVP_CIPHER_CTX_key_length, _EVP_CIPHER_CTX_new, _EVP_CIPHER_CTX_nid, _EVP_CIPHER_CTX_rand_key, _EVP_CIPHER_CTX_set_app_data, _EVP_CIPHER_CTX_set_flags, _EVP_CIPHER_CTX_set_key_length, _EVP_CIPHER_CTX_set_padding, _EVP_CIPHER_CTX_test_flags, _EVP_CIPHER_asn1_to_param, _EVP_CIPHER_block_size, _EVP_CIPHER_flags, _EVP_CIPHER_get_asn1_iv, _EVP_CIPHER_iv_length, _EVP_CIPHER_key_length, _EVP_CIPHER_nid, _EVP_CIPHER_param_to_asn1, _EVP_CIPHER_set_asn1_iv, _EVP_CIPHER_type, _EVP_Cipher, _EVP_CipherFinal, _EVP_CipherFinal_ex, _EVP_CipherInit, _EVP_CipherInit_ex, _EVP_CipherUpdate, _EVP_DecodeBlock, _EVP_DecodeFinal, _EVP_DecodeInit, _EVP_DecodeUpdate, _EVP_DecryptFinal, _EVP_DecryptFinal_ex, _EVP_DecryptInit, _EVP_DecryptInit_ex, _EVP_DecryptUpdate, _EVP_Digest, _EVP_DigestFinal, _EVP_DigestFinal_ex, _EVP_DigestInit, _EVP_DigestInit_ex, _EVP_DigestUpdate, _EVP_EncodeBlock, _EVP_EncodeFinal, _EVP_EncodeInit, _EVP_EncodeUpdate, _EVP_EncryptFinal, _EVP_EncryptFinal_ex, _EVP_EncryptInit, _EVP_EncryptInit_ex, _EVP_EncryptUpdate, _EVP_MD_CTX_cleanup, _EVP_MD_CTX_clear_flags, _EVP_MD_CTX_copy, _EVP_MD_CTX_copy_ex, _EVP_MD_CTX_create, _EVP_MD_CTX_destroy, _EVP_MD_CTX_init, _EVP_MD_CTX_md, _EVP_MD_CTX_set_flags, _EVP_MD_CTX_test_flags, _EVP_MD_block_size, _EVP_MD_pkey_type, _EVP_MD_size, _EVP_MD_type, _EVP_OpenFinal, _EVP_OpenInit, _EVP_PBE_CipherInit, _EVP_PBE_alg_add, _EVP_PBE_cleanup, _EVP_PKCS82PKEY, _EVP_PKEY2PKCS8, _EVP_PKEY2PKCS8_broken, _EVP_PKEY_add1_attr, _EVP_PKEY_add1_attr_by_NID, _EVP_PKEY_add1_attr_by_OBJ, _EVP_PKEY_add1_attr_by_txt, _EVP_PKEY_assign, _EVP_PKEY_bits, _EVP_PKEY_cmp, _EVP_PKEY_cmp_parameters, _EVP_PKEY_copy_parameters, _EVP_PKEY_decrypt, _EVP_PKEY_delete_attr, _EVP_PKEY_encrypt, _EVP_PKEY_free, _EVP_PKEY_get1_DH, _EVP_PKEY_get1_DSA, _EVP_PKEY_get1_EC_KEY, _EVP_PKEY_get1_RSA, _EVP_PKEY_get_attr, _EVP_PKEY_get_attr_by_NID, _EVP_PKEY_get_attr_by_OBJ, _EVP_PKEY_get_attr_count, _EVP_PKEY_missing_parameters, _EVP_PKEY_new, _EVP_PKEY_save_parameters, _EVP_PKEY_set1_DH, _EVP_PKEY_set1_DSA, _EVP_PKEY_set1_EC_KEY, _EVP_PKEY_set1_RSA, _EVP_PKEY_size, _EVP_PKEY_type, _EVP_SealFinal, _EVP_SealInit, _EVP_SignFinal, _EVP_VerifyFinal, _EVP_add_alg_module, _EVP_add_cipher, _EVP_add_digest, _EVP_aes_128_cbc, _EVP_aes_128_cfb, _EVP_aes_128_cfb1, _EVP_aes_128_cfb128, _EVP_aes_128_cfb8, _EVP_aes_128_ecb, _EVP_aes_128_ofb, _EVP_aes_192_cbc, _EVP_aes_192_cfb, _EVP_aes_192_cfb1, _EVP_aes_192_cfb128, _EVP_aes_192_cfb8, _EVP_aes_192_ecb, _EVP_aes_192_ofb, _EVP_aes_256_cbc, _EVP_aes_256_cfb, _EVP_aes_256_cfb1, _EVP_aes_256_cfb128, _EVP_aes_256_cfb8, _EVP_aes_256_ecb, _EVP_aes_256_ofb, _EVP_bf_cbc, _EVP_bf_cfb, _EVP_bf_cfb64, _EVP_bf_ecb, _EVP_bf_ofb, _EVP_cast5_cbc, _EVP_cast5_cfb, _EVP_cast5_cfb64, _EVP_cast5_ecb, _EVP_cast5_ofb, _EVP_cleanup, _EVP_des_cbc, _EVP_des_cfb, _EVP_des_cfb1, _EVP_des_cfb64, _EVP_des_cfb8, _EVP_des_ecb, _EVP_des_ede, _EVP_des_ede3, _EVP_des_ede3_cbc, _EVP_des_ede3_cfb, _EVP_des_ede3_cfb1, _EVP_des_ede3_cfb64, _EVP_des_ede3_cfb8, _EVP_des_ede3_ecb, _EVP_des_ede3_ofb, _EVP_des_ede_cbc, _EVP_des_ede_cfb, _EVP_des_ede_cfb64, _EVP_des_ede_ecb, _EVP_des_ede_ofb, _EVP_des_ofb, _EVP_desx_cbc, _EVP_dss, _EVP_dss1, _EVP_ecdsa, _EVP_enc_null, _EVP_get_cipherbyname, _EVP_get_digestbyname, _EVP_get_pw_prompt, _EVP_md2, _EVP_md4, _EVP_md5, _EVP_md_null, _EVP_mdc2, _EVP_rc2_40_cbc, _EVP_rc2_64_cbc, _EVP_rc2_cbc, _EVP_rc2_cfb, _EVP_rc2_cfb64, _EVP_rc2_ecb, _EVP_rc2_ofb, _EVP_rc4, _EVP_rc4_40, _EVP_read_pw_string, _EVP_ripemd160, _EVP_seed_cbc, _EVP_seed_cfb128, _EVP_seed_ecb, _EVP_seed_ofb, _EVP_set_pw_prompt, _EVP_sha, _EVP_sha1, _EVP_sha224, _EVP_sha256, _EVP_sha384, _EVP_sha512, _EVP_version, _EXTENDED_KEY_USAGE_free, _EXTENDED_KEY_USAGE_it, _EXTENDED_KEY_USAGE_new, _GENERAL_NAMES_free, _GENERAL_NAMES_it, _GENERAL_NAMES_new, _GENERAL_NAME_free, _GENERAL_NAME_it, _GENERAL_NAME_new, _GENERAL_NAME_print, _GENERAL_SUBTREE_free, _GENERAL_SUBTREE_it, _GENERAL_SUBTREE_new, _HMAC, _HMAC_CTX_cleanup, _HMAC_CTX_init, _HMAC_CTX_set_flags, _HMAC_Final, _HMAC_Init, _HMAC_Init_ex, _HMAC_Update, _KRB5_APREQBODY_free, _KRB5_APREQBODY_it, _KRB5_APREQBODY_new, _KRB5_APREQ_free, _KRB5_APREQ_it, _KRB5_APREQ_new, _KRB5_AUTHDATA_free, _KRB5_AUTHDATA_it, _KRB5_AUTHDATA_new, _KRB5_AUTHENTBODY_free, _KRB5_AUTHENTBODY_it, _KRB5_AUTHENTBODY_new, _KRB5_AUTHENT_free, _KRB5_AUTHENT_it, _KRB5_AUTHENT_new, _KRB5_CHECKSUM_free, _KRB5_CHECKSUM_it, _KRB5_CHECKSUM_new, _KRB5_ENCDATA_free, _KRB5_ENCDATA_it, _KRB5_ENCDATA_new, _KRB5_ENCKEY_free, _KRB5_ENCKEY_it, _KRB5_ENCKEY_new, _KRB5_PRINCNAME_free, _KRB5_PRINCNAME_it, _KRB5_PRINCNAME_new, _KRB5_TICKET_free, _KRB5_TICKET_it, _KRB5_TICKET_new, _KRB5_TKTBODY_free, _KRB5_TKTBODY_it, _KRB5_TKTBODY_new, _LONG_it, _MD2, _MD2_Final, _MD2_Init, _MD2_Update, _MD2_options, _MD2_version, _MD4, _MD4_Final, _MD4_Init, _MD4_Transform, _MD4_Update, _MD4_version, _MD5, _MD5_Final, _MD5_Init, _MD5_Transform, _MD5_Update, _MD5_version, _MDC2, _MDC2_Final, _MDC2_Init, _MDC2_Update, _MGF1, _NAME_CONSTRAINTS_free, _NAME_CONSTRAINTS_it, _NAME_CONSTRAINTS_new, _NCONF_WIN32, _NCONF_default, _NCONF_dump_bio, _NCONF_dump_fp, _NCONF_free, _NCONF_free_data, _NCONF_get_number_e, _NCONF_get_section, _NCONF_get_string, _NCONF_load, _NCONF_load_bio, _NCONF_load_fp, _NCONF_new, _NETSCAPE_CERT_SEQUENCE_free, _NETSCAPE_CERT_SEQUENCE_it, _NETSCAPE_CERT_SEQUENCE_new, _NETSCAPE_ENCRYPTED_PKEY_free, _NETSCAPE_ENCRYPTED_PKEY_it, _NETSCAPE_ENCRYPTED_PKEY_new, _NETSCAPE_PKEY_free, _NETSCAPE_PKEY_it, _NETSCAPE_PKEY_new, _NETSCAPE_SPKAC_free, _NETSCAPE_SPKAC_it, _NETSCAPE_SPKAC_new, _NETSCAPE_SPKI_b64_decode, _NETSCAPE_SPKI_b64_encode, _NETSCAPE_SPKI_free, _NETSCAPE_SPKI_get_pubkey, _NETSCAPE_SPKI_it, _NETSCAPE_SPKI_new, _NETSCAPE_SPKI_print, _NETSCAPE_SPKI_set_pubkey, _NETSCAPE_SPKI_sign, _NETSCAPE_SPKI_verify, _NOTICEREF_free, _NOTICEREF_it, _NOTICEREF_new, _OBJ_NAME_add, _OBJ_NAME_cleanup, _OBJ_NAME_do_all, _OBJ_NAME_do_all_sorted, _OBJ_NAME_get, _OBJ_NAME_init, _OBJ_NAME_new_index, _OBJ_NAME_remove, _OBJ_add_object, _OBJ_bsearch, _OBJ_bsearch_ex, _OBJ_cleanup, _OBJ_cmp, _OBJ_create, _OBJ_create_objects, _OBJ_dup, _OBJ_ln2nid, _OBJ_new_nid, _OBJ_nid2ln, _OBJ_nid2obj, _OBJ_nid2sn, _OBJ_obj2nid, _OBJ_obj2txt, _OBJ_sn2nid, _OBJ_txt2nid, _OBJ_txt2obj, _OCSP_BASICRESP_add1_ext_i2d, _OCSP_BASICRESP_add_ext, _OCSP_BASICRESP_delete_ext, _OCSP_BASICRESP_free, _OCSP_BASICRESP_get1_ext_d2i, _OCSP_BASICRESP_get_ext, _OCSP_BASICRESP_get_ext_by_NID, _OCSP_BASICRESP_get_ext_by_OBJ, _OCSP_BASICRESP_get_ext_by_critical, _OCSP_BASICRESP_get_ext_count, _OCSP_BASICRESP_it, _OCSP_BASICRESP_new, _OCSP_CERTID_free, _OCSP_CERTID_it, _OCSP_CERTID_new, _OCSP_CERTSTATUS_free, _OCSP_CERTSTATUS_it, _OCSP_CERTSTATUS_new, _OCSP_CRLID_free, _OCSP_CRLID_it, _OCSP_CRLID_new, _OCSP_ONEREQ_add1_ext_i2d, _OCSP_ONEREQ_add_ext, _OCSP_ONEREQ_delete_ext, _OCSP_ONEREQ_free, _OCSP_ONEREQ_get1_ext_d2i, _OCSP_ONEREQ_get_ext, _OCSP_ONEREQ_get_ext_by_NID, _OCSP_ONEREQ_get_ext_by_OBJ, _OCSP_ONEREQ_get_ext_by_critical, _OCSP_ONEREQ_get_ext_count, _OCSP_ONEREQ_it, _OCSP_ONEREQ_new, _OCSP_REQINFO_free, _OCSP_REQINFO_it, _OCSP_REQINFO_new, _OCSP_REQUEST_add1_ext_i2d, _OCSP_REQUEST_add_ext, _OCSP_REQUEST_delete_ext, _OCSP_REQUEST_free, _OCSP_REQUEST_get1_ext_d2i, _OCSP_REQUEST_get_ext, _OCSP_REQUEST_get_ext_by_NID, _OCSP_REQUEST_get_ext_by_OBJ, _OCSP_REQUEST_get_ext_by_critical, _OCSP_REQUEST_get_ext_count, _OCSP_REQUEST_it, _OCSP_REQUEST_new, _OCSP_REQUEST_print, _OCSP_REQ_CTX_free, _OCSP_RESPBYTES_free, _OCSP_RESPBYTES_it, _OCSP_RESPBYTES_new, _OCSP_RESPDATA_free, _OCSP_RESPDATA_it, _OCSP_RESPDATA_new, _OCSP_RESPID_free, _OCSP_RESPID_it, _OCSP_RESPID_new, _OCSP_RESPONSE_free, _OCSP_RESPONSE_it, _OCSP_RESPONSE_new, _OCSP_RESPONSE_print, _OCSP_REVOKEDINFO_free, _OCSP_REVOKEDINFO_it, _OCSP_REVOKEDINFO_new, _OCSP_SERVICELOC_free, _OCSP_SERVICELOC_it, _OCSP_SERVICELOC_new, _OCSP_SIGNATURE_free, _OCSP_SIGNATURE_it, _OCSP_SIGNATURE_new, _OCSP_SINGLERESP_add1_ext_i2d, _OCSP_SINGLERESP_add_ext, _OCSP_SINGLERESP_delete_ext, _OCSP_SINGLERESP_free, _OCSP_SINGLERESP_get1_ext_d2i, _OCSP_SINGLERESP_get_ext, _OCSP_SINGLERESP_get_ext_by_NID, _OCSP_SINGLERESP_get_ext_by_OBJ, _OCSP_SINGLERESP_get_ext_by_critical, _OCSP_SINGLERESP_get_ext_count, _OCSP_SINGLERESP_it, _OCSP_SINGLERESP_new, _OCSP_accept_responses_new, _OCSP_archive_cutoff_new, _OCSP_basic_add1_cert, _OCSP_basic_add1_nonce, _OCSP_basic_add1_status, _OCSP_basic_sign, _OCSP_basic_verify, _OCSP_cert_id_new, _OCSP_cert_status_str, _OCSP_cert_to_id, _OCSP_check_nonce, _OCSP_check_validity, _OCSP_copy_nonce, _OCSP_crlID_new, _OCSP_crl_reason_str, _OCSP_id_cmp, _OCSP_id_get0_info, _OCSP_id_issuer_cmp, _OCSP_onereq_get0_id, _OCSP_parse_url, _OCSP_request_add0_id, _OCSP_request_add1_cert, _OCSP_request_add1_nonce, _OCSP_request_is_signed, _OCSP_request_onereq_count, _OCSP_request_onereq_get0, _OCSP_request_set1_name, _OCSP_request_sign, _OCSP_request_verify, _OCSP_resp_count, _OCSP_resp_find, _OCSP_resp_find_status, _OCSP_resp_get0, _OCSP_response_create, _OCSP_response_get1_basic, _OCSP_response_status, _OCSP_response_status_str, _OCSP_sendreq_bio, _OCSP_sendreq_nbio, _OCSP_sendreq_new, _OCSP_single_get0_status, _OCSP_url_svcloc_new, _OPENSSL_DIR_end, _OPENSSL_DIR_read, _OPENSSL_NONPIC_relocated, _OPENSSL_add_all_algorithms_conf, _OPENSSL_add_all_algorithms_noconf, _OPENSSL_cleanse, _OPENSSL_config, _OPENSSL_cpuid_setup, _OPENSSL_gmtime, _OPENSSL_ia32cap_P, _OPENSSL_ia32cap_loc, _OPENSSL_init, _OPENSSL_isservice, _OPENSSL_issetugid, _OPENSSL_load_builtin_modules, _OPENSSL_memcmp, _OPENSSL_no_config, _OPENSSL_showfatal, _OPENSSL_stderr, _OPENSSL_strcasecmp, _OPENSSL_strncasecmp, _OSSL_DES_version, _OSSL_libdes_version, _OTHERNAME_free, _OTHERNAME_it, _OTHERNAME_new, _OpenSSLDie, _OpenSSL_add_all_ciphers, _OpenSSL_add_all_digests, _PBE2PARAM_free, _PBE2PARAM_it, _PBE2PARAM_new, _PBEPARAM_free, _PBEPARAM_it, _PBEPARAM_new, _PBKDF2PARAM_free, _PBKDF2PARAM_it, _PBKDF2PARAM_new, _PEM_ASN1_read, _PEM_ASN1_read_bio, _PEM_ASN1_write, _PEM_ASN1_write_bio, _PEM_SealFinal, _PEM_SealInit, _PEM_SealUpdate, _PEM_SignFinal, _PEM_SignInit, _PEM_SignUpdate, _PEM_X509_INFO_read, _PEM_X509_INFO_read_bio, _PEM_X509_INFO_write_bio, _PEM_bytes_read_bio, _PEM_def_callback, _PEM_dek_info, _PEM_do_header, _PEM_get_EVP_CIPHER_INFO, _PEM_proc_type, _PEM_read, _PEM_read_DHparams, _PEM_read_DSAPrivateKey, _PEM_read_DSA_PUBKEY, _PEM_read_DSAparams, _PEM_read_ECPKParameters, _PEM_read_ECPrivateKey, _PEM_read_EC_PUBKEY, _PEM_read_NETSCAPE_CERT_SEQUENCE, _PEM_read_PKCS7, _PEM_read_PKCS8, _PEM_read_PKCS8_PRIV_KEY_INFO, _PEM_read_PUBKEY, _PEM_read_PrivateKey, _PEM_read_RSAPrivateKey, _PEM_read_RSAPublicKey, _PEM_read_RSA_PUBKEY, _PEM_read_X509, _PEM_read_X509_AUX, _PEM_read_X509_CERT_PAIR, _PEM_read_X509_CRL, _PEM_read_X509_REQ, _PEM_read_bio, _PEM_read_bio_DHparams, _PEM_read_bio_DSAPrivateKey, _PEM_read_bio_DSA_PUBKEY, _PEM_read_bio_DSAparams, _PEM_read_bio_ECPKParameters, _PEM_read_bio_ECPrivateKey, _PEM_read_bio_EC_PUBKEY, _PEM_read_bio_NETSCAPE_CERT_SEQUENCE, _PEM_read_bio_PKCS7, _PEM_read_bio_PKCS8, _PEM_read_bio_PKCS8_PRIV_KEY_INFO, _PEM_read_bio_PUBKEY, _PEM_read_bio_PrivateKey, _PEM_read_bio_RSAPrivateKey, _PEM_read_bio_RSAPublicKey, _PEM_read_bio_RSA_PUBKEY, _PEM_read_bio_X509, _PEM_read_bio_X509_AUX, _PEM_read_bio_X509_CERT_PAIR, _PEM_read_bio_X509_CRL, _PEM_read_bio_X509_REQ, _PEM_version, _PEM_write, _PEM_write_DHparams, _PEM_write_DSAPrivateKey, _PEM_write_DSA_PUBKEY, _PEM_write_DSAparams, _PEM_write_ECPKParameters, _PEM_write_ECPrivateKey, _PEM_write_EC_PUBKEY, _PEM_write_NETSCAPE_CERT_SEQUENCE, _PEM_write_PKCS7, _PEM_write_PKCS8, _PEM_write_PKCS8PrivateKey, _PEM_write_PKCS8PrivateKey_nid, _PEM_write_PKCS8_PRIV_KEY_INFO, _PEM_write_PUBKEY, _PEM_write_PrivateKey, _PEM_write_RSAPrivateKey, _PEM_write_RSAPublicKey, _PEM_write_RSA_PUBKEY, _PEM_write_X509, _PEM_write_X509_AUX, _PEM_write_X509_CERT_PAIR, _PEM_write_X509_CRL, _PEM_write_X509_REQ, _PEM_write_X509_REQ_NEW, _PEM_write_bio, _PEM_write_bio_DHparams, _PEM_write_bio_DSAPrivateKey, _PEM_write_bio_DSA_PUBKEY, _PEM_write_bio_DSAparams, _PEM_write_bio_ECPKParameters, _PEM_write_bio_ECPrivateKey, _PEM_write_bio_EC_PUBKEY, _PEM_write_bio_NETSCAPE_CERT_SEQUENCE, _PEM_write_bio_PKCS7, _PEM_write_bio_PKCS8, _PEM_write_bio_PKCS8PrivateKey, _PEM_write_bio_PKCS8PrivateKey_nid, _PEM_write_bio_PKCS8_PRIV_KEY_INFO, _PEM_write_bio_PUBKEY, _PEM_write_bio_PrivateKey, _PEM_write_bio_RSAPrivateKey, _PEM_write_bio_RSAPublicKey, _PEM_write_bio_RSA_PUBKEY, _PEM_write_bio_X509, _PEM_write_bio_X509_AUX, _PEM_write_bio_X509_CERT_PAIR, _PEM_write_bio_X509_CRL, _PEM_write_bio_X509_REQ, _PEM_write_bio_X509_REQ_NEW, _PKCS12_AUTHSAFES_it, _PKCS12_BAGS_free, _PKCS12_BAGS_it, _PKCS12_BAGS_new, _PKCS12_MAC_DATA_free, _PKCS12_MAC_DATA_it, _PKCS12_MAC_DATA_new, _PKCS12_MAKE_KEYBAG, _PKCS12_MAKE_SHKEYBAG, _PKCS12_PBE_add, _PKCS12_PBE_keyivgen, _PKCS12_SAFEBAGS_it, _PKCS12_SAFEBAG_free, _PKCS12_SAFEBAG_it, _PKCS12_SAFEBAG_new, _PKCS12_add_CSPName_asc, _PKCS12_add_cert, _PKCS12_add_friendlyname_asc, _PKCS12_add_friendlyname_uni, _PKCS12_add_key, _PKCS12_add_localkeyid, _PKCS12_add_safe, _PKCS12_add_safes, _PKCS12_certbag2x509, _PKCS12_certbag2x509crl, _PKCS12_create, _PKCS12_decrypt_skey, _PKCS12_free, _PKCS12_gen_mac, _PKCS12_get_attr_gen, _PKCS12_get_friendlyname, _PKCS12_init, _PKCS12_it, _PKCS12_item_decrypt_d2i, _PKCS12_item_i2d_encrypt, _PKCS12_item_pack_safebag, _PKCS12_key_gen_asc, _PKCS12_key_gen_uni, _PKCS12_new, _PKCS12_newpass, _PKCS12_pack_authsafes, _PKCS12_pack_p7data, _PKCS12_pack_p7encdata, _PKCS12_parse, _PKCS12_pbe_crypt, _PKCS12_set_mac, _PKCS12_setup_mac, _PKCS12_unpack_authsafes, _PKCS12_unpack_p7data, _PKCS12_unpack_p7encdata, _PKCS12_verify_mac, _PKCS12_x5092certbag, _PKCS12_x509crl2certbag, _PKCS1_MGF1, _PKCS5_PBE_add, _PKCS5_PBE_keyivgen, _PKCS5_PBKDF2_HMAC_SHA1, _PKCS5_pbe2_set, _PKCS5_pbe_set, _PKCS5_v2_PBE_keyivgen, _PKCS7_ATTR_SIGN_it, _PKCS7_ATTR_VERIFY_it, _PKCS7_DIGEST_free, _PKCS7_DIGEST_it, _PKCS7_DIGEST_new, _PKCS7_ENCRYPT_free, _PKCS7_ENCRYPT_it, _PKCS7_ENCRYPT_new, _PKCS7_ENC_CONTENT_free, _PKCS7_ENC_CONTENT_it, _PKCS7_ENC_CONTENT_new, _PKCS7_ENVELOPE_free, _PKCS7_ENVELOPE_it, _PKCS7_ENVELOPE_new, _PKCS7_ISSUER_AND_SERIAL_digest, _PKCS7_ISSUER_AND_SERIAL_free, _PKCS7_ISSUER_AND_SERIAL_it, _PKCS7_ISSUER_AND_SERIAL_new, _PKCS7_RECIP_INFO_free, _PKCS7_RECIP_INFO_it, _PKCS7_RECIP_INFO_new, _PKCS7_RECIP_INFO_set, _PKCS7_SIGNED_free, _PKCS7_SIGNED_it, _PKCS7_SIGNED_new, _PKCS7_SIGNER_INFO_free, _PKCS7_SIGNER_INFO_it, _PKCS7_SIGNER_INFO_new, _PKCS7_SIGNER_INFO_set, _PKCS7_SIGN_ENVELOPE_free, _PKCS7_SIGN_ENVELOPE_it, _PKCS7_SIGN_ENVELOPE_new, _PKCS7_add_attrib_smimecap, _PKCS7_add_attribute, _PKCS7_add_certificate, _PKCS7_add_crl, _PKCS7_add_recipient, _PKCS7_add_recipient_info, _PKCS7_add_signature, _PKCS7_add_signed_attribute, _PKCS7_add_signer, _PKCS7_cert_from_signer_info, _PKCS7_content_new, _PKCS7_ctrl, _PKCS7_dataDecode, _PKCS7_dataFinal, _PKCS7_dataInit, _PKCS7_dataVerify, _PKCS7_decrypt, _PKCS7_digest_from_attributes, _PKCS7_dup, _PKCS7_encrypt, _PKCS7_free, _PKCS7_get0_signers, _PKCS7_get_attribute, _PKCS7_get_issuer_and_serial, _PKCS7_get_signed_attribute, _PKCS7_get_signer_info, _PKCS7_get_smimecap, _PKCS7_it, _PKCS7_new, _PKCS7_set0_type_other, _PKCS7_set_attributes, _PKCS7_set_cipher, _PKCS7_set_content, _PKCS7_set_digest, _PKCS7_set_signed_attributes, _PKCS7_set_type, _PKCS7_sign, _PKCS7_signatureVerify, _PKCS7_simple_smimecap, _PKCS7_verify, _PKCS8_PRIV_KEY_INFO_free, _PKCS8_PRIV_KEY_INFO_it, _PKCS8_PRIV_KEY_INFO_new, _PKCS8_add_keyusage, _PKCS8_decrypt, _PKCS8_encrypt, _PKCS8_set_broken, _PKEY_USAGE_PERIOD_free, _PKEY_USAGE_PERIOD_it, _PKEY_USAGE_PERIOD_new, _POLICYINFO_free, _POLICYINFO_it, _POLICYINFO_new, _POLICYQUALINFO_free, _POLICYQUALINFO_it, _POLICYQUALINFO_new, _POLICY_CONSTRAINTS_free, _POLICY_CONSTRAINTS_it, _POLICY_CONSTRAINTS_new, _POLICY_MAPPINGS_it, _POLICY_MAPPING_free, _POLICY_MAPPING_it, _POLICY_MAPPING_new, _PROXY_CERT_INFO_EXTENSION_free, _PROXY_CERT_INFO_EXTENSION_it, _PROXY_CERT_INFO_EXTENSION_new, _PROXY_POLICY_free, _PROXY_POLICY_it, _PROXY_POLICY_new, _RAND_SSLeay, _RAND_add, _RAND_bytes, _RAND_cleanup, _RAND_egd, _RAND_egd_bytes, _RAND_file_name, _RAND_get_rand_method, _RAND_load_file, _RAND_poll, _RAND_pseudo_bytes, _RAND_query_egd_bytes, _RAND_seed, _RAND_set_rand_engine, _RAND_set_rand_method, _RAND_status, _RAND_version, _RAND_write_file, _RC2_cbc_encrypt, _RC2_cfb64_encrypt, _RC2_decrypt, _RC2_ecb_encrypt, _RC2_encrypt, _RC2_ofb64_encrypt, _RC2_set_key, _RC2_version, _RC4, _RC4_options, _RC4_set_key, _RC4_version, _RIPEMD160, _RIPEMD160_Final, _RIPEMD160_Init, _RIPEMD160_Transform, _RIPEMD160_Update, _RMD160_version, _RSAPrivateKey_asn1_meth, _RSAPrivateKey_dup, _RSAPrivateKey_it, _RSAPublicKey_dup, _RSAPublicKey_it, _RSA_PKCS1_SSLeay, _RSA_X931_derive_ex, _RSA_X931_generate_key_ex, _RSA_X931_hash_id, _RSA_blinding_off, _RSA_blinding_on, _RSA_check_key, _RSA_flags, _RSA_free, _RSA_generate_key, _RSA_generate_key_ex, _RSA_get_default_method, _RSA_get_ex_data, _RSA_get_ex_new_index, _RSA_get_method, _RSA_memory_lock, _RSA_new, _RSA_new_method, _RSA_null_method, _RSA_padding_add_PKCS1_OAEP, _RSA_padding_add_PKCS1_PSS, _RSA_padding_add_PKCS1_type_1, _RSA_padding_add_PKCS1_type_2, _RSA_padding_add_SSLv23, _RSA_padding_add_X931, _RSA_padding_add_none, _RSA_padding_check_PKCS1_OAEP, _RSA_padding_check_PKCS1_type_1, _RSA_padding_check_PKCS1_type_2, _RSA_padding_check_SSLv23, _RSA_padding_check_X931, _RSA_padding_check_none, _RSA_print, _RSA_print_fp, _RSA_private_decrypt, _RSA_private_encrypt, _RSA_public_decrypt, _RSA_public_encrypt, _RSA_set_default_method, _RSA_set_ex_data, _RSA_set_method, _RSA_setup_blinding, _RSA_sign, _RSA_sign_ASN1_OCTET_STRING, _RSA_size, _RSA_up_ref, _RSA_verify, _RSA_verify_ASN1_OCTET_STRING, _RSA_verify_PKCS1_PSS, _RSA_version, _SEED_cbc_encrypt, _SEED_cfb128_encrypt, _SEED_decrypt, _SEED_ecb_encrypt, _SEED_encrypt, _SEED_ofb128_encrypt, _SEED_set_key, _SHA, _SHA1, _SHA1_Final, _SHA1_Init, _SHA1_Transform, _SHA1_Update, _SHA1_version, _SHA224, _SHA224_Final, _SHA224_Init, _SHA224_Update, _SHA256, _SHA256_Final, _SHA256_Init, _SHA256_Transform, _SHA256_Update, _SHA256_version, _SHA384, _SHA384_Final, _SHA384_Init, _SHA384_Update, _SHA512, _SHA512_Final, _SHA512_Init, _SHA512_Transform, _SHA512_Update, _SHA512_version, _SHA_Final, _SHA_Init, _SHA_Transform, _SHA_Update, _SHA_version, _SMIME_crlf_copy, _SMIME_read_ASN1, _SMIME_read_PKCS7, _SMIME_text, _SMIME_write_PKCS7, _SSLeay, _SSLeay_version, _STACK_version, _STORE_ATTR_INFO_compare, _STORE_ATTR_INFO_free, _STORE_ATTR_INFO_get0_cstr, _STORE_ATTR_INFO_get0_dn, _STORE_ATTR_INFO_get0_number, _STORE_ATTR_INFO_get0_sha1str, _STORE_ATTR_INFO_in, _STORE_ATTR_INFO_in_ex, _STORE_ATTR_INFO_in_range, _STORE_ATTR_INFO_modify_cstr, _STORE_ATTR_INFO_modify_dn, _STORE_ATTR_INFO_modify_number, _STORE_ATTR_INFO_modify_sha1str, _STORE_ATTR_INFO_new, _STORE_ATTR_INFO_set_cstr, _STORE_ATTR_INFO_set_dn, _STORE_ATTR_INFO_set_number, _STORE_ATTR_INFO_set_sha1str, _STORE_Memory, _STORE_OBJECT_free, _STORE_OBJECT_new, _STORE_attr_sizes, _STORE_create_method, _STORE_ctrl, _STORE_delete_arbitrary, _STORE_delete_certificate, _STORE_delete_crl, _STORE_delete_number, _STORE_delete_private_key, _STORE_delete_public_key, _STORE_destroy_method, _STORE_free, _STORE_generate_crl, _STORE_generate_key, _STORE_get_arbitrary, _STORE_get_certificate, _STORE_get_crl, _STORE_get_ex_data, _STORE_get_ex_new_index, _STORE_get_method, _STORE_get_number, _STORE_get_private_key, _STORE_get_public_key, _STORE_list_certificate_end, _STORE_list_certificate_endp, _STORE_list_certificate_next, _STORE_list_certificate_start, _STORE_list_crl_end, _STORE_list_crl_endp, _STORE_list_crl_next, _STORE_list_crl_start, _STORE_list_private_key_end, _STORE_list_private_key_endp, _STORE_list_private_key_next, _STORE_list_private_key_start, _STORE_list_public_key_end, _STORE_list_public_key_endp, _STORE_list_public_key_next, _STORE_list_public_key_start, _STORE_method_get_cleanup_function, _STORE_method_get_ctrl_function, _STORE_method_get_delete_function, _STORE_method_get_generate_function, _STORE_method_get_get_function, _STORE_method_get_initialise_function, _STORE_method_get_list_end_function, _STORE_method_get_list_next_function, _STORE_method_get_list_start_function, _STORE_method_get_lock_store_function, _STORE_method_get_modify_function, _STORE_method_get_revoke_function, _STORE_method_get_store_function, _STORE_method_get_unlock_store_function, _STORE_method_get_update_store_function, _STORE_method_set_cleanup_function, _STORE_method_set_ctrl_function, _STORE_method_set_delete_function, _STORE_method_set_generate_function, _STORE_method_set_get_function, _STORE_method_set_initialise_function, _STORE_method_set_list_end_function, _STORE_method_set_list_next_function, _STORE_method_set_list_start_function, _STORE_method_set_lock_store_function, _STORE_method_set_modify_function, _STORE_method_set_revoke_function, _STORE_method_set_store_function, _STORE_method_set_unlock_store_function, _STORE_method_set_update_store_function, _STORE_modify_arbitrary, _STORE_modify_certificate, _STORE_modify_crl, _STORE_modify_number, _STORE_modify_private_key, _STORE_modify_public_key, _STORE_new_engine, _STORE_new_method, _STORE_object_type_string, _STORE_param_sizes, _STORE_parse_attrs_end, _STORE_parse_attrs_endp, _STORE_parse_attrs_next, _STORE_parse_attrs_start, _STORE_revoke_certificate, _STORE_revoke_private_key, _STORE_revoke_public_key, _STORE_set_ex_data, _STORE_set_method, _STORE_store_arbitrary, _STORE_store_certificate, _STORE_store_crl, _STORE_store_number, _STORE_store_private_key, _STORE_store_public_key, _SXNETID_free, _SXNETID_it, _SXNETID_new, _SXNET_add_id_INTEGER, _SXNET_add_id_asc, _SXNET_add_id_ulong, _SXNET_free, _SXNET_get_id_INTEGER, _SXNET_get_id_asc, _SXNET_get_id_ulong, _SXNET_it, _SXNET_new, _TXT_DB_create_index, _TXT_DB_free, _TXT_DB_get_by_index, _TXT_DB_insert, _TXT_DB_read, _TXT_DB_version, _TXT_DB_write, _UI_OpenSSL, _UI_UTIL_read_pw, _UI_UTIL_read_pw_string, _UI_add_error_string, _UI_add_info_string, _UI_add_input_boolean, _UI_add_input_string, _UI_add_user_data, _UI_add_verify_string, _UI_construct_prompt, _UI_create_method, _UI_ctrl, _UI_destroy_method, _UI_dup_error_string, _UI_dup_info_string, _UI_dup_input_boolean, _UI_dup_input_string, _UI_dup_verify_string, _UI_free, _UI_get0_action_string, _UI_get0_output_string, _UI_get0_result, _UI_get0_result_string, _UI_get0_test_string, _UI_get0_user_data, _UI_get_default_method, _UI_get_ex_data, _UI_get_ex_new_index, _UI_get_input_flags, _UI_get_method, _UI_get_result_maxsize, _UI_get_result_minsize, _UI_get_string_type, _UI_method_get_closer, _UI_method_get_flusher, _UI_method_get_opener, _UI_method_get_reader, _UI_method_get_writer, _UI_method_set_closer, _UI_method_set_flusher, _UI_method_set_opener, _UI_method_set_reader, _UI_method_set_writer, _UI_new, _UI_new_method, _UI_process, _UI_set_default_method, _UI_set_ex_data, _UI_set_method, _UI_set_result, _USERNOTICE_free, _USERNOTICE_it, _USERNOTICE_new, _UTF8_getc, _UTF8_putc, _X509V3_EXT_CRL_add_conf, _X509V3_EXT_CRL_add_nconf, _X509V3_EXT_REQ_add_conf, _X509V3_EXT_REQ_add_nconf, _X509V3_EXT_add, _X509V3_EXT_add_alias, _X509V3_EXT_add_conf, _X509V3_EXT_add_list, _X509V3_EXT_add_nconf, _X509V3_EXT_add_nconf_sk, _X509V3_EXT_cleanup, _X509V3_EXT_conf, _X509V3_EXT_conf_nid, _X509V3_EXT_d2i, _X509V3_EXT_get, _X509V3_EXT_get_nid, _X509V3_EXT_i2d, _X509V3_EXT_nconf, _X509V3_EXT_nconf_nid, _X509V3_EXT_print, _X509V3_EXT_print_fp, _X509V3_EXT_val_prn, _X509V3_NAME_from_section, _X509V3_add1_i2d, _X509V3_add_standard_extensions, _X509V3_add_value, _X509V3_add_value_bool, _X509V3_add_value_bool_nf, _X509V3_add_value_int, _X509V3_add_value_uchar, _X509V3_conf_free, _X509V3_extensions_print, _X509V3_get_d2i, _X509V3_get_section, _X509V3_get_string, _X509V3_get_value_bool, _X509V3_get_value_int, _X509V3_parse_list, _X509V3_section_free, _X509V3_set_conf_lhash, _X509V3_set_ctx, _X509V3_set_nconf, _X509V3_string_free, _X509_ALGORS_it, _X509_ALGOR_cmp, _X509_ALGOR_dup, _X509_ALGOR_free, _X509_ALGOR_get0, _X509_ALGOR_it, _X509_ALGOR_new, _X509_ALGOR_set0, _X509_ATTRIBUTE_SET_it, _X509_ATTRIBUTE_count, _X509_ATTRIBUTE_create, _X509_ATTRIBUTE_create_by_NID, _X509_ATTRIBUTE_create_by_OBJ, _X509_ATTRIBUTE_create_by_txt, _X509_ATTRIBUTE_dup, _X509_ATTRIBUTE_free, _X509_ATTRIBUTE_get0_data, _X509_ATTRIBUTE_get0_object, _X509_ATTRIBUTE_get0_type, _X509_ATTRIBUTE_it, _X509_ATTRIBUTE_new, _X509_ATTRIBUTE_set1_data, _X509_ATTRIBUTE_set1_object, _X509_CERT_AUX_free, _X509_CERT_AUX_it, _X509_CERT_AUX_new, _X509_CERT_AUX_print, _X509_CERT_PAIR_free, _X509_CERT_PAIR_it, _X509_CERT_PAIR_new, _X509_CINF_free, _X509_CINF_it, _X509_CINF_new, _X509_CRL_INFO_free, _X509_CRL_INFO_it, _X509_CRL_INFO_new, _X509_CRL_add0_revoked, _X509_CRL_add1_ext_i2d, _X509_CRL_add_ext, _X509_CRL_cmp, _X509_CRL_delete_ext, _X509_CRL_digest, _X509_CRL_dup, _X509_CRL_free, _X509_CRL_get_ext, _X509_CRL_get_ext_by_NID, _X509_CRL_get_ext_by_OBJ, _X509_CRL_get_ext_by_critical, _X509_CRL_get_ext_count, _X509_CRL_get_ext_d2i, _X509_CRL_it, _X509_CRL_new, _X509_CRL_print, _X509_CRL_print_fp, _X509_CRL_set_issuer_name, _X509_CRL_set_lastUpdate, _X509_CRL_set_nextUpdate, _X509_CRL_set_version, _X509_CRL_sign, _X509_CRL_sort, _X509_CRL_verify, _X509_EXTENSIONS_it, _X509_EXTENSION_create_by_NID, _X509_EXTENSION_create_by_OBJ, _X509_EXTENSION_dup, _X509_EXTENSION_free, _X509_EXTENSION_get_critical, _X509_EXTENSION_get_data, _X509_EXTENSION_get_object, _X509_EXTENSION_it, _X509_EXTENSION_new, _X509_EXTENSION_set_critical, _X509_EXTENSION_set_data, _X509_EXTENSION_set_object, _X509_INFO_free, _X509_INFO_new, _X509_LOOKUP_by_alias, _X509_LOOKUP_by_fingerprint, _X509_LOOKUP_by_issuer_serial, _X509_LOOKUP_by_subject, _X509_LOOKUP_ctrl, _X509_LOOKUP_file, _X509_LOOKUP_free, _X509_LOOKUP_hash_dir, _X509_LOOKUP_init, _X509_LOOKUP_new, _X509_LOOKUP_shutdown, _X509_NAME_ENTRIES_it, _X509_NAME_ENTRY_create_by_NID, _X509_NAME_ENTRY_create_by_OBJ, _X509_NAME_ENTRY_create_by_txt, _X509_NAME_ENTRY_dup, _X509_NAME_ENTRY_free, _X509_NAME_ENTRY_get_data, _X509_NAME_ENTRY_get_object, _X509_NAME_ENTRY_it, _X509_NAME_ENTRY_new, _X509_NAME_ENTRY_set_data, _X509_NAME_ENTRY_set_object, _X509_NAME_INTERNAL_it, _X509_NAME_add_entry, _X509_NAME_add_entry_by_NID, _X509_NAME_add_entry_by_OBJ, _X509_NAME_add_entry_by_txt, _X509_NAME_cmp, _X509_NAME_delete_entry, _X509_NAME_digest, _X509_NAME_dup, _X509_NAME_entry_count, _X509_NAME_free, _X509_NAME_get_entry, _X509_NAME_get_index_by_NID, _X509_NAME_get_index_by_OBJ, _X509_NAME_get_text_by_NID, _X509_NAME_get_text_by_OBJ, _X509_NAME_hash, _X509_NAME_it, _X509_NAME_new, _X509_NAME_oneline, _X509_NAME_print, _X509_NAME_print_ex, _X509_NAME_print_ex_fp, _X509_NAME_set, _X509_OBJECT_free_contents, _X509_OBJECT_idx_by_subject, _X509_OBJECT_retrieve_by_subject, _X509_OBJECT_retrieve_match, _X509_OBJECT_up_ref_count, _X509_PKEY_free, _X509_PKEY_new, _X509_POLICY_NODE_print, _X509_PUBKEY_free, _X509_PUBKEY_get, _X509_PUBKEY_it, _X509_PUBKEY_new, _X509_PUBKEY_set, _X509_PURPOSE_add, _X509_PURPOSE_cleanup, _X509_PURPOSE_get0, _X509_PURPOSE_get0_name, _X509_PURPOSE_get0_sname, _X509_PURPOSE_get_by_id, _X509_PURPOSE_get_by_sname, _X509_PURPOSE_get_count, _X509_PURPOSE_get_id, _X509_PURPOSE_get_trust, _X509_PURPOSE_set, _X509_REQ_INFO_free, _X509_REQ_INFO_it, _X509_REQ_INFO_new, _X509_REQ_add1_attr, _X509_REQ_add1_attr_by_NID, _X509_REQ_add1_attr_by_OBJ, _X509_REQ_add1_attr_by_txt, _X509_REQ_add_extensions, _X509_REQ_add_extensions_nid, _X509_REQ_check_private_key, _X509_REQ_delete_attr, _X509_REQ_digest, _X509_REQ_dup, _X509_REQ_extension_nid, _X509_REQ_free, _X509_REQ_get1_email, _X509_REQ_get_attr, _X509_REQ_get_attr_by_NID, _X509_REQ_get_attr_by_OBJ, _X509_REQ_get_attr_count, _X509_REQ_get_extension_nids, _X509_REQ_get_extensions, _X509_REQ_get_pubkey, _X509_REQ_it, _X509_REQ_new, _X509_REQ_print, _X509_REQ_print_ex, _X509_REQ_print_fp, _X509_REQ_set_extension_nids, _X509_REQ_set_pubkey, _X509_REQ_set_subject_name, _X509_REQ_set_version, _X509_REQ_sign, _X509_REQ_to_X509, _X509_REQ_verify, _X509_REVOKED_add1_ext_i2d, _X509_REVOKED_add_ext, _X509_REVOKED_delete_ext, _X509_REVOKED_free, _X509_REVOKED_get_ext, _X509_REVOKED_get_ext_by_NID, _X509_REVOKED_get_ext_by_OBJ, _X509_REVOKED_get_ext_by_critical, _X509_REVOKED_get_ext_count, _X509_REVOKED_get_ext_d2i, _X509_REVOKED_it, _X509_REVOKED_new, _X509_REVOKED_set_revocationDate, _X509_REVOKED_set_serialNumber, _X509_SIG_free, _X509_SIG_it, _X509_SIG_new, _X509_STORE_CTX_cleanup, _X509_STORE_CTX_free, _X509_STORE_CTX_get0_param, _X509_STORE_CTX_get0_policy_tree, _X509_STORE_CTX_get1_chain, _X509_STORE_CTX_get1_issuer, _X509_STORE_CTX_get_chain, _X509_STORE_CTX_get_current_cert, _X509_STORE_CTX_get_error, _X509_STORE_CTX_get_error_depth, _X509_STORE_CTX_get_ex_data, _X509_STORE_CTX_get_ex_new_index, _X509_STORE_CTX_get_explicit_policy, _X509_STORE_CTX_init, _X509_STORE_CTX_new, _X509_STORE_CTX_purpose_inherit, _X509_STORE_CTX_set0_crls, _X509_STORE_CTX_set0_param, _X509_STORE_CTX_set_cert, _X509_STORE_CTX_set_chain, _X509_STORE_CTX_set_default, _X509_STORE_CTX_set_depth, _X509_STORE_CTX_set_error, _X509_STORE_CTX_set_ex_data, _X509_STORE_CTX_set_flags, _X509_STORE_CTX_set_purpose, _X509_STORE_CTX_set_time, _X509_STORE_CTX_set_trust, _X509_STORE_CTX_set_verify_cb, _X509_STORE_CTX_trusted_stack, _X509_STORE_add_cert, _X509_STORE_add_crl, _X509_STORE_add_lookup, _X509_STORE_free, _X509_STORE_get_by_subject, _X509_STORE_load_locations, _X509_STORE_new, _X509_STORE_set1_param, _X509_STORE_set_default_paths, _X509_STORE_set_depth, _X509_STORE_set_flags, _X509_STORE_set_purpose, _X509_STORE_set_trust, _X509_TEA_is_enabled, _X509_TEA_set_state, _X509_TRUST_add, _X509_TRUST_cleanup, _X509_TRUST_get0, _X509_TRUST_get0_name, _X509_TRUST_get_by_id, _X509_TRUST_get_count, _X509_TRUST_get_flags, _X509_TRUST_get_trust, _X509_TRUST_set, _X509_TRUST_set_default, _X509_VAL_free, _X509_VAL_it, _X509_VAL_new, _X509_VERIFY_PARAM_add0_policy, _X509_VERIFY_PARAM_add0_table, _X509_VERIFY_PARAM_clear_flags, _X509_VERIFY_PARAM_free, _X509_VERIFY_PARAM_get_depth, _X509_VERIFY_PARAM_get_flags, _X509_VERIFY_PARAM_inherit, _X509_VERIFY_PARAM_lookup, _X509_VERIFY_PARAM_new, _X509_VERIFY_PARAM_set1, _X509_VERIFY_PARAM_set1_name, _X509_VERIFY_PARAM_set1_policies, _X509_VERIFY_PARAM_set_depth, _X509_VERIFY_PARAM_set_flags, _X509_VERIFY_PARAM_set_purpose, _X509_VERIFY_PARAM_set_time, _X509_VERIFY_PARAM_set_trust, _X509_VERIFY_PARAM_table_cleanup, _X509_add1_ext_i2d, _X509_add1_reject_object, _X509_add1_trust_object, _X509_add_ext, _X509_alias_get0, _X509_alias_set1, _X509_asn1_meth, _X509_certificate_type, _X509_check_ca, _X509_check_issued, _X509_check_private_key, _X509_check_purpose, _X509_check_trust, _X509_cmp, _X509_cmp_current_time, _X509_cmp_time, _X509_delete_ext, _X509_digest, _X509_dup, _X509_email_free, _X509_find_by_issuer_and_serial, _X509_find_by_subject, _X509_free, _X509_get0_pubkey_bitstr, _X509_get1_email, _X509_get1_ocsp, _X509_get_default_cert_area, _X509_get_default_cert_dir, _X509_get_default_cert_dir_env, _X509_get_default_cert_file, _X509_get_default_cert_file_env, _X509_get_default_private_dir, _X509_get_ex_data, _X509_get_ex_new_index, _X509_get_ext, _X509_get_ext_by_NID, _X509_get_ext_by_OBJ, _X509_get_ext_by_critical, _X509_get_ext_count, _X509_get_ext_d2i, _X509_get_issuer_name, _X509_get_pubkey, _X509_get_pubkey_parameters, _X509_get_serialNumber, _X509_get_subject_name, _X509_gmtime_adj, _X509_issuer_and_serial_cmp, _X509_issuer_and_serial_hash, _X509_issuer_name_cmp, _X509_issuer_name_hash, _X509_it, _X509_keyid_get0, _X509_keyid_set1, _X509_load_cert_crl_file, _X509_load_cert_file, _X509_load_crl_file, _X509_new, _X509_ocspid_print, _X509_policy_check, _X509_policy_level_get0_node, _X509_policy_level_node_count, _X509_policy_node_get0_parent, _X509_policy_node_get0_policy, _X509_policy_node_get0_qualifiers, _X509_policy_tree_free, _X509_policy_tree_get0_level, _X509_policy_tree_get0_policies, _X509_policy_tree_get0_user_policies, _X509_policy_tree_level_count, _X509_print, _X509_print_ex, _X509_print_ex_fp, _X509_print_fp, _X509_pubkey_digest, _X509_reject_clear, _X509_set_ex_data, _X509_set_issuer_name, _X509_set_notAfter, _X509_set_notBefore, _X509_set_pubkey, _X509_set_serialNumber, _X509_set_subject_name, _X509_set_version, _X509_sign, _X509_signature_print, _X509_subject_name_cmp, _X509_subject_name_hash, _X509_supported_extension, _X509_time_adj, _X509_to_X509_REQ, _X509_trust_clear, _X509_verify, _X509_verify_cert, _X509_verify_cert_error_string, _X509_verify_cert_orig, _X509_version, _X509at_add1_attr, _X509at_add1_attr_by_NID, _X509at_add1_attr_by_OBJ, _X509at_add1_attr_by_txt, _X509at_delete_attr, _X509at_get0_data_by_OBJ, _X509at_get_attr, _X509at_get_attr_by_NID, _X509at_get_attr_by_OBJ, _X509at_get_attr_count, _X509v3_add_ext, _X509v3_delete_ext, _X509v3_get_ext, _X509v3_get_ext_by_NID, _X509v3_get_ext_by_OBJ, _X509v3_get_ext_by_critical, _X509v3_get_ext_count, _X9_62_CHARACTERISTIC_TWO_free, _X9_62_CHARACTERISTIC_TWO_it, _X9_62_CHARACTERISTIC_TWO_new, _X9_62_CURVE_it, _X9_62_FIELDID_it, _X9_62_PENTANOMIAL_free, _X9_62_PENTANOMIAL_it, _X9_62_PENTANOMIAL_new, _ZLONG_it, __CONF_add_string, __CONF_free_data, __CONF_get_section, __CONF_get_section_values, __CONF_get_string, __CONF_new_data, __CONF_new_section, __des_crypt, __ossl_096_des_random_seed, __ossl_old_crypt, __ossl_old_des_cbc_cksum, __ossl_old_des_cbc_encrypt, __ossl_old_des_cfb64_encrypt, __ossl_old_des_cfb_encrypt, __ossl_old_des_crypt, __ossl_old_des_decrypt3, __ossl_old_des_ecb3_encrypt, __ossl_old_des_ecb_encrypt, __ossl_old_des_ede3_cbc_encrypt, __ossl_old_des_ede3_cfb64_encrypt, __ossl_old_des_ede3_ofb64_encrypt, __ossl_old_des_enc_read, __ossl_old_des_enc_write, __ossl_old_des_encrypt, __ossl_old_des_encrypt2, __ossl_old_des_encrypt3, __ossl_old_des_fcrypt, __ossl_old_des_is_weak_key, __ossl_old_des_key_sched, __ossl_old_des_ncbc_encrypt, __ossl_old_des_ofb64_encrypt, __ossl_old_des_ofb_encrypt, __ossl_old_des_options, __ossl_old_des_pcbc_encrypt, __ossl_old_des_quad_cksum, __ossl_old_des_random_key, __ossl_old_des_random_seed, __ossl_old_des_read_2passwords, __ossl_old_des_read_password, __ossl_old_des_read_pw, __ossl_old_des_read_pw_string, __ossl_old_des_set_key, __ossl_old_des_set_odd_parity, __ossl_old_des_string_to_2keys, __ossl_old_des_string_to_key, __ossl_old_des_xcbc_encrypt, __shadow_DES_check_key, __shadow_DES_rw_mode, _a2d_ASN1_OBJECT, _a2i_ASN1_ENUMERATED, _a2i_ASN1_INTEGER, _a2i_ASN1_STRING, _a2i_IPADDRESS, _a2i_IPADDRESS_NC, _a2i_ipadd, _asc2uni, _asn1_Finish, _asn1_GetSequence, _asn1_add_error, _asn1_const_Finish, _asn1_do_adb, _asn1_do_lock, _asn1_enc_free, _asn1_enc_init, _asn1_enc_restore, _asn1_enc_save, _asn1_ex_c2i, _asn1_ex_i2c, _asn1_get_choice_selector, _asn1_get_field_ptr, _asn1_primitive_clear, _asn1_set_choice_selector, _bn_add_part_words, _bn_add_words, _bn_cmp_part_words, _bn_cmp_words, _bn_div_words, _bn_dup_expand, _bn_expand2, _bn_mul_add_words, _bn_mul_comba4, _bn_mul_comba8, _bn_mul_high, _bn_mul_low_normal, _bn_mul_low_recursive, _bn_mul_normal, _bn_mul_part_recursive, _bn_mul_recursive, _bn_mul_words, _bn_sqr_comba4, _bn_sqr_comba8, _bn_sqr_normal, _bn_sqr_recursive, _bn_sqr_words, _bn_sub_part_words, _bn_sub_words, _c2i_ASN1_BIT_STRING, _c2i_ASN1_INTEGER, _c2i_ASN1_OBJECT, _cleanse_ctr, _d2i_ACCESS_DESCRIPTION, _d2i_ASN1_BIT_STRING, _d2i_ASN1_BMPSTRING, _d2i_ASN1_BOOLEAN, _d2i_ASN1_ENUMERATED, _d2i_ASN1_GENERALIZEDTIME, _d2i_ASN1_GENERALSTRING, _d2i_ASN1_HEADER, _d2i_ASN1_IA5STRING, _d2i_ASN1_INTEGER, _d2i_ASN1_NULL, _d2i_ASN1_OBJECT, _d2i_ASN1_OCTET_STRING, _d2i_ASN1_PRINTABLE, _d2i_ASN1_PRINTABLESTRING, _d2i_ASN1_SET, _d2i_ASN1_T61STRING, _d2i_ASN1_TIME, _d2i_ASN1_TYPE, _d2i_ASN1_UINTEGER, _d2i_ASN1_UNIVERSALSTRING, _d2i_ASN1_UTCTIME, _d2i_ASN1_UTF8STRING, _d2i_ASN1_VISIBLESTRING, _d2i_ASN1_bytes, _d2i_ASN1_type_bytes, _d2i_AUTHORITY_INFO_ACCESS, _d2i_AUTHORITY_KEYID, _d2i_AutoPrivateKey, _d2i_BASIC_CONSTRAINTS, _d2i_CERTIFICATEPOLICIES, _d2i_CRL_DIST_POINTS, _d2i_DHparams, _d2i_DIRECTORYSTRING, _d2i_DISPLAYTEXT, _d2i_DIST_POINT, _d2i_DIST_POINT_NAME, _d2i_DSAPrivateKey, _d2i_DSAPrivateKey_bio, _d2i_DSAPrivateKey_fp, _d2i_DSAPublicKey, _d2i_DSA_PUBKEY, _d2i_DSA_PUBKEY_bio, _d2i_DSA_PUBKEY_fp, _d2i_DSA_SIG, _d2i_DSAparams, _d2i_ECDSA_SIG, _d2i_ECPKPARAMETERS, _d2i_ECPKParameters, _d2i_ECParameters, _d2i_ECPrivateKey, _d2i_ECPrivateKey_bio, _d2i_ECPrivateKey_fp, _d2i_EC_PRIVATEKEY, _d2i_EC_PUBKEY, _d2i_EC_PUBKEY_bio, _d2i_EC_PUBKEY_fp, _d2i_EDIPARTYNAME, _d2i_EXTENDED_KEY_USAGE, _d2i_GENERAL_NAME, _d2i_GENERAL_NAMES, _d2i_KRB5_APREQ, _d2i_KRB5_APREQBODY, _d2i_KRB5_AUTHDATA, _d2i_KRB5_AUTHENT, _d2i_KRB5_AUTHENTBODY, _d2i_KRB5_CHECKSUM, _d2i_KRB5_ENCDATA, _d2i_KRB5_ENCKEY, _d2i_KRB5_PRINCNAME, _d2i_KRB5_TICKET, _d2i_KRB5_TKTBODY, _d2i_NETSCAPE_CERT_SEQUENCE, _d2i_NETSCAPE_ENCRYPTED_PKEY, _d2i_NETSCAPE_PKEY, _d2i_NETSCAPE_SPKAC, _d2i_NETSCAPE_SPKI, _d2i_NOTICEREF, _d2i_Netscape_RSA, _d2i_OCSP_BASICRESP, _d2i_OCSP_CERTID, _d2i_OCSP_CERTSTATUS, _d2i_OCSP_CRLID, _d2i_OCSP_ONEREQ, _d2i_OCSP_REQINFO, _d2i_OCSP_REQUEST, _d2i_OCSP_RESPBYTES, _d2i_OCSP_RESPDATA, _d2i_OCSP_RESPID, _d2i_OCSP_RESPONSE, _d2i_OCSP_REVOKEDINFO, _d2i_OCSP_SERVICELOC, _d2i_OCSP_SIGNATURE, _d2i_OCSP_SINGLERESP, _d2i_OTHERNAME, _d2i_PBE2PARAM, _d2i_PBEPARAM, _d2i_PBKDF2PARAM, _d2i_PKCS12, _d2i_PKCS12_BAGS, _d2i_PKCS12_MAC_DATA, _d2i_PKCS12_SAFEBAG, _d2i_PKCS12_bio, _d2i_PKCS12_fp, _d2i_PKCS7, _d2i_PKCS7_DIGEST, _d2i_PKCS7_ENCRYPT, _d2i_PKCS7_ENC_CONTENT, _d2i_PKCS7_ENVELOPE, _d2i_PKCS7_ISSUER_AND_SERIAL, _d2i_PKCS7_RECIP_INFO, _d2i_PKCS7_SIGNED, _d2i_PKCS7_SIGNER_INFO, _d2i_PKCS7_SIGN_ENVELOPE, _d2i_PKCS7_bio, _d2i_PKCS7_fp, _d2i_PKCS8PrivateKey_bio, _d2i_PKCS8PrivateKey_fp, _d2i_PKCS8_PRIV_KEY_INFO, _d2i_PKCS8_PRIV_KEY_INFO_bio, _d2i_PKCS8_PRIV_KEY_INFO_fp, _d2i_PKCS8_bio, _d2i_PKCS8_fp, _d2i_PKEY_USAGE_PERIOD, _d2i_POLICYINFO, _d2i_POLICYQUALINFO, _d2i_PROXY_CERT_INFO_EXTENSION, _d2i_PROXY_POLICY, _d2i_PUBKEY, _d2i_PUBKEY_bio, _d2i_PUBKEY_fp, _d2i_PrivateKey, _d2i_PrivateKey_bio, _d2i_PrivateKey_fp, _d2i_PublicKey, _d2i_RSAPrivateKey, _d2i_RSAPrivateKey_bio, _d2i_RSAPrivateKey_fp, _d2i_RSAPublicKey, _d2i_RSAPublicKey_bio, _d2i_RSAPublicKey_fp, _d2i_RSA_NET, _d2i_RSA_PUBKEY, _d2i_RSA_PUBKEY_bio, _d2i_RSA_PUBKEY_fp, _d2i_SXNET, _d2i_SXNETID, _d2i_USERNOTICE, _d2i_X509, _d2i_X509_ALGOR, _d2i_X509_ALGORS, _d2i_X509_ATTRIBUTE, _d2i_X509_AUX, _d2i_X509_CERT_AUX, _d2i_X509_CERT_PAIR, _d2i_X509_CINF, _d2i_X509_CRL, _d2i_X509_CRL_INFO, _d2i_X509_CRL_bio, _d2i_X509_CRL_fp, _d2i_X509_EXTENSION, _d2i_X509_EXTENSIONS, _d2i_X509_NAME, _d2i_X509_NAME_ENTRY, _d2i_X509_PKEY, _d2i_X509_PUBKEY, _d2i_X509_REQ, _d2i_X509_REQ_INFO, _d2i_X509_REQ_bio, _d2i_X509_REQ_fp, _d2i_X509_REVOKED, _d2i_X509_SIG, _d2i_X509_VAL, _d2i_X509_bio, _d2i_X509_fp, _dsa_pub_internal_it, _ec_GF2m_have_precompute_mult, _ec_GF2m_precompute_mult, _ec_GF2m_simple_add, _ec_GF2m_simple_cmp, _ec_GF2m_simple_dbl, _ec_GF2m_simple_field_div, _ec_GF2m_simple_field_mul, _ec_GF2m_simple_field_sqr, _ec_GF2m_simple_group_check_discriminant, _ec_GF2m_simple_group_clear_finish, _ec_GF2m_simple_group_copy, _ec_GF2m_simple_group_finish, _ec_GF2m_simple_group_get_curve, _ec_GF2m_simple_group_get_degree, _ec_GF2m_simple_group_init, _ec_GF2m_simple_group_set_curve, _ec_GF2m_simple_invert, _ec_GF2m_simple_is_at_infinity, _ec_GF2m_simple_is_on_curve, _ec_GF2m_simple_make_affine, _ec_GF2m_simple_mul, _ec_GF2m_simple_oct2point, _ec_GF2m_simple_point2oct, _ec_GF2m_simple_point_clear_finish, _ec_GF2m_simple_point_copy, _ec_GF2m_simple_point_finish, _ec_GF2m_simple_point_get_affine_coordinates, _ec_GF2m_simple_point_init, _ec_GF2m_simple_point_set_affine_coordinates, _ec_GF2m_simple_point_set_to_infinity, _ec_GF2m_simple_points_make_affine, _ec_GF2m_simple_set_compressed_coordinates, _ec_GFp_mont_field_decode, _ec_GFp_mont_field_encode, _ec_GFp_mont_field_mul, _ec_GFp_mont_field_set_to_one, _ec_GFp_mont_field_sqr, _ec_GFp_mont_group_clear_finish, _ec_GFp_mont_group_copy, _ec_GFp_mont_group_finish, _ec_GFp_mont_group_init, _ec_GFp_mont_group_set_curve, _ec_GFp_nist_field_mul, _ec_GFp_nist_field_sqr, _ec_GFp_nist_group_copy, _ec_GFp_nist_group_set_curve, _ec_GFp_simple_add, _ec_GFp_simple_cmp, _ec_GFp_simple_dbl, _ec_GFp_simple_field_mul, _ec_GFp_simple_field_sqr, _ec_GFp_simple_get_Jprojective_coordinates_GFp, _ec_GFp_simple_group_check_discriminant, _ec_GFp_simple_group_clear_finish, _ec_GFp_simple_group_copy, _ec_GFp_simple_group_finish, _ec_GFp_simple_group_get_curve, _ec_GFp_simple_group_get_degree, _ec_GFp_simple_group_init, _ec_GFp_simple_group_set_curve, _ec_GFp_simple_invert, _ec_GFp_simple_is_at_infinity, _ec_GFp_simple_is_on_curve, _ec_GFp_simple_make_affine, _ec_GFp_simple_oct2point, _ec_GFp_simple_point2oct, _ec_GFp_simple_point_clear_finish, _ec_GFp_simple_point_copy, _ec_GFp_simple_point_finish, _ec_GFp_simple_point_get_affine_coordinates, _ec_GFp_simple_point_init, _ec_GFp_simple_point_set_affine_coordinates, _ec_GFp_simple_point_set_to_infinity, _ec_GFp_simple_points_make_affine, _ec_GFp_simple_set_Jprojective_coordinates_GFp, _ec_GFp_simple_set_compressed_coordinates, _ec_wNAF_have_precompute_mult, _ec_wNAF_mul, _ec_wNAF_precompute_mult, _ecdh_check, _ecdsa_check, _engine_cleanup_add_first, _engine_cleanup_add_last, _engine_free_util, _engine_set_all_null, _engine_table_cleanup, _engine_table_register, _engine_table_select, _engine_table_unregister, _engine_unlocked_finish, _engine_unlocked_init, _fcrypt_body, _get_rfc2409_prime_1024, _get_rfc2409_prime_768, _get_rfc3526_prime_1536, _get_rfc3526_prime_2048, _get_rfc3526_prime_3072, _get_rfc3526_prime_4096, _get_rfc3526_prime_6144, _get_rfc3526_prime_8192, _hex_to_string, _i2a_ACCESS_DESCRIPTION, _i2a_ASN1_ENUMERATED, _i2a_ASN1_INTEGER, _i2a_ASN1_OBJECT, _i2a_ASN1_STRING, _i2c_ASN1_BIT_STRING, _i2c_ASN1_INTEGER, _i2d_ACCESS_DESCRIPTION, _i2d_ASN1_BIT_STRING, _i2d_ASN1_BMPSTRING, _i2d_ASN1_BOOLEAN, _i2d_ASN1_ENUMERATED, _i2d_ASN1_GENERALIZEDTIME, _i2d_ASN1_GENERALSTRING, _i2d_ASN1_HEADER, _i2d_ASN1_IA5STRING, _i2d_ASN1_INTEGER, _i2d_ASN1_NULL, _i2d_ASN1_OBJECT, _i2d_ASN1_OCTET_STRING, _i2d_ASN1_PRINTABLE, _i2d_ASN1_PRINTABLESTRING, _i2d_ASN1_SET, _i2d_ASN1_T61STRING, _i2d_ASN1_TIME, _i2d_ASN1_TYPE, _i2d_ASN1_UNIVERSALSTRING, _i2d_ASN1_UTCTIME, _i2d_ASN1_UTF8STRING, _i2d_ASN1_VISIBLESTRING, _i2d_ASN1_bytes, _i2d_AUTHORITY_INFO_ACCESS, _i2d_AUTHORITY_KEYID, _i2d_BASIC_CONSTRAINTS, _i2d_CERTIFICATEPOLICIES, _i2d_CRL_DIST_POINTS, _i2d_DHparams, _i2d_DIRECTORYSTRING, _i2d_DISPLAYTEXT, _i2d_DIST_POINT, _i2d_DIST_POINT_NAME, _i2d_DSAPrivateKey, _i2d_DSAPrivateKey_bio, _i2d_DSAPrivateKey_fp, _i2d_DSAPublicKey, _i2d_DSA_PUBKEY, _i2d_DSA_PUBKEY_bio, _i2d_DSA_PUBKEY_fp, _i2d_DSA_SIG, _i2d_DSAparams, _i2d_ECDSA_SIG, _i2d_ECPKPARAMETERS, _i2d_ECPKParameters, _i2d_ECParameters, _i2d_ECPrivateKey, _i2d_ECPrivateKey_bio, _i2d_ECPrivateKey_fp, _i2d_EC_PRIVATEKEY, _i2d_EC_PUBKEY, _i2d_EC_PUBKEY_bio, _i2d_EC_PUBKEY_fp, _i2d_EDIPARTYNAME, _i2d_EXTENDED_KEY_USAGE, _i2d_GENERAL_NAME, _i2d_GENERAL_NAMES, _i2d_KRB5_APREQ, _i2d_KRB5_APREQBODY, _i2d_KRB5_AUTHDATA, _i2d_KRB5_AUTHENT, _i2d_KRB5_AUTHENTBODY, _i2d_KRB5_CHECKSUM, _i2d_KRB5_ENCDATA, _i2d_KRB5_ENCKEY, _i2d_KRB5_PRINCNAME, _i2d_KRB5_TICKET, _i2d_KRB5_TKTBODY, _i2d_NETSCAPE_CERT_SEQUENCE, _i2d_NETSCAPE_ENCRYPTED_PKEY, _i2d_NETSCAPE_PKEY, _i2d_NETSCAPE_SPKAC, _i2d_NETSCAPE_SPKI, _i2d_NOTICEREF, _i2d_Netscape_RSA, _i2d_OCSP_BASICRESP, _i2d_OCSP_CERTID, _i2d_OCSP_CERTSTATUS, _i2d_OCSP_CRLID, _i2d_OCSP_ONEREQ, _i2d_OCSP_REQINFO, _i2d_OCSP_REQUEST, _i2d_OCSP_RESPBYTES, _i2d_OCSP_RESPDATA, _i2d_OCSP_RESPID, _i2d_OCSP_RESPONSE, _i2d_OCSP_REVOKEDINFO, _i2d_OCSP_SERVICELOC, _i2d_OCSP_SIGNATURE, _i2d_OCSP_SINGLERESP, _i2d_OTHERNAME, _i2d_PBE2PARAM, _i2d_PBEPARAM, _i2d_PBKDF2PARAM, _i2d_PKCS12, _i2d_PKCS12_BAGS, _i2d_PKCS12_MAC_DATA, _i2d_PKCS12_SAFEBAG, _i2d_PKCS12_bio, _i2d_PKCS12_fp, _i2d_PKCS7, _i2d_PKCS7_DIGEST, _i2d_PKCS7_ENCRYPT, _i2d_PKCS7_ENC_CONTENT, _i2d_PKCS7_ENVELOPE, _i2d_PKCS7_ISSUER_AND_SERIAL, _i2d_PKCS7_NDEF, _i2d_PKCS7_RECIP_INFO, _i2d_PKCS7_SIGNED, _i2d_PKCS7_SIGNER_INFO, _i2d_PKCS7_SIGN_ENVELOPE, _i2d_PKCS7_bio, _i2d_PKCS7_fp, _i2d_PKCS8PrivateKeyInfo_bio, _i2d_PKCS8PrivateKeyInfo_fp, _i2d_PKCS8PrivateKey_bio, _i2d_PKCS8PrivateKey_fp, _i2d_PKCS8PrivateKey_nid_bio, _i2d_PKCS8PrivateKey_nid_fp, _i2d_PKCS8_PRIV_KEY_INFO, _i2d_PKCS8_PRIV_KEY_INFO_bio, _i2d_PKCS8_PRIV_KEY_INFO_fp, _i2d_PKCS8_bio, _i2d_PKCS8_fp, _i2d_PKEY_USAGE_PERIOD, _i2d_POLICYINFO, _i2d_POLICYQUALINFO, _i2d_PROXY_CERT_INFO_EXTENSION, _i2d_PROXY_POLICY, _i2d_PUBKEY, _i2d_PUBKEY_bio, _i2d_PUBKEY_fp, _i2d_PrivateKey, _i2d_PrivateKey_bio, _i2d_PrivateKey_fp, _i2d_PublicKey, _i2d_RSAPrivateKey, _i2d_RSAPrivateKey_bio, _i2d_RSAPrivateKey_fp, _i2d_RSAPublicKey, _i2d_RSAPublicKey_bio, _i2d_RSAPublicKey_fp, _i2d_RSA_NET, _i2d_RSA_PUBKEY, _i2d_RSA_PUBKEY_bio, _i2d_RSA_PUBKEY_fp, _i2d_SXNET, _i2d_SXNETID, _i2d_USERNOTICE, _i2d_X509, _i2d_X509_ALGOR, _i2d_X509_ALGORS, _i2d_X509_ATTRIBUTE, _i2d_X509_AUX, _i2d_X509_CERT_AUX, _i2d_X509_CERT_PAIR, _i2d_X509_CINF, _i2d_X509_CRL, _i2d_X509_CRL_INFO, _i2d_X509_CRL_bio, _i2d_X509_CRL_fp, _i2d_X509_EXTENSION, _i2d_X509_EXTENSIONS, _i2d_X509_NAME, _i2d_X509_NAME_ENTRY, _i2d_X509_PKEY, _i2d_X509_PUBKEY, _i2d_X509_REQ, _i2d_X509_REQ_INFO, _i2d_X509_REQ_bio, _i2d_X509_REQ_fp, _i2d_X509_REVOKED, _i2d_X509_SIG, _i2d_X509_VAL, _i2d_X509_bio, _i2d_X509_fp, _i2o_ECPublicKey, _i2s_ASN1_ENUMERATED, _i2s_ASN1_ENUMERATED_TABLE, _i2s_ASN1_INTEGER, _i2s_ASN1_OCTET_STRING, _i2t_ASN1_OBJECT, _i2v_ASN1_BIT_STRING, _i2v_GENERAL_NAME, _i2v_GENERAL_NAMES, _int_CRYPTO_set_do_dynlock_callback, _int_smime_write_ASN1, _level_add_node, _level_find_node, _lh_delete, _lh_doall, _lh_doall_arg, _lh_free, _lh_insert, _lh_new, _lh_node_stats, _lh_node_stats_bio, _lh_node_usage_stats, _lh_node_usage_stats_bio, _lh_num_items, _lh_retrieve, _lh_stats, _lh_stats_bio, _lh_strhash, _lh_version, _md4_block_data_order, _md5_block_data_order, _ms_time_cmp, _ms_time_diff, _ms_time_free, _ms_time_get, _ms_time_new, _name_cmp, _o2i_ECPublicKey, _pitem_free, _pitem_new, _policy_cache_find_data, _policy_cache_free, _policy_cache_set, _policy_cache_set_mapping, _policy_data_free, _policy_data_new, _policy_node_cmp_new, _policy_node_free, _pqueue_find, _pqueue_free, _pqueue_insert, _pqueue_iterator, _pqueue_new, _pqueue_next, _pqueue_peek, _pqueue_pop, _pqueue_print, _pqueue_size, _rand_ssleay_meth, _ripemd160_block_data_order, _s2i_ASN1_INTEGER, _s2i_ASN1_OCTET_STRING, _sk_delete, _sk_delete_ptr, _sk_dup, _sk_find, _sk_find_ex, _sk_free, _sk_insert, _sk_is_sorted, _sk_new, _sk_new_null, _sk_num, _sk_pop, _sk_pop_free, _sk_push, _sk_set, _sk_set_cmp_func, _sk_shift, _sk_sort, _sk_unshift, _sk_value, _sk_zero, _string_to_hex, _tree_find_sk, _uni2asc, _v2i_ASN1_BIT_STRING, _v2i_GENERAL_NAME, _v2i_GENERAL_NAMES, _v2i_GENERAL_NAME_ex, _v3_akey_id, _v3_alt, _v3_bcons, _v3_cpols, _v3_crl_hold, _v3_crl_invdate, _v3_crl_num, _v3_crl_reason, _v3_crld, _v3_delta_crl, _v3_ext_ku, _v3_info, _v3_inhibit_anyp, _v3_key_usage, _v3_name_constraints, _v3_ns_ia5_list, _v3_nscert, _v3_ocsp_accresp, _v3_ocsp_acutoff, _v3_ocsp_crlid, _v3_ocsp_nocheck, _v3_ocsp_nonce, _v3_ocsp_serviceloc, _v3_pci, _v3_pkey_usage_period, _v3_policy_constraints, _v3_policy_mappings, _v3_sinfo, _v3_skey_id, _v3_sxnet, _x509_dir_lookup, _x509_file_lookup, _x509_name_ff ] ... ================================================ FILE: src/adium/openssl/opensslconf.h ================================================ /* opensslconf.h */ /* WARNING: Generated automatically from opensslconf.h.in by Configure. */ /* OpenSSL was configured with the following options: */ #ifndef OPENSSL_SYSNAME_MACOSX # define OPENSSL_SYSNAME_MACOSX #endif #ifndef OPENSSL_DOING_MAKEDEPEND #ifndef OPENSSL_NO_CAMELLIA # define OPENSSL_NO_CAMELLIA #endif #ifndef OPENSSL_NO_CAPIENG # define OPENSSL_NO_CAPIENG #endif #ifndef OPENSSL_NO_CMS # define OPENSSL_NO_CMS #endif #ifndef OPENSSL_NO_GMP # define OPENSSL_NO_GMP #endif #ifndef OPENSSL_NO_IDEA # define OPENSSL_NO_IDEA #endif #ifndef OPENSSL_NO_JPAKE # define OPENSSL_NO_JPAKE #endif #ifndef OPENSSL_NO_KRB5 # define OPENSSL_NO_KRB5 #endif #ifndef OPENSSL_NO_RFC3779 # define OPENSSL_NO_RFC3779 #endif #endif /* OPENSSL_DOING_MAKEDEPEND */ #ifndef OPENSSL_THREADS # define OPENSSL_THREADS #endif #ifndef OPENSSL_NO_HW # define OPENSSL_NO_HW #endif #ifndef OPENSSL_NO_STATIC_ENGINE # define OPENSSL_NO_STATIC_ENGINE #endif /* The OPENSSL_NO_* macros are also defined as NO_* if the application asks for it. This is a transient feature that is provided for those who haven't had the time to do the appropriate changes in their applications. */ #ifdef OPENSSL_ALGORITHM_DEFINES # if defined(OPENSSL_NO_CAMELLIA) && !defined(NO_CAMELLIA) # define NO_CAMELLIA # endif # if defined(OPENSSL_NO_CAPIENG) && !defined(NO_CAPIENG) # define NO_CAPIENG # endif # if defined(OPENSSL_NO_CMS) && !defined(NO_CMS) # define NO_CMS # endif # if defined(OPENSSL_NO_GMP) && !defined(NO_GMP) # define NO_GMP # endif # if defined(OPENSSL_NO_IDEA) && !defined(NO_IDEA) # define NO_IDEA # endif # if defined(OPENSSL_NO_JPAKE) && !defined(NO_JPAKE) # define NO_JPAKE # endif # if defined(OPENSSL_NO_KRB5) && !defined(NO_KRB5) # define NO_KRB5 # endif # if defined(OPENSSL_NO_RFC3779) && !defined(NO_RFC3779) # define NO_RFC3779 # endif #endif /* crypto/opensslconf.h.in */ #ifdef OPENSSL_DOING_MAKEDEPEND /* Include any symbols here that have to be explicitly set to enable a feature * that should be visible to makedepend. * * [Our "make depend" doesn't actually look at this, we use actual build settings * instead; we want to make it easy to remove subdirectories with disabled algorithms.] */ #ifndef OPENSSL_FIPS #define OPENSSL_FIPS #endif #endif /* Generate 80386 code? */ #undef I386_ONLY #if !(defined(VMS) || defined(__VMS)) /* VMS uses logical names instead */ #if defined(HEADER_CRYPTLIB_H) && !defined(OPENSSLDIR) #define ENGINESDIR "/usr/lib/openssl/engines" #define OPENSSLDIR "/System/Library/OpenSSL" #endif #endif #undef OPENSSL_UNISTD #define OPENSSL_UNISTD #undef OPENSSL_EXPORT_VAR_AS_FUNCTION #if defined(HEADER_IDEA_H) && !defined(IDEA_INT) #define IDEA_INT unsigned int #endif #if defined(HEADER_MD2_H) && !defined(MD2_INT) #define MD2_INT unsigned int #endif #if defined(HEADER_RC2_H) && !defined(RC2_INT) /* I need to put in a mod for the alpha - eay */ #define RC2_INT unsigned int #endif #if defined(HEADER_RC4_H) #if !defined(RC4_INT) /* using int types make the structure larger but make the code faster * on most boxes I have tested - up to %20 faster. */ /* * I don't know what does "most" mean, but declaring "int" is a must on: * - Intel P6 because partial register stalls are very expensive; * - elder Alpha because it lacks byte load/store instructions; */ #define RC4_INT unsigned char #endif #if !defined(RC4_CHUNK) /* * This enables code handling data aligned at natural CPU word * boundary. See crypto/rc4/rc4_enc.c for further details. */ #define RC4_CHUNK unsigned long #endif #endif #if (defined(HEADER_NEW_DES_H) || defined(HEADER_DES_H)) && !defined(DES_LONG) /* If this is set to 'unsigned int' on a DEC Alpha, this gives about a * %20 speed up (longs are 8 bytes, int's are 4). */ #ifndef DES_LONG #ifdef __LP64__ #define DES_LONG unsigned int #else #define DES_LONG unsigned long #endif #endif #endif #if defined(HEADER_BN_H) && !defined(CONFIG_HEADER_BN_H) #define CONFIG_HEADER_BN_H #ifdef __LP64__ #undef BN_LLONG #else #define BN_LLONG #endif /* Should we define BN_DIV2W here? */ /* Only one for the following should be defined */ /* The prime number generation stuff may not work when * EIGHT_BIT but I don't care since I've only used this mode * for debuging the bignum libraries */ #ifdef __LP64__ #define SIXTY_FOUR_BIT_LONG #else #undef SIXTY_FOUR_BIT_LONG #endif #undef SIXTY_FOUR_BIT #ifdef __LP64__ #undef THIRTY_TWO_BIT #else #define THIRTY_TWO_BIT #endif #undef SIXTEEN_BIT #undef EIGHT_BIT #endif #if defined(HEADER_RC4_LOCL_H) && !defined(CONFIG_HEADER_RC4_LOCL_H) #define CONFIG_HEADER_RC4_LOCL_H /* if this is defined data[i] is used instead of *data, this is a %20 * speedup on x86 */ #undef RC4_INDEX #endif #if defined(HEADER_BF_LOCL_H) && !defined(CONFIG_HEADER_BF_LOCL_H) #define CONFIG_HEADER_BF_LOCL_H #ifdef __LP64__ #undef BF_PTR #else #define BF_PTR #endif #endif /* HEADER_BF_LOCL_H */ #if defined(HEADER_DES_LOCL_H) && !defined(CONFIG_HEADER_DES_LOCL_H) #define CONFIG_HEADER_DES_LOCL_H #ifndef DES_DEFAULT_OPTIONS /* the following is tweaked from a config script, that is why it is a * protected undef/define */ #ifndef DES_PTR #undef DES_PTR #endif /* This helps C compiler generate the correct code for multiple functional * units. It reduces register dependancies at the expense of 2 more * registers */ #ifndef DES_RISC1 #undef DES_RISC1 #endif #ifndef DES_RISC2 #undef DES_RISC2 #endif #if defined(DES_RISC1) && defined(DES_RISC2) YOU SHOULD NOT HAVE BOTH DES_RISC1 AND DES_RISC2 DEFINED!!!!! #endif /* Unroll the inner loop, this sometimes helps, sometimes hinders. * Very mucy CPU dependant */ #ifndef DES_UNROLL #define DES_UNROLL #endif /* These default values were supplied by * Peter Gutman * They are only used if nothing else has been defined */ #if !defined(DES_PTR) && !defined(DES_RISC1) && !defined(DES_RISC2) && !defined(DES_UNROLL) /* Special defines which change the way the code is built depending on the CPU and OS. For SGI machines you can use _MIPS_SZLONG (32 or 64) to find even newer MIPS CPU's, but at the moment one size fits all for optimization options. Older Sparc's work better with only UNROLL, but there's no way to tell at compile time what it is you're running on */ #if defined( sun ) /* Newer Sparc's */ # define DES_PTR # define DES_RISC1 # define DES_UNROLL #elif defined( __ultrix ) /* Older MIPS */ # define DES_PTR # define DES_RISC2 # define DES_UNROLL #elif defined( __osf1__ ) /* Alpha */ # define DES_PTR # define DES_RISC2 #elif defined ( _AIX ) /* RS6000 */ /* Unknown */ #elif defined( __hpux ) /* HP-PA */ /* Unknown */ #elif defined( __aux ) /* 68K */ /* Unknown */ #elif defined( __dgux ) /* 88K (but P6 in latest boxes) */ # define DES_UNROLL #elif defined( __sgi ) /* Newer MIPS */ # define DES_PTR # define DES_RISC2 # define DES_UNROLL #elif defined(i386) || defined(__i386__) /* x86 boxes, should be gcc */ # define DES_PTR # define DES_RISC1 # define DES_UNROLL #endif /* Systems-specific speed defines */ #endif #endif /* DES_DEFAULT_OPTIONS */ #endif /* HEADER_DES_LOCL_H */ ================================================ FILE: src/adium/xcconfigs/Base.xcconfig ================================================ // // Base.xcconfig // SIPEAdiumPlugin // // Created by Lamb, Michael on 11/13/13. // // INFOPLIST_PREPROCESSOR_DEFINITIONS = ${GCC_PREPROCESSOR_DEFINITIONS} INFOPLIST_PREPROCESS = YES MACOSX_DEPLOYMENT_TARGET = 10.8 HEADER_SEARCH_PATHS = "../../../adium/Frameworks/libglib.framework/Headers" "../../../adium/Frameworks/libpurple.framework/Headers" "../../../adium/Frameworks/libintl.framework/Headers" ../purple ../api /usr/include/libxml2 . ../../../openssl-0.9.8za/include // Hack to work around how xcconfig pre-processor handles double-slash as a comment (even in a quoted string!!) :( SLASH=/ SIPE_TRANSLATIONS_URL=\"https:${SLASH}/www.transifex.com/stefanb/pidgin-sipe/\" PACKAGE_BUGREPORT=\"https:${SLASH}/sourceforge.net/p/sipe/bugs/\" PACKAGE_URL=\"http:${SLASH}/sipe.sourceforge.net/\" // No need to change version number in the xcodeproj file every release, just here. PACKAGE_VERSION=1.25.0 PACKAGE_STRING=\"pidgin-sipe\ ${PACKAGE_VERSION}\" GCC_PREPROCESSOR_DEFINITIONS = SIPE_TRANSLATIONS_URL=${SIPE_TRANSLATIONS_URL} LOCALEDIR=\"translations\" PURPLE_STATIC_PRPL=1 HAVE_BIND_TEXTDOMAIN_CODESET=1 ENABLE_NLS=1 ADIUM=1 PACKAGE_NAME=\"pidgin-sipe\" PACKAGE_TARNAME=\"pidgin-sipe\" PACKAGE_BUGREPORT=${PACKAGE_BUGREPORT} PACKAGE_VERSION=\"${PACKAGE_VERSION}\" PACKAGE_STRING=${PACKAGE_STRING} PACKAGE_URL=${PACKAGE_URL} STDC_HEADERS=1 HAVE_SYS_TYPES_H=1 HAVE_SYS_STAT_H=1 HAVE_STDLIB_H=1 HAVE_STRING_H=1 HAVE_MEMORY_H=1 HAVE_STRINGS_H=1 HAVE_INTTYPES_H=1 HAVE_STDINT_H=1 HAVE_UNISTD_H=1 HAVE_DLFCN_H=1 LT_OBJDIR=\".libs/\" STDC_HEADERS=1 HAVE_LOCALE_H=1 HAVE_LC_MESSAGES=1 HAVE_DLFCN_H=1 HAVE_DCGETTEXT=1 HAVE_GETTEXT=1 HAVE_LOCALE_H=1 HAVE_GSSAPI_GSSAPI_H=1 HAVE_LANGINFO_CODESET=1 ARCHS = $(ARCHS_STANDARD_64_BIT) STRINGS_FILE_OUTPUT_ENCODING = UTF-16 GCC_MODEL_TUNING = G5 GCC_C_LANGUAGE_STANDARD = gnu99 GCC_WARN_ABOUT_RETURN_TYPE = YES GCC_WARN_UNUSED_VARIABLE = YES GCC_PRECOMPILE_PREFIX_HEADER = YES DEBUG_INFORMATION_FORMAT = dwarf-with-dsym ================================================ FILE: src/adium/xcconfigs/Debug-Release.xcconfig ================================================ // // Debug-Release.xcconfig // SIPEAdiumPlugin // // Created by Lamb, Michael on 11/13/13. // // #include "Base.xcconfig" //:configuration = Debug-Release ================================================ FILE: src/adium/xcconfigs/Debug.xcconfig ================================================ // // Debug.xcconfig // SIPEAdiumPlugin // // Created by Lamb, Michael on 11/13/13. // // #include "Base.xcconfig" //:configuration = Debug ONLY_ACTIVE_ARCH = YES COPY_PHASE_STRIP = NO GCC_DYNAMIC_NO_PIC = NO GCC_OPTIMIZATION_LEVEL = 0 ================================================ FILE: src/adium/xcconfigs/Release.xcconfig ================================================ // // Release.xcconfig // SIPEAdiumPlugin // // Created by Lamb, Michael on 11/13/13. // // #include "Base.xcconfig" //:configuration = Release DEBUG_INFORMATION_FORMAT = dwarf-with-dsym COPY_PHASE_STRIP = YES GCC_MODEL_TUNING = G5 ZERO_LINK = NO ================================================ FILE: src/adium/xcconfigs/SIPEAdiumPlugin.xcconfig ================================================ // // SIPEAdiumPlugin.xcconfig // SIPEAdiumPlugin // // Created by Lamb, Michael on 11/13/13. // // PRODUCT_NAME = SIPEAdiumPlugin OTHER_LDFLAGS = -lxml2 -Lopenssl -lcrypto.0.9.8 -undefined dynamic_lookup LD_RUNPATH_SEARCH_PATHS = @loader_path/../Frameworks INFOPLIST_FILE = Info.plist WRAPPER_EXTENSION = AdiumLibpurplePlugin GCC_PREFIX_HEADER = SIPEAdiumPlugin_Prefix.pch DEPLOYMENT_LOCATION = YES DEPLOYMENT_POSTPROCESSING = YES DSTROOT = INSTALL_PATH = $(HOME)/Library/Application Support/Adium 2.0/PlugIns/ FRAMEWORK_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)" ./adium-frameworks ../../../adium/Frameworks USER_HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/Adium.framework/Headers" ./adium-frameworks/Adium.framework/Headers ../../../adium/Source ================================================ FILE: src/adium/xcconfigs/libpidgin-sipe.xcconfig ================================================ // // libpidgin-sipe.xcconfig // SIPEAdiumPlugin // // Created by Lamb, Michael on 11/13/13. // // PRODUCT_NAME = pidgin-sipe ================================================ FILE: src/api/Makefile.am ================================================ MAINTAINERCLEANFILES = \ Makefile.in EXTRA_DIST = \ sipe-backend.h \ sipe-common.h \ sipe-core.h \ sipe-mime.h \ sipe-nls.h ================================================ FILE: src/api/sipe-backend.h ================================================ /** * @file sipe-backend.h * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * * SIPE Core -> Backend API - functions called by SIPE core code * ***************** !!! IMPORTANT NOTE FOR BACKEND CODERS !!! ***************** * * The SIPE core assumes atomicity and is *NOT* thread-safe. * * It *does not* protect any of its data structures or code paths with locks! * * In no circumstances it must be possible that a sipe_core_xxx() function can * be entered through another thread while the first thread has entered the * backend specific code through a sipe_backend_xxx() function. * ***************** !!! IMPORTANT NOTE FOR BACKEND CODERS !!! ***************** */ #ifdef __cplusplus extern "C" { #endif /* Forward declarations */ struct sipe_backend_chat_session; struct sipe_chat_session; struct sipe_core_public; struct sipe_transport_connection; struct sipe_file_transfer; struct sipe_media_call; struct sipe_media; /** MISC. STUFF **************************************************************/ /** * Get the version of the backend suitable for e.g. UserAgent * * @return backend version string. Will be g_free()'d.by the core. */ gchar *sipe_backend_version(void); /** DEBUGGING ****************************************************************/ typedef enum { SIPE_LOG_LEVEL_INFO, SIPE_LOG_LEVEL_WARNING, SIPE_LOG_LEVEL_ERROR, SIPE_DEBUG_LEVEL_INFO, SIPE_DEBUG_LEVEL_WARNING, SIPE_DEBUG_LEVEL_ERROR, } sipe_debug_level; #define SIPE_DEBUG_LEVEL_LOWEST SIPE_DEBUG_LEVEL_INFO /** * Output debug information without formatting * * Shouldn't be used directly. Instead use SIPE_DEBUG_xxx() macros * * @param level debug level * @param msg debug message "\n" will be automatically appended. */ void sipe_backend_debug_literal(sipe_debug_level level, const gchar *msg); /** * Output debug information * * Shouldn't be used directly. Instead use SIPE_DEBUG_xxx() macros * * @param level debug level * @param format format string. "\n" will be automatically appended. */ void sipe_backend_debug(sipe_debug_level level, const gchar *format, ...) G_GNUC_PRINTF(2, 3); /* Convenience macros */ #define SIPE_LOG_INFO(fmt, ...) sipe_backend_debug(SIPE_LOG_LEVEL_INFO, fmt, __VA_ARGS__) #define SIPE_LOG_INFO_NOFORMAT(msg) sipe_backend_debug_literal(SIPE_LOG_LEVEL_INFO, msg) #define SIPE_LOG_WARNING(fmt, ...) sipe_backend_debug(SIPE_LOG_LEVEL_WARNING, fmt, __VA_ARGS__) #define SIPE_LOG_WARNING_NOFORMAT(msg) sipe_backend_debug_literal(SIPE_LOG_LEVEL_WARNING, msg) #define SIPE_LOG_ERROR(fmt, ...) sipe_backend_debug(SIPE_LOG_LEVEL_ERROR, fmt, __VA_ARGS__) #define SIPE_LOG_ERROR_NOFORMAT(msg) sipe_backend_debug_literal(SIPE_LOG_LEVEL_ERROR, msg) #define SIPE_DEBUG_INFO(fmt, ...) sipe_backend_debug(SIPE_DEBUG_LEVEL_INFO, fmt, __VA_ARGS__) #define SIPE_DEBUG_INFO_NOFORMAT(msg) sipe_backend_debug_literal(SIPE_DEBUG_LEVEL_INFO, msg) #define SIPE_DEBUG_WARNING(fmt, ...) sipe_backend_debug(SIPE_DEBUG_LEVEL_WARNING, fmt, __VA_ARGS__) #define SIPE_DEBUG_WARNING_NOFORMAT(msg) sipe_backend_debug_literal(SIPE_DEBUG_LEVEL_WARNING, msg) #define SIPE_DEBUG_ERROR(fmt, ...) sipe_backend_debug(SIPE_DEBUG_LEVEL_ERROR, fmt, __VA_ARGS__) #define SIPE_DEBUG_ERROR_NOFORMAT(msg) sipe_backend_debug_literal(SIPE_DEBUG_LEVEL_ERROR, msg) /** * Check backend debugging status * * @return TRUE if debugging is enabled */ gboolean sipe_backend_debug_enabled(void); /** CHAT *********************************************************************/ void sipe_backend_chat_session_destroy(struct sipe_backend_chat_session *session); void sipe_backend_chat_add(struct sipe_backend_chat_session *backend_session, const gchar *uri, gboolean is_new); void sipe_backend_chat_close(struct sipe_backend_chat_session *backend_session); /** * Joined a new chat */ struct sipe_backend_chat_session *sipe_backend_chat_create(struct sipe_core_public *sipe_public, struct sipe_chat_session *session, const gchar *title, const gchar *nick); gboolean sipe_backend_chat_find(struct sipe_backend_chat_session *backend_session, const gchar *uri); gboolean sipe_backend_chat_is_operator(struct sipe_backend_chat_session *backend_session, const gchar *uri); void sipe_backend_chat_message(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *from, time_t when, const gchar *html); void sipe_backend_chat_operator(struct sipe_backend_chat_session *backend_session, const gchar *uri); /** * Rejoin an existing chat window after connection re-establishment */ void sipe_backend_chat_rejoin(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *nick, const gchar *title); /** * Core has completed connection re-establishment. * Should call sipe_core_chat_rejoin() for existing chats. */ void sipe_backend_chat_rejoin_all(struct sipe_core_public *sipe_public); void sipe_backend_chat_remove(struct sipe_backend_chat_session *backend_session, const gchar *uri); /** * Move chat window to the front. Will be called when * a user tries to join an already joined chat again. */ void sipe_backend_chat_show(struct sipe_backend_chat_session *backend_session); void sipe_backend_chat_topic(struct sipe_backend_chat_session *backend_session, const gchar *topic); /** CONNECTION ***************************************************************/ void sipe_backend_connection_completed(struct sipe_core_public *sipe_public); typedef enum { SIPE_CONNECTION_ERROR_NETWORK = 0, SIPE_CONNECTION_ERROR_INVALID_USERNAME, SIPE_CONNECTION_ERROR_INVALID_SETTINGS, SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED, SIPE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, SIPE_CONNECTION_ERROR_LAST } sipe_connection_error; void sipe_backend_connection_error(struct sipe_core_public *sipe_public, sipe_connection_error error, const gchar *msg); gboolean sipe_backend_connection_is_disconnecting(struct sipe_core_public *sipe_public); gboolean sipe_backend_connection_is_valid(struct sipe_core_public *sipe_public); /** DNS QUERY ****************************************************************/ typedef void (*sipe_dns_resolved_cb)(gpointer data, const gchar *hostname, guint port); struct sipe_dns_query *sipe_backend_dns_query_srv(struct sipe_core_public *sipe_public, const gchar *protocol, const gchar *transport, const gchar *domain, sipe_dns_resolved_cb callback, gpointer data); struct sipe_dns_query *sipe_backend_dns_query_a(struct sipe_core_public *sipe_public, const gchar *hostname, guint port, sipe_dns_resolved_cb callback, gpointer data); void sipe_backend_dns_query_cancel(struct sipe_dns_query *query); /** FILE TRANSFER ************************************************************/ struct sipe_backend_fd; void sipe_backend_ft_error(struct sipe_file_transfer *ft, const gchar *errmsg); const gchar *sipe_backend_ft_get_error(struct sipe_file_transfer *ft); void sipe_backend_ft_deallocate(struct sipe_file_transfer *ft); /** * Try to read up to @c size bytes from file transfer connection * * @param ft file transfer data. * @param data buffer to read data into. * @param size buffer size in bytes. * * @return number of bytes read or negative on failure. * EAGAIN should return 0 bytes read. */ gssize sipe_backend_ft_read(struct sipe_file_transfer *ft, guchar *data, gsize size); /** * Try to write up to @c size bytes to file transfer connection * * @param ft file transfer data. * @param data data to write * @param size buffer size in bytes. * * @return number of bytes read or negative on failure. * EAGAIN should return 0 bytes written. */ gssize sipe_backend_ft_write(struct sipe_file_transfer *ft, const guchar *data, gsize size); void sipe_backend_ft_set_completed(struct sipe_file_transfer *ft); void sipe_backend_ft_cancel_local(struct sipe_file_transfer *ft); void sipe_backend_ft_cancel_remote(struct sipe_file_transfer *ft); void sipe_backend_ft_incoming(struct sipe_core_public *sipe_public, struct sipe_file_transfer *ft, const gchar *who, const gchar *file_name, gsize file_size); /** * Allocates and initializes backend file transfer structure for sending a file. * * @param sipe_public (in) the handle representing the protocol instance * @param ft (in) sipe core file transfer structure * @param who (in) SIP URI of the file recipient * @param file_name (in) filesystem path of the file being sent */ void sipe_backend_ft_outgoing(struct sipe_core_public *sipe_public, struct sipe_file_transfer *ft, const gchar *who, const gchar *file_name); /** * Begins file transfer with remote peer. * * You can provide either opened file descriptor to use for read/write operations * or ip address and port where the backend should connect. * * @param ft file transfer data * @param fd opaque file descriptor pointer or NULL if ip and port are used * @param ip ip address to connect of NULL when file descriptor is used * @param port port to connect or 0 when file descriptor is used */ void sipe_backend_ft_start(struct sipe_file_transfer *ft, struct sipe_backend_fd *fd, const char* ip, unsigned port); /** * Check whether file transfer is incoming or outgoing * * @param ft file transfer data * @return @c TRUE if @c ft is incoming, otherwise @c FALSE */ gboolean sipe_backend_ft_is_incoming(struct sipe_file_transfer *ft); /** GROUP CHAT ***************************************************************/ #define SIPE_GROUPCHAT_ROOM_FILEPOST 0x00000001 #define SIPE_GROUPCHAT_ROOM_INVITE 0x00000002 #define SIPE_GROUPCHAT_ROOM_LOGGED 0x00000004 #define SIPE_GROUPCHAT_ROOM_PRIVATE 0x00000008 /** * Add a room found through room query * * @param uri room URI * @param name human readable name for room * @param description room description * @param users number of users in the room * @param flags SIPE_GROUPCHAT_ROOM_* flags */ void sipe_backend_groupchat_room_add(struct sipe_core_public *sipe_public, const gchar *uri, const gchar *name, const gchar *description, guint users, guint32 flags); /** * Terminate room query */ void sipe_backend_groupchat_room_terminate(struct sipe_core_public *sipe_public); /** IM ***********************************************************************/ void sipe_backend_im_message(struct sipe_core_public *sipe_public, const gchar *from, const gchar *html); void sipe_backend_im_topic(struct sipe_core_public *sipe_public, const gchar *with, const gchar *topic); /** MARKUP *******************************************************************/ gchar *sipe_backend_markup_css_property(const gchar *style, const gchar *option); gchar *sipe_backend_markup_strip_html(const gchar *html); /** MEDIA ********************************************************************/ typedef enum { /* This client is the one who invites other participant to the call. */ SIPE_MEDIA_CALL_INITIATOR = 1, /* Don't show any user interface elements for the call. */ SIPE_MEDIA_CALL_NO_UI = 2 } SipeMediaCallFlags; typedef enum { SIPE_ICE_NO_ICE, SIPE_ICE_DRAFT_6, SIPE_ICE_RFC_5245 } SipeIceVersion; typedef enum { SIPE_CANDIDATE_TYPE_ANY, SIPE_CANDIDATE_TYPE_HOST, SIPE_CANDIDATE_TYPE_RELAY, SIPE_CANDIDATE_TYPE_SRFLX, SIPE_CANDIDATE_TYPE_PRFLX } SipeCandidateType; typedef enum { SIPE_COMPONENT_NONE = 0, SIPE_COMPONENT_RTP = 1, SIPE_COMPONENT_RTCP = 2 } SipeComponentType; typedef enum { SIPE_MEDIA_AUDIO, SIPE_MEDIA_VIDEO, SIPE_MEDIA_APPLICATION } SipeMediaType; typedef enum { SIPE_NETWORK_PROTOCOL_UDP, SIPE_NETWORK_PROTOCOL_TCP_ACTIVE, SIPE_NETWORK_PROTOCOL_TCP_PASSIVE, SIPE_NETWORK_PROTOCOL_TCP_SO, } SipeNetworkProtocol; typedef enum { SIPE_ENCRYPTION_POLICY_REJECTED, SIPE_ENCRYPTION_POLICY_OPTIONAL, SIPE_ENCRYPTION_POLICY_REQUIRED, SIPE_ENCRYPTION_POLICY_OBEY_SERVER } SipeEncryptionPolicy; struct sipe_media_call; struct sipe_backend_media; struct sipe_backend_codec; struct sipe_backend_candidate; struct sipe_backend_media_stream; struct sipe_backend_media_relays; struct ssrc_range { guint32 begin; guint32 end; }; struct sipe_media_stream { struct sipe_backend_media_stream *backend_private; struct sipe_media_call *call; gchar *id; struct ssrc_range *ssrc_range; guint32 media_source_id; void (*candidate_pairs_established_cb)(struct sipe_media_stream *); void (*read_cb)(struct sipe_media_stream *); void (*writable_cb)(struct sipe_media_stream *); void (*mute_cb)(struct sipe_media_stream *, gboolean is_muted); }; struct sipe_media_call { struct sipe_backend_media *backend_private; gchar *with; void (*stream_initialized_cb)(struct sipe_media_call *, struct sipe_media_stream *); void (*media_end_cb)(struct sipe_media_call *); void (*call_accept_cb)(struct sipe_media_call *, gboolean local); void (*call_reject_cb)(struct sipe_media_call *, gboolean local); void (*call_hold_cb) (struct sipe_media_call *, gboolean local, gboolean state); void (*call_hangup_cb)(struct sipe_media_call *, gboolean local); void (*error_cb)(struct sipe_media_call *, gchar *message); }; struct sipe_media_relay { gchar *hostname; guint udp_port; guint tcp_port; struct sipe_dns_query *dns_query; }; /* Media handling */ struct sipe_backend_media *sipe_backend_media_new(struct sipe_core_public *sipe_public, struct sipe_media_call *call, const gchar *participant, SipeMediaCallFlags flags); void sipe_backend_media_free(struct sipe_backend_media *media); void sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname); struct sipe_backend_media_relays * sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password); void sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays); struct sipe_backend_media_stream *sipe_backend_media_add_stream(struct sipe_media_stream *stream, SipeMediaType type, SipeIceVersion ice_version, gboolean initiator, struct sipe_backend_media_relays *media_relays, guint min_port, guint max_port); void sipe_backend_media_add_remote_candidates(struct sipe_media_call *media, struct sipe_media_stream *stream, GList *candidates); gboolean sipe_backend_media_is_initiator(struct sipe_media_call *media, struct sipe_media_stream *stream); gboolean sipe_backend_media_accepted(struct sipe_backend_media *media); gboolean sipe_backend_stream_initialized(struct sipe_media_call *media, struct sipe_media_stream *stream); void sipe_backend_media_set_encryption_keys(struct sipe_media_call *media, struct sipe_media_stream *stream, const guchar *encryption_key, const guchar *decryption_key); void sipe_backend_media_set_require_encryption(struct sipe_media_call *media, struct sipe_media_stream *stream, const gboolean require_encryption); /* Stream handling */ void sipe_backend_stream_hold(struct sipe_media_call *media, struct sipe_media_stream *stream, gboolean local); void sipe_backend_stream_unhold(struct sipe_media_call *media, struct sipe_media_stream *stream, gboolean local); gboolean sipe_backend_stream_is_held(struct sipe_media_stream *stream); GList *sipe_backend_media_stream_get_active_local_candidates(struct sipe_media_stream *stream); GList *sipe_backend_media_stream_get_active_remote_candidates(struct sipe_media_stream *stream); gssize sipe_backend_media_stream_read(struct sipe_media_stream *stream, guint8 *buffer, gsize len); gssize sipe_backend_media_stream_write(struct sipe_media_stream *stream, guint8 *buffer, gsize len); void sipe_backend_media_stream_end(struct sipe_media_call *media, struct sipe_media_stream *stream); void sipe_backend_media_stream_free(struct sipe_backend_media_stream *stream); /* Codec handling */ struct sipe_backend_codec *sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate, guint channels); void sipe_backend_codec_free(struct sipe_backend_codec *codec); int sipe_backend_codec_get_id(struct sipe_backend_codec *codec); /** * @return codec name. Will be g_free'd() by the core. */ gchar *sipe_backend_codec_get_name(struct sipe_backend_codec *codec); guint sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec); void sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec, const gchar *name, const gchar *value); GList *sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec); gboolean sipe_backend_set_remote_codecs(struct sipe_media_call *media, struct sipe_media_stream *stream, GList *codecs); GList* sipe_backend_get_local_codecs(struct sipe_media_call *media, struct sipe_media_stream *stream); /* Candidate handling */ struct sipe_backend_candidate * sipe_backend_candidate_new(const gchar *foundation, SipeComponentType component, SipeCandidateType type, SipeNetworkProtocol proto, const gchar *ip, guint port, const gchar *username, const gchar *password); void sipe_backend_candidate_free(struct sipe_backend_candidate *candidate); /** * @return user name. Will be g_free'd() by the core. */ gchar *sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate); /** * @return password. Will be g_free'd() by the core. */ gchar *sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate); /** * @return foundation. Will be g_free'd() by the core. */ gchar *sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate); /** * @return IP address string. Will be g_free'd() by the core. */ gchar *sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate); guint sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate); /** * @return IP address string. Will be g_free'd() by the core. */ gchar *sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate); guint sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate); guint32 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate); void sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority); SipeComponentType sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate); SipeCandidateType sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate); SipeNetworkProtocol sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate); GList* sipe_backend_get_local_candidates(struct sipe_media_call *media, struct sipe_media_stream *stream); void sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local); void sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local); void sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local); /** NETWORK ******************************************************************/ struct sipe_backend_listendata; typedef void (*sipe_listen_start_cb)(unsigned short port, gpointer data); typedef void (*sipe_client_connected_cb)(struct sipe_backend_fd *fd, gpointer data); struct sipe_backend_listendata * sipe_backend_network_listen_range(unsigned short port_min, unsigned short port_max, sipe_listen_start_cb listen_cb, sipe_client_connected_cb connect_cb, gpointer data); void sipe_backend_network_listen_cancel(struct sipe_backend_listendata *ldata); struct sipe_backend_fd * sipe_backend_fd_from_int(int fd); gboolean sipe_backend_fd_is_valid(struct sipe_backend_fd *fd); void sipe_backend_fd_free(struct sipe_backend_fd *fd); /** NOTIFICATIONS *************************************************************/ void sipe_backend_notify_message_error(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *who, const gchar *message); void sipe_backend_notify_message_info(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *who, const gchar *message); /** * @param msg error message. Maybe @NULL */ void sipe_backend_notify_error(struct sipe_core_public *sipe_public, const gchar *title, const gchar *msg); /** SCHEDULE *****************************************************************/ gpointer sipe_backend_schedule_seconds(struct sipe_core_public *sipe_public, guint timeout, gpointer data); gpointer sipe_backend_schedule_mseconds(struct sipe_core_public *sipe_public, guint timeout, gpointer data); void sipe_backend_schedule_cancel(struct sipe_core_public *sipe_public, gpointer data); /** SEARCH *******************************************************************/ struct sipe_backend_search_results; struct sipe_backend_search_token; void sipe_backend_search_failed(struct sipe_core_public *sipe_public, struct sipe_backend_search_token *token, const gchar *msg); struct sipe_backend_search_results *sipe_backend_search_results_start(struct sipe_core_public *sipe_public, struct sipe_backend_search_token *token); void sipe_backend_search_results_add(struct sipe_core_public *sipe_public, struct sipe_backend_search_results *results, const gchar *uri, const gchar *name, const gchar *company, const gchar *country, const gchar *email); void sipe_backend_search_results_finalize(struct sipe_core_public *sipe_public, struct sipe_backend_search_results *results, const gchar *description, gboolean more); /** SETTINGS *****************************************************************/ typedef enum { SIPE_SETTING_EMAIL_URL = 0, SIPE_SETTING_EMAIL_LOGIN, SIPE_SETTING_EMAIL_PASSWORD, SIPE_SETTING_GROUPCHAT_USER, SIPE_SETTING_RDP_CLIENT, SIPE_SETTING_USER_AGENT, SIPE_SETTING_LAST } sipe_setting; const gchar *sipe_backend_setting(struct sipe_core_public *sipe_public, sipe_setting type); /** STATUS *******************************************************************/ guint sipe_backend_status(struct sipe_core_public *sipe_public); gboolean sipe_backend_status_changed(struct sipe_core_public *sipe_public, guint activity, const gchar *message); /** * Update user client with new status and note received from server * * NOTE: this must *NOT* trigger a call to @c sipe_core_status_set()! * * @param sipe_public The handle representing the protocol instance * @param activity New activity * @param message New note text */ void sipe_backend_status_and_note(struct sipe_core_public *sipe_public, guint activity, const gchar *message); /** TRANSPORT ****************************************************************/ typedef void transport_connected_cb(struct sipe_transport_connection *conn); typedef void transport_input_cb(struct sipe_transport_connection *conn); typedef void transport_error_cb(struct sipe_transport_connection *conn, const gchar *msg); typedef struct { guint type; const gchar *server_name; guint server_port; gpointer user_data; transport_connected_cb *connected; transport_input_cb *input; transport_error_cb *error; } sipe_connect_setup; struct sipe_transport_connection *sipe_backend_transport_connect(struct sipe_core_public *sipe_public, const sipe_connect_setup *setup); void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn); gchar *sipe_backend_transport_ip_address(struct sipe_transport_connection *conn); void sipe_backend_transport_message(struct sipe_transport_connection *conn, const gchar *buffer); void sipe_backend_transport_flush(struct sipe_transport_connection *conn); /** USER *********************************************************************/ void sipe_backend_user_feedback_typing(struct sipe_core_public *sipe_public, const gchar *from); void sipe_backend_user_feedback_typing_stop(struct sipe_core_public *sipe_public, const gchar *from); /** * Present a query that is to be accepted or declined by the user * * @param sipe_public The handle representing the protocol instance * @param message Text of the query to be shown to user * @param accept_label Label to be displayed on UI control that accepts query * @param decline_label Label to be displayed on UI control that declines query. * When @c NULL, the control will not be shown. * @param key Opaque handle uniquely identifying the query. Backend * should store it for the case SIPE core requests the * query to be closed prematurely. */ void sipe_backend_user_ask(struct sipe_core_public *sipe_public, const gchar *message, const gchar *accept_label, const gchar *decline_label, gpointer key); /** * Present a set of options to the user to choose from. * * @param sipe_public (in) The handle representing the protocol instance * @param message (in) Text message to be shown to the user * @param choices (in) List of choice options * @param key (in) Opaque handle uniquely identifying the query. Backend * should store it for the case SIPE core requests the query * to be closed prematurely. */ void sipe_backend_user_ask_choice(struct sipe_core_public *sipe_public, const gchar *message, GSList *choices, gpointer key); /** * Closes the pending user query * * @param key Opaque handle uniquely identifying the query. */ void sipe_backend_user_close_ask(gpointer key); /** BUDDIES ******************************************************************/ /* * sipe_backend_buddy_get/set_string(): properties a buddy can have * sipe_backend_buddy_info_add(): mapped, e.g. to a string label */ typedef enum { SIPE_BUDDY_INFO_DISPLAY_NAME = 0, SIPE_BUDDY_INFO_JOB_TITLE, SIPE_BUDDY_INFO_CITY, SIPE_BUDDY_INFO_STATE, SIPE_BUDDY_INFO_OFFICE, SIPE_BUDDY_INFO_DEPARTMENT, SIPE_BUDDY_INFO_COUNTRY, SIPE_BUDDY_INFO_WORK_PHONE, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, SIPE_BUDDY_INFO_COMPANY, SIPE_BUDDY_INFO_EMAIL, SIPE_BUDDY_INFO_SITE, SIPE_BUDDY_INFO_ZIPCODE, SIPE_BUDDY_INFO_STREET, SIPE_BUDDY_INFO_MOBILE_PHONE, SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY, SIPE_BUDDY_INFO_HOME_PHONE, SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY, SIPE_BUDDY_INFO_OTHER_PHONE, SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY, SIPE_BUDDY_INFO_CUSTOM1_PHONE, SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY, SIPE_BUDDY_INFO_ALIAS, /* only for sipe_backend_buddy_info_add() */ SIPE_BUDDY_INFO_DEVICE, /* only for sipe_backend_buddy_info_add() */ } sipe_buddy_info_fields; /* Opaque token */ typedef void* sipe_backend_buddy; /** * Find a buddy in the given group of the buddy list, or anywhere on the * list if @group_name is empty * * @param sipe_public The handle representing the protocol instance making the call * @param buddy_name The name of the buddy * @param group_name The name of the group to look in, or NULL for any group * @return opaque handle to the buddy, or NULL if no buddy found */ sipe_backend_buddy sipe_backend_buddy_find(struct sipe_core_public *sipe_public, const gchar *buddy_name, const gchar *group_name); /* * Find all named buddies in the given group of the buddy list, or anywhere on the * list if @group_name is empty; or all buddies if @name is empty * * @param sipe_public The handle representing the protocol instance making the call * @param name The name of the buddy * @param group_name The name of the group to look in, or NULL for any group * @return GSList of opaque handles to the buddies */ GSList* sipe_backend_buddy_find_all(struct sipe_core_public *sipe_public, const gchar *buddy_name, const gchar *group_name); /** * Gets the name of a contact. * * @param sipe_public The handle representing the protocol instance making the call * @param who The opaque handle to the contact as found by find_buddy * @return The name. Must be freed. */ gchar* sipe_backend_buddy_get_name(struct sipe_core_public *sipe_public, const sipe_backend_buddy who); /** * Gets the alias for a contact. * * @param sipe_public The handle representing the protocol instance making the call * @param who The opaque handle to the contact as found by find_buddy * @return The alias. Must be gfree'd. */ gchar* sipe_backend_buddy_get_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who); /** * Gets the server alias for a contact. * * @param sipe_public The handle representing the protocol instance making the call * @param who The opaque handle to the contact as found by find_buddy * @return The alias. Must be freed. */ gchar* sipe_backend_buddy_get_server_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who); /** * Gets the local alias for a contact * * @param sipe_public The handle representing the protocol instance making the call * @param uri the budyy name * * @return the alias. Must be @g_free()'d. */ gchar *sipe_backend_buddy_get_local_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who); /** * Gets the name of the group a contact belongs to. * * @param sipe_public The handle representing the protocol instance making the call * @param who The opaque handle to the contact as found by find_buddy * @return The name. Must be freed. */ gchar* sipe_backend_buddy_get_group_name(struct sipe_core_public *sipe_public, const sipe_backend_buddy who); /** * Called to retrieve a buddy-specific setting. * * @param sipe_public The handle representing the protocol instance making the call * @param buddy The handle representing the buddy * @param key The name of the setting * @return The value of the setting. Must be freed. */ gchar* sipe_backend_buddy_get_string(struct sipe_core_public *sipe_public, sipe_backend_buddy buddy, const sipe_buddy_info_fields key); /** * Called to set a buddy-specific setting. * * @param sipe_public The handle representing the protocol instance making the call * @param buddy The handle representing the buddy * @param key The name of the setting * @param val The value to set */ void sipe_backend_buddy_set_string(struct sipe_core_public *sipe_public, sipe_backend_buddy buddy, const sipe_buddy_info_fields key, const gchar *val); /** * Called after one ore more buddy-specific settings have been updated. * * Can be used by the backend to trigger an UI update event * * @param sipe_public The handle representing the protocol instance making the call * @param uri SIP URI of the contact */ void sipe_backend_buddy_refresh_properties(struct sipe_core_public *sipe_public, const gchar *uri); /** * Get the status token for a contact * * @param sipe_public The handle representing the protocol instance making the call * @param uri SIP URI of the contact * * @return activity */ guint sipe_backend_buddy_get_status(struct sipe_core_public *sipe_public, const gchar *uri); /** * Sets the alias for a contact. * * @param sipe_public The handle representing the protocol instance making the call * @param who The opaque handle to the contact as found by find_buddy * @param alias The location where the alias will be put * case. FALSE if the buddy was not found. The value of alias will not be changed. */ void sipe_backend_buddy_set_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who, const gchar *alias); /** * Sets the server alias for a contact. * * @param sipe_public The handle representing the protocol instance making the call * @param who The opaque handle to the contact as found by find_buddy * @param alias The server alias of the contact */ void sipe_backend_buddy_set_server_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who, const gchar *alias); /** * Start processing buddy list * * Will be called every time we receive a buddy list in roaming contacts * * @param sipe_public The handle representing the protocol instance making the call */ void sipe_backend_buddy_list_processing_start(struct sipe_core_public *sipe_public); /** * Finished processing buddy list * * Will be called every time we receive a buddy list in roaming contacts * * @param sipe_public The handle representing the protocol instance making the call */ void sipe_backend_buddy_list_processing_finish(struct sipe_core_public *sipe_public); /** * Add a contact to the buddy list * * @param sipe_public The handle representing the protocol instance making the call * @param name The name of the contact * @param alias The alias of the contact * @param groupname The name of the group to add this contact to * @return A handle to the newly created buddy */ sipe_backend_buddy sipe_backend_buddy_add(struct sipe_core_public *sipe_public, const gchar *name, const gchar *alias, const gchar *groupname); /** * Remove a contact from the buddy list * * @param sipe_public The handle representing the protocol instance making the call * @param who The opaque handle to the contact as found by find_buddy */ void sipe_backend_buddy_remove(struct sipe_core_public *sipe_public, const sipe_backend_buddy who); /** * Notifies the user that a remote user has wants to add the local user to his * or her buddy list and requires authorization to do so. * * @param sipe_public The handle representing the protocol instance making the call * @param who The name of the user that added this account * @param alias The optional alias of the remote user * @param on_list True if the user is already in our list * @param auth_cb The callback called when the local user accepts * @param deny_cb The callback called when the local user rejects * @param data Data to be passed back to the above callbacks */ typedef void (*sipe_backend_buddy_request_authorization_cb)(void *); void sipe_backend_buddy_request_add(struct sipe_core_public *sipe_public, const gchar *who, const gchar *alias); void sipe_backend_buddy_request_authorization(struct sipe_core_public *sipe_public, const gchar *who, const gchar *alias, gboolean on_list, sipe_backend_buddy_request_authorization_cb auth_cb, sipe_backend_buddy_request_authorization_cb deny_cb, gpointer data); gboolean sipe_backend_buddy_is_blocked(struct sipe_core_public *sipe_public, const gchar *who); void sipe_backend_buddy_set_blocked_status(struct sipe_core_public *sipe_public, const gchar *who, gboolean blocked); /** * Set the buddy status * * @param sipe_public The handle representing the protocol instance making the call * @param who The name of the user whose status should be updated * @param activity New activity * @param last_active Seconds since epoch when user entered idle state. * May be @c 0 if unknown. */ void sipe_backend_buddy_set_status(struct sipe_core_public *sipe_public, const gchar *who, guint activity, time_t last_active); /** * Checks whether backend has a capability to use buddy photos. If this function * returns @c FALSE, SIPE core will not attempt to download the photos from * server to save bandwidth. * * @return @c TRUE if backend is photo capable, otherwise @FALSE */ gboolean sipe_backend_uses_photo(void); /** * Gives backend a photo image associated with a SIP URI. Backend has ownership * of the data and must free it when not needed. * * @param sipe_public The handle representing the protocol instance making the call * @param who The name of the user whose photo is being set * @param image_data The photo image data, must be g_free()'d by backend * @param image_len Size of the image in Bytes * @param photo_hash A data checksum provided by the server */ void sipe_backend_buddy_set_photo(struct sipe_core_public *sipe_public, const gchar *who, gpointer image_data, gsize image_len, const gchar *photo_hash); /** * Retrieves a photo hash stored together with image data by * @c sipe_backend_buddy_set_photo. Value is used by the core to detect photo * file changes on server. * * @param sipe_public The handle representing the protocol instance making the call * @param who The name of the user whose photo hash to retrieve * @return a photo hash (may be NULL) */ const gchar *sipe_backend_buddy_get_photo_hash(struct sipe_core_public *sipe_public, const gchar *who); /** * Called when a new internal group is about to be added. If this returns FALSE, * the group will not be added. * * @param sipe_public The handle representing the protocol instance making the call * @param group_name The group being added * @return TRUE if everything is ok, FALSE if the group should not be added */ gboolean sipe_backend_buddy_group_add(struct sipe_core_public *sipe_public, const gchar *group_name); /** * Called when a new internal group has been renamed * * @param sipe_public The handle representing the protocol instance making the call * @param old_name old name of the group * @param new_name new name of the group * @return TRUE if the group was found and renamed */ gboolean sipe_backend_buddy_group_rename(struct sipe_core_public *sipe_public, const gchar *old_name, const gchar *new_name); /** * Called when a new internal group should be deleted * * NOTE: this will only be called on empty groups. * * @param sipe_public The handle representing the protocol instance making the call * @param group_name The group that should be removed */ void sipe_backend_buddy_group_remove(struct sipe_core_public *sipe_public, const gchar *group_name); /** * Present requested buddy information to the user */ struct sipe_backend_buddy_info; struct sipe_backend_buddy_info *sipe_backend_buddy_info_start(struct sipe_core_public *sipe_public); void sipe_backend_buddy_info_add(struct sipe_core_public *sipe_public, struct sipe_backend_buddy_info *info, sipe_buddy_info_fields key, const gchar *value); void sipe_backend_buddy_info_break(struct sipe_core_public *sipe_public, struct sipe_backend_buddy_info *info); void sipe_backend_buddy_info_finalize(struct sipe_core_public *sipe_public, struct sipe_backend_buddy_info *info, const gchar *uri); struct sipe_backend_buddy_tooltip; void sipe_backend_buddy_tooltip_add(struct sipe_core_public *sipe_public, struct sipe_backend_buddy_tooltip *tooltip, const gchar *description, const gchar *value); /** * Buddy menu creation */ enum sipe_buddy_menu_type { SIPE_BUDDY_MENU_MAKE_CHAT_LEADER = 0, SIPE_BUDDY_MENU_REMOVE_FROM_CHAT, SIPE_BUDDY_MENU_INVITE_TO_CHAT, SIPE_BUDDY_MENU_NEW_CHAT, SIPE_BUDDY_MENU_MAKE_CALL, SIPE_BUDDY_MENU_SEND_EMAIL, SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP, SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL, SIPE_BUDDY_MENU_ADD_NEW_DOMAIN, SIPE_BUDDY_MENU_SHARE_DESKTOP, SIPE_BUDDY_MENU_GIVE_DESKTOP_CONTROL, SIPE_BUDDY_MENU_TAKE_DESKTOP_CONTROL, SIPE_BUDDY_MENU_TYPES }; struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_start(struct sipe_core_public *sipe_public); struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_add(struct sipe_core_public *sipe_public, struct sipe_backend_buddy_menu *menu, const gchar *label, enum sipe_buddy_menu_type type, gpointer parameter); struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_separator(struct sipe_core_public *sipe_public, struct sipe_backend_buddy_menu *menu, const gchar *label); struct sipe_backend_buddy_menu *sipe_backend_buddy_sub_menu_add(struct sipe_core_public *sipe_public, struct sipe_backend_buddy_menu *menu, const gchar *label, struct sipe_backend_buddy_menu *sub); SipeEncryptionPolicy sipe_backend_media_get_encryption_policy(struct sipe_core_public *sipe_public); #ifdef __cplusplus } #endif /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/api/sipe-common.h ================================================ /** * @file sipe-common.h * * pidgin-sipe * * Copyright (C) 2010-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Everything in here must be independent of any other header file! * * I.e. it must be possible to include this header * in any module without requiring any other #include. */ #ifdef __GNUC__ #define SIPE_UNUSED_PARAMETER __attribute__((unused)) #else #define SIPE_UNUSED_PARAMETER #endif #if defined(__GNUC__) && (__GNUC__ >= 7) #define SIPE_FALLTHROUGH __attribute__((fallthrough)); #else #define SIPE_FALLTHROUGH #endif /* in order to remove internal.h dependency in mingw builds */ #ifndef G_GNUC_NULL_TERMINATED # if defined(__GNUC__) && (__GNUC__ >= 4) # define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) # else # define G_GNUC_NULL_TERMINATED # endif #endif #ifdef _MSC_VER typedef long ssize_t; #endif ================================================ FILE: src/api/sipe-core.h ================================================ /** * @file sipe-core.h * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * * Backend -> SIPE Core API - functions called by backend code * ***************** !!! IMPORTANT NOTE FOR BACKEND CODERS !!! ***************** * * The SIPE core assumes atomicity and is *NOT* thread-safe. * * It *does not* protect any of its data structures or code paths with locks! * * In no circumstances it must be interrupted by another thread calling * sipe_core_xxx() while the first thread has entered the SIPE core through * a sipe_core_xxx() function. * ***************** !!! IMPORTANT NOTE FOR BACKEND CODERS !!! ***************** */ #ifdef __cplusplus extern "C" { #endif /** * Transport type */ #define SIPE_TRANSPORT_AUTO 0 #define SIPE_TRANSPORT_TLS 1 #define SIPE_TRANSPORT_TCP 2 /** * Transport connection (public part) * * The receiver in the backend fills "buffer". The backend has to zero * terminate the buffer before calling the processing function in the core. * * The processing function in the core can remove content from the buffer. * It has to update buffer_used accordingly. * */ struct sipe_transport_connection { gpointer user_data; gchar *buffer; gsize buffer_used; /* 0 < buffer_used < buffer_length */ gsize buffer_length; /* read-only */ guint type; /* read-only */ guint client_port; /* read-only */ }; /** * Opaque data type for chat session */ struct sipe_chat_session; /** * File transport (public part) */ struct sipe_file_transfer { struct sipe_backend_file_transfer *backend_private; void (* ft_init)(struct sipe_file_transfer *ft, const gchar *filename, gsize size, const gchar *who); void (* ft_start)(struct sipe_file_transfer *ft, gsize total_size); gssize (* ft_read)(struct sipe_file_transfer *ft, guchar **buffer, gsize bytes_remaining, gsize bytes_available); gssize (* ft_write)(struct sipe_file_transfer *ft, const guchar *buffer, gsize size); gboolean (* ft_end)(struct sipe_file_transfer *ft); void (* ft_request_denied)(struct sipe_file_transfer *ft); void (* ft_cancelled)(struct sipe_file_transfer *ft); }; /** * Opaque data type for backend private data. * The backend is responsible to allocate and free it. */ struct sipe_backend_private; /** * SIP transport authentication scheme */ #define SIPE_AUTHENTICATION_TYPE_UNSET 0 #define SIPE_AUTHENTICATION_TYPE_BASIC 1 /* internal use only */ #define SIPE_AUTHENTICATION_TYPE_NTLM 2 #define SIPE_AUTHENTICATION_TYPE_KERBEROS 3 #define SIPE_AUTHENTICATION_TYPE_NEGOTIATE 4 /* internal use only */ #define SIPE_AUTHENTICATION_TYPE_TLS_DSK 5 #define SIPE_AUTHENTICATION_TYPE_AUTOMATIC 6 /* always last */ /** * Flags */ /* user disabled calendar information publishing */ #define SIPE_CORE_FLAG_DONT_PUBLISH 0x00000001 /* user enabled insecure buddy icon download from web */ #define SIPE_CORE_FLAG_ALLOW_WEB_PHOTO 0x00000002 #define SIPE_CORE_FLAG_IS(flag) \ ((sipe_public->flags & SIPE_CORE_FLAG_ ## flag) == SIPE_CORE_FLAG_ ## flag) #define SIPE_CORE_FLAG_SET(flag) \ (sipe_public->flags |= SIPE_CORE_FLAG_ ## flag) #define SIPE_CORE_FLAG_UNSET(flag) \ (sipe_public->flags &= ~SIPE_CORE_FLAG_ ## flag) /** * Byte length of cryptographic key for call encryption. */ #define SIPE_SRTP_KEY_LEN 30 /** * Public part of the Sipe data structure * * This part contains the information needed by the core and the backend. */ struct sipe_core_public { /** * This points to the private data for the backend. * The backend is responsible to allocate and free it. */ struct sipe_backend_private *backend_private; /* flags (see above) */ guint32 flags; /* user information */ gchar *sip_name; gchar *sip_domain; /* server information */ /* currently nothing */ }; /** * Initialize & destroy functions for the SIPE core * Should be called on loading and unloading of the plugin. */ void sipe_core_init(const char *locale_dir); void sipe_core_destroy(void); /** Utility functions exported by the core to backends ***********************/ gboolean sipe_strequal(const gchar *left, const gchar *right); gboolean sipe_strcase_equal(const gchar *left, const gchar *right); GSList * sipe_utils_nameval_add(GSList *list, const gchar *name, const gchar *value); const gchar * sipe_utils_nameval_find(const GSList *list, const gchar *name); const gchar * sipe_utils_nameval_find_instance(const GSList *list, const gchar *name, int which); void sipe_utils_nameval_free(GSList *list); gchar *sip_uri_from_name(const gchar *name); gchar *sip_uri_if_valid(const gchar *string); /*****************************************************************************/ /** * Other functions (need to be sorted once structure becomes clear. */ /* Get translated about string. Must be g_free'd(). */ gchar *sipe_core_about(void); /* Execute a scheduled action */ void sipe_core_schedule_execute(gpointer data); /* menu actions */ void sipe_core_update_calendar(struct sipe_core_public *sipe_public); void sipe_core_reset_status(struct sipe_core_public *sipe_public); /* access levels */ void sipe_core_change_access_level_from_container(struct sipe_core_public *sipe_public, gpointer parameter); void sipe_core_change_access_level_for_domain(struct sipe_core_public *sipe_public, const gchar *domain, guint index); /** * Activity * - core: maps this to OCS protocol values * maps this to translated descriptions * - backend: maps this to backend status values * backend token string can be used as "ID" in protocol * * This is passed back-and-forth and therefore defined as list, not as enum. * Can be used as array index */ #define SIPE_ACTIVITY_UNSET 0 #define SIPE_ACTIVITY_AVAILABLE 1 #define SIPE_ACTIVITY_ONLINE 2 #define SIPE_ACTIVITY_INACTIVE 3 #define SIPE_ACTIVITY_BUSY 4 #define SIPE_ACTIVITY_BUSYIDLE 5 #define SIPE_ACTIVITY_DND 6 #define SIPE_ACTIVITY_BRB 7 #define SIPE_ACTIVITY_AWAY 8 #define SIPE_ACTIVITY_LUNCH 9 #define SIPE_ACTIVITY_INVISIBLE 10 #define SIPE_ACTIVITY_OFFLINE 11 #define SIPE_ACTIVITY_ON_PHONE 12 #define SIPE_ACTIVITY_IN_CONF 13 #define SIPE_ACTIVITY_IN_MEETING 14 #define SIPE_ACTIVITY_OOF 15 #define SIPE_ACTIVITY_URGENT_ONLY 16 #define SIPE_ACTIVITY_IN_PRES 17 #define SIPE_ACTIVITY_NUM_TYPES 18 /* use to define array size */ const gchar *sipe_core_activity_description(guint type); /* buddy actions */ /** * Get status text for buddy. * * @param sipe_public Sipe core public data structure. * @param uri SIP URI of the buddy * @param activity activity value for buddy * @param status_text backend-specific buddy status text for activity. * * @return HTML status text for the buddy or NULL. Must be g_free()'d. */ gchar *sipe_core_buddy_status(struct sipe_core_public *sipe_public, const gchar *uri, guint activity, const gchar *status_text); /** * Received new status for buddy. * * @param sipe_public Sipe core public data structure. * @param uri SIP URI of the buddy * @param activity Activity value for buddy * @param last_active Seconds since epoch when buddy entered idle state * May be @c 0 if unknown or buddy is not idle. */ void sipe_core_buddy_got_status(struct sipe_core_public *sipe_public, const gchar *uri, guint activity, time_t last_active); /** * Trigger generation of buddy information label/text pairs * * @param sipe_public Sipe core public data structure. * @param uri SIP URI of the buddy * @param status_text backend-specific buddy status text for ID. * @param is_online backend considers buddy to be online. * @param tooltip opaque backend identifier for tooltip info. This is the * parameter given to @c sipe_backend_buddy_tooltip_add() */ struct sipe_backend_buddy_tooltip; void sipe_core_buddy_tooltip_info(struct sipe_core_public *sipe_public, const gchar *uri, const gchar *status_name, gboolean is_online, struct sipe_backend_buddy_tooltip *tooltip); /** * Add a buddy * * @param sipe_public Sipe core public data structure * @param uri SIP URI of the buddy * @param group_name backend-specific group name */ void sipe_core_buddy_add(struct sipe_core_public *sipe_public, const gchar *uri, const gchar *group_name); /** * Remove a buddy * * @param sipe_public Sipe core public data structure * @param uri SIP URI of the buddy * @param group_name backend-specific group name */ void sipe_core_buddy_remove(struct sipe_core_public *sipe_public, const gchar *uri, const gchar *group_name); void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public, const gchar *who, gboolean allow); void sipe_core_group_set_alias(struct sipe_core_public *sipe_public, const gchar *who, const gchar *alias); /** * Setup core data */ struct sipe_core_public *sipe_core_allocate(const gchar *signin_name, gboolean sso, const gchar *login_account, const gchar *password, const gchar *email, const gchar *email_url, const gchar **errmsg); void sipe_core_deallocate(struct sipe_core_public *sipe_public); /** * Check if SIP authentication scheme requires a password * * NOTE: this can be called *BEFORE* @c sipe_core_allocate()! * * @param authentication SIP transport authentication type * @param sso TRUE if user selected Single-Sign On * * @return TRUE if password is required */ gboolean sipe_core_transport_sip_requires_password(guint authentication, gboolean sso); /** * Connect to SIP server */ void sipe_core_transport_sip_connect(struct sipe_core_public *sipe_public, guint transport, guint authentication, const gchar *server, const gchar *port); /** * Get SIP server host name * * @param sipe_public Sipe core public data structure * * @return server host name (may be @c NULL if not fully connected yet) */ const gchar *sipe_core_transport_sip_server_name(struct sipe_core_public *sipe_public); /** * Get chat ID, f.ex. group chat URI */ const gchar *sipe_core_chat_id(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session); /** * Get type of chat session, e.g. group chat */ #define SIPE_CHAT_TYPE_UNKNOWN 0 #define SIPE_CHAT_TYPE_MULTIPARTY 1 #define SIPE_CHAT_TYPE_CONFERENCE 2 #define SIPE_CHAT_TYPE_GROUPCHAT 3 guint sipe_core_chat_type(struct sipe_chat_session *chat_session); /** * Invite to chat */ void sipe_core_chat_invite(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session, const char *name); /** * Rejoin a chat after connection re-establishment */ void sipe_core_chat_rejoin(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session); /** * Leave a chat */ void sipe_core_chat_leave(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session); /** * Send message to chat */ void sipe_core_chat_send(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session, const char *what); /** * Check chat lock status */ typedef enum { SIPE_CHAT_LOCK_STATUS_NOT_ALLOWED = 0, SIPE_CHAT_LOCK_STATUS_UNLOCKED, SIPE_CHAT_LOCK_STATUS_LOCKED } sipe_chat_lock_status; sipe_chat_lock_status sipe_core_chat_lock_status(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session); /** * Lock chat */ void sipe_core_chat_modify_lock(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session, const gboolean locked); /** * Create new session with Focus URI * * @param sipe_public (in) SIPE core data. * @param focus_uri (in) focus URI string */ void sipe_core_conf_create(struct sipe_core_public *sipe_public, const gchar *focus_uri, const gchar *organizer, const gchar *meeting_id); /* buddy menu callback: parameter == chat_session */ void sipe_core_conf_make_leader(struct sipe_core_public *sipe_public, gpointer parameter, const gchar *buddy_name); void sipe_core_conf_remove_from(struct sipe_core_public *sipe_public, gpointer parameter, const gchar *buddy_name); gchar * sipe_core_conf_entry_info(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session); typedef enum { SIPE_APPSHARE_ROLE_NONE, SIPE_APPSHARE_ROLE_VIEWER, SIPE_APPSHARE_ROLE_PRESENTER } sipe_appshare_role; /** * Gets user's application sharing role in given chat session. * * @param sipe_public (in) SIPE core data. * @param chat_session (in) chat session structure * * @return User's application sharing role. */ sipe_appshare_role sipe_core_conf_get_appshare_role(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session); /* call control (CSTA) */ void sipe_core_buddy_make_call(struct sipe_core_public *sipe_public, const gchar *phone); /* media */ void sipe_core_media_initiate_call(struct sipe_core_public *sipe_public, const char *participant, gboolean with_video); struct sipe_media_call; struct sipe_media_stream * sipe_core_media_get_stream_by_id(struct sipe_media_call *call, const gchar *id); /** * Called by media backend after a candidate pair for a media stream component * has been established. * * @param stream (in) SIPE media stream data. */ void sipe_core_media_stream_candidate_pair_established(struct sipe_media_stream *stream); void sipe_core_media_stream_readable(struct sipe_media_stream *stream); /** * Called by media backend when a @c SIPE_MEDIA_APPLICATION stream changes its * state between writable and unwritable. * * @param stream (in) SIPE media stream data. * @param writable (in) @c TRUE if stream has become writable, otherwise * @c FALSE. */ void sipe_core_media_stream_writable(struct sipe_media_stream *stream, gboolean writable); /** * Called by media backend when @c stream has ended and should be destroyed. * * @param stream (in) SIPE media stream data. */ void sipe_core_media_stream_end(struct sipe_media_stream *stream); /** * Connects to a conference call specified by given chat session * * @param sipe_public (in) SIPE core data. * @param chat_session (in) chat session structure * @param with_video (in) TRUE if a video call should be created. */ void sipe_core_media_connect_conference(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session, gboolean with_video); /** * Retrieves the media call in progress * * The function checks only for voice and video calls, ignoring other types of * data transfers. * * @param sipe_public (in) SIPE core data. * * @return @c sipe_media_call structure or @c NULL if call is not in progress. */ struct sipe_media_call * sipe_core_media_get_call(struct sipe_core_public *sipe_public); /** * Initiates a call with given phone number * * @param sipe_public (in) SIPE core data. * @parem phone_number (in) a mobile or landline phone number, i.e. +46123456 */ void sipe_core_media_phone_call(struct sipe_core_public *sipe_public, const gchar *phone_number); /** * Checks voice quality by making a call to the test service * * @param sipe_public (in) SIPE core data. */ void sipe_core_media_test_call(struct sipe_core_public *sipe_public); /* file transfer */ struct sipe_file_transfer * sipe_core_ft_create_outgoing(struct sipe_core_public *sipe_public, const gchar *who, const gchar *file); /* application sharing */ /** * Connects to a meeting's presentation * * @param sipe_public (in) SIPE core data. * @param chat_session (in) chat session structure * @param user_must_accept (in) @c TRUE if user should be shown accept/decline * dialog before the action can proceed. */ void sipe_core_appshare_connect_conference(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session, gboolean user_must_accept); /** * Starts presenting user's desktop * * @param sipe_public (in) SIPE core data. * @param with (in) SIP URI of the contact to share the desktop with. */ void sipe_core_appshare_share_desktop(struct sipe_core_public *sipe_public, const gchar *with); /** * Starts presenting user's desktop with a conference call * * @param sipe_public (in) SIPE core data. * @param chat_session (in) chat session structure. */ void sipe_core_conf_share_desktop(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session); /** * Changes the state of remote desktop control on an application sharing call. * * @param call (in) media call structure. * @param enabled (in) @c TRUE to enable remote control, @c FALSE to disable it. */ void sipe_core_appshare_set_remote_control(struct sipe_media_call *call, gboolean enabled); /** * Gets the state of remote desktop control on an application sharing call. * * @param call (in) media call structure. * * @return @c TRUE if remote control is enabled, otherwise @c FALSE. */ gboolean sipe_core_appshare_get_remote_control(struct sipe_media_call *call); /* group chat */ gboolean sipe_core_groupchat_query_rooms(struct sipe_core_public *sipe_public); void sipe_core_groupchat_join(struct sipe_core_public *sipe_public, const gchar *uri); /* IM */ void sipe_core_im_send(struct sipe_core_public *sipe_public, const gchar *who, const gchar *what); void sipe_core_im_close(struct sipe_core_public *sipe_public, const gchar *who); /* user */ void sipe_core_user_feedback_typing(struct sipe_core_public *sipe_public, const gchar *to, gboolean typing); void sipe_core_user_ask_cb(gpointer key, gboolean accepted); static const guint SIPE_CHOICE_CANCELLED = G_MAXUINT; void sipe_core_user_ask_choice_cb(gpointer key, guint choice_id); /* groups */ void sipe_core_group_rename(struct sipe_core_public *sipe_public, const gchar *old_name, const gchar *new_name); void sipe_core_group_remove(struct sipe_core_public *sipe_public, const gchar *name); /* buddies */ void sipe_core_buddy_group(struct sipe_core_public *sipe_public, const gchar *who, const gchar *old_group_name, const gchar *new_group_name); struct sipe_backend_search_token; void sipe_core_buddy_search(struct sipe_core_public *sipe_public, struct sipe_backend_search_token *token, const gchar *given_name, const gchar *surname, const gchar *email, const gchar *sipid, const gchar *company, const gchar *country); void sipe_core_buddy_get_info(struct sipe_core_public *sipe_public, const gchar *who); void sipe_core_buddy_new_chat(struct sipe_core_public *sipe_public, const gchar *who); void sipe_core_buddy_send_email(struct sipe_core_public *sipe_public, const gchar *who); struct sipe_backend_buddy_menu; struct sipe_backend_buddy_menu *sipe_core_buddy_create_menu(struct sipe_core_public *sipe_public, const gchar *buddy_name, struct sipe_backend_buddy_menu *menu); void sipe_core_buddy_menu_free(struct sipe_core_public *sipe_public); /** * User/Machine has changed the user status * * NOTE: must *NEVER* be triggered by @c sipe_backend_status_and_note()! * * @param sipe_public The handle representing the protocol instance * @param set_by_user @c TRUE if status has been changed by user * @param activity New activity * @param message New note text */ void sipe_core_status_set(struct sipe_core_public *sipe_public, gboolean set_by_user, guint activity, const gchar *note); #define SIPE_MSRTP_VSR_HEADER_LEN 20 #define SIPE_MSRTP_VSR_ENTRY_LEN 0x44 #define SIPE_MSRTP_VSR_FCI_WORDLEN \ (SIPE_MSRTP_VSR_HEADER_LEN + SIPE_MSRTP_VSR_ENTRY_LEN) / 4 #define SIPE_MSRTP_VSR_SOURCE_ANY 0xFFFFFFFE #define SIPE_MSRTP_VSR_SOURCE_NONE 0xFFFFFFFF /** * Fills @buffer with Video Source Request described in [MS-RTP] 2.2.12.2. * * @param buffer (out) destination the VSR will be written to. The byte length * of @c buffer MUST be at least @c SIPE_MSRTP_VSR_HEADER_LEN + * @c SIPE_MSRTP_VSR_ENTRY_LEN. * @param payload_type (in) payload ID of the codec negotiated with the peer. * @param media_source_id (in) ID of the video stream to request. */ void sipe_core_msrtp_write_video_source_request(guint8 *buffer, guint8 payload_type, guint32 media_source_id); /** * Fills @buffer with customized Payload Content Scalability Information packet * described in [MS-H264PF] consisting of a Stream Layout SEI Message (section * 2.2.5) and a Bitstream Info SEI Message (section 2.2.7). * * @param buffer (out) destination the PACSI will be written to. * @param nal_count (in) number of NAL units this packet describes. * * @return Byte length of the PACSI packet. */ gsize sipe_core_msrtp_write_video_scalability_info(guint8 *buffer, guint8 nal_count); #ifdef __cplusplus } #endif /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/api/sipe-mime.h ================================================ /** * @file sipe-mime.h * * pidgin-sipe * * Copyright (C) 2010-2015 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * MIME backend initialization */ void sipe_mime_init(void); /** * MIME backend shutdown */ void sipe_mime_shutdown(void); /** * Callback type for sipe_mime_parts_foreach(). * * @param user_data callback data. * @param fields list of @c sipnameval structures with the header fields * @param body text of the MIME part. * @param length length of the body text. */ typedef void (*sipe_mime_parts_cb)(gpointer user_data, const GSList *fields, const gchar *body, gsize length); /** * Parse MIME document and call a function for each part. * * @param type content type of the MIME document. * @param body body of the MIME document. * @param callback function to call for each MIME part. * @param user_data callback data. */ void sipe_mime_parts_foreach(const gchar *type, const gchar *body, sipe_mime_parts_cb callback, gpointer user_data); /** * Checks whether MIME document contains a part with given type. * * @param type content type of the whole MIME document. * @param body body of the MIME document. * @param part_type the MIME type to search for in document parts. * * @return @c TRUE if @c body contains such part, otherwise @c FALSE. */ gboolean sipe_mime_parts_contain(const gchar *type, const gchar *body, const gchar *part_type); ================================================ FILE: src/api/sipe-nls.h ================================================ /** * @file sipe-nls.h * * pidgin-sipe * * Copyright (C) 2009-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * config.h must be included for correct definition of ENABLE_NLS. * PACKAGE_NAME is always defined when config.h has been included. */ #if defined(HAVE_CONFIG_H) && !defined(PACKAGE_NAME) #error you must include "config.h" before "sipe-nls.h" #endif #ifdef ENABLE_NLS #include #else #define _(String) ((const char *) (String)) #define N_(String) ((const char *) (String)) #define gettext(String) ((const char *) (String)) #define dngettext(package, StringS, StringP, p) ((const char *) ((p) ? (StringP) : (StringS))) #endif /* ENABLE_NLS */ ================================================ FILE: src/core/Makefile.am ================================================ EXTRA_DIST = \ Makefile.mingw \ libsiperc.rc.in noinst_LTLIBRARIES = \ libsipe_core.la \ libsipe_core_crypto.la \ libsipe_core_libxml2.la MAINTAINERCLEANFILES = \ Makefile.in libsipe_core_la_SOURCES = \ sipmsg.h \ sipmsg.c \ sip-csta.h \ sip-csta.c \ sip-sec.h \ sip-sec.c \ sip-sec-basic.h \ sip-sec-basic.c \ sip-sec-digest.h \ sip-sec-digest.c \ sip-sec-mech.h \ sip-sec-tls-dsk.h \ sip-sec-tls-dsk.c \ sip-soap.h \ sip-soap.c \ sip-transport.h \ sip-transport.c \ sipe-buddy.h \ sipe-buddy.c \ sipe-cal.h \ sipe-cal.c \ sipe-certificate.h \ sipe-certificate.c \ sipe-cert-crypto.h \ sipe-chat.h \ sipe-chat.c \ sipe-conf.h \ sipe-conf.c \ sipe-core-private.h \ sipe-core.c \ sipe-crypt.h \ sipe-dialog.h \ sipe-dialog.c \ sipe-digest.h \ sipe-ews.h \ sipe-ews.c \ sipe-ews-autodiscover.h \ sipe-ews-autodiscover.c \ sipe-ft.h \ sipe-ft.c \ sipe-ft-tftp.h \ sipe-ft-tftp.c \ sipe-group.h \ sipe-group.c \ sipe-groupchat.h \ sipe-groupchat.c \ sipe-http.h \ sipe-http.c \ sipe-http-request.h \ sipe-http-request.c \ sipe-http-transport.h \ sipe-http-transport.c \ sipe-im.h \ sipe-im.c \ sipe-incoming.h \ sipe-incoming.c \ sipe-lync-autodiscover.h \ sipe-lync-autodiscover.c \ sipe-mime-common.c \ sipe-notify.h \ sipe-notify.c \ sipe-ocs2005.h \ sipe-ocs2005.c \ sipe-ocs2007.h \ sipe-ocs2007.c \ sipe-rtf.h \ sipe-rtf.l \ sipe-schedule.h \ sipe-schedule.c \ sipe-session.h \ sipe-session.c \ sipe-sign.h \ sipe-sign.c \ sipe-status.h \ sipe-status.c \ sipe-subscriptions.h \ sipe-subscriptions.c \ sipe-svc.h \ sipe-svc.c \ sipe-tls.h \ sipe-tls.c \ sipe-ucs.h \ sipe-ucs.c \ sipe-user.h \ sipe-user.c \ sipe-utils.h \ sipe-utils.c \ sipe-webticket.h \ sipe-webticket.c \ sipe-xml.h \ uuid.h \ uuid.c if SIPE_OS_WIN32 libsipe_core_la_SOURCES += \ sip-sec-sspi.h \ sip-sec-sspi.c \ sipe-domino.h \ sipe-domino.c \ sipe-win32dep.h \ sipe-win32dep.c else if !SIP_SEC_GSSAPI_ONLY libsipe_core_la_SOURCES += \ sip-sec-ntlm.h \ sip-sec-ntlm.c noinst_LTLIBRARIES += \ libsipe_core_tests.la libsipe_core_tests_la_SOURCES = \ sip-sec-ntlm-tests.c libsipe_core_tests_la_LIBADD = \ libsipe_core_la-sipmsg.lo \ libsipe_core_la-sipe-rtf.lo \ libsipe_core_la-sipe-sign.lo \ libsipe_core_la-sipe-utils.lo \ libsipe_core_la-uuid.lo endif endif if SIPE_OPENSSL libsipe_core_crypto_la_SOURCES = \ sipe-cert-crypto-openssl.c \ sipe-crypt-openssl.c \ sipe-digest-openssl.c else libsipe_core_crypto_la_SOURCES = \ sipe-cert-crypto-nss.c \ sipe-crypt-nss.c \ sipe-digest-nss.c if !SIP_SEC_GSSAPI_ONLY libsipe_core_crypto_la_SOURCES += \ md4.h \ md4.c endif endif libsipe_core_libxml2_la_SOURCES = \ sipe-xml.c AM_CFLAGS = $(st) libsipe_core_la_CFLAGS = \ $(DEBUG_CFLAGS) \ $(QUALITY_CFLAGS) \ $(GLIB_CFLAGS) \ $(LOCALE_CPPFLAGS) \ -I$(srcdir)/../api if !SIPE_OS_WIN32 if !SIP_SEC_GSSAPI_ONLY libsipe_core_tests_la_CFLAGS = $(libsipe_core_la_CFLAGS) endif endif if SIP_SEC_GSSAPI libsipe_core_la_SOURCES += \ sip-sec-gssapi.h \ sip-sec-gssapi.c libsipe_core_la_CFLAGS += $(KRB5_CFLAGS) if !SIP_SEC_GSSAPI_ONLY libsipe_core_la_SOURCES += \ sip-sec-negotiate.h \ sip-sec-negotiate.c endif endif if SIPE_MIME_GMIME noinst_LTLIBRARIES += libsipe_core_mime.la libsipe_core_mime_la_SOURCES = sipe-mime.c libsipe_core_mime_la_CFLAGS = $(libsipe_core_la_CFLAGS) $(GMIME_CFLAGS) endif if SIPE_OS_WIN32 libsipe_core_la_CFLAGS += -DHAVE_SSPI=1 endif libsipe_core_crypto_la_CFLAGS = \ $(libsipe_core_la_CFLAGS) if SIPE_OPENSSL libsipe_core_crypto_la_CFLAGS += \ $(OPENSSL_CFLAGS) else libsipe_core_crypto_la_CFLAGS += \ $(NSS_CFLAGS) \ $(VALGRIND_CFLAGS) endif libsipe_core_libxml2_la_CFLAGS = $(libsipe_core_la_CFLAGS) $(LIBXML2_CFLAGS) if SIPE_WITH_VV libsipe_core_la_SOURCES += sipe-media.h sipe-media.c \ sdpmsg.h sdpmsg.c \ sipe-msrtp.c endif if SIPE_HAVE_XDATA libsipe_core_la_SOURCES += \ sipe-ft-lync.h sipe-ft-lync.c if SIPE_HAVE_APPSHARE libsipe_core_la_SOURCES += \ sipe-appshare.c \ sipe-appshare.h \ sipe-appshare-client.h \ sipe-appshare-remmina.c \ sipe-appshare-xfreerdp.c libsipe_core_la_CFLAGS += $(GIO_CFLAGS) if SIPE_HAVE_APPSHARE_SERVER libsipe_core_la_CFLAGS += \ $(FREERDP_SHADOW_CFLAGS) endif endif endif check_PROGRAMS = check_PROGRAMS += sipe_generic_tests sipe_generic_tests_SOURCES = sipe-generic-tests.c sipe_generic_tests_CFLAGS = $(libsipe_core_la_CFLAGS) sipe_generic_tests_LDADD = \ libsipe_core_la-sipe-utils.lo \ libsipe_core_la-uuid.lo if SIPE_OPENSSL sipe_generic_tests_LDADD += \ libsipe_core_crypto_la-sipe-crypt-openssl.lo \ libsipe_core_crypto_la-sipe-digest-openssl.lo \ $(OPENSSL_LIBS) else sipe_generic_tests_LDADD += \ libsipe_core_crypto_la-sipe-crypt-nss.lo \ libsipe_core_crypto_la-sipe-digest-nss.lo \ $(NSS_LIBS) endif sipe_generic_tests_LDADD += \ $(GLIB_LIBS) check_PROGRAMS += sipe_sipmsg_tests sipe_sipmsg_tests_SOURCES = sipe-sipmsg-tests.c sipe_sipmsg_tests_CFLAGS = $(libsipe_core_la_CFLAGS) sipe_sipmsg_tests_LDADD = \ libsipe_core_la-sipe-sign.lo \ libsipe_core_la-sipe-utils.lo \ libsipe_core_la-sipmsg.lo \ libsipe_core_la-uuid.lo if SIPE_OPENSSL sipe_sipmsg_tests_LDADD += \ libsipe_core_crypto_la-sipe-crypt-openssl.lo \ libsipe_core_crypto_la-sipe-digest-openssl.lo \ $(OPENSSL_LIBS) else sipe_sipmsg_tests_LDADD += \ libsipe_core_crypto_la-sipe-crypt-nss.lo \ libsipe_core_crypto_la-sipe-digest-nss.lo \ $(NSS_LIBS) endif sipe_sipmsg_tests_LDADD += \ $(GLIB_LIBS) check_PROGRAMS += sipe_rtf_tests sipe_rtf_tests_SOURCES = sipe-rtf-tests.c sipe_rtf_tests_CFLAGS = $(libsipe_core_la_CFLAGS) sipe_rtf_tests_LDADD = \ libsipe_core_la-sipe-rtf.lo \ $(GLIB_LIBS) check_PROGRAMS += sipe_xml_tests sipe_xml_tests_SOURCES = sipe-xml-tests.c sipe_xml_tests_CFLAGS = $(libsipe_core_la_CFLAGS) sipe_xml_tests_LDADD = \ libsipe_core_libxml2.la \ libsipe_core_la-sipe-utils.lo \ $(LIBXML2_LIBS) \ $(GLIB_LIBS) check_PROGRAMS += sip_sec_digest_tests sip_sec_digest_tests_SOURCES = sip-sec-digest-tests.c sip_sec_digest_tests_CFLAGS = $(libsipe_core_la_CFLAGS) sip_sec_digest_tests_LDADD = \ libsipe_core_la-sipe-utils.lo if SIPE_OPENSSL sip_sec_digest_tests_LDADD += \ libsipe_core_crypto_la-sipe-crypt-openssl.lo \ libsipe_core_crypto_la-sipe-digest-openssl.lo \ $(OPENSSL_LIBS) else sip_sec_digest_tests_LDADD += \ libsipe_core_crypto_la-sipe-crypt-nss.lo \ libsipe_core_crypto_la-sipe-digest-nss.lo \ $(NSS_LIBS) endif sip_sec_digest_tests_LDADD += \ $(GLIB_LIBS) # disables "caching" of memory blocks in tests TESTS_ENVIRONMENT = G_SLICE="always-malloc" TESTS = $(check_PROGRAMS) noinst_PROGRAMS = if !SIPE_OS_WIN32 # TODO: the code needs some TLC from a MinGW expert to compile... noinst_PROGRAMS += sipe_tls_analyzer sipe_tls_analyzer_SOURCES = sipe-tls-analyzer.c sipe_tls_analyzer_CFLAGS = $(libsipe_core_la_CFLAGS) sipe_tls_analyzer_LDADD = \ $(GLIB_LIBS) noinst_PROGRAMS += sipe_tls_tester sipe_tls_tester_SOURCES = sipe-tls-tester.c sipe_tls_tester_CFLAGS = $(libsipe_core_la_CFLAGS) sipe_tls_tester_LDADD = \ libsipe_core_la-sipe-tls.lo if SIPE_OPENSSL sipe_tls_tester_LDADD += \ libsipe_core_crypto_la-sipe-cert-crypto-openssl.lo \ libsipe_core_crypto_la-sipe-crypt-openssl.lo \ libsipe_core_crypto_la-sipe-digest-openssl.lo \ $(OPENSSL_LIBS) else sipe_tls_tester_LDADD += \ libsipe_core_crypto_la-sipe-cert-crypto-nss.lo \ libsipe_core_crypto_la-sipe-crypt-nss.lo \ libsipe_core_crypto_la-sipe-digest-nss.lo \ $(NSS_LIBS) endif sipe_tls_tester_LDADD += \ $(GLIB_LIBS) endif noinst_PROGRAMS += sipe_ntlm_analyzer sipe_ntlm_analyzer_SOURCES = sip-sec-ntlm-analyzer.c sipe_ntlm_analyzer_CFLAGS = $(libsipe_core_la_CFLAGS) sipe_ntlm_analyzer_LDADD = \ $(GLIB_LIBS) ================================================ FILE: src/core/Makefile.mingw ================================================ ################################### tell Emacs this is a -*- makefile-gmake -*- # # Copyright (C) 2011-2018 SIPE Project # # Makefile.mingw # # Author: zup@sbox.tugraz.at # Date 8/28/07 # Description: Makefile for win32 (mingw) version of libsipe # ############################################################################### # # Configuration option: # - USE_SSPI defined: NTLM, Kerberos, TLS-DSK & Single Sign-On supported # - USE_SSPI not defined: NTLM & TLS-DSK but without Single Sign-On support # [eg.: make -f Makefile.mingw USE_SSPI= ] # # @TODO: HAVE_GSSAPI_GSSAPI_H support for USE_SSPI=. Where to find the libraries? # Those would need to be packaged alongside the plugin. # USE_SSPI := 1 ifdef PIDGIN_TREE_TOP # standalone MinGW build OLD_PIDGIN_TREE_TOP := $(PIDGIN_TREE_TOP) PIDGIN_TREE_TOP := ../$(OLD_PIDGIN_TREE_TOP) else # MinGW cross-compile build (see contrib/mingw-cross-compile/README.txt) PIDGIN_TREE_TOP := ../../.. VERSION := $(shell cat ../../VERSION) endif include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak TARGET = libsipe DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR) # dump higher level defines DEFINES = DEFINES += -DENABLE_NLS=1 DEFINES += -DPACKAGE_BUGREPORT=\"https://sourceforge.net/p/sipe/bugs/\" DEFINES += -DPACKAGE_NAME=\"pidgin-sipe\" DEFINES += -DPACKAGE_URL=\"http://sipe.sourceforge.net/\" DEFINES += -DPACKAGE_VERSION=\"$(VERSION)\" DEFINES += -DSIPE_TRANSLATIONS_URL=\"https://www.transifex.com/stefanb/pidgin-sipe/\" #DEFINES += -DENABLE_OCS2005_MESSAGE_HACK=1 ifdef USE_SSPI DEFINES += -DHAVE_SSPI=1 endif ## ## INCLUDE PATHS ## INCLUDE_PATHS += -I. \ -I../api \ -I../purple \ -I$(GTK_TOP)/include \ -I$(GTK_TOP)/include/glib-2.0 \ -I$(GTK_TOP)/lib/glib-2.0/include \ -I$(LIBXML2_TOP)/include/libxml2 \ -I$(NSPR_TOP)/include \ -I$(NSS_TOP)/include PURPLE_INCLUDE_PATHS += -I$(PURPLE_TOP) \ -I$(PURPLE_TOP)/win32 \ -I$(PIDGIN_TREE_TOP) ## ## SOURCES, OBJECTS ## CLEAN_C_SRC = sip-soap.c \ sip-transport.c \ sipe-conf.c \ sipe-core.c \ sipe-domino.c \ sipe-buddy.c \ sipe-cal.c \ sipe-certificate.c \ sipe-cert-crypto-nss.c \ sipe-chat.c \ sipe-crypt-nss.c \ sipe-dialog.c \ sipe-digest-nss.c \ sipe-ft.c \ sipe-ft-tftp.c \ sipe-group.c \ sipe-groupchat.c \ sipe-http.c \ sipe-http-request.c \ sipe-http-transport.c \ sipe-im.c \ sipe-incoming.c \ sipe-lync-autodiscover.c \ sipe-mime-common.c \ sipe-notify.c \ sipe-ocs2005.c \ sipe-ocs2007.c \ sipe-rtf.c \ sipe-schedule.c \ sipe-session.c \ sipe-status.c \ sipe-subscriptions.c \ sipe-svc.c \ sipe-tls.c \ sipe-ucs.c \ sipe-user.c \ sipe-utils.c \ sipe-ews.c \ sipe-ews-autodiscover.c \ sipmsg.c \ sipe-sign.c \ sip-sec.c \ sip-sec-basic.c \ sip-sec-digest.c \ sip-sec-tls-dsk.c \ sip-csta.c \ sipe-webticket.c \ sipe-xml.c \ uuid.c \ sipe-win32dep.c PURPLE_C_SRC = ../purple/purple-buddy.c \ ../purple/purple-chat.c \ ../purple/purple-connection.c \ ../purple/purple-debug.c \ ../purple/purple-dnsquery.c \ ../purple/purple-ft.c \ ../purple/purple-groupchat.c \ ../purple/purple-im.c \ ../purple/purple-markup.c \ ../purple/purple-mime.c \ ../purple/purple-network.c \ ../purple/purple-notify.c \ ../purple/purple-plugin.c \ ../purple/purple-plugin-common.c \ ../purple/purple-schedule.c \ ../purple/purple-search.c \ ../purple/purple-setting.c \ ../purple/purple-status.c \ ../purple/purple-transport.c \ ../purple/purple-user.c C_TEST_SRC = sipe-xml-tests.c ifdef USE_SSPI CLEAN_C_SRC += sip-sec-sspi.c else CLEAN_C_SRC += md4.c \ sip-sec-ntlm.c C_TEST_SRC += sip-sec-ntlm-tests.c \ ../purple/tests.c endif C_SRC = $(CLEAN_C_SRC) $(PURPLE_C_SRC) RC_SRC = libsiperc.rc CLEAN_OBJECTS = $(CLEAN_C_SRC:%.c=%.o) PURPLE_OBJECTS = $(PURPLE_C_SRC:%.c=%.o) RC_OBJECTS = $(RC_SRC:%.rc=%.o) OBJECTS = $(CLEAN_OBJECTS) $(PURPLE_OBJECTS) $(RC_OBJECTS) # Only these modules need the purple headers $(PURPLE_OBJECTS): INCLUDE_PATHS += $(PURPLE_INCLUDE_PATHS) TEST_OBJECTS = $(C_TEST_SRC:%.c=%.o) ## ## LIBRARIES ## LIB_PATHS = -L$(GTK_TOP)/lib \ -L$(LIBXML2_TOP)/lib \ -L$(NSS_TOP)/lib \ -L$(PURPLE_TOP) LIBS = -lglib-2.0 \ -lgobject-2.0 \ -lintl \ -lxml2 \ -lnss3 \ -lsmime3 \ -lnspr4 \ -lws2_32 \ -lpurple ifdef USE_SSPI LIBS += -lsecur32 endif # These flags are used in mingw build TESTS_WARN = -Werror -Wall -Wextra -Waggregate-return -Wcast-align -Winit-self -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wpointer-arith -Wundef include $(PIDGIN_COMMON_RULES) ## ## TARGET DEFINITIONS ## .PHONY: all clean install all: $(TARGET).dll _comma = , libsiperc.rc: libsiperc.rc.in sed \ -e 's/@SIPE_VERSION@/$(VERSION)/' \ -e 's/#SIPE_VERSION#/$(subst .,$(_comma),$(VERSION))/' \ <$< >$@ $(OBJECTS): $(PURPLE_CONFIG_H) $(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $@.dbgsym $(STRIP) --strip-unneeded $@.dbgsym -o $@ ## ## CLEAN RULES ## clean: tests-clean rm -f $(RC_SRC) rm -f $(OBJECTS) rm -f $(TARGET).dll rmbak: rm -f *~ install: test -z "$(DLL_INSTALL_DIR)" || mkdir -p "$(DLL_INSTALL_DIR)" /usr/bin/install -c $(TARGET).dll $(TARGET).dll.dbgsym '$(DLL_INSTALL_DIR)' $(TEST_OBJECTS): tests: tests-clean $(TEST_OBJECTS) $(CC) sipe-utils.o uuid.o sipe-xml.o sipe-xml-tests.o -L. $(LIB_PATHS) $(LIBS) -lsipe -o sipe-xml-tests.exe ./sipe-xml-tests.exe ifdef USE_SSPI # nothing to do else $(CC) ../purple/purple-debug.o ../purple/purple-markup.o ../purple/purple-network.o md4.o sipe-digest.o sipe-crypt.o sipe-mime.o sipe-sign.o sipmsg.o sipe-utils.o uuid.o sip-sec-ntlm-tests.o ../purple/tests.o -L. $(LIB_PATHS) $(LIBS) -lsipe -o ../purple/tests.exe ../purple/tests.exe endif tests-clean: rm -f $(TEST_OBJECTS) rm -f sipe-xml-tests.exe ../purple/tests.exe include $(PIDGIN_COMMON_TARGETS) ================================================ FILE: src/core/libsiperc.rc.in ================================================ #include VS_VERSION_INFO VERSIONINFO FILEVERSION #SIPE_VERSION#,0 PRODUCTVERSION #SIPE_VERSION#,0 FILEFLAGSMASK 0 FILEFLAGS 0 FILEOS VOS__WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904B0" BEGIN VALUE "CompanyName", "SIPE Project " VALUE "FileDescription", "LibSipe third-party plugin for LibPurple" VALUE "FileVersion", "@SIPE_VERSION@" VALUE "InternalName", "libsipe" VALUE "LegalCopyright", "Copyright (C) 2010-2013 SIPE Project (See the COPYRIGHT file in the source distribution)." VALUE "OriginalFilename", "libsipe.dll" VALUE "ProductName", "LibSipe" VALUE "ProductVersion", "@SIPE_VERSION@" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END ================================================ FILE: src/core/md4.c ================================================ /* vim:set ts=2 sw=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * "clean room" MD4 implementation (see RFC 1320) */ #include #include "md4.h" /* the "conditional" function */ #define F(x,y,z) (((x) & (y)) | (~(x) & (z))) /* the "majority" function */ #define G(x,y,z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) /* the "parity" function */ #define H(x,y,z) ((x) ^ (y) ^ (z)) /* rotate n-bits to the left */ #define ROTL(x,n) (((x) << (n)) | ((x) >> (0x20 - n))) /* round 1: [abcd k s]: a = (a + F(b,c,d) + X[k]) <<< s */ #define RD1(a,b,c,d,k,s) a += F(b,c,d) + X[k]; a = ROTL(a,s) /* round 2: [abcd k s]: a = (a + G(b,c,d) + X[k] + MAGIC) <<< s */ #define RD2(a,b,c,d,k,s) a += G(b,c,d) + X[k] + 0x5A827999; a = ROTL(a,s) /* round 3: [abcd k s]: a = (a + H(b,c,d) + X[k] + MAGIC) <<< s */ #define RD3(a,b,c,d,k,s) a += H(b,c,d) + X[k] + 0x6ED9EBA1; a = ROTL(a,s) /* converts from word array to byte array, len is number of bytes */ static void w2b(uint8_t *out, const uint32_t *in, uint32_t len) { uint8_t *bp; const uint32_t *wp, *wpend; bp = out; wp = in; wpend = wp + (len >> 2); for (; wp != wpend; ++wp, bp += 4) { bp[0] = (uint8_t) ((*wp ) & 0xFF); bp[1] = (uint8_t) ((*wp >> 8) & 0xFF); bp[2] = (uint8_t) ((*wp >> 16) & 0xFF); bp[3] = (uint8_t) ((*wp >> 24) & 0xFF); } } /* converts from byte array to word array, len is number of bytes */ static void b2w(uint32_t *out, const uint8_t *in, uint32_t len) { uint32_t *wp; const uint8_t *bp, *bpend; wp = out; bp = in; bpend = in + len; for (; bp != bpend; bp += 4, ++wp) { *wp = (uint32_t) (bp[0] ) | (uint32_t) (bp[1] << 8) | (uint32_t) (bp[2] << 16) | (uint32_t) (bp[3] << 24); } } /* update state: data is 64 bytes in length */ static void md4step(uint32_t state[4], const uint8_t *data) { uint32_t A, B, C, D, X[16]; b2w(X, data, 64); A = state[0]; B = state[1]; C = state[2]; D = state[3]; RD1(A,B,C,D, 0,3); RD1(D,A,B,C, 1,7); RD1(C,D,A,B, 2,11); RD1(B,C,D,A, 3,19); RD1(A,B,C,D, 4,3); RD1(D,A,B,C, 5,7); RD1(C,D,A,B, 6,11); RD1(B,C,D,A, 7,19); RD1(A,B,C,D, 8,3); RD1(D,A,B,C, 9,7); RD1(C,D,A,B,10,11); RD1(B,C,D,A,11,19); RD1(A,B,C,D,12,3); RD1(D,A,B,C,13,7); RD1(C,D,A,B,14,11); RD1(B,C,D,A,15,19); RD2(A,B,C,D, 0,3); RD2(D,A,B,C, 4,5); RD2(C,D,A,B, 8, 9); RD2(B,C,D,A,12,13); RD2(A,B,C,D, 1,3); RD2(D,A,B,C, 5,5); RD2(C,D,A,B, 9, 9); RD2(B,C,D,A,13,13); RD2(A,B,C,D, 2,3); RD2(D,A,B,C, 6,5); RD2(C,D,A,B,10, 9); RD2(B,C,D,A,14,13); RD2(A,B,C,D, 3,3); RD2(D,A,B,C, 7,5); RD2(C,D,A,B,11, 9); RD2(B,C,D,A,15,13); RD3(A,B,C,D, 0,3); RD3(D,A,B,C, 8,9); RD3(C,D,A,B, 4,11); RD3(B,C,D,A,12,15); RD3(A,B,C,D, 2,3); RD3(D,A,B,C,10,9); RD3(C,D,A,B, 6,11); RD3(B,C,D,A,14,15); RD3(A,B,C,D, 1,3); RD3(D,A,B,C, 9,9); RD3(C,D,A,B, 5,11); RD3(B,C,D,A,13,15); RD3(A,B,C,D, 3,3); RD3(D,A,B,C,11,9); RD3(C,D,A,B, 7,11); RD3(B,C,D,A,15,15); state[0] += A; state[1] += B; state[2] += C; state[3] += D; } void md4sum(const uint8_t *input, uint32_t inputLen, uint8_t *result) { uint8_t final[128]; uint32_t i, n, m, state[4]; uint64_t inputLenBits; uint32_t inputLenBitsLow; uint32_t inputLenBitsHigh; /* magic initial states */ state[0] = 0x67452301; state[1] = 0xEFCDAB89; state[2] = 0x98BADCFE; state[3] = 0x10325476; /* compute number of complete 64-byte segments contained in input */ m = inputLen >> 6; /* digest first m segments */ for (i=0; i= 56 ? 120 : 56), &inputLenBitsLow, 4); inputLenBitsHigh = (uint32_t)((inputLenBits >> 32) & 0xFFFFFFFF); w2b(final + (n >= 56 ? 124 : 60), &inputLenBitsHigh, 4); md4step(state, final); if (n >= 56) md4step(state, final + 64); /* copy state to result */ w2b(result, state, 16); } ================================================ FILE: src/core/md4.h ================================================ /* vim:set ts=2 sw=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef md4_h__ #define md4_h__ #ifdef __cplusplus extern "C" { #endif #include /** * md4sum - computes the MD4 sum over the input buffer per RFC 1320 * * @param input * buffer containing input data * @param inputLen * length of input buffer (number of bytes) * @param result * 16-byte buffer that will contain the MD4 sum upon return * * NOTE: MD4 is superceded by MD5. do not use MD4 unless required by the * protocol you are implementing (e.g., NTLM requires MD4). * * NOTE: this interface is designed for relatively small buffers. A streaming * interface would make more sense if that were a requirement. Currently, this * is good enough for the applications we care about. */ void md4sum(const uint8_t *input, uint32_t inputLen, uint8_t *result); #ifdef __cplusplus } #endif #endif /* md4_h__ */ ================================================ FILE: src/core/sdpmsg.c ================================================ /** * @file sdpmsg.c * * pidgin-sipe * * Copyright (C) 2013-2017 SIPE Project * Copyright (C) 2010 Jakub Adam * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "sipe-backend.h" #include "sipe-core.h" #include "sdpmsg.h" #include "sipe-utils.h" static gboolean append_attribute(struct sdpmedia *media, gchar *attr) { gchar **parts = g_strsplit(attr + 2, ":", 2); if(!parts[0]) { g_strfreev(parts); return FALSE; } media->attributes = sipe_utils_nameval_add(media->attributes, parts[0], parts[1] ? parts[1] : ""); g_strfreev(parts); return TRUE; } static gboolean parse_attributes(struct sdpmsg *smsg, const gchar *msg) { gchar **lines = g_strsplit(msg, "\r\n", 0); gchar **ptr = lines; while (*ptr != NULL) { if (g_str_has_prefix(*ptr, "o=")) { gchar **parts = g_strsplit(*ptr + 2, " ", 6); if (g_strv_length(parts) != 6) { g_strfreev(parts); g_strfreev(lines); return FALSE; } smsg->ip = g_strdup(parts[5]); g_strfreev(parts); } else if (g_str_has_prefix(*ptr, "m=")) { gchar **parts; struct sdpmedia *media; parts = g_strsplit(*ptr + 2, " ", 3); if (g_strv_length(parts) < 3) { g_strfreev(parts); g_strfreev(lines); return FALSE; } media = g_new0(struct sdpmedia, 1); smsg->media = g_slist_append(smsg->media, media); media->name = g_strdup(parts[0]); media->port = atoi(parts[1]); media->encryption_active = g_strstr_len(parts[2], -1, "/SAVP") != NULL; g_strfreev(parts); while (*(++ptr) && !g_str_has_prefix(*ptr, "m=")) { if (g_str_has_prefix(*ptr, "a=")) { if (!append_attribute(media, *ptr)) { g_strfreev(lines); return FALSE; } } } continue; } ++ptr; } g_strfreev(lines); return TRUE; } static struct sdpcandidate * sdpcandidate_copy(struct sdpcandidate *candidate); static SipeComponentType parse_component(const gchar *str) { switch (atoi(str)) { case 1: return SIPE_COMPONENT_RTP; case 2: return SIPE_COMPONENT_RTCP; default: return SIPE_COMPONENT_NONE; } } static gchar * base64_pad(const gchar* str) { size_t str_len = strlen(str); int mod = str_len % 4; if (mod > 0) { gchar *result = NULL; int pad = 4 - mod; gchar *ptr = result = g_malloc(str_len + pad + 1); memcpy(ptr, str, str_len); ptr += str_len; memset(ptr, '=', pad); ptr += pad; *ptr = '\0'; return result; } else return g_strdup(str); } static gboolean parse_append_candidate_draft_6(gchar **tokens, GSList **candidates) { struct sdpcandidate *candidate; if (g_strv_length(tokens) < 7 || strlen(tokens[4]) < 3) { return FALSE; } candidate = g_new0(struct sdpcandidate, 1); candidate->username = base64_pad(tokens[0]); candidate->component = parse_component(tokens[1]); candidate->password = base64_pad(tokens[2]); if (sipe_strequal(tokens[3], "UDP")) candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP; else if (sipe_strequal(tokens[3], "TCP")) candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_ACTIVE; else { sdpcandidate_free(candidate); return FALSE; } candidate->priority = atoi(tokens[4] + 2); candidate->ip = g_strdup(tokens[5]); candidate->port = atoi(tokens[6]); *candidates = g_slist_append(*candidates, candidate); // draft 6 candidates are both active and passive if (candidate->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) { candidate = sdpcandidate_copy(candidate); candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_PASSIVE; *candidates = g_slist_append(*candidates, candidate); } return TRUE; } static gboolean parse_append_candidate_rfc_5245(gchar **tokens, GSList **candidates) { struct sdpcandidate *candidate; if (g_strv_length(tokens) < 8) { return FALSE; } candidate = g_new0(struct sdpcandidate, 1); candidate->foundation = g_strdup(tokens[0]); candidate->component = parse_component(tokens[1]); if (sipe_strcase_equal(tokens[2], "UDP")) candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP; else if (sipe_strcase_equal(tokens[2], "TCP-ACT")) candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_ACTIVE; else if (sipe_strcase_equal(tokens[2], "TCP-PASS")) candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_PASSIVE; else { sdpcandidate_free(candidate); return FALSE; } candidate->priority = atoi(tokens[3]); candidate->ip = g_strdup(tokens[4]); candidate->port = atoi(tokens[5]); if (sipe_strcase_equal(tokens[7], "host")) candidate->type = SIPE_CANDIDATE_TYPE_HOST; else if (sipe_strcase_equal(tokens[7], "relay")) candidate->type = SIPE_CANDIDATE_TYPE_RELAY; else if (sipe_strcase_equal(tokens[7], "srflx")) candidate->type = SIPE_CANDIDATE_TYPE_SRFLX; else if (sipe_strcase_equal(tokens[7], "prflx")) candidate->type = SIPE_CANDIDATE_TYPE_PRFLX; else { sdpcandidate_free(candidate); return FALSE; } *candidates = g_slist_append(*candidates, candidate); return TRUE; } static gboolean parse_candidates(GSList *attrs, SipeIceVersion *ice_version, GSList **candidates) { const gchar *attr; int i = 0; g_return_val_if_fail(*candidates == NULL, FALSE); *ice_version = SIPE_ICE_NO_ICE; while ((attr = sipe_utils_nameval_find_instance(attrs, "candidate", i++))) { gchar **tokens = g_strsplit_set(attr, " ", 0); gboolean parsed_ok; if (g_strv_length(tokens) < 7) { g_strfreev(tokens); return FALSE; } if (sipe_strequal(tokens[6], "typ")) { parsed_ok = parse_append_candidate_rfc_5245(tokens, candidates); if (*candidates) *ice_version = SIPE_ICE_RFC_5245; } else { parsed_ok = parse_append_candidate_draft_6(tokens, candidates); if (*candidates) *ice_version = SIPE_ICE_DRAFT_6; } g_strfreev(tokens); if (!parsed_ok) { return FALSE; } } if (*ice_version == SIPE_ICE_RFC_5245) { const gchar *username = sipe_utils_nameval_find(attrs, "ice-ufrag"); const gchar *password = sipe_utils_nameval_find(attrs, "ice-pwd"); if (username && password) { GSList *i; for (i = *candidates; i; i = i->next) { struct sdpcandidate *c = i->data; c->username = g_strdup(username); c->password = g_strdup(password); } } } return TRUE; } static GSList * create_legacy_candidates(gchar *ip, guint16 port) { struct sdpcandidate *candidate; GSList *candidates = NULL; candidate = g_new0(struct sdpcandidate, 1); candidate->foundation = g_strdup("1"); candidate->component = SIPE_COMPONENT_RTP; candidate->type = SIPE_CANDIDATE_TYPE_HOST; candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP; candidate->ip = g_strdup(ip); candidate->port = port; candidates = g_slist_append(candidates, candidate); candidate = g_new0(struct sdpcandidate, 1); candidate->foundation = g_strdup("1"); candidate->component = SIPE_COMPONENT_RTCP; candidate->type = SIPE_CANDIDATE_TYPE_HOST; candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP; candidate->ip = g_strdup(ip); candidate->port = port + 1; candidates = g_slist_append(candidates, candidate); return candidates; } static gboolean parse_codec_parameters(GSList *attrs, struct sdpcodec *codec) { const gchar* params; int i = 0; while((params = sipe_utils_nameval_find_instance(attrs, "fmtp", i++))) { gchar **tokens; gchar **param; tokens = g_strsplit(params, " ", 0); if (g_strv_length(tokens) < 1) { g_strfreev(tokens); return FALSE; } if (atoi(tokens[0]) != codec->id) { g_strfreev(tokens); continue; } for (param = tokens + 1; *param; ++param) { gchar **nameval = g_strsplit(*param, "=", 2); if (g_strv_length(nameval) != 2) { g_strfreev(nameval); continue; } codec->parameters = sipe_utils_nameval_add(codec->parameters, nameval[0], nameval[1]); g_strfreev(nameval); } g_strfreev(tokens); } return TRUE; } static gboolean parse_codecs(GSList *attrs, SipeMediaType type, GSList **codecs) { int i = 0; const gchar *attr; while ((attr = sipe_utils_nameval_find_instance(attrs, "rtpmap", i++))) { struct sdpcodec *codec; gchar **tokens; tokens = g_strsplit_set(attr, " /", 4); if (g_strv_length(tokens) < 3) { g_strfreev(tokens); return FALSE; } codec = g_new0(struct sdpcodec, 1); codec->id = atoi(tokens[0]); codec->name = g_strdup(tokens[1]); codec->clock_rate = atoi(tokens[2]); codec->type = type; if (type == SIPE_MEDIA_AUDIO) { codec->channels = tokens[3] ? atoi(tokens[3]) : 1; } g_strfreev(tokens); if (!parse_codec_parameters(attrs, codec)) { sdpcodec_free(codec); return FALSE; } *codecs = g_slist_append(*codecs, codec); } return TRUE; } static void parse_encryption_key(GSList *attrs, guchar **key, int *key_id) { int i = 0; const gchar *attr; while ((attr = sipe_utils_nameval_find_instance(attrs, "crypto", i++))) { gchar **tokens = g_strsplit_set(attr, " :|", 6); if (tokens[0] && tokens[1] && tokens[2] && tokens[3] && tokens[4] && sipe_strcase_equal(tokens[1], "AES_CM_128_HMAC_SHA1_80") && sipe_strequal(tokens[2], "inline") && !tokens[5]) { gsize key_len; *key = g_base64_decode(tokens[3], &key_len); if (key_len != SIPE_SRTP_KEY_LEN) { g_free(*key); *key = NULL; } *key_id = atoi(tokens[0]); } g_strfreev(tokens); if (*key) { break; } } } struct sdpmsg * sdpmsg_parse_msg(const gchar *msg) { struct sdpmsg *smsg = g_new0(struct sdpmsg, 1); GSList *i; if (!parse_attributes(smsg, msg)) { sdpmsg_free(smsg); return NULL; } smsg->ice_version = SIPE_ICE_NO_ICE; for (i = smsg->media; i; i = i->next) { struct sdpmedia *media = i->data; SipeMediaType type; SipeIceVersion detected_ice_version; if (!parse_candidates(media->attributes, &detected_ice_version, &media->candidates)) { sdpmsg_free(smsg); return NULL; } if (media->port != 0) { smsg->ice_version = detected_ice_version; if (!media->candidates) { // No a=candidate in SDP message, this seems to be MSOC 2005 media->candidates = create_legacy_candidates(smsg->ip, media->port); } } if (sipe_strequal(media->name, "audio")) type = SIPE_MEDIA_AUDIO; else if (sipe_strequal(media->name, "video")) type = SIPE_MEDIA_VIDEO; else if (sipe_strequal(media->name, "data")) type = SIPE_MEDIA_APPLICATION; else if (sipe_strequal(media->name, "applicationsharing")) type = SIPE_MEDIA_APPLICATION; else { // Unknown media type sdpmsg_free(smsg); return NULL; } if (!parse_codecs(media->attributes, type, &media->codecs)) { sdpmsg_free(smsg); return NULL; } parse_encryption_key(media->attributes, &media->encryption_key, &media->encryption_key_id); } return smsg; } static gchar * codecs_to_string(GSList *codecs) { GString *result = g_string_new(NULL); for (; codecs; codecs = codecs->next) { struct sdpcodec *c = codecs->data; GSList *params = c->parameters; g_string_append_printf(result, "a=rtpmap:%d %s/%d\r\n", c->id, c->name, c->clock_rate); if (params) { GString *param_str = g_string_new(NULL); int written_params = 0; g_string_append_printf(param_str, "a=fmtp:%d", c->id); for (; params; params = params->next) { struct sipnameval* par = params->data; if (sipe_strequal(par->name, "farsight-send-profile")) { // Lync AVMCU doesn't like this property. continue; } g_string_append_printf(param_str, " %s=%s", par->name, par->value); ++written_params; } g_string_append(param_str, "\r\n"); if (written_params > 0) { g_string_append(result, param_str->str); } g_string_free(param_str, TRUE); } } return g_string_free(result, FALSE); } static gchar * codec_ids_to_string(GSList *codecs) { GString *result = g_string_new(NULL); for (; codecs; codecs = codecs->next) { struct sdpcodec *c = codecs->data; g_string_append_printf(result, " %d", c->id); } return g_string_free(result, FALSE); } static gchar * base64_unpad(const gchar *str) { gchar *result = g_strdup(str); gchar *ptr; for (ptr = result + strlen(result); ptr != result; --ptr) { if (*(ptr - 1) != '=') { *ptr = '\0'; break; } } return result; } static gchar * candidates_to_string(GSList *candidates, SipeIceVersion ice_version) { GString *result = g_string_new(""); GSList *i; GSList *processed_tcp_candidates = NULL; for (i = candidates; i; i = i->next) { struct sdpcandidate *c = i->data; const gchar *protocol; const gchar *type; gchar *related = NULL; if (ice_version == SIPE_ICE_RFC_5245) { switch (c->protocol) { case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE: protocol = "TCP-ACT"; break; case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE: protocol = "TCP-PASS"; break; case SIPE_NETWORK_PROTOCOL_UDP: protocol = "UDP"; break; default: /* error unknown/unsupported type */ protocol = "UNKNOWN"; break; } switch (c->type) { case SIPE_CANDIDATE_TYPE_HOST: type = "host"; break; case SIPE_CANDIDATE_TYPE_RELAY: type = "relay"; break; case SIPE_CANDIDATE_TYPE_SRFLX: type = "srflx"; break; case SIPE_CANDIDATE_TYPE_PRFLX: type = "prflx"; break; default: /* error unknown/unsupported type */ type = "unknown"; break; } switch (c->type) { case SIPE_CANDIDATE_TYPE_RELAY: case SIPE_CANDIDATE_TYPE_SRFLX: case SIPE_CANDIDATE_TYPE_PRFLX: related = g_strdup_printf("raddr %s rport %d", c->base_ip, c->base_port); break; default: break; } g_string_append_printf(result, "a=candidate:%s %u %s %u %s %d typ %s %s\r\n", c->foundation, c->component, protocol, c->priority, c->ip, c->port, type, related ? related : ""); g_free(related); } else if (ice_version == SIPE_ICE_DRAFT_6) { gchar *username; gchar *password; switch (c->protocol) { case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE: case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE: { GSList *prev_cand = processed_tcp_candidates; for (; prev_cand; prev_cand = prev_cand->next) { struct sdpcandidate *c2 = (struct sdpcandidate *)prev_cand->data; if (sipe_strequal(c->ip, c2->ip) && c->component == c2->component) { break; } } if (prev_cand) { protocol = NULL; } else { protocol = "TCP"; processed_tcp_candidates = g_slist_append(processed_tcp_candidates, c); } break; } case SIPE_NETWORK_PROTOCOL_UDP: protocol = "UDP"; break; default: /* unknown/unsupported type, ignore */ protocol = NULL; break; } if (!protocol) { continue; } username = base64_unpad(c->username); password = base64_unpad(c->password); g_string_append_printf(result, "a=candidate:%s %u %s %s 0.%u %s %d\r\n", username, c->component, password, protocol, c->priority, c->ip, c->port); g_free(username); g_free(password); } } g_slist_free(processed_tcp_candidates); return g_string_free(result, FALSE); } static gint remote_candidates_sort_cb(struct sdpcandidate *c1, struct sdpcandidate *c2) { return c1->component - c2->component; } static gchar * remote_candidates_to_string(GSList *candidates, SipeIceVersion ice_version) { GString *result = g_string_new(""); if (candidates) { // Sort the candidates by increasing component IDs. candidates = g_slist_sort(candidates, (GCompareFunc)remote_candidates_sort_cb); if (ice_version == SIPE_ICE_RFC_5245) { GSList *i; g_string_append(result, "a=remote-candidates:"); for (i = candidates; i; i = i->next) { struct sdpcandidate *c = i->data; g_string_append_printf(result, "%u %s %u ", c->component, c->ip, c->port); } g_string_append(result, "\r\n"); } else if (ice_version == SIPE_ICE_DRAFT_6) { struct sdpcandidate *c = candidates->data; g_string_append_printf(result, "a=remote-candidate:%s\r\n", c->username); } } return g_string_free(result, FALSE); } static gchar * attributes_to_string(GSList *attributes) { GString *result = g_string_new(""); for (; attributes; attributes = attributes->next) { struct sipnameval *a = attributes->data; g_string_append_printf(result, "a=%s", a->name); if (!sipe_strequal(a->value, "")) g_string_append_printf(result, ":%s", a->value); g_string_append(result, "\r\n"); } return g_string_free(result, FALSE); } static gchar * media_to_string(const struct sdpmsg *msg, const struct sdpmedia *media) { gchar *media_str; gchar *transport_profile = NULL; gchar *media_conninfo = NULL; gchar *codecs_str = NULL; gchar *codec_ids_str = codec_ids_to_string(media->codecs); gchar *candidates_str = NULL; gchar *remote_candidates_str = NULL; gchar *attributes_str = NULL; gchar *credentials = NULL; gchar *crypto = NULL; gboolean uses_tcp_transport = TRUE; if (media->port != 0) { if (!sipe_strequal(msg->ip, media->ip)) { media_conninfo = g_strdup_printf("c=IN %s %s\r\n", sipe_utils_ip_sdp_address_marker(media->ip), media->ip); } codecs_str = codecs_to_string(media->codecs); candidates_str = candidates_to_string(media->candidates, msg->ice_version); remote_candidates_str = remote_candidates_to_string(media->remote_candidates, msg->ice_version); if (media->remote_candidates) { struct sdpcandidate *c = media->remote_candidates->data; uses_tcp_transport = c->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE || c->protocol == SIPE_NETWORK_PROTOCOL_TCP_PASSIVE || c->protocol == SIPE_NETWORK_PROTOCOL_TCP_SO; } else { GSList *candidates = media->candidates; for (; candidates; candidates = candidates->next) { struct sdpcandidate *c = candidates->data; if (c->protocol == SIPE_NETWORK_PROTOCOL_UDP) { uses_tcp_transport = FALSE; break; } } } attributes_str = attributes_to_string(media->attributes); if (msg->ice_version == SIPE_ICE_RFC_5245 && media->candidates) { struct sdpcandidate *c = media->candidates->data; credentials = g_strdup_printf("a=ice-ufrag:%s\r\n" "a=ice-pwd:%s\r\n", c->username, c->password); } if (media->encryption_key) { gchar *key_encoded = g_base64_encode(media->encryption_key, SIPE_SRTP_KEY_LEN); crypto = g_strdup_printf("a=crypto:%d AES_CM_128_HMAC_SHA1_80 inline:%s|2^31\r\n", media->encryption_key_id, key_encoded); g_free(key_encoded); } } transport_profile = g_strdup_printf("%sRTP/%sAVP", uses_tcp_transport ? "TCP/" : "", media->encryption_active ? "S" : ""); media_str = g_strdup_printf("m=%s %d %s%s\r\n" "%s" "%s" "%s" "%s" "%s" "%s" "%s", media->name, media->port, transport_profile, codec_ids_str, media_conninfo ? media_conninfo : "", candidates_str ? candidates_str : "", crypto ? crypto : "", remote_candidates_str ? remote_candidates_str : "", codecs_str ? codecs_str : "", attributes_str ? attributes_str : "", credentials ? credentials : ""); g_free(transport_profile); g_free(media_conninfo); g_free(codecs_str); g_free(codec_ids_str); g_free(candidates_str); g_free(remote_candidates_str); g_free(attributes_str); g_free(credentials); g_free(crypto); return media_str; } gchar * sdpmsg_to_string(const struct sdpmsg *msg) { GString *body = g_string_new(NULL); GSList *i; const gchar *marker = sipe_utils_ip_sdp_address_marker(msg->ip); g_string_append_printf( body, "v=0\r\n" "o=- 0 0 IN %s %s\r\n" "s=session\r\n" "c=IN %s %s\r\n" "b=CT:99980\r\n" "t=0 0\r\n", marker, msg->ip, marker, msg->ip); for (i = msg->media; i; i = i->next) { gchar *media_str = media_to_string(msg, i->data); g_string_append(body, media_str); g_free(media_str); } return g_string_free(body, FALSE); } static struct sdpcandidate * sdpcandidate_copy(struct sdpcandidate *candidate) { if (candidate) { struct sdpcandidate *copy = g_new0(struct sdpcandidate, 1); copy->foundation = g_strdup(candidate->foundation); copy->component = candidate->component; copy->type = candidate->type; copy->protocol = candidate->protocol; copy->priority = candidate->priority; copy->ip = g_strdup(candidate->ip); copy->port = candidate->port; copy->base_ip = g_strdup(candidate->base_ip); copy->base_port = candidate->base_port; copy->username = g_strdup(candidate->username); copy->password = g_strdup(candidate->password); return copy; } else return NULL; } void sdpcandidate_free(struct sdpcandidate *candidate) { if (candidate) { g_free(candidate->foundation); g_free(candidate->ip); g_free(candidate->base_ip); g_free(candidate->username); g_free(candidate->password); g_free(candidate); } } void sdpcodec_free(struct sdpcodec *codec) { if (codec) { g_free(codec->name); sipe_utils_nameval_free(codec->parameters); g_free(codec); } } void sdpmedia_free(struct sdpmedia *media) { if (media) { g_free(media->name); g_free(media->ip); sipe_utils_nameval_free(media->attributes); sipe_utils_slist_free_full(media->candidates, (GDestroyNotify) sdpcandidate_free); sipe_utils_slist_free_full(media->codecs, (GDestroyNotify) sdpcodec_free); sipe_utils_slist_free_full(media->remote_candidates, (GDestroyNotify) sdpcandidate_free); g_free(media->encryption_key); g_free(media); } } void sdpmsg_free(struct sdpmsg *msg) { if (msg) { g_free(msg->ip); sipe_utils_slist_free_full(msg->media, (GDestroyNotify) sdpmedia_free); g_free(msg); } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sdpmsg.h ================================================ /** * @file sdpmsg.h * * pidgin-sipe * * Copyright (C) 2014-2017 SIPE Project * Copyright (C) 2010 Jakub Adam * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ struct sdpmsg { gchar *ip; /* currently this has to be an IPv4 address */ GSList *media; SipeIceVersion ice_version; }; struct sdpmedia { gchar *name; gchar *ip; /* currently this has to be an IPv4 address */ guint port; GSList *attributes; GSList *candidates; GSList *codecs; GSList *remote_candidates; guchar *encryption_key; int encryption_key_id; gboolean encryption_active; }; struct sdpcandidate { gchar *foundation; SipeComponentType component; SipeCandidateType type; SipeNetworkProtocol protocol; guint32 priority; gchar *ip; /* currently this has to be an IPv4 address */ guint port; gchar *base_ip; guint base_port; gchar *username; gchar *password; }; struct sdpcodec { gint id; gchar *name; gint clock_rate; gint channels; SipeMediaType type; GSList *parameters; }; /** * Parses SDP message into @c sdpmsg structure. * * @param msg SDP message as character string * * @return New @c sdpmsg or NULL if message can not be parsed. */ struct sdpmsg *sdpmsg_parse_msg(const gchar *msg); /** * Creates a SDP message from the @c sdpmsg structure. * * @param msg a @c sdpmsg * * @return SDP message as a character string that must be g_free'd after use. */ gchar *sdpmsg_to_string(const struct sdpmsg *msg); /** * Deallocates @c sdpmsg. */ void sdpmsg_free(struct sdpmsg *msg); /** * Deallocates @c sdpcandidate. */ void sdpcandidate_free(struct sdpcandidate *candidate); /** * Deallocates @c sdpcodec. */ void sdpcodec_free(struct sdpcodec *codec); /** * Deallocates @c sdpmedia. */ void sdpmedia_free(struct sdpmedia *media); ================================================ FILE: src/core/sip-csta.c ================================================ /** * @file sip-csta.c * * pidgin-sipe * * Copyright (C) 2010-11 SIPE Project * Copyright (C) 2009 pier11 * * Implements Remote Call Control (RCC) feature for * integration with legacy enterprise PBX (wired telephony) systems. * Should be applicable to 2005 and 2007(R2) systems. * Inderlying XML protocol CSTA is defined in ECMA-323. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "sipe-common.h" #include "sipmsg.h" #include "sip-csta.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-dialog.h" #include "sipe-schedule.h" #include "sipe-utils.h" #include "sipe-xml.h" #define ORIGINATED_CSTA_STATUS "originated" #define DELIVERED_CSTA_STATUS "delivered" #define ESTABLISHED_CSTA_STATUS "established" /** * Data model for interaction with SIP/CSTA Gateway */ struct sip_csta { gchar *line_uri; /** SIP/CSTA Gateway's SIP URI */ gchar *gateway_uri; /** dialog with SIP/CSTA Gateway */ struct sip_dialog *dialog; gchar *gateway_status; gchar *monitor_cross_ref_id; gchar *line_status; /** destination tel: URI */ gchar *to_tel_uri; gchar *call_id; /* our device ID as reported by SIP/CSTA gateway */ gchar *device_id; }; /** * Sends CSTA RequestSystemStatus request to SIP/CSTA Gateway. * @param line_uri (%s) Ex.: tel:73124;phone-context=dialstring;partition=BE_BRS_INT */ #define SIP_SEND_CSTA_REQUEST_SYSTEM_STATUS \ ""\ ""\ ""\ ""\ ""\ "%s"\ ""\ ""\ ""\ "" /** * Sends CSTA GetCSTAFeatures request to SIP/CSTA Gateway. * @param line_uri (%s) Ex.: tel:73124;phone-context=dialstring;partition=BE_BRS_INT */ #define SIP_SEND_CSTA_GET_CSTA_FEATURES \ ""\ ""\ ""\ ""\ ""\ "%s"\ ""\ ""\ ""\ "" /** * Sends CSTA start monitor request to SIP/CSTA Gateway. * @param line_uri (%s) Ex.: tel:73124;phone-context=dialstring;partition=BE_BRS_INT */ #define SIP_SEND_CSTA_MONITOR_START \ ""\ ""\ ""\ "%s"\ ""\ "" /** * Sends CSTA stop monitor request to SIP/CSTA Gateway. * @param monitor_cross_ref_id (%s) Ex.: 99fda87c */ #define SIP_SEND_CSTA_MONITOR_STOP \ ""\ ""\ "%s"\ "" /** * Sends CSTA make call request to SIP/CSTA Gateway. * @param line_uri (%s) Ex.: tel:73124;phone-context=dialstring;partition=BE_BRS_INT * @param to_tel_uri (%s) Ex.: tel:+3222220220 */ #define SIP_SEND_CSTA_MAKE_CALL \ ""\ ""\ "%s"\ "%s"\ "doNotPrompt"\ "" /** * Sends CSTA ClearConnection request to SIP/CSTA Gateway. * @param call_id (%s) Ex.: 0_99f261b4 * @param device_id (%s) Same as in OriginatedEvent, DeliveredEvent notifications. * Ex.: tel:73124;phone-context=dialstring */ #define SIP_SEND_CSTA_CLEAR_CONNECTION \ ""\ ""\ ""\ "%s"\ "%s"\ ""\ "" static gchar * sip_to_tel_uri0(const gchar *phone) { if (!phone || strlen(phone) == 0) return NULL; if (g_str_has_prefix(phone, "tel:")) { return g_strdup(phone); } else { gchar *tel_uri = g_malloc(strlen(phone) + 4 + 1); gchar *dest_p = g_stpcpy(tel_uri, "tel:"); for (; *phone; phone++) { if (*phone == ' ') continue; if (*phone == '(') continue; if (*phone == ')') continue; if (*phone == '-') continue; if (*phone == '.') continue; *dest_p++ = *phone; } *dest_p = '\0'; return tel_uri; } } gchar * sip_to_tel_uri(const gchar *phone) { gchar *res = sip_to_tel_uri0(phone); gchar *v; /* strips everything starting with 'v:' if any */ if (res && (v = strstr(res, "v:"))) { gchar *tmp = res; res = g_strndup(res, v - res); g_free(tmp); return res; } return res; } gchar * sip_tel_uri_denormalize(const gchar *tel_uri) { if (!tel_uri) return NULL; if (g_str_has_prefix(tel_uri, "tel:")) { return g_strdup(tel_uri + 4); } else { return g_strdup(tel_uri); } } static void sip_csta_initialize(struct sipe_core_private *sipe_private, const gchar *line_uri, const gchar *server) { if(!sipe_private->csta) { sipe_private->csta = g_new0(struct sip_csta, 1); sipe_private->csta->line_uri = g_strdup(line_uri); sipe_private->csta->gateway_uri = g_strdup(server); } else { SIPE_DEBUG_INFO_NOFORMAT("sip_csta_initialize: sipe_private->csta is already instantiated, exiting."); } } /** get CSTA feautures's callback */ static gboolean process_csta_get_features_response(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { if (msg->response >= 400) { SIPE_DEBUG_INFO_NOFORMAT("process_csta_get_features_response: Get CSTA features response is not 200. Failed to get features."); /* @TODO notify user of failure to get CSTA features */ return FALSE; } else if (msg->response == 200) { SIPE_DEBUG_INFO("process_csta_get_features_response:\n%s", msg->body ? msg->body : ""); } return TRUE; } /** get CSTA feautures */ static void sip_csta_get_features(struct sipe_core_private *sipe_private) { gchar *hdr; gchar *body; if (!sipe_private->csta || !sipe_private->csta->dialog || !sipe_private->csta->dialog->is_established) { SIPE_DEBUG_INFO_NOFORMAT("sip_csta_get_features: no dialog with CSTA, exiting."); return; } hdr = g_strdup( "Content-Disposition: signal;handling=required\r\n" "Content-Type: application/csta+xml\r\n"); body = g_strdup_printf( SIP_SEND_CSTA_GET_CSTA_FEATURES, sipe_private->csta->line_uri); sip_transport_info(sipe_private, hdr, body, sipe_private->csta->dialog, process_csta_get_features_response); g_free(body); g_free(hdr); } /** Monitor Start's callback */ static gboolean process_csta_monitor_start_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { SIPE_DEBUG_INFO("process_csta_monitor_start_response:\n%s", msg->body ? msg->body : ""); if (!sipe_private->csta) { SIPE_DEBUG_INFO_NOFORMAT("process_csta_monitor_start_response: sipe_private->csta is not initializzed, exiting"); return FALSE; } if (msg->response >= 400) { SIPE_DEBUG_INFO_NOFORMAT("process_csta_monitor_start_response: Monitor Start response is not 200. Failed to start monitor."); /* @TODO notify user of failure to start monitor */ return FALSE; } else if (msg->response == 200) { sipe_xml *xml = sipe_xml_parse(msg->body, msg->bodylen); g_free(sipe_private->csta->monitor_cross_ref_id); sipe_private->csta->monitor_cross_ref_id = sipe_xml_data(sipe_xml_child(xml, "monitorCrossRefID")); SIPE_DEBUG_INFO("process_csta_monitor_start_response: monitor_cross_ref_id=%s", sipe_private->csta->monitor_cross_ref_id ? sipe_private->csta->monitor_cross_ref_id : ""); sipe_xml_free(xml); } return TRUE; } /** Monitor Start */ static void sip_csta_monitor_start(struct sipe_core_private *sipe_private) { gchar *hdr; gchar *body; if (!sipe_private->csta || !sipe_private->csta->dialog || !sipe_private->csta->dialog->is_established) { SIPE_DEBUG_INFO_NOFORMAT("sip_csta_monitor_start: no dialog with CSTA, exiting."); return; } hdr = g_strdup( "Content-Disposition: signal;handling=required\r\n" "Content-Type: application/csta+xml\r\n"); body = g_strdup_printf( SIP_SEND_CSTA_MONITOR_START, sipe_private->csta->line_uri); sip_transport_info(sipe_private, hdr, body, sipe_private->csta->dialog, process_csta_monitor_start_response); g_free(body); g_free(hdr); } /** Monitor Stop */ static void sip_csta_monitor_stop(struct sipe_core_private *sipe_private) { gchar *hdr; gchar *body; if (!sipe_private->csta || !sipe_private->csta->dialog || !sipe_private->csta->dialog->is_established) { SIPE_DEBUG_INFO_NOFORMAT("sip_csta_monitor_stop: no dialog with CSTA, exiting."); return; } if (!sipe_private->csta->monitor_cross_ref_id) { SIPE_DEBUG_INFO_NOFORMAT("sip_csta_monitor_stop: no monitor_cross_ref_id, exiting."); return; } hdr = g_strdup( "Content-Disposition: signal;handling=required\r\n" "Content-Type: application/csta+xml\r\n"); body = g_strdup_printf( SIP_SEND_CSTA_MONITOR_STOP, sipe_private->csta->monitor_cross_ref_id); sip_transport_info(sipe_private, hdr, body, sipe_private->csta->dialog, NULL); g_free(body); g_free(hdr); } static void sipe_invite_csta_gateway(struct sipe_core_private *sipe_private, gpointer unused); /** a callback */ static gboolean process_invite_csta_gateway_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { SIPE_DEBUG_INFO("process_invite_csta_gateway_response:\n%s", msg->body ? msg->body : ""); if (!sipe_private->csta) { SIPE_DEBUG_INFO_NOFORMAT("process_invite_csta_gateway_response: sipe_private->csta is not initializzed, exiting"); return FALSE; } if (!sipe_private->csta->dialog) { SIPE_DEBUG_INFO_NOFORMAT("process_invite_csta_gateway_response: GSTA dialog is NULL, exiting"); return FALSE; } sipe_dialog_parse(sipe_private->csta->dialog, msg, TRUE); if (msg->response >= 200) { /* send ACK to CSTA */ sipe_private->csta->dialog->cseq = 0; sip_transport_ack(sipe_private, sipe_private->csta->dialog); sipe_private->csta->dialog->outgoing_invite = NULL; sipe_private->csta->dialog->is_established = TRUE; } if (msg->response >= 400) { SIPE_DEBUG_INFO_NOFORMAT("process_invite_csta_gateway_response: INVITE response is not 200. Failed to join CSTA."); /* @TODO notify user of failure to join CSTA */ return FALSE; } else if (msg->response == 200) { sipe_xml *xml = sipe_xml_parse(msg->body, msg->bodylen); g_free(sipe_private->csta->gateway_status); sipe_private->csta->gateway_status = sipe_xml_data(sipe_xml_child(xml, "systemStatus")); SIPE_DEBUG_INFO("process_invite_csta_gateway_response: gateway_status=%s", sipe_private->csta->gateway_status ? sipe_private->csta->gateway_status : ""); if (sipe_strcase_equal(sipe_private->csta->gateway_status, "normal")) { if (!sipe_private->csta->monitor_cross_ref_id) { sip_csta_get_features(sipe_private); sip_csta_monitor_start(sipe_private); } } else { SIPE_DEBUG_INFO("process_invite_csta_gateway_response: ERROR: CSTA status is %s, won't continue.", sipe_private->csta->gateway_status); /* @TODO notify user of failure to join CSTA */ } sipe_xml_free(xml); /* schedule re-invite. RFC4028 */ if (sipe_private->csta->dialog->expires) { sipe_schedule_seconds(sipe_private, "<+csta>", NULL, sipe_private->csta->dialog->expires - 60, /* 1 minute earlier */ sipe_invite_csta_gateway, NULL); } } return TRUE; } /** Creates long living dialog with SIP/CSTA Gateway */ /* should be re-entrant as require to sent re-invites every 10 min to refresh */ static void sipe_invite_csta_gateway(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER gpointer unused) { gchar *hdr; gchar *contact; gchar *body; if (!sipe_private->csta) { SIPE_DEBUG_INFO_NOFORMAT("sipe_invite_csta_gateway: sipe_private->csta is uninitialized, exiting"); return; } if(!sipe_private->csta->dialog) { sipe_private->csta->dialog = g_new0(struct sip_dialog, 1); sipe_private->csta->dialog->callid = gencallid(); sipe_private->csta->dialog->with = g_strdup(sipe_private->csta->gateway_uri); } if (!(sipe_private->csta->dialog->ourtag)) { sipe_private->csta->dialog->ourtag = gentag(); } contact = get_contact(sipe_private); hdr = g_strdup_printf( "Contact: %s\r\n" "Supported: timer\r\n" "Content-Disposition: signal;handling=required\r\n" "Content-Type: application/csta+xml\r\n", contact); g_free(contact); body = g_strdup_printf( SIP_SEND_CSTA_REQUEST_SYSTEM_STATUS, sipe_private->csta->line_uri); sipe_private->csta->dialog->outgoing_invite = sip_transport_invite(sipe_private, hdr, body, sipe_private->csta->dialog, process_invite_csta_gateway_response); g_free(body); g_free(hdr); } void sip_csta_open(struct sipe_core_private *sipe_private, const gchar *line_uri, const gchar *server) { sip_csta_initialize(sipe_private, line_uri, server); sipe_invite_csta_gateway(sipe_private, NULL); } static void sip_csta_free(struct sip_csta *csta) { if (!csta) return; g_free(csta->line_uri); g_free(csta->gateway_uri); sipe_dialog_free(csta->dialog); g_free(csta->gateway_status); g_free(csta->monitor_cross_ref_id); g_free(csta->line_status); g_free(csta->to_tel_uri); g_free(csta->call_id); g_free(csta->device_id); g_free(csta); } void sip_csta_close(struct sipe_core_private *sipe_private) { if (sipe_private->csta) { sip_csta_monitor_stop(sipe_private); } if (sipe_private->csta && sipe_private->csta->dialog) { /* send BYE to CSTA */ sip_transport_bye(sipe_private, sipe_private->csta->dialog); } sip_csta_free(sipe_private->csta); } /** Make Call's callback */ static gboolean process_csta_make_call_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { SIPE_DEBUG_INFO("process_csta_make_call_response:\n%s", msg->body ? msg->body : ""); if (!sipe_private->csta) { SIPE_DEBUG_INFO_NOFORMAT("process_csta_make_call_response: sipe_private->csta is not initializzed, exiting"); return FALSE; } if (msg->response >= 400) { SIPE_DEBUG_INFO_NOFORMAT("process_csta_make_call_response: Make Call response is not 200. Failed to make call."); /* @TODO notify user of failure to make call */ return FALSE; } else if (msg->response == 200) { sipe_xml *xml; const sipe_xml *xn_calling_device; gchar *device_id; SIPE_DEBUG_INFO_NOFORMAT("process_csta_make_call_response: SUCCESS"); xml = sipe_xml_parse(msg->body, msg->bodylen); xn_calling_device = sipe_xml_child(xml, "callingDevice"); device_id = sipe_xml_data(sipe_xml_child(xn_calling_device, "deviceID")); if (sipe_strequal(sipe_private->csta->line_uri, device_id)) { g_free(sipe_private->csta->call_id); sipe_private->csta->call_id = sipe_xml_data(sipe_xml_child(xn_calling_device, "callID")); SIPE_DEBUG_INFO("process_csta_make_call_response: call_id=%s", sipe_private->csta->call_id ? sipe_private->csta->call_id : ""); } g_free(device_id); sipe_xml_free(xml); } return TRUE; } /** Make Call */ static void sip_csta_make_call(struct sipe_core_private *sipe_private, const gchar* to_tel_uri) { gchar *hdr; gchar *body; if (!to_tel_uri) { SIPE_DEBUG_INFO_NOFORMAT("sip_csta_make_call: no tel URI parameter provided, exiting."); return; } if (!sipe_private->csta || !sipe_private->csta->dialog || !sipe_private->csta->dialog->is_established) { SIPE_DEBUG_INFO_NOFORMAT("sip_csta_make_call: no dialog with CSTA, exiting."); return; } g_free(sipe_private->csta->to_tel_uri); sipe_private->csta->to_tel_uri = g_strdup(to_tel_uri); hdr = g_strdup( "Content-Disposition: signal;handling=required\r\n" "Content-Type: application/csta+xml\r\n"); body = g_strdup_printf( SIP_SEND_CSTA_MAKE_CALL, sipe_private->csta->line_uri, sipe_private->csta->to_tel_uri); sip_transport_info(sipe_private, hdr, body, sipe_private->csta->dialog, process_csta_make_call_response); g_free(body); g_free(hdr); } static void sip_csta_update_id_and_status(struct sip_csta *csta, const sipe_xml *node, const char *status) { gchar *call_id = sipe_xml_data(sipe_xml_child(node, "callID")); if (!sipe_strequal(call_id, csta->call_id)) { SIPE_DEBUG_INFO("sipe_csta_update_id_and_status: callID (%s) does not match", call_id); } else { /* free old line status */ g_free(csta->line_status); csta->line_status = NULL; if (status) { /* save deviceID */ gchar *device_id = sipe_xml_data(sipe_xml_child(node, "deviceID")); SIPE_DEBUG_INFO("sipe_csta_update_id_and_status: device_id=(%s)", device_id ? device_id : ""); if (device_id) { g_free(csta->device_id); csta->device_id = device_id; } /* set new line status */ csta->line_status = g_strdup(status); } else { /* clean up cleared connection */ g_free(csta->to_tel_uri); csta->to_tel_uri = NULL; g_free(csta->call_id); csta->call_id = NULL; g_free(csta->device_id); csta->device_id = NULL; } } g_free(call_id); } void process_incoming_info_csta(struct sipe_core_private *sipe_private, struct sipmsg *msg) { gchar *monitor_cross_ref_id; sipe_xml *xml = sipe_xml_parse(msg->body, msg->bodylen); if (!xml) return; monitor_cross_ref_id = sipe_xml_data(sipe_xml_child(xml, "monitorCrossRefID")); if(!sipe_private->csta || !sipe_strequal(monitor_cross_ref_id, sipe_private->csta->monitor_cross_ref_id)) { SIPE_DEBUG_INFO("process_incoming_info_csta: monitorCrossRefID (%s) does not match, exiting", monitor_cross_ref_id ? monitor_cross_ref_id : ""); } else { if (sipe_strequal(sipe_xml_name(xml), "OriginatedEvent")) { sip_csta_update_id_and_status(sipe_private->csta, sipe_xml_child(xml, "originatedConnection"), ORIGINATED_CSTA_STATUS); } else if (sipe_strequal(sipe_xml_name(xml), "DeliveredEvent")) { sip_csta_update_id_and_status(sipe_private->csta, sipe_xml_child(xml, "connection"), DELIVERED_CSTA_STATUS); } else if (sipe_strequal(sipe_xml_name(xml), "EstablishedEvent")) { sip_csta_update_id_and_status(sipe_private->csta, sipe_xml_child(xml, "establishedConnection"), ESTABLISHED_CSTA_STATUS); } else if (sipe_strequal(sipe_xml_name(xml), "ConnectionClearedEvent")) { sip_csta_update_id_and_status(sipe_private->csta, sipe_xml_child(xml, "droppedConnection"), NULL); } } g_free(monitor_cross_ref_id); sipe_xml_free(xml); } gboolean sip_csta_is_idle(struct sipe_core_private *sipe_private) { return(sipe_private->csta && !sipe_private->csta->line_status); } void sipe_core_buddy_make_call(struct sipe_core_public *sipe_public, const gchar *phone) { if (phone) { gchar *tel_uri = sip_to_tel_uri(phone); SIPE_DEBUG_INFO("sipe_core_buddy_make_call: calling number: %s", tel_uri ? tel_uri : ""); sip_csta_make_call(SIPE_CORE_PRIVATE, tel_uri); g_free(tel_uri); } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-csta.h ================================================ /** * @file sip-csta.h * * pidgin-sipe * * Copyright (C) 2011 SIPE Project * Copyright (C) 2009 pier11 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Interface dependencies: * * */ /* Forward declarations */ struct sipmsg; struct sipe_core_private; /** * Transform telephone number representation to tel: URI form. * Removes white space, parenthesis ( ), hyphen - symbols * * @param phone Ex. +32 2 245 00 00 * @return Ex. tel:+3222450000 or @c NULL. Must be @c g_free()'d after use. */ gchar *sip_to_tel_uri(const gchar *phone); /** * Transform telephone number from tel: URI representation * to more human readable form. * Removes tel: prefix if such exist. * (Maybe will add spaces in the future according to local phone patterns.) * * @param tel_uri Ex. tel:+3222450000 * @return Ex. +3222450000. Must be @c g_free()'d after use. */ gchar *sip_tel_uri_denormalize(const gchar *tel_uri); /** * Initializes CSTA * * @param line_uri (in) our line tel URI. Ex.: tel:73124;phone-context=dialstring;partition=BE_BRS_INT * @param server (in) SIP URI of SIP/CSTA Gateway. Ex.: sip:73124@euuklhccups01.eu.company.local */ void sip_csta_open(struct sipe_core_private *sipe_private, const gchar *line_uri, const gchar *server); /** * Closes CSTA */ void sip_csta_close(struct sipe_core_private *sipe_private); /** * Processes incoming CSTA commands */ void process_incoming_info_csta(struct sipe_core_private *sipe_private, struct sipmsg *msg); /** * Is CSTA in idle state? */ gboolean sip_csta_is_idle(struct sipe_core_private *sipe_private); ================================================ FILE: src/core/sip-sec-basic.c ================================================ /** * @file sip-sec-basic.c * * pidgin-sipe * * Copyright (C) 2013-2015 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Implementation for HTTP "WWW-Authenticate: Basic" scheme (RFC1945). */ #include #include #include "sipe-common.h" #include "sip-sec.h" #include "sip-sec-mech.h" #include "sip-sec-basic.h" #include "sipe-backend.h" /* Security context for Basic */ typedef struct _context_basic { struct sip_sec_context common; gchar *token; guint length; } *context_basic; /* sip-sec-mech.h API implementation for Basic */ static gboolean sip_sec_acquire_cred__basic(SipSecContext context, const gchar *username, const gchar *password) { context_basic ctx = (context_basic) context; SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__basic: entering"); if (!username || !password) return(FALSE); /* calculate Basic token (RFC1945 section 11.1) */ ctx->token = g_strdup_printf("%s:%s", username, password); ctx->length = strlen(ctx->token); return(TRUE); } static gboolean sip_sec_init_sec_context__basic(SipSecContext context, SIPE_UNUSED_PARAMETER SipSecBuffer in_buff, SipSecBuffer *out_buff, SIPE_UNUSED_PARAMETER const gchar *service_name) { context_basic ctx = (context_basic) context; out_buff->length = ctx->length; out_buff->value = (guint8 *) g_strdup(ctx->token); return(TRUE); } static gboolean sip_sec_make_signature__basic(SIPE_UNUSED_PARAMETER SipSecContext context, SIPE_UNUSED_PARAMETER const gchar *message, SIPE_UNUSED_PARAMETER SipSecBuffer *signature) { /* No implementation needed, as Basic is not used for SIP */ return(FALSE); } static gboolean sip_sec_verify_signature__basic(SIPE_UNUSED_PARAMETER SipSecContext context, SIPE_UNUSED_PARAMETER const gchar *message, SIPE_UNUSED_PARAMETER SipSecBuffer signature) { /* No implementation needed, as Basic is not used for SIP */ return(FALSE); } static void sip_sec_destroy_sec_context__basic(SipSecContext context) { context_basic ctx = (context_basic) context; g_free(ctx->token); g_free(ctx); } static const gchar * sip_sec_context_name__basic(SIPE_UNUSED_PARAMETER SipSecContext context) { return("Basic"); } SipSecContext sip_sec_create_context__basic(SIPE_UNUSED_PARAMETER guint type) { context_basic context = g_malloc0(sizeof(struct _context_basic)); if (!context) return(NULL); context->common.acquire_cred_func = sip_sec_acquire_cred__basic; context->common.init_context_func = sip_sec_init_sec_context__basic; context->common.destroy_context_func = sip_sec_destroy_sec_context__basic; context->common.make_signature_func = sip_sec_make_signature__basic; context->common.verify_signature_func = sip_sec_verify_signature__basic; context->common.context_name_func = sip_sec_context_name__basic; return((SipSecContext) context); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec-basic.h ================================================ /** * @file sip-sec-basic.h * * pidgin-sipe * * Copyright (C) 2013 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ SipSecContext sip_sec_create_context__basic(guint type); ================================================ FILE: src/core/sip-sec-digest-tests.c ================================================ /** * @file sip-sec-digest-test.c * * pidgin-sipe * * Copyright (C) 2013-2019 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "sip-transport.h" #include "sipe-common.h" #include "sipe-crypt.h" #include "uuid.h" #define SIP_SEC_DIGEST_COMPILING_TEST static const gchar *cnonce_fixed; #include "sip-sec-digest.c" /* * Stubs */ gboolean sipe_backend_debug_enabled(void) { return(TRUE); } void sipe_backend_debug_literal(sipe_debug_level level, const gchar *msg) { printf("DEBUG(%d): %s\n", level, msg); } void sipe_backend_debug(sipe_debug_level level, const gchar *format, ...) { va_list ap; gchar *newformat = g_strdup_printf("DEBUG(%d): %s\n", level, format); va_start(ap, format); vprintf(newformat, ap); va_end(ap); g_free(newformat); } const gchar *sip_transport_epid(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private) { return(NULL); } const gchar *sip_transport_ip_address(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private) { return(NULL); } char *generateUUIDfromEPID(SIPE_UNUSED_PARAMETER const gchar *epid) { return(NULL); } char *sipe_get_epid(SIPE_UNUSED_PARAMETER const char *self_sip_uri, SIPE_UNUSED_PARAMETER const char *hostname, SIPE_UNUSED_PARAMETER const char *ip_address) { return(NULL); } /* needed when linking against NSS */ void md4sum(const uint8_t *data, uint32_t length, uint8_t *digest); void md4sum(SIPE_UNUSED_PARAMETER const uint8_t *data, SIPE_UNUSED_PARAMETER uint32_t length, SIPE_UNUSED_PARAMETER uint8_t *digest) { } /* * Tester code */ #define PARSED_USERNAME 0 #define PARSED_REALM 1 #define PARSED_NONCE 2 #define PARSED_URI 3 #define PARSED_QOP 4 #define PARSED_NC 5 #define PARSED_CNONCE 6 #define PARSED_RESPONSE 7 #define PARSED_OPAQUE 8 #define PARSED_MAX 9 static void parse(const gchar *string, gchar *parsed[PARSED_MAX]) { const gchar *header; const gchar *param; guint i; for (i = 0; i < PARSED_MAX; i++) parsed[i] = NULL; if (strstr(string, "Digest ") == NULL) return; header = string + 7; /* skip white space */ while (*header == ' ') header++; /* start of next parameter value */ while ((param = strchr(header, '=')) != NULL) { const gchar *end; /* parameter value type */ param++; if (*param == '"') { /* string: xyz="..."(,) */ end = strchr(++param, '"'); if (!end) { SIPE_DEBUG_ERROR("parse: corrupted string parameter near '%s'", header); break; } } else { /* number: xyz=12345(,) */ end = strchr(param, ','); if (!end) { /* last parameter */ end = param + strlen(param); } } #define COMPARE(string, index) \ if (g_str_has_prefix(header, #string "=")) { \ g_free(parsed[ PARSED_ ## index]); \ parsed[ PARSED_ ## index] = g_strndup(param, end - param); \ } else COMPARE(username, USERNAME) COMPARE(realm, REALM) COMPARE(nonce, NONCE) COMPARE(uri, URI) COMPARE(qop, QOP) COMPARE(nc, NC) COMPARE(cnonce, CNONCE) COMPARE(response, RESPONSE) COMPARE(opaque, OPAQUE) { /* ignore */ } /* skip to next parameter */ while ((*end == '"') || (*end == ',') || (*end == ' ')) end++; header = end; } } static guint expected(const gchar *reference, const gchar *testvalue) { gchar *reference_parsed[PARSED_MAX]; gchar *testvalue_parsed[PARSED_MAX]; guint i; guint failed = 0; parse(reference, reference_parsed); parse(testvalue, testvalue_parsed); for (i = 0; i < PARSED_MAX; i++) { gchar *ref = reference_parsed[i]; gchar *test = testvalue_parsed[i]; if (!sipe_strequal(ref, test) && (ref || test)) { SIPE_DEBUG_ERROR("FAILED(%d): expected '%s' got '%s'", i, ref, test); failed = 1; } g_free(test); g_free(ref); } SIPE_DEBUG_INFO("Response: %s", testvalue); return(failed); } int main(SIPE_UNUSED_PARAMETER int argc, SIPE_UNUSED_PARAMETER char *argv[]) { guint failed = 0; /* Initialization for crypto backend (test mode) */ sipe_crypto_init(FALSE); #define RUNTEST(_user, _password, _cnonce, _header, _method, _uri, _reference) \ { \ struct sipe_core_private sipe_private; \ gchar *response; \ printf("\n"); \ sipe_private.authuser = _user ; \ sipe_private.password = _password ; \ cnonce_fixed = _cnonce; \ response = sip_sec_digest_authorization(&sipe_private, _header, _method, _uri); \ failed += expected(_reference, response); \ g_free(response); \ } /* * RFC-2617 Section 3.5 */ RUNTEST("Mufasa", "Circle Of Life", "0a4f113b", "realm=\"testrealm@host.com\", qop=\"auth,auth-int\", nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"", "GET", "/dir/index.html", "Digest username=\"Mufasa\", realm=\"testrealm@host.com\", nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", uri=\"/dir/index.html\", qop=auth, nc=00000001, cnonce=\"0a4f113b\", response=\"6629fae49393a05397450978507c4ef1\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""); /* * http://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Authentication.html */ RUNTEST("bob", "bob", "1672b410efa182c061c2f0a58acaa17d", /* * The Server challenge shown does not correspond to the * Client response. Use realm/nonce from the Client response. * * "realm=\"Members only\", nonce=\"LHOKe1l2BAA=5c373ae0d933a0bb6321125a56a2fcdb6fd7c93b\", algorithm=MD5, qop=\"auth\"", */ "realm=\"members only\", nonce=\"5UImQA==3d76b2ab859e1770ec60ed285ec68a3e63028461\", algorithm=MD5, qop=\"auth\"", "GET", "/digest_auth/test.html", "Digest username=\"bob\", realm=\"members only\", qop=\"auth\", algorithm=\"MD5\", uri=\"/digest_auth/test.html\", nonce=\"5UImQA==3d76b2ab859e1770ec60ed285ec68a3e63028461\", nc=00000001, cnonce=\"1672b410efa182c061c2f0a58acaa17d\", response=\"3d9ebe6b9534a7135a3fde59a5a72668\""); return(failed); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec-digest.c ================================================ /** * @file sip-sec-digest.c * * pidgin-sipe * * Copyright (C) 2013 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "sip-sec-digest.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-digest.h" #include "sipe-utils.h" /* * Calculate a response for HTTP MD5 Digest authentication (RFC 2617) */ static gchar *digest_HA1(const gchar *user, const gchar *realm, const gchar *password) { /* H(A1): H(user ":" realm ":" password) */ gchar *string = g_strdup_printf("%s:%s:%s", user, realm, password); gchar *HA1; guchar digest[SIPE_DIGEST_MD5_LENGTH]; sipe_digest_md5((guchar *)string, strlen(string), digest); g_free(string); /* Result: LOWER(HEXSTRING(H(A1))) */ string = buff_to_hex_str(digest, sizeof(digest)); HA1 = g_ascii_strdown(string, -1); g_free(string); return(HA1); } static gchar *digest_HA2(const gchar *method, const gchar *target) { /* H(A2): H(method ":" target) */ gchar *string = g_strdup_printf("%s:%s", method, target); gchar *HA2; guchar digest[SIPE_DIGEST_MD5_LENGTH]; sipe_digest_md5((guchar *)string, strlen(string), digest); g_free(string); /* Result: LOWER(HEXSTRING(H(A1))) */ string = buff_to_hex_str(digest, sizeof(digest)); HA2 = g_ascii_strdown(string, -1); g_free(string); return(HA2); } static gchar *generate_cnonce(void) { #ifdef SIP_SEC_DIGEST_COMPILING_TEST return(g_strdup(cnonce_fixed)); #else return(g_strdup_printf("%04x%04x", rand() & 0xFFFF, rand() & 0xFFFF)); #endif } static gchar *digest_response(const gchar *user, const gchar *realm, const gchar *password, const gchar *nonce, const gchar *nc, const gchar *cnonce, const gchar *qop, const gchar *method, const gchar *target) { gchar *HA1 = digest_HA1(user, realm, password); gchar *HA2 = digest_HA2(method, target); gchar *string, *Digest; guchar digest[SIPE_DIGEST_MD5_LENGTH]; #ifdef SIP_SEC_DIGEST_COMPILING_TEST SIPE_DEBUG_INFO("HA1 %s", HA1); SIPE_DEBUG_INFO("HA2 %s", HA2); #endif /* Digest: H(H(A1) ":" nonce ":" nc ":" cnonce ":" qop ":" H(A2) */ string = g_strdup_printf("%s:%s:%s:%s:%s:%s", HA1, nonce, nc, cnonce, qop, HA2); g_free(HA2); g_free(HA1); sipe_digest_md5((guchar *)string, strlen(string), digest); g_free(string); /* Result: LOWER(HEXSTRING(H(A1))) */ string = buff_to_hex_str(digest, sizeof(digest)); Digest = g_ascii_strdown(string, -1); g_free(string); return(Digest); } gchar *sip_sec_digest_authorization(struct sipe_core_private *sipe_private, const gchar *header, const gchar *method, const gchar *target) { const gchar *param; gchar *nonce = NULL; gchar *opaque = NULL; gchar *realm = NULL; gchar *authorization = NULL; /* sanity checks */ if (!sipe_private->password) return(NULL); /* skip white space */ while (*header == ' ') header++; /* start of next parameter value */ while ((param = strchr(header, '=')) != NULL) { const gchar *end; /* parameter value type */ param++; if (*param == '"') { /* string: xyz="..."(,) */ end = strchr(++param, '"'); if (!end) { SIPE_DEBUG_ERROR("sip_sec_digest_authorization: corrupted string parameter near '%s'", header); break; } } else { /* number: xyz=12345(,) */ end = strchr(param, ','); if (!end) { /* last parameter */ end = param + strlen(param); } } /* parameter type */ if (g_str_has_prefix(header, "nonce=\"")) { g_free(nonce); nonce = g_strndup(param, end - param); } else if (g_str_has_prefix(header, "opaque=\"")) { g_free(opaque); opaque = g_strndup(param, end - param); } else if (g_str_has_prefix(header, "realm=\"")) { g_free(realm); realm = g_strndup(param, end - param); } /* skip to next parameter */ while ((*end == '"') || (*end == ',') || (*end == ' ')) end++; header = end; } if (nonce && realm) { const gchar *authuser = sipe_private->authuser ? sipe_private->authuser : sipe_private->username; const gchar *nc = "00000001"; gchar *cnonce = generate_cnonce(); gchar *opt_opaque = opaque ? g_strdup_printf("opaque=\"%s\", ", opaque) : g_strdup(""); gchar *response = digest_response(authuser, realm, sipe_private->password, nonce, nc, cnonce, "auth", method, target); #ifdef SIP_SEC_DIGEST_COMPILING_TEST SIPE_DEBUG_INFO("RES %s", response); #endif authorization = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", qop=auth, nc=%s, cnonce=\"%s\", %sresponse=\"%s\"", authuser, realm, nonce, target, nc, cnonce, opt_opaque, response); g_free(response); g_free(opt_opaque); g_free(cnonce); } else SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_digest_authorization: no digest parameters found. Giving up."); g_free(realm); g_free(opaque); g_free(nonce); return(authorization); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec-digest.h ================================================ /** * @file sip-sec-digest.h * * pidgin-sipe * * Copyright (C) 2013 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_core_private; /** * Generate Digest authorization header * * @param sipe_private SIPE core private data * @param header Digest authentication header contents * * @return Digest authorization header or @c NULL. Must be @c g_free'd(). */ gchar *sip_sec_digest_authorization(struct sipe_core_private *sipe_private, const gchar *header, const gchar *method, const gchar *target); ================================================ FILE: src/core/sip-sec-gssapi.c ================================================ /** * @file sip-sec-gssapi.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * This module implements sip-sec authentication API using GSSAPI. * * It can be compiled in two different modes: * * - Kerberos-only: NTLM & SPNEGO are using SIPE internal implementation * [HAVE_GSSAPI_ONLY is not defined] * * - pure GSSAPI: this modules handles Kerberos, NTLM & SPNEGO * [HAVE_GSSAPI_ONLY is defined] */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef HAVE_GSSAPI_PASSWORD_SUPPORT #include #endif #include #ifdef HAVE_GSSAPI_ONLY #include #endif #include "sipe-common.h" #include "sip-sec.h" #include "sip-sec-mech.h" #include "sip-sec-gssapi.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-utils.h" /* Security context for Kerberos */ typedef struct _context_gssapi { struct sip_sec_context common; gss_cred_id_t cred_gssapi; gss_ctx_id_t ctx_gssapi; gss_name_t target_name; } *context_gssapi; #ifdef HAVE_GSSAPI_ONLY static const gss_OID_desc gss_mech_ntlmssp = { GSS_NTLMSSP_OID_LENGTH, (char *) GSS_NTLMSSP_OID_STRING /* read-only in reality */ }; static const gss_OID_desc gss_mech_spnego = { 6, (char *) "\x2b\x06\x01\x05\x05\x02" /* read-only in reality */ }; /* * The SPNEGO implementation on older Microsoft IIS servers sends a * non-conformant final empty token that is not accepted by the SPNEGO * implementation in older MIT KRB5 releases: * * Base64-encoded DER: oRgwFqADCgEAoQsGCSqGSIb3EgECAqICBAA= * * Decoded ASN.1: * 0:d=0 hl=2 l= 24 cons: cont [ 1 ] * 2:d=1 hl=2 l= 22 cons: SEQUENCE * 4:d=2 hl=2 l= 3 cons: cont [ 0 ] * 6:d=3 hl=2 l= 1 prim: ENUMERATED :00 * 9:d=2 hl=2 l= 11 cons: cont [ 1 ] * 11:d=3 hl=2 l= 9 prim: OBJECT :1.2.840.113554.1.2.2 * 22:d=2 hl=2 l= 2 cons: cont [ 2 ] | this empty element is not * 24:d=3 hl=2 l= 0 prim: OCTET STRING | correct according to spec * * We can circumvent this problem by setting GSS_C_MUTUAL_FLAG which causes * the server to send a non-empty final token. We set the following flag to * TRUE after the first time gss_init_sec_context() returns with a * "defective token" error. */ static gboolean spnego_mutual_flag = FALSE; #endif #define SIP_SEC_FLAG_GSSAPI_SIP_NTLM 0x00010000 #define SIP_SEC_FLAG_GSSAPI_NEGOTIATE_FALLBACK 0x00020000 static void sip_sec_gssapi_print_gss_error0(const char *func, OM_uint32 status, int type) { OM_uint32 minor; OM_uint32 message_context = 0; gss_buffer_desc status_string; do { gss_display_status(&minor, status, type, GSS_C_NO_OID, &message_context, &status_string); SIPE_DEBUG_ERROR("sip_sec_gssapi: GSSAPI error in %s (%s): %s", func, (type == GSS_C_GSS_CODE ? "GSS" : "Mech"), (gchar *) status_string.value); gss_release_buffer(&minor, &status_string); } while (message_context != 0); } /* Prints out errors of GSSAPI function invocation */ static void sip_sec_gssapi_print_gss_error(const char *func, OM_uint32 ret, OM_uint32 minor) { sip_sec_gssapi_print_gss_error0(func, ret, GSS_C_GSS_CODE); sip_sec_gssapi_print_gss_error0(func, minor, GSS_C_MECH_CODE); } #if defined(HAVE_GSSAPI_PASSWORD_SUPPORT) || defined(HAVE_GSSAPI_ONLY) /* NOTE: releases "set" on error */ static gboolean add_mech(gss_OID_set set, gss_OID mech, const gchar *name) { OM_uint32 ret; OM_uint32 minor; ret = gss_add_oid_set_member(&minor, mech, &set); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_add_oid_set_member", ret, minor); SIPE_DEBUG_ERROR("add_mech: can't add %s to mech set (ret=%u)", name, ret); gss_release_oid_set(&minor, &set); return(FALSE); } SIPE_DEBUG_INFO("add_mech: added %s to mech set", name); return(TRUE); } static gss_OID_set create_mechs_set(guint type) { OM_uint32 ret; OM_uint32 minor; gss_OID_set set = GSS_C_NO_OID_SET; gss_OID mech_oid; const gchar *name; ret = gss_create_empty_oid_set(&minor, &set); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_create_empty_oid_set", ret, minor); SIPE_DEBUG_ERROR("create_mechs_set: can't create mech set (ret=%u)", ret); return(GSS_C_NO_OID_SET); } #ifdef HAVE_GSSAPI_ONLY switch (type) { case SIPE_AUTHENTICATION_TYPE_NTLM: mech_oid = (gss_OID) &gss_mech_ntlmssp; name = "NTLM"; break; case SIPE_AUTHENTICATION_TYPE_KERBEROS: #else (void) type; /* keep compiler happy */ #endif mech_oid = (gss_OID) gss_mech_krb5; name = "Kerberos"; #ifdef HAVE_GSSAPI_ONLY break; case SIPE_AUTHENTICATION_TYPE_NEGOTIATE: mech_oid = (gss_OID) &gss_mech_spnego; name = "SPNEGO"; break; default: SIPE_DEBUG_ERROR("create_mechs_set: invoked with invalid type %u", type); gss_release_oid_set(&minor, &set); return(GSS_C_NO_OID_SET); break; } #endif return(add_mech(set, mech_oid, name) ? set : GSS_C_NO_OID_SET); } #endif #ifdef HAVE_GSSAPI_ONLY static gss_OID_set create_neg_mechs_set(void) { OM_uint32 ret; OM_uint32 minor; gss_OID_set set = GSS_C_NO_OID_SET; ret = gss_create_empty_oid_set(&minor, &set); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_create_empty_oid_set", ret, minor); SIPE_DEBUG_ERROR("create_neg_mechs_set: can't create mech set (ret=%u)", ret); return(GSS_C_NO_OID_SET); } return((add_mech(set, (gss_OID) gss_mech_krb5, "Kerberos") && add_mech(set, (gss_OID) &gss_mech_ntlmssp, "NTLM")) ? set : GSS_C_NO_OID_SET); } static gboolean gssntlm_reset_mic_sequence(context_gssapi context) { OM_uint32 ret; OM_uint32 minor; gss_buffer_desc value; guint sequence = 100; static const gss_OID_desc set_sequence_num_oid = { GSS_NTLMSSP_SET_SEQ_NUM_OID_LENGTH, (char *) GSS_NTLMSSP_SET_SEQ_NUM_OID_STRING /* read-only in reality */ }; value.length = sizeof(sequence); value.value = &sequence; ret = gss_set_sec_context_option(&minor, &context->ctx_gssapi, (gss_OID_desc *) &set_sequence_num_oid, &value); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_set_sec_context_option", ret, minor); SIPE_DEBUG_ERROR("gssntlm_reset_mic_sequence: failed to reset MIC sequence number (ret=%u)", ret); return(FALSE); } return(TRUE); } #endif static void drop_gssapi_context(SipSecContext context) { context_gssapi ctx = (context_gssapi) context; OM_uint32 ret; OM_uint32 minor; ret = gss_delete_sec_context(&minor, &(ctx->ctx_gssapi), GSS_C_NO_BUFFER); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_delete_sec_context", ret, minor); SIPE_DEBUG_ERROR("drop_gssapi_context: failed to delete security context (ret=%u)", ret); } ctx->ctx_gssapi = GSS_C_NO_CONTEXT; context->flags &= ~SIP_SEC_FLAG_COMMON_READY; } /* sip-sec-mech.h API implementation for Kerberos/GSSAPI */ static gboolean sip_sec_acquire_cred__gssapi(SipSecContext context, const gchar *username, const gchar *password) { context_gssapi ctx = (context_gssapi) context; SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__gssapi: started"); /* this is the first time we are allowed to set private flags */ if (((context->flags & SIP_SEC_FLAG_COMMON_HTTP) == 0) && (context->type == SIPE_AUTHENTICATION_TYPE_NTLM)) context->flags |= SIP_SEC_FLAG_GSSAPI_SIP_NTLM; /* With SSO we use the default credentials */ if ((context->flags & SIP_SEC_FLAG_COMMON_SSO) == 0) { #ifdef HAVE_GSSAPI_PASSWORD_SUPPORT gchar *username_new = NULL; OM_uint32 ret; OM_uint32 minor, minor_ignore; gss_OID_set mechs_set; gss_cred_id_t credentials; gss_buffer_desc input_name_buffer; gss_name_t user_name; /* Without SSO we need user name and password */ if (is_empty(username) || is_empty(password)) { SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__gssapi: no valid authentication information provided"); return(FALSE); } mechs_set = create_mechs_set(context->type); if (mechs_set == GSS_C_NO_OID_SET) return(FALSE); if (!SIP_SEC_USERNAME_IS_ENTERPRISE) { SIP_SEC_USERNAME_SPLIT_START; /* Construct user name to acquire credentials for */ if (SIP_SEC_USERNAME_HAS_DOMAIN) { /* User specified a domain */ gchar *realm = g_ascii_strup(SIP_SEC_USERNAME_DOMAIN, -1); username_new = g_strdup_printf("%s@%s", SIP_SEC_USERNAME_ACCOUNT, realm); g_free(realm); } else if (strchr(username, '@')) { /* No domain, username matches XXX@YYY */ gchar **user_realm = g_strsplit(username, "@", 2); gchar *realm = g_ascii_strup(user_realm[1], -1); /* * We should escape the "@" to generate a enterprise * principal, i.e. XXX\@YYY * * But krb5 libraries currently don't support this: * * http://krbdev.mit.edu/rt/Ticket/Display.html?id=7729 * * username_new = g_strdup_printf("%s\\@%s", */ username_new = g_strdup_printf("%s@%s", user_realm[0], realm); g_free(realm); g_strfreev(user_realm); } SIP_SEC_USERNAME_SPLIT_END; } if (username_new) username = username_new; SIPE_DEBUG_INFO("sip_sec_acquire_cred__gssapi: username '%s'", username); /* Import user name into GSS format */ input_name_buffer.value = (void *) username; input_name_buffer.length = strlen(username) + 1; ret = gss_import_name(&minor, &input_name_buffer, (gss_OID) GSS_C_NT_USER_NAME, &user_name); g_free(username_new); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_import_name", ret, minor); SIPE_DEBUG_ERROR("sip_sec_acquire_cred__gssapi: failed to construct user name (ret=%u)", ret); gss_release_oid_set(&minor, &mechs_set); return(FALSE); } /* Acquire user credentials with password */ input_name_buffer.value = (void *) password; input_name_buffer.length = strlen(password) + 1; ret = gss_acquire_cred_with_password(&minor, user_name, &input_name_buffer, GSS_C_INDEFINITE, mechs_set, GSS_C_INITIATE, &credentials, NULL, NULL); gss_release_name(&minor_ignore, &user_name); gss_release_oid_set(&minor_ignore, &mechs_set); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_acquire_cred_with_password", ret, minor); SIPE_DEBUG_ERROR("sip_sec_acquire_cred__gssapi: failed to acquire credentials (ret=%u)", ret); return(FALSE); } ctx->cred_gssapi = credentials; #else /* * non-SSO support requires gss_acquire_cred_with_password() * which is not available on older GSSAPI releases. */ (void) username; /* keep compiler happy */ (void) password; /* keep compiler happy */ (void) ctx; /* keep compiler happy */ SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__gssapi: non-SSO mode not supported"); return(FALSE); #endif } #ifdef HAVE_GSSAPI_ONLY else { OM_uint32 ret; OM_uint32 minor, minor_ignore; gss_OID_set mechs_set; gss_cred_id_t credentials; mechs_set = create_mechs_set(context->type); if (mechs_set == GSS_C_NO_OID_SET) return(FALSE); ret = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE, mechs_set, GSS_C_INITIATE, &credentials, NULL, NULL); gss_release_oid_set(&minor_ignore, &mechs_set); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_acquire_cred", ret, minor); SIPE_DEBUG_ERROR("sip_sec_acquire_cred__gssapi: failed to acquire credentials (ret=%u)", ret); return(FALSE); } ctx->cred_gssapi = credentials; } if (context->type == SIPE_AUTHENTICATION_TYPE_NEGOTIATE) { OM_uint32 ret; OM_uint32 minor, minor_ignore; gss_OID_set mechs_set = create_neg_mechs_set(); if (mechs_set == GSS_C_NO_OID_SET) return(FALSE); ret = gss_set_neg_mechs(&minor, ctx->cred_gssapi, mechs_set); gss_release_oid_set(&minor_ignore, &mechs_set); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_set_neg_mechs", ret, minor); SIPE_DEBUG_ERROR("sip_sec_acquire_cred__gssapi: failed to set negotiate mechanisms (ret=%u)", ret); return(FALSE); } } #endif return(TRUE); } static gboolean sip_sec_init_sec_context__gssapi(SipSecContext context, SipSecBuffer in_buff, SipSecBuffer *out_buff, const gchar *service_name) { context_gssapi ctx = (context_gssapi) context; OM_uint32 ret; OM_uint32 minor, minor_ignore; OM_uint32 expiry; gss_buffer_desc input_token; gss_buffer_desc output_token; #ifdef HAVE_GSSAPI_ONLY gss_OID mech_oid; OM_uint32 flags = GSS_C_INTEG_FLAG; #endif SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__gssapi: started"); /* * If authentication was already completed, then this mean a new * authentication handshake has started on the existing connection. * We must throw away the old context, because we need a new one. */ if ((context->flags & SIP_SEC_FLAG_COMMON_READY) && (ctx->ctx_gssapi != GSS_C_NO_CONTEXT)) { SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__gssapi: dropping old context"); drop_gssapi_context(context); } /* Import service name to GSS */ if (ctx->target_name == GSS_C_NO_NAME) { gchar *hostbased_service_name = sipe_utils_str_replace(service_name, "/", "@"); input_token.value = (void *) hostbased_service_name; input_token.length = strlen(input_token.value) + 1; ret = gss_import_name(&minor, &input_token, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &(ctx->target_name)); g_free(hostbased_service_name); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_import_name", ret, minor); SIPE_DEBUG_ERROR("sip_sec_init_sec_context__gssapi: failed to construct target name (ret=%u)", ret); return(FALSE); } } #ifdef HAVE_GSSAPI_ONLY switch(context->type) { case SIPE_AUTHENTICATION_TYPE_NTLM: mech_oid = (gss_OID) &gss_mech_ntlmssp; if (context->flags & SIP_SEC_FLAG_GSSAPI_SIP_NTLM) flags |= GSS_C_DATAGRAM_FLAG; break; case SIPE_AUTHENTICATION_TYPE_KERBEROS: mech_oid = (gss_OID) gss_mech_krb5; break; case SIPE_AUTHENTICATION_TYPE_NEGOTIATE: /* * Some servers do not accept SPNEGO for Negotiate. * If come back here with an existing security context * and NULL input token we will fall back to NTLM */ if (ctx->ctx_gssapi && (in_buff.value == NULL)) { /* Only try this once */ if (context->flags & SIP_SEC_FLAG_GSSAPI_NEGOTIATE_FALLBACK) { SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_init_sec_context__gssapi: SPNEGO-to-NTLM fallback failed"); return(FALSE); } SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__gssapi: SPNEGO failed. Falling back to NTLM"); drop_gssapi_context(context); context->flags |= SIP_SEC_FLAG_GSSAPI_NEGOTIATE_FALLBACK; } if (context->flags & SIP_SEC_FLAG_GSSAPI_NEGOTIATE_FALLBACK) { mech_oid = (gss_OID) &gss_mech_ntlmssp; } else { mech_oid = (gss_OID) &gss_mech_spnego; if (spnego_mutual_flag) flags |= GSS_C_MUTUAL_FLAG; } break; default: SIPE_DEBUG_ERROR("sip_sec_init_sec_context__gssapi: invoked for invalid type %u", context->type); return(FALSE); } #endif /* Create context */ input_token.length = in_buff.length; input_token.value = in_buff.value; output_token.length = 0; output_token.value = NULL; ret = gss_init_sec_context(&minor, ctx->cred_gssapi, &(ctx->ctx_gssapi), ctx->target_name, #ifdef HAVE_GSSAPI_ONLY mech_oid, flags, #else (gss_OID) gss_mech_krb5, GSS_C_INTEG_FLAG, #endif GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &input_token, NULL, &output_token, NULL, &expiry); if (GSS_ERROR(ret)) { gss_release_buffer(&minor_ignore, &output_token); sip_sec_gssapi_print_gss_error("gss_init_sec_context", ret, minor); SIPE_DEBUG_ERROR("sip_sec_init_sec_context__gssapi: failed to initialize context (ret=%u)", ret); #ifdef HAVE_GSSAPI_ONLY /* Enable workaround for SPNEGO (see above) */ if (ret == GSS_S_DEFECTIVE_TOKEN) { SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_init_sec_context__gssapi: enabling workaround for SPNEGO"); spnego_mutual_flag = TRUE; } #endif return(FALSE); } out_buff->length = output_token.length; if (out_buff->length) out_buff->value = g_memdup(output_token.value, output_token.length); else /* Special case: empty token */ out_buff->value = (guint8 *) g_strdup(""); gss_release_buffer(&minor_ignore, &output_token); context->expires = (int)expiry; if (ret == GSS_S_COMPLETE) { /* Authentication is completed */ context->flags |= SIP_SEC_FLAG_COMMON_READY; #ifdef HAVE_GSSAPI_ONLY if ((context->flags & SIP_SEC_FLAG_GSSAPI_SIP_NTLM) && !gssntlm_reset_mic_sequence(ctx)) return(FALSE); #endif } return(TRUE); } /** * @param message a NULL terminated string to sign */ static gboolean sip_sec_make_signature__gssapi(SipSecContext context, const gchar *message, SipSecBuffer *signature) { OM_uint32 ret; OM_uint32 minor; gss_buffer_desc input_message; gss_buffer_desc output_token; input_message.value = (void *)message; input_message.length = strlen(input_message.value); ret = gss_get_mic(&minor, ((context_gssapi)context)->ctx_gssapi, GSS_C_QOP_DEFAULT, &input_message, &output_token); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_get_mic", ret, minor); SIPE_DEBUG_ERROR("sip_sec_make_signature__gssapi: failed to make signature (ret=%u)", ret); return FALSE; } else { signature->length = output_token.length; signature->value = g_memdup(output_token.value, output_token.length); gss_release_buffer(&minor, &output_token); return TRUE; } } /** * @param message a NULL terminated string to check signature of */ static gboolean sip_sec_verify_signature__gssapi(SipSecContext context, const gchar *message, SipSecBuffer signature) { OM_uint32 ret; OM_uint32 minor; gss_buffer_desc input_message; gss_buffer_desc input_token; input_message.value = (void *)message; input_message.length = strlen(input_message.value); input_token.value = signature.value; input_token.length = signature.length; ret = gss_verify_mic(&minor, ((context_gssapi)context)->ctx_gssapi, &input_message, &input_token, NULL); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_verify_mic", ret, minor); SIPE_DEBUG_ERROR("sip_sec_verify_signature__gssapi: failed to verify signature (ret=%u)", ret); return FALSE; } else { return TRUE; } } static void sip_sec_destroy_sec_context__gssapi(SipSecContext context) { context_gssapi ctx = (context_gssapi) context; OM_uint32 ret; OM_uint32 minor; if (ctx->ctx_gssapi != GSS_C_NO_CONTEXT) drop_gssapi_context(context); if (ctx->cred_gssapi != GSS_C_NO_CREDENTIAL) { ret = gss_release_cred(&minor, &(ctx->cred_gssapi)); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_release_cred", ret, minor); SIPE_DEBUG_ERROR("sip_sec_destroy_sec_context__gssapi: failed to release credentials (ret=%u)", ret); } ctx->cred_gssapi = GSS_C_NO_CREDENTIAL; } if (ctx->target_name != GSS_C_NO_NAME) { ret = gss_release_name(&minor, &(ctx->target_name)); if (GSS_ERROR(ret)) { sip_sec_gssapi_print_gss_error("gss_release_name", ret, minor); SIPE_DEBUG_ERROR("sip_sec_destroy_sec_context__gssapi: failed to release name (ret=%u)", ret); } ctx->target_name = GSS_C_NO_NAME; } g_free(context); } static const gchar * sip_sec_context_name__gssapi(SipSecContext context) { #ifdef HAVE_GSSAPI_ONLY const gchar *name; switch(context->type) { case SIPE_AUTHENTICATION_TYPE_NTLM: name = "NTLM"; break; case SIPE_AUTHENTICATION_TYPE_KERBEROS: name = "Kerberos"; break; case SIPE_AUTHENTICATION_TYPE_NEGOTIATE: if (context->flags & SIP_SEC_FLAG_GSSAPI_NEGOTIATE_FALLBACK) name = "NTLM"; else name = "Negotiate"; break; default: SIPE_DEBUG_ERROR("sip_sec_context_name__gssapi: invoked for invalid type %u", context->type); name = ""; break; } return(name); #else (void) context; /* keep compiler happy */ return("Kerberos"); #endif } SipSecContext sip_sec_create_context__gssapi(SIPE_UNUSED_PARAMETER guint type) { context_gssapi context = g_malloc0(sizeof(struct _context_gssapi)); if (!context) return(NULL); context->common.acquire_cred_func = sip_sec_acquire_cred__gssapi; context->common.init_context_func = sip_sec_init_sec_context__gssapi; context->common.destroy_context_func = sip_sec_destroy_sec_context__gssapi; context->common.make_signature_func = sip_sec_make_signature__gssapi; context->common.verify_signature_func = sip_sec_verify_signature__gssapi; context->common.context_name_func = sip_sec_context_name__gssapi; context->cred_gssapi = GSS_C_NO_CREDENTIAL; context->ctx_gssapi = GSS_C_NO_CONTEXT; context->target_name = GSS_C_NO_NAME; return((SipSecContext) context); } gboolean sip_sec_password__gssapi(void) { /* GSSAPI supports Single-Sign On */ return(FALSE); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec-gssapi.h ================================================ /** * @file sip-sec-gssapi.h * * pidgin-sipe * * Copyright (C) 2010,2012 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ SipSecContext sip_sec_create_context__gssapi(guint type); gboolean sip_sec_password__gssapi(void); ================================================ FILE: src/core/sip-sec-mech.h ================================================ /** * @file sip-sec-mech.h * * pidgin-sipe * * Copyright (C) 2011-2015 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Mechanism wrappers API (Inspired by GSS-API) * All mechanisms should implement this API * * Current mechanisms are: Kerberos/GSS-API, sipe's NTLM and SSPI. */ typedef struct { gsize length; guint8 *value; } SipSecBuffer; typedef SipSecContext (*sip_sec_create_context_func)(guint type); typedef gboolean (*sip_sec_acquire_cred_func)(SipSecContext context, const gchar *username, const gchar *password); typedef gboolean (*sip_sec_init_context_func)(SipSecContext context, SipSecBuffer in_buff, SipSecBuffer *out_buff, const gchar *service_name); typedef void (*sip_sec_destroy_context_func)(SipSecContext context); typedef gboolean (*sip_sec_make_signature_func)(SipSecContext context, const gchar *message, SipSecBuffer *signature); typedef gboolean (*sip_sec_verify_signature_func)(SipSecContext context, const gchar *message, SipSecBuffer signature); typedef const gchar *(*sip_sec_context_name_func)(SipSecContext context); typedef gboolean (*sip_sec_password_func)(void); struct sip_sec_context { sip_sec_acquire_cred_func acquire_cred_func; sip_sec_init_context_func init_context_func; sip_sec_destroy_context_func destroy_context_func; sip_sec_make_signature_func make_signature_func; sip_sec_verify_signature_func verify_signature_func; sip_sec_context_name_func context_name_func; guint type; /** Security Context expiration interval in seconds */ guint expires; guint flags; }; /** * sip_sec_context.flags * * 0x00000001 - 0x00008000: common flags * 0x00010000 - 0x80000000: mechanism private flags * * NOTE: private flags must be set in acquire_cred_func()! */ #define SIP_SEC_FLAG_COMMON_SSO 0x00000001 #define SIP_SEC_FLAG_COMMON_HTTP 0x00000002 #define SIP_SEC_FLAG_COMMON_READY 0x00000004 /** * Helper macro for parsing username into domain & account parts */ #define SIP_SEC_USERNAME_ENTERPRISE_STRING "\\@" #define SIP_SEC_USERNAME_IS_ENTERPRISE (strstr(username, SIP_SEC_USERNAME_ENTERPRISE_STRING) != NULL) #define SIP_SEC_USERNAME_SPLIT_START gchar **_domain_user = g_strsplit_set(username, "/\\", 2) #define SIP_SEC_USERNAME_SPLIT_END g_strfreev(_domain_user) #define SIP_SEC_USERNAME_DOMAIN _domain_user[0] #define SIP_SEC_USERNAME_ACCOUNT _domain_user[1] #define SIP_SEC_USERNAME_HAS_DOMAIN (SIP_SEC_USERNAME_ACCOUNT != NULL) ================================================ FILE: src/core/sip-sec-negotiate.c ================================================ /** * @file sip-sec-negotiate.c * * pidgin-sipe * * Copyright (C) 2013-2015 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Implementation for HTTP "WWW-Authenticate: Negotiate" scheme. * It is a wrapper that will always try Kerberos first and fall back to NTLM. */ #include #include "sipe-common.h" #include "sip-sec.h" #include "sip-sec-mech.h" #include "sip-sec-gssapi.h" /* for Kerberos */ #include "sip-sec-negotiate.h" #include "sip-sec-ntlm.h" #include "sipe-backend.h" #include "sipe-core.h" /* Security context for Negotiate */ typedef struct _context_negotiate { struct sip_sec_context common; const gchar *username; const gchar *password; SipSecContext krb5; SipSecContext ntlm; } *context_negotiate; #define SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK 0x80000000 static void sip_sec_negotiate_drop_krb5(context_negotiate context) { if (context->krb5) context->krb5->destroy_context_func(context->krb5); context->krb5 = NULL; } static void sip_sec_negotiate_copy_flags(context_negotiate ctx, SipSecContext context) { context->flags = ctx->common.flags; } static void sip_sec_negotiate_copy_settings(context_negotiate ctx, SipSecContext context) { if (context->flags & SIP_SEC_FLAG_COMMON_READY) ctx->common.flags |= SIP_SEC_FLAG_COMMON_READY; else ctx->common.flags &= ~SIP_SEC_FLAG_COMMON_READY; ctx->common.expires = context->expires; } static gboolean sip_sec_negotiate_ntlm_fallback(context_negotiate context) { if (context->common.flags & SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK) { SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_negotiate_ntlm_fallback: forbidden"); return(FALSE); } sip_sec_negotiate_drop_krb5(context); sip_sec_negotiate_copy_flags(context, context->ntlm); return(context->ntlm->acquire_cred_func(context->ntlm, context->username, context->password)); } /* sip-sec-mech.h API implementation for Negotiate */ static gboolean sip_sec_acquire_cred__negotiate(SipSecContext context, const gchar *username, const gchar *password) { context_negotiate ctx = (context_negotiate) context; gboolean ret; SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__negotiate: entering"); ctx->username = username; ctx->password = password; context = ctx->krb5; sip_sec_negotiate_copy_flags(ctx, context); ret = context->acquire_cred_func(context, username, password); if (!ret) { /* Kerberos failed -> fall back to NTLM immediately */ SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__negotiate: fallback to NTLM"); ret = sip_sec_negotiate_ntlm_fallback(ctx); } return(ret); } static gboolean sip_sec_init_sec_context__negotiate(SipSecContext context, SipSecBuffer in_buff, SipSecBuffer *out_buff, const gchar *service_name) { context_negotiate ctx = (context_negotiate) context; gboolean ret; SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__negotiate: entering"); /* Kerberos available? */ context = ctx->krb5; if (context) { ret = context->init_context_func(context, in_buff, out_buff, service_name); if (!ret) { /* Kerberos failed -> fall back to NTLM */ SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__negotiate: fallback to NTLM"); ret = sip_sec_negotiate_ntlm_fallback(ctx); if (ret) { context = ctx->ntlm; ret = context->init_context_func(context, in_buff, out_buff, service_name); } } else { /* Kerberos succeeded -> disable fallback to NTLM */ ctx->common.flags |= SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK; } /* No Kerberos available -> use NTLM */ } else { context = ctx->ntlm; ret = context->init_context_func(context, in_buff, out_buff, service_name); } /* context points to the last used child context */ if (ret) sip_sec_negotiate_copy_settings(ctx, context); return(ret); } static gboolean sip_sec_make_signature__negotiate(SIPE_UNUSED_PARAMETER SipSecContext context, SIPE_UNUSED_PARAMETER const gchar *message, SIPE_UNUSED_PARAMETER SipSecBuffer *signature) { /* No implementation needed, as Negotiate is not used for SIP */ return(FALSE); } static gboolean sip_sec_verify_signature__negotiate(SIPE_UNUSED_PARAMETER SipSecContext context, SIPE_UNUSED_PARAMETER const gchar *message, SIPE_UNUSED_PARAMETER SipSecBuffer signature) { /* No implementation needed, as Negotiate is not used for SIP */ return(FALSE); } static void sip_sec_destroy_sec_context__negotiate(SipSecContext context) { context_negotiate ctx = (context_negotiate) context; if (ctx->ntlm) ctx->ntlm->destroy_context_func(ctx->ntlm); sip_sec_negotiate_drop_krb5(ctx); g_free(ctx); } /* * This module doesn't implement SPNEGO (RFC 4559) but instead returns raw * NTLM. Therefore we should not use "Authorization: Negotiate" for NTLM * although Microsoft servers *do* accept them. */ static const gchar * sip_sec_context_name__negotiate(SipSecContext context) { context_negotiate ctx = (context_negotiate) context; if (ctx->krb5) return("Negotiate"); else return("NTLM"); } SipSecContext sip_sec_create_context__negotiate(SIPE_UNUSED_PARAMETER guint type) { context_negotiate context = NULL; SipSecContext krb5 = sip_sec_create_context__gssapi(SIPE_AUTHENTICATION_TYPE_KERBEROS); if (krb5) { SipSecContext ntlm = sip_sec_create_context__ntlm(SIPE_AUTHENTICATION_TYPE_NTLM); if (ntlm) { context = g_malloc0(sizeof(struct _context_negotiate)); if (context) { context->common.acquire_cred_func = sip_sec_acquire_cred__negotiate; context->common.init_context_func = sip_sec_init_sec_context__negotiate; context->common.destroy_context_func = sip_sec_destroy_sec_context__negotiate; context->common.make_signature_func = sip_sec_make_signature__negotiate; context->common.verify_signature_func = sip_sec_verify_signature__negotiate; context->common.context_name_func = sip_sec_context_name__negotiate; context->krb5 = krb5; context->ntlm = ntlm; krb5->type = SIPE_AUTHENTICATION_TYPE_KERBEROS; ntlm->type = SIPE_AUTHENTICATION_TYPE_NTLM; } else { ntlm->destroy_context_func(ntlm); } } if (!context) { krb5->destroy_context_func(krb5); } } return((SipSecContext) context); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec-negotiate.h ================================================ /** * @file sip-sec-negotiate.h * * pidgin-sipe * * Copyright (C) 2013 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ SipSecContext sip_sec_create_context__negotiate(guint type); ================================================ FILE: src/core/sip-sec-ntlm-analyzer.c ================================================ /** * @file sip-sec-ntlm-analyzer.c * * pidgin-sipe * * Copyright (C) 2013-2017 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Takes Base64-encoded gssapi-data= values from NTLM authentication attempt * on the command line and prints out the NTLM message contents in human readable * format. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-common.h" #define _SIPE_COMPILING_ANALYZER #include "sip-sec-ntlm.c" /* stub functions */ void sipe_backend_debug(SIPE_UNUSED_PARAMETER sipe_debug_level level, const gchar *format, ...) { va_list ap; va_start(ap, format); vprintf(format, ap); va_end(ap); } gboolean sipe_strequal(const gchar *left, const gchar *right) { return (g_strcmp0(left, right) == 0); } /* copied from sipe-utils.c */ char *buff_to_hex_str(const guint8 *buff, const size_t buff_len) { char *res; size_t i, j; if (!buff) return NULL; res = g_malloc(buff_len * 2 + 1); for (i = 0, j = 0; i < buff_len; i++, j+=2) { sprintf(&res[j], "%02X", buff[i]); } res[j] = '\0'; return res; } int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s ...\n", argv[0]); return(1); } sip_sec_init__ntlm(); while (--argc > 0) { const gchar *base64 = *++argv; SipSecBuffer buffer; printf("Base64: %s\n", base64); buffer.value = g_base64_decode(base64, &buffer.length); if (buffer.value && buffer.length) { printf("Decoded %" G_GSIZE_FORMAT " bytes\n", buffer.length); sip_sec_ntlm_message_describe(&buffer, "analyzed"); printf("-------------------------------------------------------------------------------\n"); g_free(buffer.value); } else { printf("Corrupted Base64 - skipping\n"); } } sip_sec_destroy__ntlm(); return(0); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec-ntlm-tests.c ================================================ /** * @file sipe-sec-ntlm-tests.c * * pidgin-sipe * * Copyright (C) 2011-2019 SIPE Project * Copyright (C) 2010 pier11 * Copyright (C) 2008 Novell, Inc. * * Implemented with reference to the follow documentation: * - http://davenport.sourceforge.net/ntlm.html * - MS-NLMP: http://msdn.microsoft.com/en-us/library/cc207842.aspx * - MS-SIP : http://msdn.microsoft.com/en-us/library/cc246115.aspx * * Please use "make tests" to build & run them! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sipmsg.h" #include "sip-transport.h" #include "sipe-common.h" #include "sipe-sign.h" #define _SIPE_COMPILING_TESTS #include "sip-sec-ntlm.c" /* stub for sipe-utils.c */ const gchar *sip_transport_epid(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private) { return(NULL); } static int successes = 0; static int failures = 0; gboolean sip_sec_ntlm_tests(void); static void assert_equal(const char * expected, gpointer got, int len, gboolean stringify) { const gchar * res = (gchar *) got; gchar to_str[len*2 + 1]; if (stringify) { const guint8 *bin = got; int i, j; for (i = 0, j = 0; i < len; i++, j+=2) { g_sprintf(&to_str[j], "%02X", (bin[i]&0xff)); } len *= 2; res = to_str; } printf("expected: %s\n", expected); printf("received: %s\n", res); if (g_ascii_strncasecmp(expected, res, len) == 0) { successes++; printf("PASSED\n"); } else { failures++; printf("FAILED\n"); } } /* NOTE: both values are expected to be in host byte order! */ static void assert_equal_guint32(guint32 expected, guint32 got) { printf("expected: %08X\n", expected); printf("received: %08X\n", got); if (expected == got) { successes++; printf("PASSED\n"); } else { failures++; printf("FAILED\n"); } } gboolean sip_sec_ntlm_tests(void) { const char *password; const char *user; const char *domain; const guchar *client_challenge; const guchar *nonce; const guchar *exported_session_key; const guchar *text; guchar md4 [16]; guchar md5 [16]; guchar hmac_md5 [16]; guint32 flags; guchar response_key_lm [16]; guchar response_key_nt [16]; guchar nt_challenge_response [24]; guchar lm_challenge_response [24]; guchar session_base_key [16]; guchar key_exchange_key [16]; guchar encrypted_random_session_key [16]; guint32 crc; guchar client_seal_key [16]; guchar client_sign_key [16]; guchar server_sign_key [16]; guchar server_seal_key [16]; guint32 mac [4]; guchar text_enc [18 + 12]; struct sipmsg *msg; struct sipmsg_breakdown msgbd; gchar *msg_str; const char *password2; const char *user2; const char *domain2; const char *host2; const char *type2_hex; const char *type3_hex; const char *request; const char *response; const gchar *request_sig; const gchar *response_sig; printf ("Starting Tests\n"); /* Initialization for crypto backend (test mode) */ sipe_crypto_init(FALSE); /* Initialization for NTLM */ sip_sec_init__ntlm(); /* These tests are from the MS-SIPE document */ password = "Password"; user = "User"; domain = "Domain"; client_challenge = (guchar *)"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"; /* server challenge */ nonce = (guchar *)"\x01\x23\x45\x67\x89\xab\xcd\xef"; /* 16 bytes */ exported_session_key = (guchar *)"\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55"; text = (guchar *)"\x50\x00\x6c\x00\x61\x00\x69\x00\x6e\x00\x74\x00\x65\x00\x78\x00\x74\x00"; //P·l·a·i·n·t·e·x·t· ////// internal Cyphers tests /////// printf ("\nTesting MD4()\n"); MD4 ((const unsigned char *)"message digest", 14, md4); assert_equal("D9130A8164549FE818874806E1C7014B", md4, 16, TRUE); printf ("\nTesting MD5()\n"); MD5 ((const unsigned char *)"message digest", 14, md5); assert_equal("F96B697D7CB7938D525A2F31AAF161D0", md5, 16, TRUE); printf ("\nTesting HMAC_MD5()\n"); HMAC_MD5 ((const unsigned char *)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 16, (const unsigned char *)"Hi There", 8, hmac_md5); assert_equal("9294727A3638BB1C13F48EF8158BFC9D", hmac_md5, 16, TRUE); ////// NTLMv1 (without Extended Session Security) /////// use_ntlm_v2 = FALSE; flags = 0 | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_VERSION | NTLMSSP_TARGET_TYPE_SERVER | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_NEGOTIATE_UNICODE; printf ("\n\nTesting Negotiation Flags\n"); assert_equal_guint32(0xE2028233, flags); printf ("\n\nTesting LMOWFv1()\n"); LMOWFv1 (password, user, domain, response_key_lm); assert_equal("E52CAC67419A9A224A3B108F3FA6CB6D", response_key_lm, 16, TRUE); printf ("\n\nTesting NTOWFv1()\n"); NTOWFv1 (password, user, domain, response_key_nt); assert_equal("A4F49C406510BDCAB6824EE7C30FD852", response_key_nt, 16, TRUE); printf ("\n\nTesting LM Response Generation\n"); printf ("Testing NT Response Generation\n"); printf ("Testing Session Base Key\n"); compute_response(flags, response_key_nt, response_key_lm, nonce, client_challenge, 0, NULL, /* target_info */ 0, /* target_info_len */ lm_challenge_response, /* out */ nt_challenge_response, /* out */ session_base_key); /* out */ assert_equal("98DEF7B87F88AA5DAFE2DF779688A172DEF11C7D5CCDEF13", lm_challenge_response, 24, TRUE); assert_equal("67C43011F30298A2AD35ECE64F16331C44BDBED927841F94", nt_challenge_response, 24, TRUE); assert_equal("D87262B0CDE4B1CB7499BECCCDF10784", session_base_key, 16, TRUE); printf ("\n\nTesting Key Exchange Key\n"); KXKEY(flags, session_base_key, lm_challenge_response, nonce, key_exchange_key); assert_equal("D87262B0CDE4B1CB7499BECCCDF10784", key_exchange_key, 16, TRUE); printf ("\n\nTesting Encrypted Session Key Generation\n"); RC4K (key_exchange_key, 16, exported_session_key, 16, encrypted_random_session_key); assert_equal("518822B1B3F350C8958682ECBB3E3CB7", encrypted_random_session_key, 16, TRUE); printf ("\n\nTesting CRC32\n"); crc = CRC32((char*)text, 18); assert_equal_guint32(0x93AA847D, crc); printf ("\n\nTesting Encryption\n"); { //SEALKEY (flags, exported_session_key, TRUE, client_seal_key); guchar buff [18 + 12]; guint32 to_enc [3]; memcpy(buff, text, 18); to_enc[0] = GUINT32_TO_LE(0); // random pad to_enc[1] = GUINT32_TO_LE(crc); to_enc[2] = GUINT32_TO_LE(0); // zero memcpy(buff+18, (gchar *)to_enc, 12); RC4K (exported_session_key, 16, buff, 18 + 12, text_enc); //The point is to not reinitialize rc4 cypher // 0 crc 0 (zero) assert_equal("56FE04D861F9319AF0D7238A2E3B4D457FB8" "45C844E5" "09DCD1DF" "2E459D36", text_enc, 18 + 12, TRUE); } printf ("\n\nTesting MAC\n"); { // won't work in the case with sealing because RC4 is re-initialized inside. // MAC (flags, (gchar*)text, 18, (guchar*)exported_session_key, 16, (guchar*)exported_session_key,16, 0x00000000, 0, mac); guint32 enc [3]; guint32 mac2 [4]; memcpy((gchar *)enc, text_enc+18, 12); mac2 [0] = GUINT32_TO_LE(1); // version mac2 [1] = enc [0]; mac2 [2] = enc [1]; mac2 [3] = enc [2] ^ (GUINT32_TO_LE(0)); // ^ seq assert_equal("0100000045C844E509DCD1DF2E459D36", (guchar*)mac2, 16, TRUE); } ////// EXTENDED_SESSIONSECURITY /////// use_ntlm_v2 = FALSE; flags = 0 | NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_VERSION | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | NTLMSSP_TARGET_TYPE_SERVER | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_NEGOTIATE_UNICODE; printf ("\n\n(Extended session security) Testing Negotiation Flags\n"); assert_equal_guint32(0x820A8233, flags); /* NTOWFv1() is not different from the above test for the same */ printf ("\n\n(Extended session security) Testing LM Response\n"); printf ("(Extended session security) Testing NT Response\n"); printf ("(Extended session security) Testing Session Base Key\n"); compute_response(flags, response_key_nt, response_key_lm, nonce, client_challenge, 0, NULL, /* target_info */ 0, /* target_info_len */ lm_challenge_response, /* out */ nt_challenge_response, /* out */ session_base_key); /* out */ assert_equal("AAAAAAAAAAAAAAAA00000000000000000000000000000000", lm_challenge_response, 24, TRUE); assert_equal("7537F803AE367128CA458204BDE7CAF81E97ED2683267232", nt_challenge_response, 24, TRUE); assert_equal("D87262B0CDE4B1CB7499BECCCDF10784", session_base_key, 16, TRUE); printf ("\n\n(Extended session security) Testing Key Exchange Key\n"); KXKEY(flags, session_base_key, lm_challenge_response, nonce, key_exchange_key); assert_equal("EB93429A8BD952F8B89C55B87F475EDC", key_exchange_key, 16, TRUE); printf ("\n\n(Extended session security) SIGNKEY\n"); SIGNKEY (key_exchange_key, TRUE, client_sign_key); assert_equal("60E799BE5C72FC92922AE8EBE961FB8D", client_sign_key, 16, TRUE); printf ("\n\n(Extended session security) SEALKEY\n"); SEALKEY (flags, key_exchange_key, TRUE, client_seal_key); assert_equal("04DD7F014D8504D265A25CC86A3A7C06", client_seal_key, 16, TRUE); printf ("\n\n(Extended session security) Testing Encryption\n"); RC4K (client_seal_key, 16, text, 18, text_enc); assert_equal("A02372F6530273F3AA1EB90190CE5200C99D", text_enc, 18, TRUE); printf ("\n\n(Extended session security) Testing MAC\n"); MAC (flags, (gchar*)text,18, client_sign_key,16, client_seal_key,16, 0, 0, mac); assert_equal("01000000FF2AEB52F681793A00000000", mac, 16, TRUE); ////// NTLMv2 /////// use_ntlm_v2 = TRUE; flags = 0 | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_VERSION | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | NTLMSSP_TARGET_TYPE_SERVER | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_NEGOTIATE_UNICODE; printf ("\n\nTesting (NTLMv2) Negotiation Flags\n"); assert_equal_guint32(0xE28A8233, flags); printf ("\n\nTesting NTOWFv2()\n"); NTOWFv2 (password, user, domain, response_key_nt); NTOWFv2 (password, user, domain, response_key_lm); assert_equal("0C868A403BFD7A93A3001EF22EF02E3F", response_key_nt, 16, TRUE); printf ("\n\nTesting (NTLMv2) LM Response Generation\n"); printf ("Testing (NTLMv2) NT Response Generation and Session Base Key\n"); /* Challenge: 4e544c4d53535000020000000c000c003800000033828ae20123456789abcdef00000000000000002400240044000000060070170000000f53006500720076006500720002000c0044006f006d00610069006e0001000c0053006500720076006500720000000000 NTLMSSP_NEGOTIATE_UNICODE NTLMSSP_NEGOTIATE_OEM NTLMSSP_NEGOTIATE_SIGN NTLMSSP_NEGOTIATE_SEAL NTLMSSP_NEGOTIATE_NTLM NTLMSSP_NEGOTIATE_ALWAYS_SIGN NTLMSSP_TARGET_TYPE_SERVER NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY NTLMSSP_NEGOTIATE_TARGET_INFO NTLMSSP_NEGOTIATE_VERSION NTLMSSP_NEGOTIATE_128 NTLMSSP_NEGOTIATE_KEY_EXCH NTLMSSP_NEGOTIATE_56 target_name.len : 12 target_name.maxlen: 12 target_name.offset: 56 target_info.len : 36 target_info.maxlen: 36 target_info.offset: 68 product: 6.0.6000 (Windows Vista, Windows Server 2008, Windows 7 or Windows Server 2008 R2) ntlm_revision_current: 0x0F (NTLMSSP_REVISION_W2K3) target_name: Server MsvAvNbDomainName: Domain MsvAvNbComputerName: Server target_name: 530065007200760065007200 target_info: 02000c0044006f006d00610069006e0001000c0053006500720076006500720000000000 Response: 4e544c4d5353500003000000180018006c00000054005400840000000c000c00480000000800080054000000100010005c00000010001000d8000000358288e20501280a0000000f44006f006d00610069006e00550073006500720043004f004d005000550054004500520086c35097ac9cec102554764a57cccc19aaaaaaaaaaaaaaaa68cd0ab851e51c96aabc927bebef6a1c01010000000000000000000000000000aaaaaaaaaaaaaaaa0000000002000c0044006f006d00610069006e0001000c005300650072007600650072000000000000000000c5dad2544fc9799094ce1ce90bc9d03e */ { const guint64 time_val = 0; const guint8 target_info [] = { 0x02, 0x00, 0x0C, 0x00, //NetBIOS Domain name, 4 bytes 0x44, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6E, 0x00, //D.o.m.a.i.n. 12bytes 0x01, 0x00, 0x0C, 0x00, //NetBIOS Server name, 4 bytes 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, //S.e.r.v.e.r. 12bytes 0x00, 0x00, 0x00, 0x00, //Av End, 4 bytes }; const int target_info_len = 32+4; int ntlmssp_nt_resp_len = (16 + (32+target_info_len)); guchar nt_challenge_response_v2 [ntlmssp_nt_resp_len]; compute_response(flags, response_key_nt, response_key_lm, nonce, client_challenge, time_val, target_info, /* target_info */ target_info_len, /* target_info_len */ lm_challenge_response, /* out */ nt_challenge_response_v2, /* out */ session_base_key); /* out */ assert_equal("86C35097AC9CEC102554764A57CCCC19AAAAAAAAAAAAAAAA", lm_challenge_response, 24, TRUE); assert_equal("68CD0AB851E51C96AABC927BEBEF6A1C", nt_challenge_response_v2, 16, TRUE); /* the ref string is taken from binary dump of AUTHENTICATE_MESSAGE */ assert_equal("68CD0AB851E51C96AABC927BEBEF6A1C01010000000000000000000000000000AAAAAAAAAAAAAAAA0000000002000C0044006F006D00610069006E0001000C005300650072007600650072000000000000000000", nt_challenge_response_v2, ntlmssp_nt_resp_len, TRUE); assert_equal("8DE40CCADBC14A82F15CB0AD0DE95CA3", session_base_key, 16, TRUE); } printf ("\n\nTesting (NTLMv2) Encrypted Session Key\n"); // key_exchange_key = session_base_key for NTLMv2 KXKEY(flags, session_base_key, lm_challenge_response, nonce, key_exchange_key); //RC4 encryption of the RandomSessionKey with the KeyExchangeKey: RC4K (key_exchange_key, 16, exported_session_key, 16, encrypted_random_session_key); assert_equal("C5DAD2544FC9799094CE1CE90BC9D03E", encrypted_random_session_key, 16, TRUE); printf ("\n\nTesting (NTLMv2) SIGNKEY\n"); SIGNKEY (exported_session_key, TRUE, client_sign_key); assert_equal("4788DC861B4782F35D43FD98FE1A2D39", client_sign_key, 16, TRUE); printf ("\n\nTesting (NTLMv2) SEALKEY\n"); SEALKEY (flags, exported_session_key, TRUE, client_seal_key); assert_equal("59F600973CC4960A25480A7C196E4C58", client_seal_key, 16, TRUE); printf ("\n\nTesting (NTLMv2) Encryption\n"); RC4K (client_seal_key, 16, text, 18, text_enc); assert_equal("54E50165BF1936DC996020C1811B0F06FB5F", text_enc, 18, TRUE); // printf ("\n\nTesting (NTLMv2) Encryption\n"); //const guchar text2 [] = {0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 0x00 // , 0x70, 0x35, 0x28, 0x51, 0xf2, 0x56, 0x43, 0x09}; //P·l·a·i·n·t·e·x·t· //guchar text_enc2 [18+8]; // RC4K (client_seal_key, 16, text2, 18+8, text_enc2); // assert_equal("54E50165BF1936DC996020C1811B0F06FB5F", text_enc2, 18+8, TRUE); printf ("\n\nTesting (NTLMv2) MAC (without RC4, as we don't keep its handle yet)\n"); MAC (flags & ~NTLMSSP_NEGOTIATE_KEY_EXCH, (gchar*)text,18, client_sign_key,16, client_seal_key,16, 0, 0, mac); assert_equal("0100000070352851F256430900000000", mac, 16, TRUE); /* End tests from the MS-SIPE document */ ////// davenport tests /////// // Test from http://davenport.sourceforge.net/ntlm.html#ntlm1Signing { const gchar *text_j = "jCIFS"; printf ("\n\n(davenport) Testing Signature Algorithm\n"); { guchar sk [] = {0x01, 0x02, 0x03, 0x04, 0x05, 0xe5, 0x38, 0xb0}; MAC (NEGOTIATE_FLAGS_CONNLESS & ~NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY, text_j, strlen(text_j), sk, 8, sk,8, 0x00090178, 0, mac); assert_equal("0100000078010900397420FE0E5A0F89", mac, 16, TRUE); } // Tests from http://davenport.sourceforge.net/ntlm.html#ntlm2Signing printf ("\n\n(davenport) SIGNKEY\n"); { const guchar master_key [] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}; SIGNKEY (master_key, TRUE, client_sign_key); assert_equal("F7F97A82EC390F9C903DAC4F6ACEB132", client_sign_key, 16, TRUE); printf ("\n\n(davenport) Testing MAC - no Key Exchange flag\n"); MAC (flags & ~NTLMSSP_NEGOTIATE_KEY_EXCH, text_j, strlen(text_j), client_sign_key, 16, client_sign_key,16, 0, 0, mac); assert_equal("010000000A003602317A759A00000000", mac, 16, TRUE); } } ////// SIPE internal tests /////// // Verify signature of SIPE message received from OCS 2007 after authenticating with pidgin-sipe printf ("\n\nTesting MS-SIPE Example Message Signing\n"); { const char * msg2; const char * msg1 = "<0878F41B><1><8592g5DCBa1694i5887m0D0Bt2247b3F38xAE9Fx><3><2947328781><900><200>"; guchar exported_session_key2 [] = { 0x5F, 0x02, 0x91, 0x53, 0xBC, 0x02, 0x50, 0x58, 0x96, 0x95, 0x48, 0x61, 0x5E, 0x70, 0x99, 0xBA }; MAC (NEGOTIATE_FLAGS_CONNLESS & ~NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY, msg1, strlen(msg1), exported_session_key2, 16, exported_session_key2,16, 0, 100, mac); assert_equal("0100000000000000BF2E52667DDF6DED", mac, 16, TRUE); // Verify parsing of message and signature verification printf ("\n\nTesting MS-SIPE Example Message Parsing, Signing, and Verification\n(Authentication Protocol Version 2)\n"); msg2 = "SIP/2.0 200 OK\r\nms-keep-alive: UAS; tcp=no; hop-hop=yes; end-end=no; timeout=300\r\nAuthentication-Info: NTLM rspauth=\"0100000000000000BF2E52667DDF6DED\", srand=\"0878F41B\", snum=\"1\", opaque=\"4452DFB0\", qop=\"auth\", targetname=\"ocs1.ocs.provo.novell.com\", realm=\"SIP Communications Service\"\r\nFrom: \"Gabriel Burt\";tag=2947328781;epid=1234567890\r\nTo: ;tag=B816D65C2300A32CFA6D371F2AF537FD\r\nCall-ID: 8592g5DCBa1694i5887m0D0Bt2247b3F38xAE9Fx\r\nCSeq: 3 REGISTER\r\nVia: SIP/2.0/TLS 164.99.194.49:10409;branch=z9hG4bKE0E37DBAF252C3255BAD;received=164.99.195.20;ms-received-port=10409;ms-received-cid=1E00\r\nContact: ;expires=900\r\nExpires: 900\r\nAllow-Events: vnd-microsoft-provisioning,vnd-microsoft-roaming-contacts,vnd-microsoft-roaming-ACL,presence,presence.wpending,vnd-microsoft-roaming-self,vnd-microsoft-provisioning-v2\r\nSupported: adhoclist\r\nServer: RTC/3.0\r\nSupported: com.microsoft.msrtc.presence\r\nContent-Length: 0\r\n\r\n"; msg = sipmsg_parse_msg(msg2); memset(&msgbd, 0, sizeof(struct sipmsg_breakdown)); msgbd.msg = msg; sipmsg_breakdown_parse(&msgbd, "SIP Communications Service", "ocs1.ocs.provo.novell.com", NULL); msg_str = sipmsg_breakdown_get_string(2, &msgbd); sip_sec_ntlm_sipe_signature_make (NEGOTIATE_FLAGS_CONNLESS & ~NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY, msg_str, 0, exported_session_key2, exported_session_key2, mac); sipmsg_breakdown_free(&msgbd); assert_equal ("0100000000000000BF2E52667DDF6DED", mac, 16, TRUE); /* sig = buff_to_hex_str((guint8 *)mac, 16); */ } ////// real Communicator 2007 R2 tests ////// ////// Recreated/verifyed real authentication communication between ////// Communicator 2007 R2 and Office Communications Server 2007 R2 ////// with SIPE NTLMv2 implementation. password2 = "Pa$$word"; user2 = "User"; domain2 = "COSMO"; host2 = "COSMO-OCS-R2"; //Challenge: //const char *type2 = "TlRMTVNTUAACAAAAAAAAADgAAADzgpji3Ruq9OfiGNEAAAAAAAAAAJYAlgA4AAAABQLODgAAAA8CAAoAQwBPAFMATQBPAAEAGABDAE8AUwBNAE8ALQBPAEMAUwAtAFIAMgAEABYAYwBvAHMAbQBvAC4AbABvAGMAYQBsAAMAMABjAG8AcwBtAG8ALQBvAGMAcwAtAHIAMgAuAGMAbwBzAG0AbwAuAGwAbwBjAGEAbAAFABYAYwBvAHMAbQBvAC4AbABvAGMAYQBsAAAAAAA="; //in hex (base64 decoded): type2_hex = "4E544C4D53535000020000000000000038000000F38298E2DD1BAAF4E7E218D1000000000000000096009600380000000502CE0E0000000F02000A0043004F0053004D004F000100180043004F0053004D004F002D004F00430053002D00520032000400160063006F0073006D006F002E006C006F00630061006C000300300063006F0073006D006F002D006F00630073002D00720032002E0063006F0073006D006F002E006C006F00630061006C000500160063006F0073006D006F002E006C006F00630061006C0000000000"; /* Message (length 206): NTLMSSP_NEGOTIATE_UNICODE NTLMSSP_NEGOTIATE_OEM NTLMSSP_NEGOTIATE_SIGN NTLMSSP_NEGOTIATE_SEAL NTLMSSP_NEGOTIATE_DATAGRAM NTLMSSP_NEGOTIATE_LM_KEY NTLMSSP_NEGOTIATE_NTLM NTLMSSP_NEGOTIATE_ALWAYS_SIGN NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY NTLMSSP_NEGOTIATE_IDENTIFY NTLMSSP_NEGOTIATE_TARGET_INFO NTLMSSP_NEGOTIATE_VERSION NTLMSSP_NEGOTIATE_128 NTLMSSP_NEGOTIATE_KEY_EXCH NTLMSSP_NEGOTIATE_56 server_challenge: DD1BAAF4E7E218D1 target_name.len : 0 target_name.maxlen: 0 target_name.offset: 56 target_info.len : 150 target_info.maxlen: 150 target_info.offset: 56 product: 5.2.3790 (Windows Server 2003) ntlm_revision_current: 0x0F (NTLMSSP_REVISION_W2K3) target_info raw: 02000A0043004F0053004D004F000100180043004F0053004D004F002D004F00430053002D00520032000400160063006F0073006D006F002E006C006F00630061006C000300300063006F0073006D006F002D006F00630073002D00720032002E0063006F0073006D006F002E006C006F00630061006C000500160063006F0073006D006F002E006C006F00630061006C0000000000 MsvAvNbDomainName: COSMO MsvAvNbComputerName: COSMO-OCS-R2 MsvAvDnsDomainName: cosmo.local MsvAvDnsComputerName: cosmo-ocs-r2.cosmo.local MsvAvDnsTreeName: cosmo.local */ //Response: //const char *type3 = "TlRMTVNTUAADAAAAGAAYAHIAAADGAMYAigAAAAoACgBIAAAACAAIAFIAAAAYABgAWgAAABAAEABQAQAAVYKYYgUCzg4AAAAPQwBPAFMATQBPAFUAcwBlAHIAQwBPAFMATQBPAC0ATwBDAFMALQBSADIAoeku/k4Hi/fFwASazGFmwtauh1yw/apBjcDIAK527KYG0rn769BHMQEBAAAAAAAAWVGaFye5ygHWrodcsP2qQQAAAAACAAoAQwBPAFMATQBPAAEAGABDAE8AUwBNAE8ALQBPAEMAUwAtAFIAMgAEABYAYwBvAHMAbQBvAC4AbABvAGMAYQBsAAMAMABjAG8AcwBtAG8ALQBvAGMAcwAtAHIAMgAuAGMAbwBzAG0AbwAuAGwAbwBjAGEAbAAFABYAYwBvAHMAbQBvAC4AbABvAGMAYQBsAAAAAAAAAAAAMctznhyoCkmFkeiueXEV5A=="; //in hex (base64 decoded): type3_hex = "4E544C4D53535000030000001800180072000000C600C6008A0000000A000A00480000000800080052000000180018005A0000001000100050010000558298620502CE0E0000000F43004F0053004D004F00550073006500720043004F0053004D004F002D004F00430053002D0052003200A1E92EFE4E078BF7C5C0049ACC6166C2D6AE875CB0FDAA418DC0C800AE76ECA606D2B9FBEBD04731010100000000000059519A1727B9CA01D6AE875CB0FDAA410000000002000A0043004F0053004D004F000100180043004F0053004D004F002D004F00430053002D00520032000400160063006F0073006D006F002E006C006F00630061006C000300300063006F0073006D006F002D006F00630073002D00720032002E0063006F0073006D006F002E006C006F00630061006C000500160063006F0073006D006F002E006C006F00630061006C00000000000000000031CB739E1CA80A498591E8AE797115E4"; /* Message (length 352): NTLMSSP_NEGOTIATE_UNICODE NTLMSSP_REQUEST_TARGET NTLMSSP_NEGOTIATE_SIGN NTLMSSP_NEGOTIATE_DATAGRAM NTLMSSP_NEGOTIATE_NTLM NTLMSSP_NEGOTIATE_ALWAYS_SIGN NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY NTLMSSP_NEGOTIATE_IDENTIFY NTLMSSP_NEGOTIATE_TARGET_INFO NTLMSSP_NEGOTIATE_VERSION NTLMSSP_NEGOTIATE_128 NTLMSSP_NEGOTIATE_KEY_EXCH lm_resp.len : 24 lm_resp.maxlen: 24 lm_resp.offset: 114 nt_resp.len : 198 nt_resp.maxlen: 198 nt_resp.offset: 138 domain.len : 10 domain.maxlen: 10 domain.offset: 72 user.len : 8 user.maxlen: 8 user.offset: 82 host.len : 24 host.maxlen: 24 host.offset: 90 session_key.len : 16 session_key.maxlen: 16 session_key.offset: 336 product: 5.2.3790 (Windows Server 2003) ntlm_revision_current: 0x0F (NTLMSSP_REVISION_W2K3) lm_resp: A1E92EFE4E078BF7C5C0049ACC6166C2D6AE875CB0FDAA41 nt_resp raw: 8DC0C800AE76ECA606D2B9FBEBD04731010100000000000059519A1727B9CA01D6AE875CB0FDAA410000000002000A0043004F0053004D004F000100180043004F0053004D004F002D004F00430053002D00520032000400160063006F0073006D006F002E006C006F00630061006C000300300063006F0073006D006F002D006F00630073002D00720032002E0063006F0073006D006F002E006C006F00630061006C000500160063006F0073006D006F002E006C006F00630061006C000000000000000000 nt_resp: 8DC0C800AE76ECA606D2B9FBEBD04731 target_info raw: 02000A0043004F0053004D004F000100180043004F0053004D004F002D004F00430053002D00520032000400160063006F0073006D006F002E006C006F00630061006C000300300063006F0073006D006F002D006F00630073002D00720032002E0063006F0073006D006F002E006C006F00630061006C000500160063006F0073006D006F002E006C006F00630061006C0000000000 response_version: 1 hi_response_version: 1 time: 59519A1727B9CA01 - Mon Mar 01 10:08:08 2010 client_challenge: D6AE875CB0FDAA41 MsvAvNbDomainName: COSMO MsvAvNbComputerName: COSMO-OCS-R2 MsvAvDnsDomainName: cosmo.local MsvAvDnsComputerName: cosmo-ocs-r2.cosmo.local MsvAvDnsTreeName: cosmo.local ----------- end of nt_resp v2 ----------- domain: COSMO user: User host: COSMO-OCS-R2 session_key: 31CB739E1CA80A498591E8AE797115E4 */ request = "REGISTER sip:cosmo.local SIP/2.0\r\n" "Via: SIP/2.0/TLS 192.168.172.6:12723\r\n" "Max-Forwards: 70\r\n" "From: ;tag=3e49177a52;epid=c8ca638a15\r\n" "To: \r\n" "Call-ID: 4037df9284354df39065195bd57a4b14\r\n" "CSeq: 3 REGISTER\r\n" "Contact: ;methods=\"INVITE, MESSAGE, INFO, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY\";proxy=replace;+sip.instance=\"\"\r\n" "User-Agent: UCCAPI/3.5.6907.0 OC/3.5.6907.0 (Microsoft Office Communicator 2007 R2)\r\n" "Supported: gruu-10, adhoclist, msrtc-event-categories\r\n" "Supported: ms-forking\r\n" "ms-keep-alive: UAC;hop-hop=yes\r\n" "Event: registration\r\n" "Proxy-Authorization: NTLM qop=\"auth\", realm=\"SIP Communications Service\", opaque=\"2BDBAC9D\", targetname=\"cosmo-ocs-r2.cosmo.local\", version=4, gssapi-data=\"TlRMTVNTUAADAAAAGAAYAHIAAADGAMYAigAAAAoACgBIAAAACAAIAFIAAAAYABgAWgAAABAAEABQAQAAVYKYYgUCzg4AAAAPQwBPAFMATQBPAFUAcwBlAHIAQwBPAFMATQBPAC0ATwBDAFMALQBSADIAoeku/k4Hi/fFwASazGFmwtauh1yw/apBjcDIAK527KYG0rn769BHMQEBAAAAAAAAWVGaFye5ygHWrodcsP2qQQAAAAACAAoAQwBPAFMATQBPAAEAGABDAE8AUwBNAE8ALQBPAEMAUwAtAFIAMgAEABYAYwBvAHMAbQBvAC4AbABvAGMAYQBsAAMAMABjAG8AcwBtAG8ALQBvAGMAcwAtAHIAMgAuAGMAbwBzAG0AbwAuAGwAbwBjAGEAbAAFABYAYwBvAHMAbQBvAC4AbABvAGMAYQBsAAAAAAAAAAAAMctznhyoCkmFkeiueXEV5A==\", crand=\"13317733\", cnum=\"1\", response=\"0100000029618e9651b65a7764000000\"\r\n" "Content-Length: 0\r\n" "\r\n"; request_sig = "<13317733><1><4037df9284354df39065195bd57a4b14><3><3e49177a52><><><><>"; //Signature: //0100000029618e9651b65a7764000000 response = "SIP/2.0 200 OK\r\n" "ms-keep-alive: UAS; tcp=no; hop-hop=yes; end-end=no; timeout=300\r\n" "Authentication-Info: NTLM rspauth=\"01000000E615438A917661BE64000000\", srand=\"9616454F\", snum=\"1\", opaque=\"2BDBAC9D\", qop=\"auth\", targetname=\"cosmo-ocs-r2.cosmo.local\", realm=\"SIP Communications Service\"\r\n" "From: \"User\";tag=3e49177a52;epid=c8ca638a15\r\n" "To: ;tag=5E61CCD925D17E043D9A74835A88F664\r\n" "Call-ID: 4037df9284354df39065195bd57a4b14\r\n" "CSeq: 3 REGISTER\r\n" "Via: SIP/2.0/TLS 192.168.172.6:12723;ms-received-port=12723;ms-received-cid=2600\r\n" "Contact: ;expires=7200;+sip.instance=\"\";gruu=\"sip:user@cosmo.local;opaque=user:epid:21nYNIVlkV-jtN6FPBU0fQAA;gruu\"\r\n" "Expires: 7200\r\n" "presence-state: register-action=\"added\"\r\n" "Allow-Events: vnd-microsoft-provisioning,vnd-microsoft-roaming-contacts,vnd-microsoft-roaming-ACL,presence,presence.wpending,vnd-microsoft-roaming-self,vnd-microsoft-provisioning-v2\r\n" "Supported: adhoclist\r\n" "Server: RTC/3.5\r\n" "Supported: msrtc-event-categories\r\n" "Content-Length: 0\r\n" "\r\n"; response_sig = "<9616454F><1><4037df9284354df39065195bd57a4b14><3><3e49177a52><5E61CCD925D17E043D9A74835A88F664><><><7200><200>"; //Signature: //01000000E615438A917661BE64000000 use_ntlm_v2 = TRUE; flags = 0 | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_DATAGRAM | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | NTLMSSP_NEGOTIATE_IDENTIFY | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_VERSION | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_KEY_EXCH; /* global struct */ test_version.product_major_version = 5; test_version.product_minor_version = 2; test_version.product_build = GUINT16_FROM_LE(3790); test_version.ntlm_revision_current = 0x0F; NTOWFv2 (password2, user2, domain2, response_key_nt); NTOWFv2 (password2, user2, domain2, response_key_lm); { int ntlmssp_nt_resp_len; int target_info2_len; guint8 *nonce2; guint8 *target_info2; guint64 *buff2; /* buff2 points to correctly aligned memory. Disable alignment check */ hex_str_to_buff("59519A1727B9CA01", (void *)&buff2); /* global var */ test_time_val = GUINT64_FROM_LE(*buff2); g_free(buff2); buff2 = NULL; target_info2_len = hex_str_to_buff("02000A0043004F0053004D004F000100180043004F0053004D004F002D004F00430053002D00520032000400160063006F0073006D006F002E006C006F00630061006C000300300063006F0073006D006F002D006F00630073002D00720032002E0063006F0073006D006F002E006C006F00630061006C000500160063006F0073006D006F002E006C006F00630061006C0000000000", &target_info2); hex_str_to_buff("DD1BAAF4E7E218D1", &nonce2); /* buff2 points to correctly aligned memory. Disable alignment check */ hex_str_to_buff("D6AE875CB0FDAA41", (void *)&buff2); /* global buff */ memcpy(test_client_challenge, buff2, 8); g_free(buff2); ntlmssp_nt_resp_len = (16 + (32+target_info2_len)); { guchar nt_challenge_response_v2_2 [ntlmssp_nt_resp_len]; printf ("\n\nTesting (NTLMv2 / OC 2007 R2) LM Response Generation\n"); printf ( "Testing (NTLMv2 / OC 2007 R2) NT Response Generation\n"); compute_response(flags, response_key_nt, response_key_lm, nonce2, test_client_challenge, test_time_val, target_info2, /* target_info */ target_info2_len, /* target_info_len */ lm_challenge_response, /* out */ nt_challenge_response_v2_2, /* out */ session_base_key); /* out */ g_free(target_info2); assert_equal("A1E92EFE4E078BF7C5C0049ACC6166C2D6AE875CB0FDAA41", lm_challenge_response, 24, TRUE); assert_equal("8DC0C800AE76ECA606D2B9FBEBD04731", nt_challenge_response_v2_2, 16, TRUE); /* the ref string is taken from binary dump of AUTHENTICATE_MESSAGE */ assert_equal("8DC0C800AE76ECA606D2B9FBEBD04731010100000000000059519A1727B9CA01D6AE875CB0FDAA410000000002000A0043004F0053004D004F000100180043004F0053004D004F002D004F00430053002D00520032000400160063006F0073006D006F002E006C006F00630061006C000300300063006F0073006D006F002D006F00630073002D00720032002E0063006F0073006D006F002E006C006F00630061006C000500160063006F0073006D006F002E006C006F00630061006C000000000000000000", nt_challenge_response_v2_2, ntlmssp_nt_resp_len, TRUE); } KXKEY(flags, session_base_key, lm_challenge_response, nonce2, key_exchange_key); g_free(nonce2); } //as in the Type3 message { guint8 *encrypted_random_session_key2; hex_str_to_buff("31CB739E1CA80A498591E8AE797115E4", &encrypted_random_session_key2); /* global buff - test_random_session_key */ //decoding exported_session_key RC4K (key_exchange_key, 16, encrypted_random_session_key2, 16, test_random_session_key); g_free(encrypted_random_session_key2); } SIGNKEY (test_random_session_key, TRUE, client_sign_key); SEALKEY (flags, test_random_session_key, TRUE, client_seal_key); SIGNKEY (test_random_session_key, FALSE, server_sign_key); SEALKEY (flags, test_random_session_key, FALSE, server_seal_key); printf ("\n\nTesting (NTLMv2 / OC 2007 R2) Message Parsing, Signing, and Verification\nClient request\n(Authentication Protocol version 4)\n"); msg = sipmsg_parse_msg(request); memset(&msgbd, 0, sizeof(struct sipmsg_breakdown)); msgbd.msg = msg; sipmsg_breakdown_parse(&msgbd, "SIP Communications Service", "cosmo-ocs-r2.cosmo.local", NULL); msg_str = sipmsg_breakdown_get_string(4, &msgbd); assert_equal (request_sig, (guchar *)msg_str, strlen(request_sig), FALSE); sip_sec_ntlm_sipe_signature_make (flags, msg_str, 0, client_sign_key, client_seal_key, mac); sipmsg_breakdown_free(&msgbd); assert_equal ("0100000029618e9651b65a7764000000", mac, 16, TRUE); /* sig = buff_to_hex_str((guint8 *)mac, 16); */ printf ("\n\nTesting (NTLMv2 / OC 2007 R2) Message Parsing, Signing, and Verification\nServer response\n(Authentication Protocol version 4)\n"); msg = sipmsg_parse_msg(response); memset(&msgbd, 0, sizeof(struct sipmsg_breakdown)); msgbd.msg = msg; sipmsg_breakdown_parse(&msgbd, "SIP Communications Service", "cosmo-ocs-r2.cosmo.local", NULL); msg_str = sipmsg_breakdown_get_string(4, &msgbd); assert_equal (response_sig, (guchar *)msg_str, strlen(response_sig), FALSE); // server keys here sip_sec_ntlm_sipe_signature_make (flags, msg_str, 0, server_sign_key, server_seal_key, mac); sipmsg_breakdown_free(&msgbd); assert_equal ("01000000E615438A917661BE64000000", mac, 16, TRUE); /* sig = buff_to_hex_str((guint8 *)mac, 16); */ printf ("\n\nTesting (NTLMv2 / OC 2007 R2) MAC - client signing\n"); MAC (flags, (gchar*)request_sig,strlen(request_sig), client_sign_key,16, client_seal_key,16, 0, 100, mac); assert_equal("0100000029618e9651b65a7764000000", mac, 16, TRUE); printf ("\n\nTesting (NTLMv2 / OC 2007 R2) MAC - server's verifying\n"); MAC (flags, (gchar*)response_sig,strlen(response_sig), server_sign_key,16, server_seal_key,16, 0, 100, mac); assert_equal("01000000E615438A917661BE64000000", mac, 16, TRUE); printf ("\n\nTesting (NTLMv2 / OC 2007 R2) Type3 generation test\n"); { guchar *client_sign_key2; guchar *server_sign_key2; guchar *client_seal_key2; guchar *server_seal_key2; guchar *server_challenge = NULL; guint64 time_val2 = 0; guchar *target_info3 = NULL; int target_info3_len = 0; guint32 flags2; SipSecBuffer in_buff; SipSecBuffer out_buff; memset(&in_buff, 0, sizeof(in_buff)); memset(&out_buff, 0, sizeof(out_buff)); in_buff.length = hex_str_to_buff(type2_hex, (guint8 **)&(in_buff.value)); sip_sec_ntlm_parse_challenge(in_buff, &flags2, /* out */ &server_challenge, &time_val2, &target_info3, &target_info3_len); sip_sec_ntlm_gen_authenticate(&client_sign_key2, &server_sign_key2, &client_seal_key2, &server_seal_key2, user2, password2, host2, domain2, server_challenge, test_time_val, target_info3, target_info3_len, 0, &out_buff, &flags2); g_free(server_challenge); g_free(target_info3); assert_equal(type3_hex, out_buff.value, out_buff.length, TRUE); } printf ("\nFinished With Tests; %d successs %d failures\n", successes, failures); sip_sec_destroy__ntlm(); return(failures == 0); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec-ntlm.c ================================================ /** * @file sip-sec-ntlm.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * Copyright (C) 2009, 2010 pier11 * Copyright (C) 2008 Novell, Inc. * Modify 2007, Anibal Avelar * Copyright (C) 2005, Thomas Butter * * Implemented with reference to the follow documentation: * - http://davenport.sourceforge.net/ntlm.html * - MS-NLMP: http://msdn.microsoft.com/en-us/library/cc207842.aspx * - MS-SIP : http://msdn.microsoft.com/en-us/library/cc246115.aspx * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Byte order policy: * * - NTLM messages (byte streams) should be in LE (Little-Endian) byte order. * - internal int16, int32, int64 should contain proper values. * For example: 01 00 00 00 LE should be translated to (int32)1 * - When reading/writing from/to NTLM message appropriate conversion should * be taken to properly present integer values. glib's "Byte Order Macros" * should be used for that, for example GUINT32_FROM_LE * * NOTE: The Byte Order Macros can have side effects! * Do *NOT* make any calculations inside the macros! * * - All calculations should be made in dedicated local variables (system-endian), * not in NTLM (LE) structures. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_LANGINFO_CODESET #include #endif /* HAVE_LANGINFO_CODESET */ #include "sipe-common.h" #include "sip-sec.h" #include "sip-sec-mech.h" #include "sip-sec-ntlm.h" #include "sipe-backend.h" #include "sipe-crypt.h" #include "sipe-digest.h" #include "sipe-utils.h" #include "md4.h" /* [MS-NLMP] */ #define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 /* A */ #define NTLMSSP_NEGOTIATE_OEM 0x00000002 /* B */ #define NTLMSSP_REQUEST_TARGET 0x00000004 /* C */ #define r9 0x00000008 /* r9 */ #define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* D */ #define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* E */ #define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 /* F */ #define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 /* G */ #define r8 0x00000100 /* r8 */ #define NTLMSSP_NEGOTIATE_NTLM 0x00000200 /* H */ #define NTLMSSP_NEGOTIATE_NT_ONLY 0x00000400 /* I */ #define anonymous 0x00000800 /* J */ #define NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED 0x00001000 /* K */ #define NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED 0x00002000 /* L */ #define r7 0x00004000 /* r7 */ #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 /* M */ #define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 /* N */ #define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 /* O */ #define r6 0x00040000 /* r6 */ #define NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY 0x00080000 /* P */ #define NTLMSSP_NEGOTIATE_IDENTIFY 0x00100000 /* Q */ #define r5 0x00200000 /* r5 */ #define NTLMSSP_REQUEST_NON_NT_SESSION_KEY 0x00400000 /* R */ #define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 /* S */ #define r4 0x01000000 /* r4 */ #define NTLMSSP_NEGOTIATE_VERSION 0x02000000 /* T */ #define r3 0x04000000 /* r3 */ #define r2 0x08000000 /* r2 */ #define r1 0x10000000 /* r1 */ #define NTLMSSP_NEGOTIATE_128 0x20000000 /* U */ #define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 /* V */ #define NTLMSSP_NEGOTIATE_56 0x80000000 /* W */ /* AvId */ #define MsvAvEOL 0 #define MsvAvNbComputerName 1 #define MsvAvNbDomainName 2 #define MsvAvDnsComputerName 3 #define MsvAvDnsDomainName 4 /** @since Windows XP */ #define MsvAvDnsTreeName 5 /** @since Windows XP */ #define MsvAvFlags 6 /** @since Windows Vista */ #define MsvAvTimestamp 7 /** @since Windows Vista */ #define MsAvRestrictions 8 /** @since Windows 7 */ #define MsvAvTargetName 9 /** @since Windows 7 */ #define MsvChannelBindings 10 /* time_t <-> (guint64) time_val conversion */ #define TIME_VAL_FACTOR 10000000 #define TIME_VAL_OFFSET 116444736000000000LL #define TIME_T_TO_VAL(time_t) (((guint64)(time_t)) * TIME_VAL_FACTOR + TIME_VAL_OFFSET) #define TIME_VAL_TO_T(time_val) ((time_t)((GUINT64_FROM_LE((time_val)) - TIME_VAL_OFFSET) / TIME_VAL_FACTOR)) /* 8 bytes */ /* LE (Little Endian) byte order */ struct version { guint8 product_major_version; guint8 product_minor_version; guint16 product_build; guint8 zero2[3]; guint8 ntlm_revision_current; }; /* * NTLMv1 is no longer used except in tests. R.I.P. * * It remains in this file only for documentary purposes */ #ifdef _SIPE_COMPILING_TESTS static gboolean use_ntlm_v2 = FALSE; guint64 test_time_val = 0; /* actual time in implementation */ guchar test_client_challenge [8]; /* random in implementation */ guchar test_random_session_key[16]; /* random in implementation */ struct version test_version; /* hard-coded in implementation */ #endif /* Minimum set of common features we need to work. */ /* we operate in NTLMv2 mode */ #define NEGOTIATE_FLAGS_COMMON_MIN \ ( NTLMSSP_NEGOTIATE_UNICODE | \ NTLMSSP_NEGOTIATE_NTLM | \ NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \ NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | \ NTLMSSP_NEGOTIATE_TARGET_INFO \ ) /* Negotiate flags for connection-based mode. Nice to have but optional. */ #define NEGOTIATE_FLAGS_CONN \ ( NEGOTIATE_FLAGS_COMMON_MIN | \ NTLMSSP_NEGOTIATE_VERSION | \ NTLMSSP_NEGOTIATE_128 | \ NTLMSSP_NEGOTIATE_56 | \ NTLMSSP_REQUEST_TARGET \ ) /* Extra negotiate flags required in connectionless NTLM */ #define NEGOTIATE_FLAGS_CONNLESS_EXTRA \ ( NTLMSSP_NEGOTIATE_SIGN | \ NTLMSSP_NEGOTIATE_DATAGRAM | \ NTLMSSP_NEGOTIATE_IDENTIFY | \ NTLMSSP_NEGOTIATE_KEY_EXCH \ ) /* Negotiate flags required in connectionless NTLM */ #define NEGOTIATE_FLAGS_CONNLESS \ ( NEGOTIATE_FLAGS_CONN | \ NEGOTIATE_FLAGS_CONNLESS_EXTRA \ ) #define NTLMSSP_LN_OR_NT_KEY_LEN 16 #define NTLMSSP_LM_RESP_LEN 24 #define NTLMSSP_SESSION_KEY_LEN 16 #define IS_FLAG(flags, flag) (((flags) & (flag)) == (flag)) /* 4 bytes */ /* LE (Little Endian) byte order */ struct av_pair { guint16 av_id; guint16 av_len; /* value */ }; /* to meet sparc's alignment requirement */ #define ALIGN_AV \ memcpy(&av_aligned, av, sizeof(av_aligned)); \ av_id = GUINT16_FROM_LE(av_aligned.av_id); \ av_len = GUINT16_FROM_LE(av_aligned.av_len) #define ALIGN_AV_LOOP_START \ struct av_pair av_aligned; \ guint16 av_id; \ guint16 av_len; \ ALIGN_AV; \ while (av_id != MsvAvEOL) { \ gchar *av_value = ((gchar *)av) + \ sizeof(struct av_pair); \ switch (av_id) #define ALIGN_AV_LOOP_END \ av = av_value + av_len; \ ALIGN_AV; \ } /* 8 bytes */ /* LE (Little Endian) byte order */ struct smb_header { guint16 len; guint16 maxlen; guint32 offset; }; /* LE (Little Endian) byte order */ struct ntlm_message { guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/ guint32 type; /* 0x00000003 */ }; /* LE (Little Endian) byte order */ struct negotiate_message { guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' */ guint32 type; /* 0x00000001 */ guint32 flags; /* 0xb203 */ struct smb_header domain; struct smb_header host; struct version ver; /* payload * - DomainName (always ASCII) * - WorkstationName (always ASCII) */ }; /* LE (Little Endian) byte order */ struct challenge_message { guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/ guint32 type; /* 0x00000002 */ struct smb_header target_name; guint32 flags; /* 0x8201 */ guint8 nonce[8]; guint8 zero1[8]; struct smb_header target_info; struct version ver; /* payload * - TargetName (negotiated encoding) * - TargetInfo (a sequence of AV_PAIR structures) (always Unicode) */ }; /* LE (Little Endian) byte order */ struct authenticate_message { guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/ guint32 type; /* 0x00000003 */ /** LmChallengeResponseFields */ struct smb_header lm_resp; /** NtChallengeResponseFields */ struct smb_header nt_resp; /** DomainNameFields */ struct smb_header domain; /** UserNameFields */ struct smb_header user; /** WorkstationFields */ struct smb_header host; /** EncryptedRandomSessionKeyFields */ struct smb_header session_key; guint32 flags; struct version ver; //guint8 mic[16]; /* payload * - LmChallengeResponse * - NtChallengeResponse * - DomainName (negotiated encoding) * - UserName (negotiated encoding) * - Workstation (negotiated encoding) * - EncryptedRandomSessionKey */ }; #ifndef HAVE_LANGINFO_CODESET #ifdef __sun__ static char SIPE_DEFAULT_CODESET[] = "US-ASCII"; #else static char SIPE_DEFAULT_CODESET[] = "ANSI_X3.4-1968"; #endif #endif /* Private Methods */ /* Utility Functions */ static GIConv convert_from_utf16le = (GIConv)-1; static GIConv convert_to_utf16le = (GIConv)-1; /* Analyzer only needs the _describe() functions */ #ifndef _SIPE_COMPILING_ANALYZER static gsize unicode_strconvcopy(gchar *dest, const gchar *source, gsize remlen) { gsize inbytes = strlen(source); gsize outbytes = remlen; if (remlen) g_iconv(convert_to_utf16le, (gchar **)&source, &inbytes, &dest, &outbytes); return(remlen - outbytes); } #endif /* !_SIPE_COMPILING_ANALYZER */ /* UTF-16LE to native encoding * Must be g_free'd after use */ static gchar * unicode_strconvcopy_back(const gchar *source, gsize len) { gsize outbytes = 2 * len; gchar *dest = g_new0(gchar, outbytes + 1); gchar *outbuf = dest; g_iconv(convert_from_utf16le, (gchar **)&source, &len, &outbuf, &outbytes); return dest; } /* Analyzer only needs the _describe() functions */ #ifndef _SIPE_COMPILING_ANALYZER /* crc32 source copy from gg's common.c */ static guint32 crc32_table[256]; static int crc32_initialized = 0; static void crc32_make_table() { guint32 h = 1; unsigned int i, j; memset(crc32_table, 0, sizeof(crc32_table)); for (i = 128; i; i >>= 1) { h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0); for (j = 0; j < 256; j += 2 * i) crc32_table[i + j] = crc32_table[j] ^ h; } crc32_initialized = 1; } static guint32 crc32(guint32 crc, const guint8 *buf, int len) { if (!crc32_initialized) crc32_make_table(); if (!buf || len < 0) return crc; crc ^= 0xffffffffL; while (len--) crc = (crc >> 8) ^ crc32_table[(crc ^ *buf++) & 0xff]; return crc ^ 0xffffffffL; } static guint32 CRC32 (const char *msg, int len) { guint32 crc = 0L; crc = crc32(crc, (guint8 *) msg, len); return crc; } /* Cyphers */ #ifdef _SIPE_COMPILING_TESTS static void setup_des_key(const unsigned char key_56[], unsigned char *key) { key[0] = key_56[0]; key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); key[7] = (key_56[6] << 1) & 0xFF; } /* (k = 7 byte key, d = 8 byte data) returns 8 bytes in results */ static void DES (const unsigned char *k, const unsigned char *d, unsigned char * results) { unsigned char key[8]; setup_des_key(k, key); sipe_crypt_des(key, d, 8, results); } /* (K = 21 byte key, D = 8 bytes of data) returns 24 bytes in results: */ static void DESL (const unsigned char *k, const unsigned char *d, unsigned char * results) { unsigned char keys[21]; /* Copy the first 16 bytes */ memcpy(keys, k, 16); /* Zero out the last 5 bytes of the key */ memset(keys + 16, 0, 5); DES(keys, d, results); DES(keys + 7, d, results + 8); DES(keys + 14, d, results + 16); } #endif #define RC4K(key, key_len, plain, plain_len, encrypted) \ sipe_crypt_rc4((key), (key_len), (plain), (plain_len), (encrypted)) /* out 16 bytes */ #define MD4(d, len, result) sipe_digest_md4((d), (len), (result)) /* out 16 bytes */ #define MD5(d, len, result) sipe_digest_md5((d), (len), (result)) /* out 16 bytes */ /* static void HMACT64 (const unsigned char *key, int key_len, const unsigned char *data, int data_len, unsigned char *result) { int i; unsigned char ibuff[64 + data_len]; unsigned char obuff[64 + 16]; if (key_len > 64) key_len = 64; for (i = 0; i < key_len; i++) { ibuff[i] = key[i] ^ 0x36; obuff[i] = key[i] ^ 0x5c; } for (i = key_len; i < 64; i++) { ibuff[i] = 0x36; obuff[i] = 0x5c; } memcpy(ibuff+64, data, data_len); MD5 (ibuff, 64 + data_len, obuff+64); MD5 (obuff, 64 + 16, result); } #define HMAC_MD5 HMACT64 */ /* out 16 bytes */ #define HMAC_MD5(key, key_len, data, data_len, result) \ sipe_digest_hmac_md5((key), (key_len), (data), (data_len), (result)) /* NTLM Core Methods */ static void NONCE(unsigned char *buffer, int num) { int i; for (i = 0; i < num; i++) { buffer[i] = (rand() & 0xff); } } #ifdef _SIPE_COMPILING_TESTS static void Z(unsigned char *buffer, int num) { memset(buffer, 0, num); } static void LMOWFv1 (const char *password, SIPE_UNUSED_PARAMETER const char *user, SIPE_UNUSED_PARAMETER const char *domain, unsigned char *result) { /* "KGS!@#$%" */ unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }; unsigned char uppercase_password[14]; int i; int len = strlen(password); if (len > 14) { len = 14; } // Uppercase password for (i = 0; i < len; i++) { uppercase_password[i] = g_ascii_toupper(password[i]); } // Zero the rest for (; i < 14; i++) { uppercase_password[i] = 0; } DES (uppercase_password, magic, result); DES (uppercase_password + 7, magic, result + 8); } #endif /* Define NTOWFv1(Passwd, User, UserDom) as MD4(UNICODE(Passwd)) EndDefine */ /* out 16 bytes */ static void NTOWFv1 (const char* password, SIPE_UNUSED_PARAMETER const char *user, SIPE_UNUSED_PARAMETER const char *domain, unsigned char *result) { int len_u = 2 * strlen(password); // utf16 should not be more unsigned char *unicode_password = g_malloc(len_u); /* well, if allocation failed the rest will crash & burn soon anyway... */ if (unicode_password) { len_u = unicode_strconvcopy((gchar *)unicode_password, password, len_u); MD4 (unicode_password, len_u, result); g_free(unicode_password); } } /* Define NTOWFv2(Passwd, User, UserDom) as HMAC_MD5( MD4(UNICODE(Passwd)), ConcatenationOf( Uppercase(User), UserDom ) ) EndDefine */ /* out 16 bytes */ static void NTOWFv2 (const char* password, const char *user, const char *domain, unsigned char *result) { unsigned char response_key_nt_v1 [16]; int len_user = user ? strlen(user) : 0; int len_domain = strlen(domain); int len_user_u = 2 * len_user; // utf16 should not be more int len_domain_u = 2 * len_domain; // utf16 should not be more unsigned char *user_upper = g_malloc(len_user + 1); unsigned char *buff = g_malloc((len_user + len_domain)*2); int i; /* Uppercase user */ for (i = 0; i < len_user; i++) { user_upper[i] = g_ascii_toupper(user[i]); } user_upper[len_user] = 0; len_user_u = unicode_strconvcopy((gchar *)buff, (gchar *)user_upper, len_user_u); len_domain_u = unicode_strconvcopy((gchar *)(buff+len_user_u), (gchar *)domain, len_domain_u); NTOWFv1(password, user, domain, response_key_nt_v1); HMAC_MD5(response_key_nt_v1, 16, buff, len_user_u + len_domain_u, result); g_free(buff); g_free(user_upper); } static void compute_response(const guint32 neg_flags, const unsigned char *response_key_nt, const unsigned char *response_key_lm, const guint8 *server_challenge, const guint8 *client_challenge, const guint64 time_val, const guint8 *target_info, int target_info_len, unsigned char *lm_challenge_response, unsigned char *nt_challenge_response, unsigned char *session_base_key) { #ifdef _SIPE_COMPILING_TESTS if (use_ntlm_v2) { #endif /* Responserversion - The 1-byte response version. Currently set to 1. HiResponserversion - The 1-byte highest response version understood by the client. Currently set to 1. Time - The 8-byte little-endian time in GMT. ServerName - The TargetInfo field structure of the CHALLENGE_MESSAGE payload. ClientChallenge - The 8-byte challenge message generated by the client. CHALLENGE_MESSAGE.ServerChallenge - The 8-byte challenge message generated by the server. Define ComputeResponse(NegFlg, ResponseKeyNT, ResponseKeyLM, CHALLENGE_MESSAGE.ServerChallenge, ClientChallenge, Time, ServerName) as Set temp to ConcatenationOf(Responserversion, HiResponserversion, Z(6), //8bytes - 0 Time, //8bytes - 8 ClientChallenge, //8bytes - 16 Z(4), //4bytes - 24 ServerName, //variable - 28 Z(4)) //4bytes - 28+target_info_len Set NTProofStr to HMAC_MD5(ResponseKeyNT, ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge,temp)) Set NtChallengeResponse to ConcatenationOf(NTProofStr, temp) Set LmChallengeResponse to ConcatenationOf( HMAC_MD5(ResponseKeyLM, ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge, ClientChallenge)), ClientChallenge ) Set SessionBaseKey to HMAC_MD5(ResponseKeyNT, NTProofStr) EndDefine */ guint8 tmp [16]; guint8 nt_proof_str [16]; /* client_challenge (8) & temp (temp_len) buff */ unsigned int temp_len = 8+8+8+4+target_info_len+4; guint64 *temp2 = g_malloc0(8 + temp_len); ((guint8 *) temp2)[8+0] = 1; ((guint8 *) temp2)[8+1] = 1; temp2[2] = GUINT64_TO_LE(time_val); /* should be int64 aligned: OK for sparc */ memcpy(((guint8 *) temp2)+8+16, client_challenge, 8); memcpy(((guint8 *) temp2)+8+28, target_info, target_info_len); /* NTProofStr */ //Set NTProofStr to HMAC_MD5(ResponseKeyNT, ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge,temp)) memcpy(temp2, server_challenge, 8); HMAC_MD5(response_key_nt, 16, (guint8*)temp2, 8+temp_len, nt_proof_str); /* NtChallengeResponse */ //Set NtChallengeResponse to ConcatenationOf(NTProofStr, temp) memcpy(nt_challenge_response, nt_proof_str, 16); memcpy(nt_challenge_response+16, temp2+1, temp_len); g_free(temp2); /* SessionBaseKey */ //SessionBaseKey to HMAC_MD5(ResponseKeyNT, NTProofStr) HMAC_MD5(response_key_nt, 16, nt_proof_str, 16, session_base_key); /* lm_challenge_response */ memcpy(tmp, server_challenge, 8); memcpy(tmp+8, client_challenge, 8); HMAC_MD5(response_key_lm, 16, tmp, 16, lm_challenge_response); memcpy(lm_challenge_response+16, client_challenge, 8); #ifndef _SIPE_COMPILING_TESTS /* Not used in NTLMv2 */ (void)neg_flags; #else } else { if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_LM_KEY)) { // @TODO do not even reference nt_challenge_response Z (nt_challenge_response, NTLMSSP_LM_RESP_LEN); DESL (response_key_lm, server_challenge, lm_challenge_response); } else if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) { unsigned char prehash [16]; unsigned char hash [16]; /* nt_challenge_response */ memcpy(prehash, server_challenge, 8); memcpy(prehash + 8, client_challenge, 8); MD5 (prehash, 16, hash); DESL (response_key_nt, hash, nt_challenge_response); /* lm_challenge_response */ memcpy(lm_challenge_response, client_challenge, 8); Z (lm_challenge_response+8, 16); } else { DESL (response_key_nt, server_challenge, nt_challenge_response); if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_NT_ONLY)) { memcpy(lm_challenge_response, nt_challenge_response, NTLMSSP_LM_RESP_LEN); } else { DESL (response_key_lm, server_challenge, lm_challenge_response); } } /* Session Key */ MD4(response_key_nt, 16, session_base_key); // "User Session Key" -> "master key" } #endif } static void KXKEY ( guint32 flags, const unsigned char * session_base_key, const unsigned char * lm_challenge_resonse, const guint8 * server_challenge, /* 8-bytes, nonce */ unsigned char * key_exchange_key) { #ifdef _SIPE_COMPILING_TESTS if (use_ntlm_v2) { #else /* Not used in NTLMv2 */ (void)flags; (void)lm_challenge_resonse; (void)server_challenge; #endif memcpy(key_exchange_key, session_base_key, 16); #ifdef _SIPE_COMPILING_TESTS } else { if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) { /* Define KXKEY(SessionBaseKey, LmChallengeResponse, ServerChallenge) as Set KeyExchangeKey to HMAC_MD5(SessionBaseKey, ConcatenationOf(ServerChallenge, LmChallengeResponse [0..7])) EndDefine */ guint8 tmp[16]; memcpy(tmp, server_challenge, 8); memcpy(tmp+8, lm_challenge_resonse, 8); HMAC_MD5(session_base_key, 16, tmp, 16, key_exchange_key); } else { /* Assume v1 and NTLMSSP_REQUEST_NON_NT_SESSION_KEY not set */ memcpy(key_exchange_key, session_base_key, 16); } } #endif } /* If (Mode equals "Client") Set SignKey to MD5(ConcatenationOf(RandomSessionKey, "session key to client-to-server signing key magic constant")) Else Set SignKey to MD5(ConcatenationOf(RandomSessionKey, "session key to server-to-client signing key magic constant")) Endif */ static void SIGNKEY (const unsigned char * random_session_key, gboolean client, unsigned char * result) { const char * magic = client ? "session key to client-to-server signing key magic constant" : "session key to server-to-client signing key magic constant"; int len = strlen(magic) + 1; unsigned char *md5_input = g_malloc(16 + len); memcpy(md5_input, random_session_key, 16); memcpy(md5_input + 16, magic, len); MD5 (md5_input, len + 16, result); g_free(md5_input); } /* Define SEALKEY(NegotiateFlags, RandomSessionKey, Mode) as If (NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY flag is set in NegFlg) If ( NTLMSSP_NEGOTIATE_128 is set in NegFlg) Set SealKey to RandomSessionKey ElseIf ( NTLMSSP_NEGOTIATE_56 flag is set in NegFlg) Set SealKey to RandomSessionKey[0..6] Else Set SealKey to RandomSessionKey[0..4] Endif If (Mode equals "Client") Set SealKey to MD5(ConcatenationOf(SealKey, "session key to client-to-server sealing key magic constant")) Else Set SealKey to MD5(ConcatenationOf(SealKey, "session key to server-to-client sealing key magic constant")) Endif ElseIf (NTLMSSP_NEGOTIATE_56 flag is set in NegFlg) Set SealKey to ConcatenationOf(RandomSessionKey[0..6], 0xA0) Else Set SealKey to ConcatenationOf(RandomSessionKey[0..4], 0xE5, 0x38, 0xB0) Endif EndDefine */ /* out 16 bytes or 8 bytes depending if Ext.Sess.Sec is negotiated */ static void SEALKEY (guint32 flags, const unsigned char * random_session_key, gboolean client, unsigned char * result) { if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) { const char * magic = client ? "session key to client-to-server sealing key magic constant" : "session key to server-to-client sealing key magic constant"; int len = strlen(magic) + 1; unsigned char *md5_input = g_malloc(16 + len); int key_len; if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_128)) { SIPE_DEBUG_INFO_NOFORMAT("NTLM SEALKEY(): 128-bit key (Extended session security)"); key_len = 16; } else if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_56)) { SIPE_DEBUG_INFO_NOFORMAT("NTLM SEALKEY(): 56-bit key (Extended session security)"); key_len = 7; } else { SIPE_DEBUG_INFO_NOFORMAT("NTLM SEALKEY(): 40-bit key (Extended session security)"); key_len = 5; } memcpy(md5_input, random_session_key, key_len); memcpy(md5_input + key_len, magic, len); MD5 (md5_input, key_len + len, result); g_free(md5_input); } else if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_LM_KEY)) /* http://davenport.sourceforge.net/ntlm.html#ntlm1KeyWeakening */ { if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_56)) { SIPE_DEBUG_INFO_NOFORMAT("NTLM SEALKEY(): 56-bit key"); memcpy(result, random_session_key, 7); result[7] = 0xA0; } else { SIPE_DEBUG_INFO_NOFORMAT("NTLM SEALKEY(): 40-bit key"); memcpy(result, random_session_key, 5); result[5] = 0xE5; result[6] = 0x38; result[7] = 0xB0; } } else { SIPE_DEBUG_INFO_NOFORMAT("NTLM SEALKEY(): 128-bit key"); memcpy(result, random_session_key, 16); } } /* = for Extended Session Security = Version (4 bytes): A 32-bit unsigned integer that contains the signature version. This field MUST be 0x00000001. Checksum (8 bytes): An 8-byte array that contains the checksum for the message. SeqNum (4 bytes): A 32-bit unsigned integer that contains the NTLM sequence number for this application message. = if Extended Session Security is NOT negotiated = Version (4 bytes): A 32-bit unsigned integer that contains the signature version. This field MUST be 0x00000001. RandomPad (4 bytes): A 4-byte array that contains the random pad for the message. Checksum (4 bytes): A 4-byte array that contains the checksum for the message. SeqNum (4 bytes): A 32-bit unsigned integer that contains the NTLM sequence number for this application message. --- 0x00000001, RC4K(RandomPad), RC4K(CRC32(Message)), RC4K(0x00000000) XOR (application supplied SeqNum) -- RC4(X) xor X xor Y = RC4(Y) Version(4), Checksum(8), SeqNum(4) -- for ext.sess.sec. Version(4), RandomPad(4), Checksum(4), SeqNum(4) */ /** MAC(Handle, SigningKey, SeqNum, Message) */ /* out 16 bytes */ static void MAC (guint32 flags, const char *buf, unsigned int buf_len, unsigned char *sign_key, unsigned long sign_key_len, unsigned char *seal_key, unsigned long seal_key_len, guint32 random_pad, guint32 sequence, guint32 *result) { guint32 *res_ptr; if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) { /* Define MAC(Handle, SigningKey, SeqNum, Message) as Set NTLMSSP_MESSAGE_SIGNATURE.Version to 0x00000001 Set NTLMSSP_MESSAGE_SIGNATURE.Checksum to HMAC_MD5(SigningKey, ConcatenationOf(SeqNum, Message))[0..7] Set NTLMSSP_MESSAGE_SIGNATURE.SeqNum to SeqNum Set SeqNum to SeqNum + 1 EndDefine */ /* If a key exchange key is negotiated Define MAC(Handle, SigningKey, SeqNum, Message) as Set NTLMSSP_MESSAGE_SIGNATURE.Version to 0x00000001 Set NTLMSSP_MESSAGE_SIGNATURE.Checksum to RC4(Handle, HMAC_MD5(SigningKey, ConcatenationOf(SeqNum, Message))[0..7]) Set NTLMSSP_MESSAGE_SIGNATURE.SeqNum to SeqNum Set SeqNum to SeqNum + 1 EndDefine */ unsigned char seal_key_ [16]; guchar hmac[16]; guint32 *tmp = g_malloc(4 + buf_len); /* SealingKey' = MD5(ConcatenationOf(SealingKey, SequenceNumber)) RC4Init(Handle, SealingKey') */ if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_DATAGRAM)) { guint32 tmp2[4+1]; memcpy(tmp2, seal_key, seal_key_len); tmp2[4] = GUINT32_TO_LE(sequence); MD5 ((guchar *)tmp2, sizeof(tmp2), seal_key_); } else { memcpy(seal_key_, seal_key, seal_key_len); } SIPE_DEBUG_INFO_NOFORMAT("NTLM MAC(): Extended Session Security"); res_ptr = result; res_ptr[0] = GUINT32_TO_LE(1); // 4 bytes res_ptr[3] = GUINT32_TO_LE(sequence); res_ptr = tmp; res_ptr[0] = GUINT32_TO_LE(sequence); memcpy(tmp+1, buf, buf_len); HMAC_MD5(sign_key, sign_key_len, (guchar *)tmp, 4 + buf_len, hmac); g_free(tmp); if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_KEY_EXCH)) { SIPE_DEBUG_INFO_NOFORMAT("NTLM MAC(): Key Exchange"); RC4K(seal_key_, seal_key_len, hmac, 8, (guchar *)(result+1)); } else { SIPE_DEBUG_INFO_NOFORMAT("NTLM MAC(): *NO* Key Exchange"); memcpy(result+1, hmac, 8); } } else { /* The content of the first 4 bytes is irrelevant */ guint32 crc = CRC32(buf, strlen(buf)); guint32 plaintext [] = { GUINT32_TO_LE(0), GUINT32_TO_LE(crc), GUINT32_TO_LE(sequence) }; // 4, 4, 4 bytes SIPE_DEBUG_INFO_NOFORMAT("NTLM MAC(): *NO* Extended Session Security"); RC4K(seal_key, seal_key_len, (const guchar *)plaintext, 12, (guchar *)(result+1)); res_ptr = result; // Highest four bytes are the Version res_ptr[0] = GUINT32_TO_LE(0x00000001); // 4 bytes // Replace the first four bytes of the ciphertext with the random_pad res_ptr[1] = GUINT32_TO_LE(random_pad); // 4 bytes } } /* End Core NTLM Methods */ /** * @param flags (out) flags received from server * @param server_challenge must be g_free()'d after use if requested * @param target_info must be g_free()'d after use if requested */ static void sip_sec_ntlm_parse_challenge(SipSecBuffer in_buff, guint32 *flags, guchar **server_challenge, /* 8 bytes */ guint64 *time_val, guchar **target_info, int *target_info_len) { /* SipSecBuffer.value is g_malloc()'d: use (void *) to remove guint8 alignment */ struct challenge_message *cmsg = (void *)in_buff.value; guint32 host_flags = GUINT32_FROM_LE(cmsg->flags); /* server challenge (nonce) */ if (server_challenge) { *server_challenge = g_memdup(cmsg->nonce, 8); } /* flags */ if (flags) { *flags = host_flags; } /* target_info */ if (cmsg->target_info.len && cmsg->target_info.offset) { void *content = (gchar *)cmsg + GUINT32_FROM_LE(cmsg->target_info.offset); void *av = content; guint16 len = GUINT16_FROM_LE(cmsg->target_info.len); ALIGN_AV_LOOP_START { /* @since Vista */ case MsvAvTimestamp: if (time_val) { guint64 tmp; /* to meet sparc's alignment requirement */ memcpy(&tmp, av_value, sizeof(tmp)); *time_val = GUINT64_FROM_LE(tmp); } break; } ALIGN_AV_LOOP_END; if (target_info_len) { *target_info_len = len; } if (target_info) { *target_info = g_memdup(content, len); } } } /** * @param client_sign_key (out) must be g_free()'d after use * @param server_sign_key (out) must be g_free()'d after use * @param client_seal_key (out) must be g_free()'d after use * @param server_seal_key (out) must be g_free()'d after use * @param flags (in, out) negotiated flags */ static gboolean sip_sec_ntlm_gen_authenticate(guchar **client_sign_key, guchar **server_sign_key, guchar **client_seal_key, guchar **server_seal_key, const gchar *user, const gchar *password, const gchar *hostname, const gchar *domain, const guint8 *server_challenge, /* nonce */ const guint64 time_val, const guint8 *target_info, int target_info_len, gboolean http, SipSecBuffer *out_buff, guint32 *flags) { guint32 orig_flags = http ? NEGOTIATE_FLAGS_CONN : NEGOTIATE_FLAGS_CONNLESS; guint32 neg_flags = (*flags & orig_flags) | NTLMSSP_REQUEST_TARGET; int ntlmssp_nt_resp_len = #ifdef _SIPE_COMPILING_TESTS use_ntlm_v2 ? #endif (16 + (32+target_info_len)) #ifdef _SIPE_COMPILING_TESTS : NTLMSSP_LM_RESP_LEN #endif ; gsize msglen = sizeof(struct authenticate_message) + 2*(strlen(domain) + strlen(user)+ strlen(hostname)) + NTLMSSP_LM_RESP_LEN + ntlmssp_nt_resp_len + (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_KEY_EXCH) ? NTLMSSP_SESSION_KEY_LEN : 0); struct authenticate_message *tmsg; char *tmp; guint32 offset; guint16 len; unsigned char response_key_lm [NTLMSSP_LN_OR_NT_KEY_LEN]; /* 16 */ unsigned char response_key_nt [NTLMSSP_LN_OR_NT_KEY_LEN]; /* 16 */ unsigned char lm_challenge_response [NTLMSSP_LM_RESP_LEN]; /* 24 */ unsigned char *nt_challenge_response = g_malloc(ntlmssp_nt_resp_len); /* variable or 24 */ unsigned char session_base_key [16]; unsigned char key_exchange_key [16]; unsigned char exported_session_key[16]; unsigned char encrypted_random_session_key [16]; unsigned char key [16]; unsigned char client_challenge [8]; guint64 time_vl = time_val ? time_val : TIME_T_TO_VAL(time(NULL)); if (!IS_FLAG(*flags, NEGOTIATE_FLAGS_COMMON_MIN) || !(http || IS_FLAG(*flags, NEGOTIATE_FLAGS_CONNLESS_EXTRA)) || !nt_challenge_response) /* Coverity thinks ntlmssp_nt_resp_len could be 0 */ { SIPE_DEBUG_INFO_NOFORMAT("sip_sec_ntlm_gen_authenticate: received incompatible NTLM NegotiateFlags, exiting."); g_free(nt_challenge_response); return FALSE; } if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_128)) { neg_flags = neg_flags & ~NTLMSSP_NEGOTIATE_56; } tmsg = g_malloc0(msglen); NONCE (client_challenge, 8); #ifdef _SIPE_COMPILING_TESTS memcpy(client_challenge, test_client_challenge, 8); time_vl = test_time_val ? test_time_val : time_vl; if (use_ntlm_v2) { #endif NTOWFv2 (password, user, domain, response_key_nt); memcpy(response_key_lm, response_key_nt, NTLMSSP_LN_OR_NT_KEY_LEN); #ifdef _SIPE_COMPILING_TESTS } else { NTOWFv1 (password, user, domain, response_key_nt); LMOWFv1 (password, user, domain, response_key_lm); } #endif compute_response(neg_flags, response_key_nt, response_key_lm, server_challenge, client_challenge, time_vl, target_info, target_info_len, lm_challenge_response, /* out */ nt_challenge_response, /* out */ session_base_key); /* out */ /* same as session_base_key for * - NTLNv1 w/o Ext.Sess.Sec and * - NTLMv2 */ KXKEY(neg_flags, session_base_key, lm_challenge_response, server_challenge, key_exchange_key); if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_KEY_EXCH)) { NONCE (exported_session_key, 16); // random master key #ifdef _SIPE_COMPILING_TESTS memcpy(exported_session_key, test_random_session_key, 16); #endif RC4K (key_exchange_key, 16, exported_session_key, 16, encrypted_random_session_key); } else { memcpy(exported_session_key, key_exchange_key, 16); } tmp = buff_to_hex_str(exported_session_key, 16); SIPE_DEBUG_INFO("NTLM AUTHENTICATE: exported session key (not encrypted): %s", tmp); g_free(tmp); if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_SIGN) || IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_SEAL)) { /* p.46 Set ClientSigningKey to SIGNKEY(ExportedSessionKey, "Client") Set ServerSigningKey to SIGNKEY(ExportedSessionKey, "Server") */ SIGNKEY(exported_session_key, TRUE, key); *client_sign_key = g_memdup(key, 16); SIGNKEY(exported_session_key, FALSE, key); *server_sign_key = g_memdup(key, 16); SEALKEY(neg_flags, exported_session_key, TRUE, key); *client_seal_key = g_memdup(key, 16); SEALKEY(neg_flags, exported_session_key, FALSE, key); *server_seal_key = g_memdup(key, 16); } /* @TODO: */ /* @since Vista If the CHALLENGE_MESSAGE TargetInfo field (section 2.2.1.2) has an MsvAvTimestamp present, the client SHOULD provide a MIC: - If there is an AV_PAIR structure (section 2.2.2.1) with the AvId field set to MsvAvFlags, - then in the Value field, set bit 0x2 to 1. - else add an AV_PAIR structure (section 2.2.2.1) and set the AvId field to MsvAvFlags and the Value field bit 0x2 to 1. - Populate the MIC field with the MIC. */ /* Connection-oriented: Set MIC to HMAC_MD5(ExportedSessionKey, ConcatenationOf( NEGOTIATE_MESSAGE, CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE_MIC0)); Connectionless: Set MIC to HMAC_MD5(ExportedSessionKey, ConcatenationOf( CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE)) */ /* on the server-side: If (NTLMSSP_NEGOTIATE_KEY_EXCH flag is set in NegFlg ) Set ExportedSessionKey to RC4K(KeyExchangeKey, AUTHENTICATE_MESSAGE.EncryptedRandomSessionKey) Set MIC to HMAC_MD5(ExportedSessionKey, ConcatenationOf( NEGOTIATE_MESSAGE, CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE_MIC0)) Else Set ExportedSessionKey to KeyExchangeKey Set MIC to HMAC_MD5(KeyExchangeKey, ConcatenationOf( NEGOTIATE_MESSAGE, CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE_MIC0)) EndIf ===== @since Vista If the AUTHENTICATE_MESSAGE indicates the presence of a MIC field, then the MIC value computed earlier MUST be compared to the MIC field in the message, and if the two MIC values are not equal, then an authentication failure MUST be returned. An AUTHENTICATE_MESSAGE indicates the presence of a MIC field if the TargetInfo field has an AV_PAIR structure whose two fields: - AvId == MsvAvFlags - Value bit 0x2 == 1 @supported NT, 2000, XP If NTLM v2 authentication is used and the AUTHENTICATE_MESSAGE.NtChallengeResponse. TimeStamp (section 2.2.2.7) is more than MaxLifetime (section 3.1.1.1) difference from the server time, then the server SHOULD return a failure. === Connectionless: Set MIC to HMAC_MD5(ResponseKeyNT, ConcatenationOf( CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE_MIC0)) */ /* authenticate message initialization */ memcpy(tmsg->protocol, "NTLMSSP\0", 8); tmsg->type = GUINT32_TO_LE(3); /* Initial offset */ offset = sizeof(struct authenticate_message); tmp = ((char*) tmsg) + offset; #define _FILL_SMB_HEADER(header) \ tmsg->header.offset = GUINT32_TO_LE(offset); \ tmsg->header.len = tmsg->header.maxlen = GUINT16_TO_LE(len); \ tmp += len; \ offset += len #define _APPEND_STRING(header, src) \ len = unicode_strconvcopy(tmp, (src), msglen - offset); \ _FILL_SMB_HEADER(header) #define _APPEND_DATA(header, src, srclen) \ len = (srclen); \ memcpy(tmp, (src), len); \ _FILL_SMB_HEADER(header) /* Domain */ _APPEND_STRING(domain, domain); /* User */ _APPEND_STRING(user, user); /* Host */ _APPEND_STRING(host, hostname); /* LM */ /* @since Windows 7 If NTLM v2 authentication is used and the CHALLENGE_MESSAGE contains a TargetInfo field, the client SHOULD NOT send the LmChallengeResponse and SHOULD set the LmChallengeResponseLen and LmChallengeResponseMaxLen fields in the AUTHENTICATE_MESSAGE to zero. */ _APPEND_DATA(lm_resp, lm_challenge_response, NTLMSSP_LM_RESP_LEN); /* NT */ _APPEND_DATA(nt_resp, nt_challenge_response, ntlmssp_nt_resp_len); /* Session Key */ if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_KEY_EXCH)) { _APPEND_DATA(session_key, encrypted_random_session_key, NTLMSSP_SESSION_KEY_LEN); } else { tmsg->session_key.offset = GUINT32_TO_LE(offset); tmsg->session_key.len = tmsg->session_key.maxlen = 0; } /* Version */ #ifdef _SIPE_COMPILING_TESTS memcpy(&(tmsg->ver), &test_version, sizeof(struct version)); #else if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_VERSION)) { tmsg->ver.product_major_version = 5; /* 5.1.2600 (Windows XP SP2) */ tmsg->ver.product_minor_version = 1; tmsg->ver.product_build = GUINT16_FROM_LE(2600); tmsg->ver.ntlm_revision_current = 0x0F; /* NTLMSSP_REVISION_W2K3 */ } #endif /* Set Negotiate Flags */ tmsg->flags = GUINT32_TO_LE(neg_flags); *flags = neg_flags; out_buff->value = (guint8 *)tmsg; out_buff->length = msglen; g_free(nt_challenge_response); return TRUE; } /** * Generates Type 1 (Negotiate) message for connection-oriented cases (only) */ static void sip_sec_ntlm_gen_negotiate(SipSecBuffer *out_buff) { guint32 offset; guint16 len; int msglen = sizeof(struct negotiate_message); struct negotiate_message *tmsg = g_malloc0(msglen); /* negotiate message initialization */ memcpy(tmsg->protocol, "NTLMSSP\0", 8); tmsg->type = GUINT32_TO_LE(1); /* Set Negotiate Flags */ tmsg->flags = GUINT32_TO_LE(NEGOTIATE_FLAGS_CONN); /* Domain */ offset = sizeof(struct negotiate_message); tmsg->domain.offset = GUINT32_TO_LE(offset); tmsg->domain.len = tmsg->domain.maxlen = len = 0; /* Host */ offset += len; tmsg->host.offset = GUINT32_TO_LE(offset); tmsg->host.len = tmsg->host.maxlen = len = 0; /* Version */ tmsg->ver.product_major_version = 5; /* 5.1.2600 (Windows XP SP2) */ tmsg->ver.product_minor_version = 1; tmsg->ver.product_build = GUINT16_FROM_LE(2600); tmsg->ver.ntlm_revision_current = 0x0F; /* NTLMSSP_REVISION_W2K3 */ out_buff->value = (guint8 *)tmsg; out_buff->length = msglen; } static void sip_sec_ntlm_sipe_signature_make(guint32 flags, const char *msg, guint32 random_pad, unsigned char *sign_key, unsigned char *seal_key, guint32 *result) { char *res; MAC(flags, msg, strlen(msg), sign_key, 16, seal_key, 16, random_pad, 100, result); res = buff_to_hex_str((guint8 *)result, 16); SIPE_DEBUG_INFO("NTLM calculated MAC: %s", res); g_free(res); } #endif /* !_SIPE_COMPILING_ANALYZER */ /* Describe NTLM messages functions */ #define APPEND_NEG_FLAG(str, flags, flag, desc) \ if ((flags & flag) == flag) g_string_append_printf(str, "\t%s\n", desc); static gchar * sip_sec_ntlm_negotiate_flags_describe(guint32 flags) { GString* str = g_string_new(NULL); flags = GUINT32_FROM_LE(flags); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_UNICODE, "NTLMSSP_NEGOTIATE_UNICODE"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_OEM, "NTLMSSP_NEGOTIATE_OEM"); APPEND_NEG_FLAG(str, flags, NTLMSSP_REQUEST_TARGET, "NTLMSSP_REQUEST_TARGET"); APPEND_NEG_FLAG(str, flags, r9, "r9"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_SIGN, "NTLMSSP_NEGOTIATE_SIGN"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_SEAL, "NTLMSSP_NEGOTIATE_SEAL"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_DATAGRAM, "NTLMSSP_NEGOTIATE_DATAGRAM"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_LM_KEY, "NTLMSSP_NEGOTIATE_LM_KEY"); APPEND_NEG_FLAG(str, flags, r8, "r8"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_NTLM, "NTLMSSP_NEGOTIATE_NTLM"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_NT_ONLY, "NTLMSSP_NEGOTIATE_NT_ONLY"); APPEND_NEG_FLAG(str, flags, anonymous, "anonymous"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED, "NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED, "NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED"); APPEND_NEG_FLAG(str, flags, r7, "r7"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_ALWAYS_SIGN, "NTLMSSP_NEGOTIATE_ALWAYS_SIGN"); APPEND_NEG_FLAG(str, flags, NTLMSSP_TARGET_TYPE_DOMAIN, "NTLMSSP_TARGET_TYPE_DOMAIN"); APPEND_NEG_FLAG(str, flags, NTLMSSP_TARGET_TYPE_SERVER, "NTLMSSP_TARGET_TYPE_SERVER"); APPEND_NEG_FLAG(str, flags, r6, "r6"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY, "NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_IDENTIFY, "NTLMSSP_NEGOTIATE_IDENTIFY"); APPEND_NEG_FLAG(str, flags, r5, "r5"); APPEND_NEG_FLAG(str, flags, NTLMSSP_REQUEST_NON_NT_SESSION_KEY, "NTLMSSP_REQUEST_NON_NT_SESSION_KEY"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_TARGET_INFO, "NTLMSSP_NEGOTIATE_TARGET_INFO"); APPEND_NEG_FLAG(str, flags, r4, "r4"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_VERSION, "NTLMSSP_NEGOTIATE_VERSION"); APPEND_NEG_FLAG(str, flags, r3, "r3"); APPEND_NEG_FLAG(str, flags, r2, "r2"); APPEND_NEG_FLAG(str, flags, r1, "r1"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_128, "NTLMSSP_NEGOTIATE_128"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_KEY_EXCH, "NTLMSSP_NEGOTIATE_KEY_EXCH"); APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_56, "NTLMSSP_NEGOTIATE_56"); return g_string_free(str, FALSE); } static gchar * sip_sec_ntlm_describe_version(struct version *ver) { GString* str = g_string_new(NULL); const gchar *ver_desc = ""; const gchar *ntlm_revision_desc = ""; if (ver->product_major_version == 6) { ver_desc = "Windows Vista, Windows Server 2008, Windows 7 or Windows Server 2008 R2"; } else if (ver->product_major_version == 5 && ver->product_minor_version == 2) { ver_desc = "Windows Server 2003"; } else if (ver->product_major_version == 5 && ver->product_minor_version == 1) { ver_desc = "Windows XP SP2"; } if (ver->ntlm_revision_current == 0x0F) { ntlm_revision_desc = "NTLMSSP_REVISION_W2K3"; } else if (ver->ntlm_revision_current == 0x0A) { ntlm_revision_desc = "NTLMSSP_REVISION_W2K3_RC1"; } g_string_append_printf(str, "\tproduct: %d.%d.%d (%s)\n", ver->product_major_version, ver->product_minor_version, ver->product_build, ver_desc); g_string_append_printf(str, "\tntlm_revision_current: 0x%02X (%s)\n", ver->ntlm_revision_current, ntlm_revision_desc); return g_string_free(str, FALSE); } static gchar * sip_sec_ntlm_describe_smb_header(struct smb_header *header, const char* name) { GString* str = g_string_new(NULL); g_string_append_printf(str, "\t%s.len : %d\n", name, GUINT16_FROM_LE(header->len)); g_string_append_printf(str, "\t%s.maxlen: %d\n", name, GUINT16_FROM_LE(header->maxlen)); g_string_append_printf(str, "\t%s.offset: %d\n", name, GUINT32_FROM_LE(header->offset)); return g_string_free(str, FALSE); } static gchar * sip_sec_ntlm_negotiate_message_describe(struct negotiate_message *cmsg) { GString* str = g_string_new(NULL); char *tmp; g_string_append(str, (tmp = sip_sec_ntlm_negotiate_flags_describe(cmsg->flags))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->domain), "domain"))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->host), "host"))); g_free(tmp); tmp = sip_sec_ntlm_describe_version(&(cmsg->ver)); g_string_append(str, tmp); g_free(tmp); if (cmsg->domain.len && cmsg->domain.offset) { gchar *domain = g_strndup(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->domain.offset)), GUINT16_FROM_LE(cmsg->domain.len)); g_string_append_printf(str, "\tdomain: %s\n", domain); g_free(domain); } if (cmsg->host.len && cmsg->host.offset) { gchar *host = g_strndup(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->host.offset)), GUINT16_FROM_LE(cmsg->host.len)); g_string_append_printf(str, "\thost: %s\n", host); g_free(host); } return g_string_free(str, FALSE); } static void describe_av_pairs(GString* str, const void *av) { #define AV_DESC(av_name) \ { \ gchar *tmp = unicode_strconvcopy_back(av_value, av_len); \ g_string_append_printf(str, "\t%s: %s\n", av_name, tmp); \ g_free(tmp); \ } ALIGN_AV_LOOP_START { case MsvAvNbComputerName: AV_DESC("MsvAvNbComputerName"); break; case MsvAvNbDomainName: AV_DESC("MsvAvNbDomainName"); break; case MsvAvDnsComputerName: AV_DESC("MsvAvDnsComputerName"); break; case MsvAvDnsDomainName: AV_DESC("MsvAvDnsDomainName"); break; case MsvAvDnsTreeName: AV_DESC("MsvAvDnsTreeName"); break; case MsvAvFlags: { guint32 flags; /* to meet sparc's alignment requirement */ memcpy(&flags, av_value, sizeof(guint32)); g_string_append_printf(str, "\t%s: %d\n", "MsvAvFlags", GUINT32_FROM_LE(flags)); } break; case MsvAvTimestamp: { char *tmp; guint64 time_val; time_t time_t_val; /* to meet sparc's alignment requirement */ memcpy(&time_val, av_value, sizeof(time_val)); time_t_val = TIME_VAL_TO_T(time_val); g_string_append_printf(str, "\t%s: %s - %s", "MsvAvTimestamp", (tmp = buff_to_hex_str((guint8 *) av_value, 8)), asctime(gmtime(&time_t_val))); g_free(tmp); } break; case MsAvRestrictions: g_string_append_printf(str, "\t%s\n", "MsAvRestrictions"); break; case MsvAvTargetName: AV_DESC("MsvAvTargetName"); break; case MsvChannelBindings: g_string_append_printf(str, "\t%s\n", "MsvChannelBindings"); break; } ALIGN_AV_LOOP_END; } static gchar * sip_sec_ntlm_authenticate_message_describe(struct authenticate_message *cmsg) { GString* str = g_string_new(NULL); char *tmp; gsize value_len; guint8 *value; g_string_append(str, (tmp = sip_sec_ntlm_negotiate_flags_describe(cmsg->flags))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->lm_resp), "lm_resp"))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->nt_resp), "nt_resp"))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->domain), "domain"))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->user), "user"))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->host), "host"))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->session_key), "session_key"))); g_free(tmp); tmp = sip_sec_ntlm_describe_version(&(cmsg->ver)); g_string_append(str, tmp); g_free(tmp); /* mic */ //g_string_append_printf(str, "\t%s: %s\n", "mic", (tmp = buff_to_hex_str(cmsg->mic, 16))); //g_free(tmp); if (cmsg->lm_resp.len && cmsg->lm_resp.offset) { value_len = GUINT16_FROM_LE(cmsg->lm_resp.len); value = (guint8 *)cmsg + GUINT32_FROM_LE(cmsg->lm_resp.offset); g_string_append_printf(str, "\t%s: %s\n", "lm_resp", (tmp = buff_to_hex_str(value, value_len))); g_free(tmp); } if (cmsg->nt_resp.len && cmsg->nt_resp.offset) { guint16 nt_resp_len_full = GUINT16_FROM_LE(cmsg->nt_resp.len); int nt_resp_len = nt_resp_len_full; value_len = nt_resp_len_full; value = (guint8 *)cmsg + GUINT32_FROM_LE(cmsg->nt_resp.offset); g_string_append_printf(str, "\t%s: %s\n", "nt_resp raw", (tmp = buff_to_hex_str(value, value_len))); g_free(tmp); if (nt_resp_len > 24) { /* NTLMv2 */ nt_resp_len = 16; } value_len = nt_resp_len; value = (guint8 *)cmsg + GUINT32_FROM_LE(cmsg->nt_resp.offset); g_string_append_printf(str, "\t%s: %s\n", "nt_resp", (tmp = buff_to_hex_str(value, value_len))); g_free(tmp); if (nt_resp_len_full > 24) { /* NTLMv2 */ /* Work around Debian/x86_64 compiler bug */ /* const guint8 *temp = (guint8 *)cmsg + GUINT32_FROM_LE(cmsg->nt_resp.offset) + 16; */ const guint offset = GUINT32_FROM_LE(cmsg->nt_resp.offset) + 16; const guint8 *temp = (guint8 *)cmsg + offset; const guint response_version = temp[0]; const guint hi_response_version = temp[1]; const guint8 *client_challenge = temp + 16; const guint8 *target_info = temp + 28; guint16 target_info_len = nt_resp_len_full - 16 - 32; guint64 time_val; time_t time_t_val; char *tmp; g_string_append_printf(str, "\t%s: %s\n", "target_info raw", (tmp = buff_to_hex_str((guint8 *)target_info, target_info_len))); g_free(tmp); /* This is not int64 aligned on sparc */ memcpy((gchar *)&time_val, temp+8, sizeof(time_val)); time_t_val = TIME_VAL_TO_T(time_val); g_string_append_printf(str, "\t%s: %d\n", "response_version", response_version); g_string_append_printf(str, "\t%s: %d\n", "hi_response_version", hi_response_version); g_string_append_printf(str, "\t%s: %s - %s", "time", (tmp = buff_to_hex_str((guint8 *)&time_val, 8)), asctime(gmtime(&time_t_val))); g_free(tmp); g_string_append_printf(str, "\t%s: %s\n", "client_challenge", (tmp = buff_to_hex_str((guint8 *)client_challenge, 8))); g_free(tmp); describe_av_pairs(str, target_info); g_string_append_printf(str, "\t%s\n", "----------- end of nt_resp v2 -----------"); } } if (cmsg->domain.len && cmsg->domain.offset) { gchar *domain = unicode_strconvcopy_back(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->domain.offset)), GUINT16_FROM_LE(cmsg->domain.len)); g_string_append_printf(str, "\t%s: %s\n", "domain", domain); g_free(domain); } if (cmsg->user.len && cmsg->user.offset) { gchar *user = unicode_strconvcopy_back(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->user.offset)), GUINT16_FROM_LE(cmsg->user.len)); g_string_append_printf(str, "\t%s: %s\n", "user", user); g_free(user); } if (cmsg->host.len && cmsg->host.offset) { gchar *host = unicode_strconvcopy_back(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->host.offset)), GUINT16_FROM_LE(cmsg->host.len)); g_string_append_printf(str, "\t%s: %s\n", "host", host); g_free(host); } if (cmsg->session_key.len && cmsg->session_key.offset) { value_len = GUINT16_FROM_LE(cmsg->session_key.len); value = (guint8 *)cmsg + GUINT32_FROM_LE(cmsg->session_key.offset); g_string_append_printf(str, "\t%s: %s\n", "session_key", (tmp = buff_to_hex_str(value, value_len))); g_free(tmp); } return g_string_free(str, FALSE); } static gchar * sip_sec_ntlm_challenge_message_describe(struct challenge_message *cmsg) { GString* str = g_string_new(NULL); char *tmp; g_string_append(str, (tmp = sip_sec_ntlm_negotiate_flags_describe(cmsg->flags))); g_free(tmp); /* nonce (server_challenge) */ g_string_append_printf(str, "\t%s: %s\n", "server_challenge", (tmp = buff_to_hex_str(cmsg->nonce, 8))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->target_name), "target_name"))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->target_info), "target_info"))); g_free(tmp); g_string_append(str, (tmp = sip_sec_ntlm_describe_version(&(cmsg->ver)))); g_free(tmp); if (cmsg->target_name.len && cmsg->target_name.offset) { gchar *target_name = unicode_strconvcopy_back(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->target_name.offset)), GUINT16_FROM_LE(cmsg->target_name.len)); g_string_append_printf(str, "\ttarget_name: %s\n", target_name); g_free(target_name); } if (cmsg->target_info.len && cmsg->target_info.offset) { guint8 *target_info = (guint8 *)cmsg + GUINT32_FROM_LE(cmsg->target_info.offset); guint16 target_info_len = GUINT16_FROM_LE(cmsg->target_info.len); g_string_append_printf(str, "\t%s: %s\n", "target_info raw", (tmp = buff_to_hex_str(target_info, target_info_len))); g_free(tmp); describe_av_pairs(str, target_info); } return g_string_free(str, FALSE); } static void sip_sec_ntlm_message_describe(SipSecBuffer *buff, const gchar *type) { struct ntlm_message *msg; gchar *res = NULL; if (buff->length == 0 || buff->value == NULL || buff->length < 12) return; /* SipSecBuffer.value is g_malloc()'d: use (void *) to remove guint8 alignment */ msg = (void *)buff->value; if(!sipe_strequal("NTLMSSP", (char*)msg)) return; switch (GUINT32_FROM_LE(msg->type)) { case 1: res = sip_sec_ntlm_negotiate_message_describe((struct negotiate_message *)msg); break; case 2: res = sip_sec_ntlm_challenge_message_describe((struct challenge_message *)msg); break; case 3: res = sip_sec_ntlm_authenticate_message_describe((struct authenticate_message *)msg); break; } SIPE_DEBUG_INFO("sip_sec_ntlm_message_describe: %s message is:\n%s", type, res); g_free(res); } /* Analyzer only needs the _describe() functions */ #ifndef _SIPE_COMPILING_ANALYZER /* sip-sec-mech.h API implementation for NTLM */ /* Security context for NTLM */ typedef struct _context_ntlm { struct sip_sec_context common; gchar *domain; gchar *username; const gchar *password; guchar *client_sign_key; guchar *server_sign_key; guchar *client_seal_key; guchar *server_seal_key; guint32 flags; } *context_ntlm; #define SIP_SEC_FLAG_NTLM_INITIAL 0x00010000 static gboolean sip_sec_acquire_cred__ntlm(SipSecContext context, const gchar *username, const gchar *password) { context_ntlm ctx = (context_ntlm)context; /* * Our NTLM implementation does not support Single Sign-On. * Thus username & password are required. */ if (is_empty(username) || is_empty(password)) { SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__ntlm: no valid authentication information provided"); return FALSE; } /* this is the first time we are allowed to set private flags */ context->flags |= SIP_SEC_FLAG_NTLM_INITIAL; if (SIP_SEC_USERNAME_IS_ENTERPRISE) { /* use username as-is, just replace enterprise marker with @ */ ctx->username = sipe_utils_str_replace(username, SIP_SEC_USERNAME_ENTERPRISE_STRING, "@"); } else { SIP_SEC_USERNAME_SPLIT_START; if (SIP_SEC_USERNAME_HAS_DOMAIN) { ctx->domain = g_strdup(SIP_SEC_USERNAME_DOMAIN); ctx->username = g_strdup(SIP_SEC_USERNAME_ACCOUNT); } else { ctx->username = g_strdup(username); } SIP_SEC_USERNAME_SPLIT_END; } ctx->password = password; return TRUE; } static gboolean sip_sec_init_sec_context__ntlm(SipSecContext context, SipSecBuffer in_buff, SipSecBuffer *out_buff, SIPE_UNUSED_PARAMETER const gchar *service_name) { context_ntlm ctx = (context_ntlm) context; SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__ntlm: in use"); /* * If authentication was already completed, then this mean a new * authentication handshake has started on the existing connection. * We must throw away the old context, because we need a new one. */ if (context->flags & SIP_SEC_FLAG_COMMON_READY) { SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__ntlm: dropping old context"); context->flags &= ~SIP_SEC_FLAG_COMMON_READY; context->flags |= SIP_SEC_FLAG_NTLM_INITIAL; } if (context->flags & SIP_SEC_FLAG_NTLM_INITIAL) { context->flags &= ~SIP_SEC_FLAG_NTLM_INITIAL; /* HTTP */ if (context->flags & SIP_SEC_FLAG_COMMON_HTTP) { sip_sec_ntlm_gen_negotiate(out_buff); sip_sec_ntlm_message_describe(out_buff, "Negotiate"); /* SIP */ } else { /* empty initial message for connection-less NTLM */ out_buff->length = 0; out_buff->value = (guint8 *) g_strdup(""); } } else { gboolean res; guchar *client_sign_key = NULL; guchar *server_sign_key = NULL; guchar *client_seal_key = NULL; guchar *server_seal_key = NULL; guchar *server_challenge = NULL; guint64 time_val = 0; guchar *target_info = NULL; int target_info_len = 0; guint32 flags; gchar *tmp; if (!in_buff.value || !in_buff.length) { return FALSE; } sip_sec_ntlm_message_describe(&in_buff, "Challenge"); sip_sec_ntlm_parse_challenge(in_buff, &flags, &server_challenge, /* 8 bytes */ &time_val, &target_info, &target_info_len); res = sip_sec_ntlm_gen_authenticate( &client_sign_key, &server_sign_key, &client_seal_key, &server_seal_key, ctx->username, ctx->password, (tmp = g_ascii_strup(g_get_host_name(), -1)), ctx->domain ? ctx->domain : "", server_challenge, time_val, target_info, target_info_len, context->flags & SIP_SEC_FLAG_COMMON_HTTP, out_buff, &flags); g_free(server_challenge); g_free(target_info); g_free(tmp); if (!res) { g_free(client_sign_key); g_free(server_sign_key); g_free(client_seal_key); g_free(server_seal_key); return res; } sip_sec_ntlm_message_describe(out_buff, "Authenticate"); g_free(ctx->client_sign_key); ctx->client_sign_key = client_sign_key; g_free(ctx->server_sign_key); ctx->server_sign_key = server_sign_key; g_free(ctx->client_seal_key); ctx->client_seal_key = client_seal_key; g_free(ctx->server_seal_key); ctx->server_seal_key = server_seal_key; ctx->flags = flags; /* Authentication is completed */ context->flags |= SIP_SEC_FLAG_COMMON_READY; } return TRUE; } /** * @param message a NULL terminated string to sign * */ static gboolean sip_sec_make_signature__ntlm(SipSecContext context, const gchar *message, SipSecBuffer *signature) { signature->length = 16; signature->value = g_malloc0(16); /* FIXME? We always use a random_pad of 0 */ sip_sec_ntlm_sipe_signature_make(((context_ntlm) context)->flags, message, 0, ((context_ntlm) context)->client_sign_key, ((context_ntlm) context)->client_seal_key, /* SipSecBuffer.value is g_malloc()'d: * use (void *) to remove guint8 alignment */ (void *)signature->value); return TRUE; } /** * @param message a NULL terminated string to check signature of * @return TRUE on success */ static gboolean sip_sec_verify_signature__ntlm(SipSecContext context, const gchar *message, SipSecBuffer signature) { context_ntlm ctx = (context_ntlm) context; guint32 mac[4]; /* SipSecBuffer.value is g_malloc()'d: use (void *) to remove guint8 alignment */ guint32 random_pad = GUINT32_FROM_LE(((guint32 *)((void *)signature.value))[1]); sip_sec_ntlm_sipe_signature_make(ctx->flags, message, random_pad, ctx->server_sign_key, ctx->server_seal_key, mac); return(memcmp(signature.value, mac, 16) == 0); } static void sip_sec_destroy_sec_context__ntlm(SipSecContext context) { context_ntlm ctx = (context_ntlm) context; g_free(ctx->client_sign_key); g_free(ctx->server_sign_key); g_free(ctx->client_seal_key); g_free(ctx->server_seal_key); g_free(ctx->domain); g_free(ctx->username); g_free(ctx); } static const gchar * sip_sec_context_name__ntlm(SIPE_UNUSED_PARAMETER SipSecContext context) { return("NTLM"); } SipSecContext sip_sec_create_context__ntlm(SIPE_UNUSED_PARAMETER guint type) { context_ntlm context = g_malloc0(sizeof(struct _context_ntlm)); if (!context) return(NULL); context->common.acquire_cred_func = sip_sec_acquire_cred__ntlm; context->common.init_context_func = sip_sec_init_sec_context__ntlm; context->common.destroy_context_func = sip_sec_destroy_sec_context__ntlm; context->common.make_signature_func = sip_sec_make_signature__ntlm; context->common.verify_signature_func = sip_sec_verify_signature__ntlm; context->common.context_name_func = sip_sec_context_name__ntlm; return((SipSecContext) context); } gboolean sip_sec_password__ntlm(void) { return(TRUE); } #endif /* !_SIPE_COMPILING_ANALYZER */ void sip_sec_init__ntlm(void) { #ifdef HAVE_LANGINFO_CODESET const char *sys_cp = nl_langinfo(CODESET); #else const char *sys_cp = SIPE_DEFAULT_CODESET; #endif /* HAVE_LANGINFO_CODESET */ /* fall back to utf-8 */ if (!sys_cp) sys_cp = "UTF-8"; convert_from_utf16le = g_iconv_open(sys_cp, "UTF-16LE"); if (convert_from_utf16le == (GIConv)-1) { SIPE_DEBUG_ERROR("g_iconv_open from UTF-16LE to %s failed", sys_cp); } convert_to_utf16le = g_iconv_open("UTF-16LE", sys_cp); if (convert_to_utf16le == (GIConv)-1) { SIPE_DEBUG_ERROR("g_iconv_open from %s to UTF-16LE failed", sys_cp); } } void sip_sec_destroy__ntlm(void) { g_iconv_close(convert_to_utf16le); g_iconv_close(convert_from_utf16le); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec-ntlm.h ================================================ /** * @file sip-sec-ntlm.h * * pidgin-sipe * * Copyright (C) 2010-12 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ SipSecContext sip_sec_create_context__ntlm(guint type); gboolean sip_sec_password__ntlm(void); void sip_sec_init__ntlm(void); void sip_sec_destroy__ntlm(void); ================================================ FILE: src/core/sip-sec-sspi.c ================================================ /** * @file sip-sec-sspi.c * * pidgin-sipe * * Copyright (C) 2011-2015 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _WIN32 #error sip-sec-sspi.c can only be compiled for Windows builds #endif #include #include #ifndef SECURITY_WIN32 #define SECURITY_WIN32 1 #endif #include #include #include #include "sipe-common.h" #include "sip-sec.h" #include "sip-sec-mech.h" #include "sip-sec-sspi.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-utils.h" /* Mechanism names */ static const gchar * const mech_names[] = { "", /* SIPE_AUTHENTICATION_TYPE_UNSET */ "", /* SIPE_AUTHENTICATION_TYPE_BASIC */ "NTLM", /* SIPE_AUTHENTICATION_TYPE_NTLM */ "Kerberos", /* SIPE_AUTHENTICATION_TYPE_KERBEROS */ "Negotiate", /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */ "", /* SIPE_AUTHENTICATION_TYPE_TLS_DSK */ "", /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */ }; #ifndef ISC_REQ_IDENTIFY #define ISC_REQ_IDENTIFY 0x00002000 #endif typedef struct _context_sspi { struct sip_sec_context common; CredHandle* cred_sspi; CtxtHandle* ctx_sspi; } *context_sspi; #define SIP_SEC_FLAG_SSPI_SIP_NTLM 0x00010000 /* Utility Functions */ static void sip_sec_sspi_print_error(const gchar *func, SECURITY_STATUS ret) { gchar *error_message; static char *buff; guint buff_length; buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, 0, ret, 0, (LPTSTR)&buff, 16384, 0); error_message = g_strndup(buff, buff_length); LocalFree(buff); SIPE_DEBUG_ERROR("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message); g_free(error_message); } /* Returns interval in seconds from now till provided value */ static guint sip_sec_get_interval_from_now_sec(TimeStamp timestamp) { SYSTEMTIME stNow; FILETIME ftNow; ULARGE_INTEGER uliNow, uliTo; GetLocalTime(&stNow); SystemTimeToFileTime(&stNow, &ftNow); uliNow.LowPart = ftNow.dwLowDateTime; uliNow.HighPart = ftNow.dwHighDateTime; uliTo.LowPart = timestamp.LowPart; uliTo.HighPart = timestamp.HighPart; return((uliTo.QuadPart - uliNow.QuadPart)/10/1000/1000); } static void sip_sec_destroy_sspi_context(context_sspi context) { if (context->ctx_sspi) { DeleteSecurityContext(context->ctx_sspi); g_free(context->ctx_sspi); context->ctx_sspi = NULL; } if (context->cred_sspi) { FreeCredentialsHandle(context->cred_sspi); g_free(context->cred_sspi); context->cred_sspi = NULL; } } /* sip-sec-mech.h API implementation for SSPI - Kerberos, NTLM and Negotiate */ static gboolean sip_sec_acquire_cred__sspi(SipSecContext context, const gchar *username, const gchar *password) { SECURITY_STATUS ret; TimeStamp expiry; SEC_WINNT_AUTH_IDENTITY auth_identity; context_sspi ctx = (context_sspi)context; gchar *domain_tmp = NULL; gchar *user_tmp = NULL; /* this is the first time we are allowed to set private flags */ if (((context->flags & SIP_SEC_FLAG_COMMON_HTTP) == 0) && (context->type == SIPE_AUTHENTICATION_TYPE_NTLM)) context->flags |= SIP_SEC_FLAG_SSPI_SIP_NTLM; if ((context->flags & SIP_SEC_FLAG_COMMON_SSO) == 0) { if (is_empty(username) || is_empty(password)) { SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__sspi: no valid authentication information provided"); return FALSE; } memset(&auth_identity, 0, sizeof(auth_identity)); auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; if (SIP_SEC_USERNAME_IS_ENTERPRISE) { /* use username as-is, just replace enterprise marker with @ */ user_tmp = sipe_utils_str_replace(username, SIP_SEC_USERNAME_ENTERPRISE_STRING, "@"); } else { SIP_SEC_USERNAME_SPLIT_START; if (SIP_SEC_USERNAME_HAS_DOMAIN) { domain_tmp = g_strdup(SIP_SEC_USERNAME_DOMAIN); user_tmp = g_strdup(SIP_SEC_USERNAME_ACCOUNT); auth_identity.Domain = (unsigned char *)domain_tmp; auth_identity.DomainLength = strlen(domain_tmp); } SIP_SEC_USERNAME_SPLIT_END; } auth_identity.User = (unsigned char *)(user_tmp ? user_tmp : username); auth_identity.UserLength = strlen((char *) auth_identity.User); auth_identity.Password = (unsigned char *)password; auth_identity.PasswordLength = strlen(password); } ctx->cred_sspi = g_malloc0(sizeof(CredHandle)); ret = AcquireCredentialsHandleA(NULL, (SEC_CHAR *)mech_names[context->type], SECPKG_CRED_OUTBOUND, NULL, (context->flags & SIP_SEC_FLAG_COMMON_SSO) ? NULL : &auth_identity, NULL, NULL, ctx->cred_sspi, &expiry); g_free(user_tmp); g_free(domain_tmp); if (ret != SEC_E_OK) { sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret); g_free(ctx->cred_sspi); ctx->cred_sspi = NULL; return FALSE; } else { return TRUE; } } static gboolean sip_sec_init_sec_context__sspi(SipSecContext context, SipSecBuffer in_buff, SipSecBuffer *out_buff, const gchar *service_name) { TimeStamp expiry; SecBufferDesc input_desc, output_desc; SecBuffer in_token, out_token; SECURITY_STATUS ret; ULONG req_flags; ULONG ret_flags; context_sspi ctx = (context_sspi)context; CtxtHandle* out_context; SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use"); /* * If authentication was already completed, then this mean a new * authentication handshake has started on the existing connection. * We must throw away the old context, because we need a new one. */ if ((context->flags & SIP_SEC_FLAG_COMMON_READY) && ctx->ctx_sspi) { SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: dropping old context"); DeleteSecurityContext(ctx->ctx_sspi); g_free(ctx->ctx_sspi); ctx->ctx_sspi = NULL; context->flags &= ~SIP_SEC_FLAG_COMMON_READY; } /* reuse existing context on following calls */ out_context = ctx->ctx_sspi ? ctx->ctx_sspi : g_malloc0(sizeof(CtxtHandle)); input_desc.cBuffers = 1; input_desc.pBuffers = &in_token; input_desc.ulVersion = SECBUFFER_VERSION; /* input token */ in_token.BufferType = SECBUFFER_TOKEN; in_token.cbBuffer = in_buff.length; in_token.pvBuffer = in_buff.value; output_desc.cBuffers = 1; output_desc.pBuffers = &out_token; output_desc.ulVersion = SECBUFFER_VERSION; /* to hold output token */ out_token.BufferType = SECBUFFER_TOKEN; out_token.cbBuffer = 0; out_token.pvBuffer = NULL; req_flags = (ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_INTEGRITY | ISC_REQ_IDENTIFY); if (context->flags & SIP_SEC_FLAG_SSPI_SIP_NTLM) { req_flags |= (ISC_REQ_DATAGRAM); } ret = InitializeSecurityContextA(ctx->cred_sspi, ctx->ctx_sspi, (SEC_CHAR *)service_name, req_flags, 0, SECURITY_NATIVE_DREP, &input_desc, 0, out_context, &output_desc, &ret_flags, &expiry); if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) { if (!ctx->ctx_sspi) g_free(out_context); sip_sec_destroy_sspi_context(ctx); sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret); return FALSE; } out_buff->length = out_token.cbBuffer; if (out_token.cbBuffer) { out_buff->value = g_malloc(out_token.cbBuffer); memcpy(out_buff->value, out_token.pvBuffer, out_token.cbBuffer); } else { /* Special case: empty token */ out_buff->value = (guint8 *) g_strdup(""); } FreeContextBuffer(out_token.pvBuffer); ctx->ctx_sspi = out_context; if (context->type == SIPE_AUTHENTICATION_TYPE_KERBEROS) { context->expires = sip_sec_get_interval_from_now_sec(expiry); } if (ret != SEC_I_CONTINUE_NEEDED) { /* Authentication is completed */ context->flags |= SIP_SEC_FLAG_COMMON_READY; } return TRUE; } static void sip_sec_destroy_sec_context__sspi(SipSecContext context) { sip_sec_destroy_sspi_context((context_sspi)context); g_free(context); } /** * @param message a NULL terminated string to sign * */ static gboolean sip_sec_make_signature__sspi(SipSecContext context, const gchar *message, SipSecBuffer *signature) { SecBufferDesc buffs_desc; SecBuffer buffs[2]; SECURITY_STATUS ret; SecPkgContext_Sizes context_sizes; guchar *signature_buff; size_t signature_buff_length; context_sspi ctx = (context_sspi) context; ret = QueryContextAttributes(ctx->ctx_sspi, SECPKG_ATTR_SIZES, &context_sizes); if (ret != SEC_E_OK) { sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret); return FALSE; } signature_buff_length = context_sizes.cbMaxSignature; signature_buff = g_malloc(signature_buff_length); buffs_desc.cBuffers = 2; buffs_desc.pBuffers = buffs; buffs_desc.ulVersion = SECBUFFER_VERSION; /* message to sign */ buffs[0].BufferType = SECBUFFER_DATA; buffs[0].cbBuffer = strlen(message); buffs[0].pvBuffer = (PVOID)message; /* to hold signature */ buffs[1].BufferType = SECBUFFER_TOKEN; buffs[1].cbBuffer = signature_buff_length; buffs[1].pvBuffer = signature_buff; ret = MakeSignature(ctx->ctx_sspi, (ULONG)0, &buffs_desc, 100); if (ret != SEC_E_OK) { sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret); g_free(signature_buff); return FALSE; } signature->value = signature_buff; signature->length = buffs[1].cbBuffer; return TRUE; } /** * @param message a NULL terminated string to check signature of * @return TRUE on success */ static gboolean sip_sec_verify_signature__sspi(SipSecContext context, const gchar *message, SipSecBuffer signature) { SecBufferDesc buffs_desc; SecBuffer buffs[2]; SECURITY_STATUS ret; buffs_desc.cBuffers = 2; buffs_desc.pBuffers = buffs; buffs_desc.ulVersion = SECBUFFER_VERSION; /* message to sign */ buffs[0].BufferType = SECBUFFER_DATA; buffs[0].cbBuffer = strlen(message); buffs[0].pvBuffer = (PVOID)message; /* signature to check */ buffs[1].BufferType = SECBUFFER_TOKEN; buffs[1].cbBuffer = signature.length; buffs[1].pvBuffer = signature.value; ret = VerifySignature(((context_sspi)context)->ctx_sspi, &buffs_desc, 0, 0); if (ret != SEC_E_OK) { sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret); return FALSE; } return TRUE; } /* SSPI implements SPNEGO (RFC 4559) */ static const gchar * sip_sec_context_name__sspi(SipSecContext context) { return(mech_names[context->type]); } SipSecContext sip_sec_create_context__sspi(SIPE_UNUSED_PARAMETER guint type) { context_sspi context = g_malloc0(sizeof(struct _context_sspi)); if (!context) return(NULL); context->common.acquire_cred_func = sip_sec_acquire_cred__sspi; context->common.init_context_func = sip_sec_init_sec_context__sspi; context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi; context->common.make_signature_func = sip_sec_make_signature__sspi; context->common.verify_signature_func = sip_sec_verify_signature__sspi; context->common.context_name_func = sip_sec_context_name__sspi; return((SipSecContext) context); } gboolean sip_sec_password__sspi(void) { /* SSPI supports Single-Sign On */ return(FALSE); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec-sspi.h ================================================ /** * @file sip-sec-sspi.h * * pidgin-sipe * * Copyright (C) 2012 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ SipSecContext sip_sec_create_context__sspi(guint type); gboolean sip_sec_password__sspi(void); ================================================ FILE: src/core/sip-sec-tls-dsk.c ================================================ /** * @file sip-sec-tls-dsk.c * * pidgin-sipe * * Copyright (C) 2011-2015 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Specification references: * * - [MS-SIPAE]: http://msdn.microsoft.com/en-us/library/cc431510.aspx * - [MS-OCAUTHWS]: http://msdn.microsoft.com/en-us/library/ff595592.aspx * - MS Tech-Ed Europe 2010 "UNC310: Microsoft Lync 2010 Technology Explained" * http://ecn.channel9.msdn.com/o9/te/Europe/2010/pptx/unc310.pptx */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "sipe-common.h" #include "sip-sec.h" #include "sip-sec-mech.h" #include "sip-sec-tls-dsk.h" #include "sipe-backend.h" #include "sipe-digest.h" #include "sipe-tls.h" /* Security context for TLS-DSK */ typedef struct _context_tls_dsk { struct sip_sec_context common; struct sipe_tls_state *state; enum sipe_tls_digest_algorithm algorithm; guchar *client_key; guchar *server_key; gsize key_length; } *context_tls_dsk; /* sip-sec-mech.h API implementation for TLS-DSK */ static gboolean sip_sec_acquire_cred__tls_dsk(SipSecContext context, SIPE_UNUSED_PARAMETER const gchar *username, const gchar *password) { context_tls_dsk ctx = (context_tls_dsk) context; return((ctx->state = sipe_tls_start((gpointer) password)) != NULL); } static gboolean sip_sec_init_sec_context__tls_dsk(SipSecContext context, SipSecBuffer in_buff, SipSecBuffer *out_buff, SIPE_UNUSED_PARAMETER const gchar *service_name) { context_tls_dsk ctx = (context_tls_dsk) context; struct sipe_tls_state *state = ctx->state; state->in_buffer = in_buff.value; state->in_length = in_buff.length; if (sipe_tls_next(state)) { if ((state->algorithm != SIPE_TLS_DIGEST_ALGORITHM_NONE) && state->client_key && state->server_key) { /* Authentication is completed */ context->flags |= SIP_SEC_FLAG_COMMON_READY; /* copy key pair */ ctx->algorithm = state->algorithm; ctx->key_length = state->key_length; ctx->client_key = g_memdup(state->client_key, state->key_length); ctx->server_key = g_memdup(state->server_key, state->key_length); /* extract certicate expiration time */ ctx->common.expires = sipe_tls_expires(state); SIPE_DEBUG_INFO("sip_sec_init_sec_context__tls_dsk: handshake completed, algorithm %d, key length %" G_GSIZE_FORMAT ", expires %d", ctx->algorithm, ctx->key_length, ctx->common.expires); sipe_tls_free(state); ctx->state = NULL; } else { out_buff->value = state->out_buffer; out_buff->length = state->out_length; /* we take ownership of the buffer */ state->out_buffer = NULL; } } else { sipe_tls_free(state); ctx->state = NULL; } return(((context->flags & SIP_SEC_FLAG_COMMON_READY) || ctx->state)); } static gboolean sip_sec_make_signature__tls_dsk(SipSecContext context, const gchar *message, SipSecBuffer *signature) { context_tls_dsk ctx = (context_tls_dsk) context; gboolean result = FALSE; switch (ctx->algorithm) { case SIPE_TLS_DIGEST_ALGORITHM_MD5: signature->length = SIPE_DIGEST_HMAC_MD5_LENGTH; signature->value = g_malloc0(signature->length); sipe_digest_hmac_md5(ctx->client_key, ctx->key_length, (guchar *) message, strlen(message), signature->value); result = TRUE; break; case SIPE_TLS_DIGEST_ALGORITHM_SHA1: signature->length = SIPE_DIGEST_HMAC_SHA1_LENGTH; signature->value = g_malloc0(signature->length); sipe_digest_hmac_sha1(ctx->client_key, ctx->key_length, (guchar *) message, strlen(message), signature->value); result = TRUE; break; default: /* this should not happen */ break; } return(result); } static gboolean sip_sec_verify_signature__tls_dsk(SipSecContext context, const gchar *message, SipSecBuffer signature) { context_tls_dsk ctx = (context_tls_dsk) context; SipSecBuffer mac = { 0, NULL }; gboolean result = FALSE; switch (ctx->algorithm) { case SIPE_TLS_DIGEST_ALGORITHM_MD5: mac.length = SIPE_DIGEST_HMAC_MD5_LENGTH; mac.value = g_malloc0(mac.length); sipe_digest_hmac_md5(ctx->server_key, ctx->key_length, (guchar *) message, strlen(message), mac.value); break; case SIPE_TLS_DIGEST_ALGORITHM_SHA1: mac.length = SIPE_DIGEST_HMAC_SHA1_LENGTH; mac.value = g_malloc0(mac.length); sipe_digest_hmac_sha1(ctx->server_key, ctx->key_length, (guchar *) message, strlen(message), mac.value); break; default: /* this should not happen */ break; } if (mac.value) { result = memcmp(signature.value, mac.value, mac.length) == 0; g_free(mac.value); } return(result); } static void sip_sec_destroy_sec_context__tls_dsk(SipSecContext context) { context_tls_dsk ctx = (context_tls_dsk) context; sipe_tls_free(ctx->state); g_free(ctx->client_key); g_free(ctx->server_key); g_free(ctx); } static const gchar * sip_sec_context_name__tls_dsk(SIPE_UNUSED_PARAMETER SipSecContext context) { return("TLS-DSK"); } SipSecContext sip_sec_create_context__tls_dsk(SIPE_UNUSED_PARAMETER guint type) { context_tls_dsk context = g_malloc0(sizeof(struct _context_tls_dsk)); if (!context) return(NULL); context->common.acquire_cred_func = sip_sec_acquire_cred__tls_dsk; context->common.init_context_func = sip_sec_init_sec_context__tls_dsk; context->common.destroy_context_func = sip_sec_destroy_sec_context__tls_dsk; context->common.make_signature_func = sip_sec_make_signature__tls_dsk; context->common.verify_signature_func = sip_sec_verify_signature__tls_dsk; context->common.context_name_func = sip_sec_context_name__tls_dsk; return((SipSecContext) context); } gboolean sip_sec_password__tls_dsk(void) { #if defined(HAVE_SSPI) || defined(HAVE_GSSAPI_GSSAPI_H) /* * TLS-DSK authenticates with a published client certificate. This * process uses Web Tickets and therefore goes through HTTP. If we * have authentication schemes compiled in which allow Single Sign-On * then we should allow password-less configurations. */ return(FALSE); #else return(TRUE); #endif } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec-tls-dsk.h ================================================ /** * @file sip-sec-tls-dsk.h * * pidgin-sipe * * Copyright (C) 2011-12 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ SipSecContext sip_sec_create_context__tls_dsk(guint type); gboolean sip_sec_password__tls_dsk(void); ================================================ FILE: src/core/sip-sec.c ================================================ /** * @file sip-sec.c * * pidgin-sipe * * Copyright (C) 2010-2015 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "sipe-common.h" #include "sip-sec.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-utils.h" #include "sip-sec-mech.h" /* SSPI is only supported on Windows platform */ #if defined(_WIN32) && defined(HAVE_SSPI) #include "sip-sec-sspi.h" #define SIP_SEC_WINDOWS_SSPI 1 #else #define SIP_SEC_WINDOWS_SSPI 0 #endif #ifdef HAVE_GSSAPI_GSSAPI_H #include "sip-sec-gssapi.h" #endif /* SIPE_AUTHENTICATION_TYPE_BASIC */ #include "sip-sec-basic.h" #define sip_sec_create_context__Basic sip_sec_create_context__basic /* Basic is only used for HTTP, not for SIP */ #define sip_sec_password__Basic sip_sec_password__NONE /* SIPE_AUTHENTICATION_TYPE_NTLM */ #if SIP_SEC_WINDOWS_SSPI #define sip_sec_create_context__NTLM sip_sec_create_context__sspi #define sip_sec_password__NTLM sip_sec_password__sspi #elif defined(HAVE_GSSAPI_ONLY) #define sip_sec_create_context__NTLM sip_sec_create_context__gssapi #define sip_sec_password__NTLM sip_sec_password__gssapi #else #include "sip-sec-ntlm.h" #define sip_sec_create_context__NTLM sip_sec_create_context__ntlm #define sip_sec_password__NTLM sip_sec_password__ntlm #endif /* SIPE_AUTHENTICATION_TYPE_KERBEROS */ #if SIP_SEC_WINDOWS_SSPI #define sip_sec_create_context__Kerberos sip_sec_create_context__sspi #define sip_sec_password__Kerberos sip_sec_password__sspi #elif defined(HAVE_GSSAPI_GSSAPI_H) #define sip_sec_create_context__Kerberos sip_sec_create_context__gssapi #define sip_sec_password__Kerberos sip_sec_password__gssapi #else #define sip_sec_create_context__Kerberos sip_sec_create_context__NONE #define sip_sec_password__Kerberos sip_sec_password__NONE #endif /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */ #if SIP_SEC_WINDOWS_SSPI #define sip_sec_create_context__Negotiate sip_sec_create_context__sspi #elif defined(HAVE_GSSAPI_ONLY) #define sip_sec_create_context__Negotiate sip_sec_create_context__gssapi #elif defined(HAVE_GSSAPI_GSSAPI_H) #include "sip-sec-negotiate.h" #define sip_sec_create_context__Negotiate sip_sec_create_context__negotiate #else #define sip_sec_create_context__Negotiate sip_sec_create_context__NONE #endif /* Negotiate is only used for HTTP, not for SIP */ #define sip_sec_password__Negotiate sip_sec_password__NONE /* SIPE_AUTHENTICATION_TYPE_TLS_DSK */ #include "sip-sec-tls-dsk.h" #define sip_sec_create_context__TLS_DSK sip_sec_create_context__tls_dsk #define sip_sec_password__TLS_DSK sip_sec_password__tls_dsk /* Dummy initialization hook */ static SipSecContext sip_sec_create_context__NONE(SIPE_UNUSED_PARAMETER guint type) { return(NULL); } /* Dummy SIP password hooks */ static gboolean sip_sec_password__NONE(void) { /* Don't ask for a password */ return(FALSE); } /* sip_sec API methods */ SipSecContext sip_sec_create_context(guint type, gboolean sso, gboolean http, const gchar *username, const gchar *password) { SipSecContext context = NULL; /* Map authentication type to module initialization hook & name */ static sip_sec_create_context_func const auth_to_hook[] = { sip_sec_create_context__NONE, /* SIPE_AUTHENTICATION_TYPE_UNSET */ sip_sec_create_context__Basic, /* SIPE_AUTHENTICATION_TYPE_BASIC */ sip_sec_create_context__NTLM, /* SIPE_AUTHENTICATION_TYPE_NTLM */ sip_sec_create_context__Kerberos, /* SIPE_AUTHENTICATION_TYPE_KERBEROS */ sip_sec_create_context__Negotiate, /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */ sip_sec_create_context__TLS_DSK, /* SIPE_AUTHENTICATION_TYPE_TLS_DSK */ sip_sec_create_context__NONE, /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */ }; SIPE_DEBUG_INFO("sip_sec_create_context: type: %d, Single Sign-On: %s, protocol: %s", type, sso ? "yes" : "no", http ? "HTTP" : "SIP"); context = (*(auth_to_hook[type]))(type); if (context) { context->type = type; /* NOTE: mechanism must set private flags acquire_cred_func()! */ context->flags = 0; /* set common flags */ if (sso) context->flags |= SIP_SEC_FLAG_COMMON_SSO; if (http) context->flags |= SIP_SEC_FLAG_COMMON_HTTP; if (!(*context->acquire_cred_func)(context, username, password)) { SIPE_DEBUG_INFO_NOFORMAT("ERROR: sip_sec_create_context: failed to acquire credentials."); (*context->destroy_context_func)(context); context = NULL; } } return(context); } gboolean sip_sec_init_context_step(SipSecContext context, const gchar *target, const gchar *input_toked_base64, gchar **output_toked_base64, guint *expires) { gboolean ret = FALSE; if (context) { SipSecBuffer in_buff = {0, NULL}; SipSecBuffer out_buff = {0, NULL}; /* Not NULL for NTLM Type 2 or TLS-DSK */ if (input_toked_base64) in_buff.value = g_base64_decode(input_toked_base64, &in_buff.length); ret = (*context->init_context_func)(context, in_buff, &out_buff, target); if (input_toked_base64) g_free(in_buff.value); if (ret) { if (out_buff.value) { if (out_buff.length > 0) { *output_toked_base64 = g_base64_encode(out_buff.value, out_buff.length); } else { /* special string: caller takes ownership */ *output_toked_base64 = (gchar *) out_buff.value; out_buff.value = NULL; } } g_free(out_buff.value); } if (expires) { *expires = context->expires; } } return ret; } gboolean sip_sec_context_is_ready(SipSecContext context) { return(context && (context->flags & SIP_SEC_FLAG_COMMON_READY)); } const gchar *sip_sec_context_name(SipSecContext context) { if (context) return((*context->context_name_func)(context)); else return(NULL); } guint sip_sec_context_type(SipSecContext context) { if (context) return(context->type); else return(SIPE_AUTHENTICATION_TYPE_UNSET); } void sip_sec_destroy_context(SipSecContext context) { if (context) (*context->destroy_context_func)(context); } gchar *sip_sec_make_signature(SipSecContext context, const gchar *message) { SipSecBuffer signature; gchar *signature_hex; if (!(*context->make_signature_func)(context, message, &signature)) { SIPE_DEBUG_INFO_NOFORMAT("ERROR: sip_sec_make_signature failed. Unable to sign message!"); return NULL; } signature_hex = buff_to_hex_str(signature.value, signature.length); g_free(signature.value); return signature_hex; } gboolean sip_sec_verify_signature(SipSecContext context, const gchar *message, const gchar *signature_hex) { SipSecBuffer signature; gboolean res; SIPE_DEBUG_INFO("sip_sec_verify_signature: message is:%s signature to verify is:%s", message ? message : "", signature_hex ? signature_hex : ""); if (!message || !signature_hex) return FALSE; signature.length = hex_str_to_buff(signature_hex, &signature.value); res = (*context->verify_signature_func)(context, message, signature); g_free(signature.value); return res; } /* Does authentication type require a password? */ gboolean sip_sec_requires_password(guint authentication, gboolean sso) { /* Map authentication type to module initialization hook & name */ static sip_sec_password_func const auth_to_hook[] = { sip_sec_password__NONE, /* SIPE_AUTHENTICATION_TYPE_UNSET */ sip_sec_password__Basic, /* SIPE_AUTHENTICATION_TYPE_BASIC */ sip_sec_password__NTLM, /* SIPE_AUTHENTICATION_TYPE_NTLM */ sip_sec_password__Kerberos, /* SIPE_AUTHENTICATION_TYPE_KERBEROS */ sip_sec_password__Negotiate, /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */ sip_sec_password__TLS_DSK, /* SIPE_AUTHENTICATION_TYPE_TLS_DSK */ sip_sec_password__NONE, /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */ }; /* If Single-Sign On is disabled then a password is required */ if (!sso) return(TRUE); /* Check if authentation method supports Single-Sign On */ return((*(auth_to_hook[authentication]))()); } /* Initialize & Destroy */ void sip_sec_init(void) { #if !defined(HAVE_GSSAPI_ONLY) && !defined(HAVE_SSPI) sip_sec_init__ntlm(); #endif } void sip_sec_destroy(void) { #if !defined(HAVE_GSSAPI_ONLY) && !defined(HAVE_SSPI) sip_sec_destroy__ntlm(); #endif } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-sec.h ================================================ /** * @file sip-sec.h * * pidgin-sipe * * Copyright (C) 2010-2015 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Opaque type definition for security context */ typedef struct sip_sec_context *SipSecContext; /*** Sipe convenience methods ***/ /** * Initializes Sipe security context. * Obtains cashed initial credentials (TGT for Kerberos) or requests new ones if required. * In former case domain/username/password information is unnecessary. * * @param type (in) authentication type * @param sso (in) @c TRUE if Single Sign-On should be used * @param http (in) @c TRUE if HTTP, @c FALSE for SIP * @param username (in) user name (can be NULL) (ignored for SSO) * @param password (in) password (can be NULL) (ignored for SSO) * * @return context security context to store and pass between security method invocations */ SipSecContext sip_sec_create_context(guint type, gboolean sso, gboolean http, const gchar *username, const gchar *password); /** * Obtains Service ticket (for Kerberos), base64 encodes it and provide as output. * * @param context (in) security context to pass between security method invocations * @param target (in) security target. Service principal name on case of Kerberos. * @param input_toked_base64 (in) base64 encoded input security token. This is Type2 NTLM message or NULL. * @param output_toked_base64 (out) base64 encoded output token to send to server. * @param expires (out) security context expiration time in seconds. * * @return @c TRUE if successful * */ gboolean sip_sec_init_context_step(SipSecContext context, const gchar *target, const gchar *input_toked_base64, gchar **output_toked_base64, guint *expires); /** * Check if the authentication of a security context is completed and it is * ready to be used for message signing and signature verification * * @param context (in) security context. May be @c NULL. * * @return @c TRUE if authentication is completed */ gboolean sip_sec_context_is_ready(SipSecContext context); /** * Return authentication name of a security context * * @param context (in) security context. May be @c NULL. * * @return string or @c NULL */ const gchar *sip_sec_context_name(SipSecContext context); /** * Return type of a security context * * @param context (in) security context. May be @c NULL. * * @return context type or @c SIPE_SIPE_AUTHENTICATION_TYPE_UNSET */ guint sip_sec_context_type(SipSecContext context); /** * A convenience method for sipe. * Destroys security context. * * @param context (in,out) security context to destroy */ void sip_sec_destroy_context(SipSecContext context); /** * A convenience method for sipe. * Signs incoming message. * * @param context (in) security context * @param message (in) a message to sign. * * @return signature for the message. Converted to Hex null terminated string; */ gchar *sip_sec_make_signature(SipSecContext context, const gchar *message); /** * A convenience method for sipe. * Verifies signature for the message. * * @param context (in) security context * @param message (in) which signature to verify. Null terminated string. * @param signature_hex (in) signature to test in Hex representation. Null terminated string. Example: "602306092A864886F71201020201011100FFFFFFFF1A306ACB7BE311827BBF7208D80D15E3" * * @return FALSE on error */ gboolean sip_sec_verify_signature(SipSecContext context, const gchar *message, const gchar *signature_hex); /** * Check if authentication scheme requires a password * * @param type authentication type * @param sso TRUE if user selected Single-Sign On * * @return @c TRUE if password is required */ gboolean sip_sec_requires_password(guint authentication, gboolean sso); /** * Initialize & destroy functions for sip-sec. * Should be called on loading and unloading of the core. */ void sip_sec_init(void); void sip_sec_destroy(void); ================================================ FILE: src/core/sip-soap.c ================================================ /** * @file sip-soap.c * * pidgin-sipe * * Copyright (C) 2011-2016 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * SOAP requests over SIP SERVICE messages * * Specification references: * * - [MS-SIP]: http://msdn.microsoft.com/en-us/library/cc246115.aspx * - [MS-PRES]: http://msdn.microsoft.com/en-us/library/cc431501.aspx * */ #include #include "sip-soap.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-utils.h" void sip_soap_raw_request_cb(struct sipe_core_private *sipe_private, const gchar *from, const gchar *soap, SoapTransCallback callback, struct transaction_payload *payload) { gchar *contact = get_contact(sipe_private); gchar *hdr = g_strdup_printf("Contact: %s\r\n" "Content-Type: application/SOAP+xml\r\n", contact); struct transaction *trans = sip_transport_service(sipe_private, from, hdr, soap, callback); if (trans) { trans->payload = payload; /* SIP transport is no longer valid - give up */ } else if (payload) { if (payload->destroy) (payload->destroy)(payload->data); g_free(payload); } g_free(contact); g_free(hdr); } /** * delta_num != NULL: use user sip: URI as from, include deltanum and increment it * delta_num == NULL; use sip: URI generated from domain name as from */ static void sip_soap_request_full(struct sipe_core_private *sipe_private, const gchar *method, const gchar *request, const gchar *additional, guint *deltanum, SoapTransCallback callback, struct transaction_payload *payload) { gchar *from = deltanum ? sip_uri_self(sipe_private) : sip_uri_from_name(sipe_private->public.sip_domain); gchar *delta = deltanum ? g_strdup_printf("%d", (*deltanum)++) : g_strdup(""); gchar *soap = g_strdup_printf("" "" "" "%s" "%s" "" "%s" "" "", method, request, delta, method, additional ? additional : ""); sip_soap_raw_request_cb(sipe_private, from, soap, callback, payload); g_free(soap); g_free(delta); g_free(from); } void sip_soap_request_cb(struct sipe_core_private *sipe_private, const gchar *method, const gchar *request, SoapTransCallback callback, struct transaction_payload *payload) { sip_soap_request_full(sipe_private, method, request, NULL, &sipe_private->deltanum_contacts, callback, payload); } void sip_soap_request(struct sipe_core_private *sipe_private, const gchar *method, const gchar *request) { sip_soap_request_cb(sipe_private, method, request, NULL, NULL); } /* This is the only user of deltanum_acl */ void sip_soap_ocs2005_setacl(struct sipe_core_private *sipe_private, const gchar *who, gboolean allow) { gchar *request = g_strdup_printf("USER" "%s" "%s", who, allow ? "AA" : "BD"); sip_soap_request_full(sipe_private, "setACE", request, NULL, &sipe_private->deltanum_acl, NULL, NULL); g_free(request); } /** * This request is special: * a) it is send from domain URI and not the users * b) it has XML nodes outside the [MS-PRES] method node * c) doesn't use deltaNum */ void sip_soap_directory_search(struct sipe_core_private *sipe_private, guint max, const gchar *rows, SoapTransCallback callback, struct transaction_payload *payload) { gchar *request = g_strdup_printf("" "%d", max); gchar *additional = g_strdup_printf("" "%s" "", rows); sip_soap_request_full(sipe_private, "directorySearch", request, additional, NULL, callback, payload); g_free(additional); g_free(request); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-soap.h ================================================ /** * @file sip-soap.h * * pidgin-sipe * * Copyright (C) 2011 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipmsg; struct sipe_core_private; struct transaction; struct transaction_payload; /* Must be the same as sip-transport.h/TransCallback */ typedef gboolean (*SoapTransCallback) (struct sipe_core_private *, struct sipmsg *, struct transaction *); /** * Send raw SOAP request with callback * * @param sipe_private SIPE core private data * @param from URI to use for SIP transport * @param soap SOAP XML string * @param callback callback function (may be @c NULL) * @param payload callback data (may be @c NULL) */ void sip_soap_raw_request_cb(struct sipe_core_private *sipe_private, const gchar *from, const gchar *soap, SoapTransCallback callback, struct transaction_payload *payload); /** * Send [MS-SIP] SOAP request with callback * * @param sipe_private SIPE core private data * @param method [MS-PRES] method * @param request [MS-PRES] request data * @param callback callback function (may be @c NULL) * @param payload callback data (may be @c NULL) */ void sip_soap_request_cb(struct sipe_core_private *sipe_private, const gchar *method, const gchar *request, SoapTransCallback callback, struct transaction_payload *payload); /** * Send [MS-SIP] SOAP request * * @param sipe_private SIPE core private data * @param method [MS-PRES] method * @param request [MS-PRES] request data */ void sip_soap_request(struct sipe_core_private *sipe_private, const gchar *method, const gchar *request); /** * Send [MS-SIP] setACE (set ACL for contact) SOAP request (OCS2005) * * @param sipe_private SIPE core private data * @param who sip: URI of the contact * @param allow allow or deny */ void sip_soap_ocs2005_setacl(struct sipe_core_private *sipe_private, const gchar *who, gboolean allow); /** * Send [MS-PRES] directorySearch SOAP request (OCS2007 and older) * * @param sipe_private SIPE core private data * @param max maximum number of results to return * @param rows XML m:row nodes to add to request * @param callback callback function * @param payload callback data (may be @c NULL) */ void sip_soap_directory_search(struct sipe_core_private *sipe_private, guint max, const gchar *rows, SoapTransCallback callback, struct transaction_payload *payload); /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-transport.c ================================================ /** * @file sip-transport.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * This module incapsulates SIP (RFC3261) protocol and provides * higher level API (a layer) to XML-based SIPE (SIP with Extensions). * Underlying leyer for this is TCP/SSL layer. * * A diagram in pseudographics: * * === SIPE (XML-based) layer ====================== * === SIP RFC3261 transport layer (This module) === * === TCP/SSL layer =============================== * * Authentication (Kerberos and NTLM) is applicable to this layer only. * The same with message integtity (signing). No sip-sec* code should * be used ourside of this module. * * SIP errors as codes(both as a return codes and network conditions) should be * escalated to higher leyer (SIPE). Network conditions include no response * within timeout interval. * * This module should support redirect internally. No escalations to higher * layers needed. * * NO SIP-messages (headers) composing and processing should be outside of * this module (!) Like headers: Via, Route, Contact, Authorization, etc. * It's all irrelevant to higher layer responsibilities. * * Specification references: * * - [MS-SIPAE]: http://msdn.microsoft.com/en-us/library/cc431510.aspx * - RFC2732: http://www.ietf.org/rfc/rfc2732.txt */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-common.h" #include "sipmsg.h" #include "sip-sec.h" #include "sip-sec-digest.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-certificate.h" #include "sipe-dialog.h" #include "sipe-incoming.h" #include "sipe-lync-autodiscover.h" #include "sipe-nls.h" #include "sipe-notify.h" #include "sipe-schedule.h" #include "sipe-sign.h" #include "sipe-subscriptions.h" #include "sipe-utils.h" #include "uuid.h" struct sip_auth { guint type; struct sip_sec_context *gssapi_context; gchar *gssapi_data; gchar *opaque; const gchar *protocol; gchar *realm; gchar *sts_uri; gchar *target; guint version; guint retries; guint ntlm_num; guint expires; gboolean can_retry; }; /* sip-transport.c private data */ struct sip_transport { struct sipe_transport_connection *connection; gchar *server_name; guint server_port; gchar *epid; /* local IP address of transport socket */ gchar *ip_address; /* RAW X.X.X.X (IPv4), X:X:...:X (IPv6) */ gchar *uri_address; /* URI X.X.X.X (IPv4), [X:X:...:X] (IPv6) */ const gchar *sdp_marker; /* SDP address marker: "IP4" or "IP6" */ GSList *transactions; struct sip_auth registrar; struct sip_auth proxy; guint cseq; guint register_attempt; guint keepalive_timeout; time_t last_message; gboolean processing_input; /* whether full header received */ gboolean auth_incomplete; /* whether authentication not completed */ gboolean auth_retry; /* whether next authentication should be tried */ gboolean reregister_set; /* whether reregister timer set */ gboolean reauthenticate_set; /* whether reauthenticate timer set */ gboolean subscribed; /* whether subscribed to events, except buddies presence */ gboolean deregister; /* whether in deregistration */ }; /* Keep in sync with sipe_transport_type! */ static const char *transport_descriptor[] = { "", "tls", "tcp"}; #define TRANSPORT_DESCRIPTOR (transport_descriptor[transport->connection->type]) static char *genbranch() { return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X", rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF); } static void sipe_auth_free(struct sip_auth *auth) { g_free(auth->opaque); auth->opaque = NULL; auth->protocol = NULL; g_free(auth->realm); auth->realm = NULL; g_free(auth->sts_uri); auth->sts_uri = NULL; g_free(auth->target); auth->target = NULL; auth->version = 0; auth->type = SIPE_AUTHENTICATION_TYPE_UNSET; auth->retries = 0; auth->expires = 0; auth->can_retry = FALSE; g_free(auth->gssapi_data); auth->gssapi_data = NULL; sip_sec_destroy_context(auth->gssapi_context); auth->gssapi_context = NULL; } static void sipe_make_signature(struct sipe_core_private *sipe_private, struct sipmsg *msg) { struct sip_transport *transport = sipe_private->transport; if (sip_sec_context_is_ready(transport->registrar.gssapi_context)) { struct sipmsg_breakdown msgbd; gchar *signature_input_str; msgbd.msg = msg; sipmsg_breakdown_parse(&msgbd, transport->registrar.realm, transport->registrar.target, transport->registrar.protocol); msgbd.rand = g_strdup_printf("%08x", g_random_int()); transport->registrar.ntlm_num++; msgbd.num = g_strdup_printf("%d", transport->registrar.ntlm_num); signature_input_str = sipmsg_breakdown_get_string(transport->registrar.version, &msgbd); if (signature_input_str != NULL) { char *signature_hex = sip_sec_make_signature(transport->registrar.gssapi_context, signature_input_str); g_free(msg->signature); msg->signature = signature_hex; g_free(msg->rand); msg->rand = g_strdup(msgbd.rand); g_free(msg->num); msg->num = g_strdup(msgbd.num); g_free(signature_input_str); } sipmsg_breakdown_free(&msgbd); } } static const gchar *const auth_type_to_protocol[] = { NULL, /* SIPE_AUTHENTICATION_TYPE_UNSET */ NULL, /* SIPE_AUTHENTICATION_TYPE_BASIC */ "NTLM", /* SIPE_AUTHENTICATION_TYPE_NTLM */ "Kerberos", /* SIPE_AUTHENTICATION_TYPE_KERBEROS */ NULL, /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */ "TLS-DSK", /* SIPE_AUTHENTICATION_TYPE_TLS_DSK */ NULL, /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */ }; #define AUTH_PROTOCOLS (sizeof(auth_type_to_protocol)/sizeof(gchar *)) static gchar *msg_signature_to_auth(struct sip_auth *auth, struct sipmsg *msg) { return(g_strdup_printf("%s qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", crand=\"%s\", cnum=\"%s\", response=\"%s\"", auth->protocol, auth->opaque, auth->realm, auth->target, msg->rand, msg->num, msg->signature)); } static gboolean auth_can_retry(struct sip_transport *transport, const struct sip_auth *auth) { /* NTLM is the scheme with lowest priority - don't retry */ gboolean retry = auth->can_retry && (auth->type != SIPE_AUTHENTICATION_TYPE_NTLM); if (retry) transport->auth_retry = TRUE; return(retry); } static void initialize_auth_retry(struct sipe_core_private *sipe_private, struct sip_auth *auth) { struct sip_transport *transport = sipe_private->transport; if (auth_can_retry(transport, auth)) { if (auth->gssapi_context) { /* need to drop context for retry */ sip_sec_destroy_context(auth->gssapi_context); auth->gssapi_context = NULL; } } else { sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Failed to authenticate to server")); } } static gchar *initialize_auth_context(struct sipe_core_private *sipe_private, struct sip_auth *auth, struct sipmsg *msg) { struct sip_transport *transport = sipe_private->transport; gchar *ret; gchar *gssapi_data = NULL; gchar *sign_str; gchar *gssapi_str; gchar *opaque_str; gchar *version_str; /* * If transport is de-registering when we reach this point then we * are in the middle of the previous authentication context setup * attempt. So we shouldn't try another attempt. */ if (transport->deregister) return NULL; /* Create security context or handshake continuation? */ if (auth->gssapi_context) { /* Perform next step in authentication handshake */ gboolean status = sip_sec_init_context_step(auth->gssapi_context, auth->target, auth->gssapi_data, &gssapi_data, &auth->expires); /* If authentication is completed gssapi_data can be NULL */ if (!(status && (sip_sec_context_is_ready(auth->gssapi_context) || gssapi_data))) { SIPE_DEBUG_ERROR_NOFORMAT("initialize_auth_context: security context continuation failed"); g_free(gssapi_data); initialize_auth_retry(sipe_private, auth); return NULL; } } else { /* Create security context */ gpointer password = sipe_private->password; /* For TLS-DSK the "password" is a certificate */ if (auth->type == SIPE_AUTHENTICATION_TYPE_TLS_DSK) { password = sipe_certificate_tls_dsk_find(sipe_private, auth->target); if (!password) { if (auth->sts_uri) { SIPE_DEBUG_INFO("initialize_auth_context: TLS-DSK Certificate Provisioning URI %s", auth->sts_uri); if (!sipe_certificate_tls_dsk_generate(sipe_private, auth->target, auth->sts_uri)) { gchar *tmp = g_strdup_printf(_("Can't request certificate from %s"), auth->sts_uri); sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED, tmp); g_free(tmp); } } else { sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("No URI for certificate provisioning service provided")); } /* we can't authenticate the message yet */ transport->auth_incomplete = TRUE; return(NULL); } else { SIPE_DEBUG_INFO("initialize_auth_context: TLS-DSK certificate for target '%s' found.", auth->target); } } auth->gssapi_context = sip_sec_create_context(auth->type, SIPE_CORE_PRIVATE_FLAG_IS(SSO), FALSE, /* connection-less for SIP */ sipe_private->authuser, password); if (auth->gssapi_context) { sip_sec_init_context_step(auth->gssapi_context, auth->target, NULL, &gssapi_data, &(auth->expires)); } /* if auth->gssapi_context is NULL then gssapi_data is still NULL */ if (!gssapi_data) { SIPE_DEBUG_ERROR_NOFORMAT("initialize_auth_context: security context initialization failed"); initialize_auth_retry(sipe_private, auth); return NULL; } } if ((auth->version > 3) && sip_sec_context_is_ready(auth->gssapi_context)) { sipe_make_signature(sipe_private, msg); sign_str = g_strdup_printf(", crand=\"%s\", cnum=\"%s\", response=\"%s\"", msg->rand, msg->num, msg->signature); } else { sign_str = g_strdup(""); } if (gssapi_data) { gssapi_str = g_strdup_printf(", gssapi-data=\"%s\"", gssapi_data); g_free(gssapi_data); } else { gssapi_str = g_strdup(""); } opaque_str = auth->opaque ? g_strdup_printf(", opaque=\"%s\"", auth->opaque) : g_strdup(""); if (auth->version > 2) { version_str = g_strdup_printf(", version=%d", auth->version); } else { version_str = g_strdup(""); } ret = g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\"%s%s%s", auth->protocol, opaque_str, auth->realm, auth->target, gssapi_str, version_str, sign_str); g_free(version_str); g_free(opaque_str); g_free(gssapi_str); g_free(sign_str); return(ret); } static gchar *auth_header(struct sipe_core_private *sipe_private, struct sip_auth *auth, struct sipmsg *msg) { gchar *ret = NULL; /* * If the message is already signed then we have an authentication * context, i.e. the authentication handshake is complete. Generate * authentication header from message signature. */ if (msg->signature) { ret = msg_signature_to_auth(auth, msg); /* * We should reach this point only when the authentication context * needs to be initialized. */ } else { ret = initialize_auth_context(sipe_private, auth, msg); } return(ret); } static void fill_auth(const gchar *hdr, struct sip_auth *auth) { const gchar *param; /* skip authentication identifier */ hdr = strchr(hdr, ' '); if (!hdr) { SIPE_DEBUG_ERROR_NOFORMAT("fill_auth: corrupted authentication header"); return; } while (*hdr == ' ') hdr++; /* start of next parameter value */ while ((param = strchr(hdr, '=')) != NULL) { const gchar *end; /* parameter value type */ param++; if (*param == '"') { /* string: xyz="..."(,) */ end = strchr(++param, '"'); if (!end) { SIPE_DEBUG_ERROR("fill_auth: corrupted string parameter near '%s'", hdr); break; } } else { /* number: xyz=12345(,) */ end = strchr(param, ','); if (!end) { /* last parameter */ end = param + strlen(param); } } #if 0 SIPE_DEBUG_INFO("fill_auth: hdr '%s'", hdr); SIPE_DEBUG_INFO("fill_auth: param '%s'", param); SIPE_DEBUG_INFO("fill_auth: end '%s'", end); #endif /* parameter type */ if (g_str_has_prefix(hdr, "gssapi-data=\"")) { g_free(auth->gssapi_data); auth->gssapi_data = g_strndup(param, end - param); } else if (g_str_has_prefix(hdr, "opaque=\"")) { g_free(auth->opaque); auth->opaque = g_strndup(param, end - param); } else if (g_str_has_prefix(hdr, "realm=\"")) { g_free(auth->realm); auth->realm = g_strndup(param, end - param); } else if (g_str_has_prefix(hdr, "sts-uri=\"")) { /* Only used with SIPE_AUTHENTICATION_TYPE_TLS_DSK */ g_free(auth->sts_uri); auth->sts_uri = g_strndup(param, end - param); } else if (g_str_has_prefix(hdr, "targetname=\"")) { g_free(auth->target); auth->target = g_strndup(param, end - param); } else if (g_str_has_prefix(hdr, "version=")) { auth->version = atoi(param); } /* skip to next parameter */ while ((*end == '"') || (*end == ',') || (*end == ' ')) end++; hdr = end; } return; } static void sign_outgoing_message(struct sipe_core_private *sipe_private, struct sipmsg *msg) { struct sip_transport *transport = sipe_private->transport; gchar *buf; if (transport->registrar.type == SIPE_AUTHENTICATION_TYPE_UNSET) { return; } sipe_make_signature(sipe_private, msg); buf = auth_header(sipe_private, &transport->registrar, msg); if (buf) { sipmsg_add_header_now(msg, "Authorization", buf); g_free(buf); } } /* * NOTE: Do *NOT* call sipe_backend_transport_message(...) directly! * * All SIP messages must pass through this function in order to update * the timestamp for keepalive tracking. */ static void send_sip_message(struct sip_transport *transport, const gchar *string) { sipe_utils_message_debug(transport->connection, "SIP", string, NULL, TRUE); transport->last_message = time(NULL); sipe_backend_transport_message(transport->connection, string); } static void start_keepalive_timer(struct sipe_core_private *sipe_private, guint seconds); static void keepalive_timeout(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER gpointer data) { struct sip_transport *transport = sipe_private->transport; if (transport) { guint since_last = time(NULL) - transport->last_message; guint restart = transport->keepalive_timeout; if (since_last >= restart) { SIPE_DEBUG_INFO("keepalive_timeout: expired %d", restart); send_sip_message(transport, "\r\n\r\n"); } else { /* timeout not reached since last message -> reschedule */ restart -= since_last; } start_keepalive_timer(sipe_private, restart); } } static void start_keepalive_timer(struct sipe_core_private *sipe_private, guint seconds) { sipe_schedule_seconds(sipe_private, "<+keepalive-timeout>", NULL, seconds, keepalive_timeout, NULL); } void sip_transport_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, guint code, const char *text, const char *body) { gchar *name; gchar *value; GString *outstr = g_string_new(""); gchar *contact; GSList *tmp; static const gchar *keepers[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL }; /* Can return NULL! */ contact = get_contact(sipe_private); if (contact) { sipmsg_add_header(msg, "Contact", contact); g_free(contact); } if (body) { gchar *len = g_strdup_printf("%" G_GSIZE_FORMAT , (gsize) strlen(body)); sipmsg_add_header(msg, "Content-Length", len); g_free(len); } else { sipmsg_add_header(msg, "Content-Length", "0"); } sipmsg_add_header(msg, "User-Agent", sipe_core_user_agent(sipe_private)); msg->response = code; sipmsg_strip_headers(msg, keepers); sipmsg_merge_new_headers(msg); sign_outgoing_message(sipe_private, msg); g_string_append_printf(outstr, "SIP/2.0 %d %s\r\n", code, text); tmp = msg->headers; while (tmp) { name = ((struct sipnameval*) (tmp->data))->name; value = ((struct sipnameval*) (tmp->data))->value; g_string_append_printf(outstr, "%s: %s\r\n", name, value); tmp = g_slist_next(tmp); } g_string_append_printf(outstr, "\r\n%s", body ? body : ""); send_sip_message(sipe_private->transport, outstr->str); g_string_free(outstr, TRUE); } static void transactions_remove(struct sipe_core_private *sipe_private, struct transaction *trans) { struct sip_transport *transport = sipe_private->transport; if (transport->transactions) { transport->transactions = g_slist_remove(transport->transactions, trans); SIPE_DEBUG_INFO("SIP transactions count:%d after removal", g_slist_length(transport->transactions)); if (trans->msg) sipmsg_free(trans->msg); if (trans->payload) { if (trans->payload->destroy) (*trans->payload->destroy)(trans->payload->data); g_free(trans->payload); } g_free(trans->key); if (trans->timeout_key) { sipe_schedule_cancel(sipe_private, trans->timeout_key); g_free(trans->timeout_key); } g_free(trans); } } static struct transaction *transactions_find(struct sip_transport *transport, struct sipmsg *msg) { GSList *transactions = transport->transactions; const gchar *call_id = sipmsg_find_call_id_header(msg); const gchar *cseq = sipmsg_find_cseq_header(msg); gchar *key; if (!call_id || !cseq) { SIPE_DEBUG_ERROR_NOFORMAT("transaction_find: no Call-ID or CSeq!"); return NULL; } key = g_strdup_printf("<%s><%s>", call_id, cseq); while (transactions) { struct transaction *trans = transactions->data; if (!g_ascii_strcasecmp(trans->key, key)) { g_free(key); return trans; } transactions = transactions->next; } g_free(key); return NULL; } static void transaction_timeout_cb(struct sipe_core_private *sipe_private, gpointer data) { struct transaction *trans = data; (trans->timeout_callback)(sipe_private, trans->msg, trans); transactions_remove(sipe_private, trans); } struct transaction *sip_transport_request_timeout(struct sipe_core_private *sipe_private, const gchar *method, const gchar *url, const gchar *to, const gchar *addheaders, const gchar *body, struct sip_dialog *dialog, TransCallback callback, guint timeout, TransCallback timeout_callback) { struct sip_transport *transport = sipe_private->transport; char *buf; struct sipmsg *msg; gchar *ourtag = dialog && dialog->ourtag ? g_strdup(dialog->ourtag) : NULL; gchar *theirtag = dialog && dialog->theirtag ? g_strdup(dialog->theirtag) : NULL; gchar *theirepid = dialog && dialog->theirepid ? g_strdup(dialog->theirepid) : NULL; gchar *callid = dialog && dialog->callid ? g_strdup(dialog->callid) : gencallid(); gchar *branch = dialog && dialog->callid ? NULL : genbranch(); gchar *route = g_strdup(""); const gchar *epid = transport->epid; int cseq = dialog ? ++dialog->cseq : 1 /* as Call-Id is new in this case */; struct transaction *trans = NULL; if (dialog && dialog->routes) { GSList *iter = dialog->routes; while(iter) { char *tmp = route; route = g_strdup_printf("%sRoute: %s\r\n", route, (char *)iter->data); g_free(tmp); iter = g_slist_next(iter); } } if (!ourtag && !dialog) { ourtag = gentag(); } if (sipe_strequal(method, "REGISTER")) { if (sipe_private->register_callid) { g_free(callid); callid = g_strdup(sipe_private->register_callid); } else { sipe_private->register_callid = g_strdup(callid); } cseq = ++transport->cseq; } buf = g_strdup_printf("%s %s SIP/2.0\r\n" "Via: SIP/2.0/%s %s:%d%s%s\r\n" "From: %s%s;epid=%s\r\n" "To: <%s>%s%s%s%s\r\n" "Max-Forwards: 70\r\n" "CSeq: %d %s\r\n" "User-Agent: %s\r\n" "Call-ID: %s\r\n" "%s%s" "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s", method, dialog && dialog->request ? dialog->request : url, TRANSPORT_DESCRIPTOR, transport->uri_address, transport->connection->client_port, branch ? ";branch=" : "", branch ? branch : "", sipe_private->username, ourtag ? ";tag=" : "", ourtag ? ourtag : "", epid, to, theirtag ? ";tag=" : "", theirtag ? theirtag : "", theirepid ? ";epid=" : "", theirepid ? theirepid : "", cseq, method, sipe_core_user_agent(sipe_private), callid, route, addheaders ? addheaders : "", body ? (gsize) strlen(body) : 0, body ? body : ""); //printf ("parsing msg buf:\n%s\n\n", buf); msg = sipmsg_parse_msg(buf); g_free(buf); g_free(ourtag); g_free(theirtag); g_free(theirepid); g_free(branch); g_free(route); sign_outgoing_message(sipe_private, msg); /* The authentication scheme is not ready so we can't send the message. This should only happen for REGISTER messages. */ if (!transport->auth_incomplete) { buf = sipmsg_to_string(msg); /* add to ongoing transactions */ /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */ if (!sipe_strequal(method, "ACK")) { trans = g_new0(struct transaction, 1); trans->callback = callback; trans->msg = msg; trans->key = g_strdup_printf("<%s><%d %s>", callid, cseq, method); if (timeout_callback) { trans->timeout_callback = timeout_callback; trans->timeout_key = g_strdup_printf("%s", trans->key); sipe_schedule_seconds(sipe_private, trans->timeout_key, trans, timeout, transaction_timeout_cb, NULL); } transport->transactions = g_slist_append(transport->transactions, trans); SIPE_DEBUG_INFO("SIP transactions count:%d after addition", g_slist_length(transport->transactions)); } send_sip_message(transport, buf); g_free(buf); } if (!trans) sipmsg_free(msg); g_free(callid); return trans; } struct transaction *sip_transport_request(struct sipe_core_private *sipe_private, const gchar *method, const gchar *url, const gchar *to, const gchar *addheaders, const gchar *body, struct sip_dialog *dialog, TransCallback callback) { return sip_transport_request_timeout(sipe_private, method, url, to, addheaders, body, dialog, callback, 0, NULL); } static void sip_transport_simple_request(struct sipe_core_private *sipe_private, const gchar *method, struct sip_dialog *dialog) { sip_transport_request(sipe_private, method, dialog->with, dialog->with, NULL, NULL, dialog, NULL); } void sip_transport_ack(struct sipe_core_private *sipe_private, struct sip_dialog *dialog) { sip_transport_simple_request(sipe_private, "ACK", dialog); } void sip_transport_bye(struct sipe_core_private *sipe_private, struct sip_dialog *dialog) { sip_transport_simple_request(sipe_private, "BYE", dialog); } struct transaction *sip_transport_info(struct sipe_core_private *sipe_private, const gchar *addheaders, const gchar *body, struct sip_dialog *dialog, TransCallback callback) { return sip_transport_request(sipe_private, "INFO", dialog->with, dialog->with, addheaders, body, dialog, callback); } struct transaction *sip_transport_invite(struct sipe_core_private *sipe_private, const gchar *addheaders, const gchar *body, struct sip_dialog *dialog, TransCallback callback) { return sip_transport_request(sipe_private, "INVITE", dialog->with, dialog->with, addheaders, body, dialog, callback); } struct transaction *sip_transport_service(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *addheaders, const gchar *body, TransCallback callback) { return sip_transport_request(sipe_private, "SERVICE", uri, uri, addheaders, body, NULL, callback); } void sip_transport_subscribe(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *addheaders, const gchar *body, struct sip_dialog *dialog, TransCallback callback) { sip_transport_request(sipe_private, "SUBSCRIBE", uri, uri, addheaders, body, dialog, callback); } void sip_transport_update(struct sipe_core_private *sipe_private, struct sip_dialog *dialog, TransCallback callback) { sip_transport_request(sipe_private, "UPDATE", dialog->with, dialog->with, NULL, NULL, dialog, callback); } static const gchar *get_auth_header(struct sipe_core_private *sipe_private, guint type, struct sipmsg *msg) { struct sip_auth *auth = &sipe_private->transport->registrar; auth->type = type; auth->protocol = auth_type_to_protocol[auth->type]; return(sipmsg_find_auth_header(msg, auth->protocol)); } static void do_register(struct sipe_core_private *sipe_private, gboolean deregister); static void do_reauthenticate_cb(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER gpointer unused) { struct sip_transport *transport = sipe_private->transport; /* register again when security token expires */ /* we have to start a new authentication as the security token * is almost expired by sending a not signed REGISTER message */ SIPE_LOG_INFO_NOFORMAT("do_reauthenticate_cb: do a full reauthentication"); sipe_auth_free(&transport->registrar); sipe_auth_free(&transport->proxy); sipe_schedule_cancel(sipe_private, ""); transport->auth_retry = TRUE; transport->reregister_set = FALSE; transport->register_attempt = 0; do_register(sipe_private, FALSE); transport->reauthenticate_set = FALSE; } static void sip_transport_default_contact(struct sipe_core_private *sipe_private) { struct sip_transport *transport = sipe_private->transport; sipe_private->contact = g_strdup_printf(";proxy=replace", sipe_private->username, transport->connection->client_port, transport->uri_address, TRANSPORT_DESCRIPTOR); } static void do_register_cb(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER void *unused) { do_register(sipe_private, FALSE); } static void sip_transport_set_reregister(struct sipe_core_private *sipe_private, int expires) { sipe_schedule_seconds(sipe_private, "", NULL, expires, do_register_cb, NULL); } static void sipe_server_register(struct sipe_core_private *sipe_private, guint type, gchar *server_name, guint server_port); static gboolean process_register_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { struct sip_transport *transport = sipe_private->transport; const gchar *expires_header; int expires, i; GSList *hdr = msg->headers; struct sipnameval *elem; expires_header = sipmsg_find_expires_header(msg); expires = expires_header != NULL ? strtol(expires_header, NULL, 10) : 0; SIPE_DEBUG_INFO("process_register_response: got response to REGISTER; expires = %d", expires); switch (msg->response) { case 200: if (expires) { const gchar *contact_hdr; const gchar *auth_hdr; gchar *gruu = NULL; gchar *uuid; gchar *timeout; if (!transport->reregister_set) { /* Schedule re-register 30 seconds before expiration */ if (expires > 30) expires -= 30; sip_transport_set_reregister(sipe_private, expires); transport->reregister_set = TRUE; } auth_hdr = sipmsg_find_auth_header(msg, transport->registrar.protocol); if (auth_hdr) { SIPE_DEBUG_INFO("process_register_response: Auth header: %s", auth_hdr); fill_auth(auth_hdr, &transport->registrar); } if (!transport->reauthenticate_set) { /* [MS-SIPAE] Section 3.2.2 Timers * * When the ... authentication handshake completes * and the SA enters the "established" state, the * SIP protocol client MUST start an SA expiration * timer. * ... * The expiration timer value is the lesser of * * - Kerberos: the service ticket expiry time * - TLS-DSK: the certificate expiration time * * and eight hours, further reduced by some buffer * time. * ... * The protocol client MUST choose a sufficient * buffer time to allow for the ... authentication * handshake that reestablishes the SA to complete * ... This value SHOULD be five (5) minutes or * longer. */ guint reauth_timeout = transport->registrar.expires; SIPE_LOG_INFO_NOFORMAT("process_register_response: authentication handshake completed successfully"); if ((reauth_timeout == 0) || (reauth_timeout > 8 * 60 * 60)) reauth_timeout = 8 * 60 * 60; if (reauth_timeout > 5 * 60) reauth_timeout -= 5 * 60; sipe_schedule_seconds(sipe_private, "<+reauthentication>", NULL, reauth_timeout, do_reauthenticate_cb, NULL); transport->reauthenticate_set = TRUE; } uuid = get_uuid(sipe_private); // There can be multiple Contact headers (one per location where the user is logged in) so // make sure to only get the one for this uuid for (i = 0; (contact_hdr = sipmsg_find_header_instance (msg, "Contact", i)); i++) { gchar * valid_contact = sipmsg_find_part_of_header (contact_hdr, uuid, NULL, NULL); if (valid_contact) { gruu = sipmsg_find_part_of_header(contact_hdr, "gruu=\"", "\"", NULL); //SIPE_DEBUG_INFO("process_register_response: got gruu %s from contact hdr w/ right uuid: %s", gruu, contact_hdr); g_free(valid_contact); break; } else { //SIPE_DEBUG_INFO("process_register_response: ignoring contact hdr b/c not right uuid: %s", contact_hdr); } } g_free(uuid); g_free(sipe_private->contact); if(gruu) { sipe_private->contact = g_strdup_printf("<%s>", gruu); g_free(gruu); } else { //SIPE_DEBUG_INFO_NOFORMAT("process_register_response: didn't find gruu in a Contact hdr"); sip_transport_default_contact(sipe_private); } SIPE_CORE_PRIVATE_FLAG_UNSET(OCS2007); SIPE_CORE_PRIVATE_FLAG_UNSET(REMOTE_USER); SIPE_CORE_PRIVATE_FLAG_UNSET(BATCHED_SUPPORT); SIPE_CORE_PRIVATE_FLAG_UNSET(SFB); while(hdr) { elem = hdr->data; if (sipe_strcase_equal(elem->name, "Supported")) { if (sipe_strcase_equal(elem->value, "msrtc-event-categories")) { /* We interpret this as OCS2007+ indicator */ SIPE_CORE_PRIVATE_FLAG_SET(OCS2007); SIPE_LOG_INFO("process_register_response: Supported: %s (indicates OCS2007+)", elem->value); } if (sipe_strcase_equal(elem->value, "adhoclist")) { SIPE_CORE_PRIVATE_FLAG_SET(BATCHED_SUPPORT); SIPE_DEBUG_INFO("process_register_response: Supported: %s", elem->value); } } else if (sipe_strcase_equal(elem->name, "Allow-Events")){ gchar **caps = g_strsplit(elem->value,",",0); i = 0; while (caps[i]) { sipe_private->allowed_events = g_slist_append(sipe_private->allowed_events, g_strdup(caps[i])); SIPE_DEBUG_INFO("process_register_response: Allow-Events: %s", caps[i]); i++; } g_strfreev(caps); } else if (sipe_strcase_equal(elem->name, "ms-user-logon-data")) { if (sipe_strcase_equal(elem->value, "RemoteUser")) { SIPE_CORE_PRIVATE_FLAG_SET(REMOTE_USER); SIPE_DEBUG_INFO_NOFORMAT("process_register_response: ms-user-logon-data: RemoteUser (connected " "via Edge Server)"); } } else if (sipe_strcase_equal(elem->name, "Server")) { /* Server string has format like 'RTC/6.0'. * We want to check the first digit. */ gchar **parts = g_strsplit_set(elem->value, "/.", 3); if (g_strv_length(parts) > 1) { guint version = atoi(parts[1]); if (version >= 6) { SIPE_CORE_PRIVATE_FLAG_SET(SFB); SIPE_LOG_INFO("process_register_response: server version is %d >= 6 (indicates Skype for Business+)", version); } } g_strfreev(parts); } hdr = g_slist_next(hdr); } sipe_backend_connection_completed(SIPE_CORE_PUBLIC); /* rejoin open chats to be able to use them by continue to send messages */ sipe_backend_chat_rejoin_all(SIPE_CORE_PUBLIC); /* subscriptions, done only once */ if (!transport->subscribed) { sipe_subscription_self_events(sipe_private); transport->subscribed = TRUE; } timeout = sipmsg_find_part_of_header(sipmsg_find_header(msg, "ms-keep-alive"), "timeout=", ";", NULL); if (timeout != NULL) { sscanf(timeout, "%u", &transport->keepalive_timeout); SIPE_DEBUG_INFO("process_register_response: server determined keep alive timeout is %u seconds", transport->keepalive_timeout); g_free(timeout); } SIPE_DEBUG_INFO("process_register_response: got 200, removing CSeq: %d", transport->cseq); } break; case 301: { gchar *redirect = sipmsg_parse_contact_address(msg); SIPE_LOG_INFO_NOFORMAT("process_register_response: authentication handshake completed successfully (with redirect)"); if (redirect && (g_ascii_strncasecmp("sip:", redirect, 4) == 0)) { gchar **parts = g_strsplit(redirect + 4, ";", 0); gchar **tmp; gchar *hostname; int port = 0; guint transport_type = SIPE_TRANSPORT_TLS; int i = 1; tmp = g_strsplit(parts[0], ":", 0); hostname = g_strdup(tmp[0]); if (tmp[1]) port = strtoul(tmp[1], NULL, 10); g_strfreev(tmp); while (parts[i]) { tmp = g_strsplit(parts[i], "=", 0); if (tmp[1]) { if (g_ascii_strcasecmp("transport", tmp[0]) == 0) { if (g_ascii_strcasecmp("tcp", tmp[1]) == 0) { transport_type = SIPE_TRANSPORT_TCP; } } } g_strfreev(tmp); i++; } g_strfreev(parts); /* Close old connection */ sipe_core_connection_cleanup(sipe_private); /* transport and sipe_private->transport are invalid after this */ /* Create new connection */ sipe_server_register(sipe_private, transport_type, hostname, port); /* sipe_private->transport has a new value */ SIPE_DEBUG_INFO("process_register_response: redirected to host %s port %d transport %d", hostname, port, transport_type); } g_free(redirect); } break; case 401: { const char *auth_hdr = NULL; SIPE_DEBUG_INFO("process_register_response: REGISTER retries %d", transport->registrar.retries); if (transport->reauthenticate_set) { SIPE_DEBUG_ERROR_NOFORMAT("process_register_response: RE-REGISTER rejected, triggering re-authentication"); do_reauthenticate_cb(sipe_private, NULL); return TRUE; } if (sip_sec_context_is_ready(transport->registrar.gssapi_context)) { struct sip_auth *auth = &transport->registrar; /* NTLM is the scheme with lowest priority - don't retry */ if (auth_can_retry(transport, auth)) { guint failed = auth->type; SIPE_DEBUG_INFO_NOFORMAT("process_register_response: authentication handshake failed - trying next authentication scheme."); sipe_auth_free(auth); auth->type = failed; } else { SIPE_LOG_ERROR_NOFORMAT("process_register_response: authentication handshake failed - giving up."); sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Authentication failed")); return TRUE; } } if (sipe_private->authentication_type == SIPE_AUTHENTICATION_TYPE_AUTOMATIC) { struct sip_auth *auth = &transport->registrar; guint try = auth->type; while (!auth_hdr) { /* * Determine next authentication * scheme in priority order */ if (transport->auth_retry) switch (try) { case SIPE_AUTHENTICATION_TYPE_UNSET: try = SIPE_AUTHENTICATION_TYPE_TLS_DSK; break; case SIPE_AUTHENTICATION_TYPE_TLS_DSK: #if defined(HAVE_GSSAPI_GSSAPI_H) || defined(HAVE_SSPI) try = SIPE_AUTHENTICATION_TYPE_KERBEROS; break; case SIPE_AUTHENTICATION_TYPE_KERBEROS: #endif try = SIPE_AUTHENTICATION_TYPE_NTLM; break; default: try = SIPE_AUTHENTICATION_TYPE_UNSET; break; } auth->can_retry = (try != SIPE_AUTHENTICATION_TYPE_UNSET); if (!auth->can_retry) { SIPE_DEBUG_INFO_NOFORMAT("process_register_response: no more authentication schemes to try"); break; } auth_hdr = get_auth_header(sipe_private, try, msg); } transport->auth_retry = FALSE; } else auth_hdr = get_auth_header(sipe_private, sipe_private->authentication_type, msg); if (!auth_hdr) { sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, _("Incompatible authentication scheme chosen")); return TRUE; } SIPE_DEBUG_INFO("process_register_response: Auth header: %s", auth_hdr); fill_auth(auth_hdr, &transport->registrar); transport->reregister_set = FALSE; transport->register_attempt = 0; do_register(sipe_private, sipe_backend_connection_is_disconnecting(SIPE_CORE_PUBLIC)); } break; case 403: { gchar *reason; gchar *warning; sipmsg_parse_warning(msg, &reason); reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg); warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given")); g_free(reason); sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_INVALID_SETTINGS, warning); g_free(warning); return TRUE; } break; case 404: { const gchar *diagnostics = sipmsg_find_header(msg, "ms-diagnostics"); gchar *reason = sipmsg_get_ms_diagnostics_reason(msg); gchar *warning; warning = g_strdup_printf(_("Not found: %s. Please contact your Administrator"), diagnostics ? (reason ? reason : _("no reason given")) : _("SIP is either not enabled for the destination URI or it does not exist")); g_free(reason); sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_INVALID_USERNAME, warning); g_free(warning); return TRUE; } break; case 504: /* Server time-out */ /* first attempt + 5 retries */ if (transport->register_attempt < 6) { SIPE_DEBUG_INFO("process_register_response: RE-REGISTER timeout on attempt %d, retrying later", transport->register_attempt); sip_transport_set_reregister(sipe_private, 60); return TRUE; } /* FALLTHROUGH */ case 503: { gchar *reason = sipmsg_get_ms_diagnostics_reason(msg); gchar *warning; warning = g_strdup_printf(_("Service unavailable: %s"), reason ? reason : _("no reason given")); g_free(reason); sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_NETWORK, warning); g_free(warning); return TRUE; } break; } return TRUE; } static gboolean register_response_timeout(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { struct sip_transport *transport = sipe_private->transport; if (transport->register_attempt < 6) { SIPE_DEBUG_INFO("register_response_timeout: no answer to attempt %d, retrying", transport->register_attempt); do_register(sipe_private, FALSE); } else { gchar *warning = g_strdup_printf(_("Service unavailable: %s"), _("no reason given")); sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_NETWORK, warning); g_free(warning); } return TRUE; } static void do_register(struct sipe_core_private *sipe_private, gboolean deregister) { struct sip_transport *transport = sipe_private->transport; char *uri; char *to; char *hdr; char *uuid; if (!sipe_private->public.sip_domain) return; if (!deregister) { if (transport->reregister_set) { transport->reregister_set = FALSE; transport->register_attempt = 1; } else { transport->register_attempt++; } } transport->deregister = deregister; transport->auth_incomplete = FALSE; uuid = get_uuid(sipe_private); hdr = g_strdup_printf("Contact: ;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY\";proxy=replace;+sip.instance=\"\"\r\n" "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n" "Event: registration\r\n" "Allow-Events: presence\r\n" "ms-keep-alive: UAC;hop-hop=yes\r\n" "%s", transport->uri_address, transport->connection->client_port, TRANSPORT_DESCRIPTOR, uuid, deregister ? "Expires: 0\r\n" : ""); g_free(uuid); uri = sip_uri_from_name(sipe_private->public.sip_domain); to = sip_uri_self(sipe_private); sip_transport_request_timeout(sipe_private, "REGISTER", uri, to, hdr, "", NULL, process_register_response, 60, deregister ? NULL : register_response_timeout); g_free(to); g_free(uri); g_free(hdr); if (deregister) { /* Make sure that all messages are pushed to the server before the connection gets shut down */ SIPE_LOG_INFO_NOFORMAT("De-register from server. Flushing outstanding messages."); sipe_backend_transport_flush(transport->connection); } } void sip_transport_deregister(struct sipe_core_private *sipe_private) { do_register(sipe_private, TRUE); } void sip_transport_drop(struct sipe_core_private *sipe_private) { struct sip_transport *transport = sipe_private->transport; /* transport can be NULL during connection setup */ if (transport) { SIPE_LOG_INFO("sip_transport_drop: '%s:%u'(%p)", transport->server_name, transport->server_port, transport->connection); sipe_backend_transport_disconnect(transport->connection); sipe_auth_free(&transport->registrar); sipe_auth_free(&transport->proxy); g_free(transport->server_name); g_free(transport->uri_address); g_free(transport->ip_address); g_free(transport->epid); while (transport->transactions) transactions_remove(sipe_private, transport->transactions->data); g_free(transport); } sipe_private->transport = NULL; sipe_private->service_data = NULL; sipe_private->address_data = NULL; sipe_schedule_cancel(sipe_private, "<+keepalive-timeout>"); if (sipe_private->dns_query) sipe_backend_dns_query_cancel(sipe_private->dns_query); } void sip_transport_authentication_completed(struct sipe_core_private *sipe_private) { do_reauthenticate_cb(sipe_private, NULL); } guint sip_transport_port(struct sipe_core_private *sipe_private) { return sipe_private->transport->server_port; } static void process_input_message(struct sipe_core_private *sipe_private, struct sipmsg *msg) { struct sip_transport *transport = sipe_private->transport; gboolean notfound = FALSE; const char *method = msg->method ? msg->method : "NOT FOUND"; SIPE_DEBUG_INFO("process_input_message: msg->response(%d),msg->method(%s)", msg->response, method); if (msg->response == 0) { /* request */ if (sipe_strequal(method, "MESSAGE")) { process_incoming_message(sipe_private, msg); } else if (sipe_strequal(method, "NOTIFY")) { SIPE_DEBUG_INFO_NOFORMAT("send->process_incoming_notify"); process_incoming_notify(sipe_private, msg); sip_transport_response(sipe_private, msg, 200, "OK", NULL); } else if (sipe_strequal(method, "BENOTIFY")) { SIPE_DEBUG_INFO_NOFORMAT("send->process_incoming_benotify"); process_incoming_notify(sipe_private, msg); } else if (sipe_strequal(method, "INVITE")) { process_incoming_invite(sipe_private, msg); } else if (sipe_strequal(method, "REFER")) { process_incoming_refer(sipe_private, msg); } else if (sipe_strequal(method, "OPTIONS")) { process_incoming_options(sipe_private, msg); } else if (sipe_strequal(method, "INFO")) { process_incoming_info(sipe_private, msg); } else if (sipe_strequal(method, "ACK")) { /* ACK's don't need any response */ } else if (sipe_strequal(method, "PRACK")) { sip_transport_response(sipe_private, msg, 200, "OK", NULL); } else if (sipe_strequal(method, "SUBSCRIBE")) { /* LCS 2005 sends us these - just respond 200 OK */ sip_transport_response(sipe_private, msg, 200, "OK", NULL); } else if (sipe_strequal(method, "CANCEL")) { process_incoming_cancel(sipe_private, msg); } else if (sipe_strequal(method, "BYE")) { process_incoming_bye(sipe_private, msg); } else { sip_transport_response(sipe_private, msg, 501, "Not implemented", NULL); notfound = TRUE; } } else { /* response */ struct transaction *trans = transactions_find(transport, msg); if (trans) { if (msg->response < 200) { /* ignore provisional response */ SIPE_DEBUG_INFO("process_input_message: got provisional (%d) response, ignoring", msg->response); /* Transaction not yet completed */ trans = NULL; } else if (msg->response == 401) { /* Unauthorized */ if (sipe_strequal(trans->msg->method, "REGISTER")) { /* Expected response during authentication handshake */ transport->registrar.retries++; SIPE_DEBUG_INFO("process_input_message: RE-REGISTER CSeq: %d", transport->cseq); } else { gchar *resend; /* Are we registered? */ if (transport->reregister_set) { SIPE_DEBUG_INFO_NOFORMAT("process_input_message: 401 response to non-REGISTER message. Retrying with new authentication."); sipmsg_remove_header_now(trans->msg, "Authorization"); sign_outgoing_message(sipe_private, trans->msg); } else { /** * We don't have a valid authentication at the moment. * Resend message unchanged. It will be rejected again * and hopefully by then we have a valid authentication. */ SIPE_DEBUG_INFO_NOFORMAT("process_input_message: 401 response to non-REGISTER message. Bouncing..."); } /* Resend request */ resend = sipmsg_to_string(trans->msg); send_sip_message(sipe_private->transport, resend); g_free(resend); /* Transaction not yet completed */ trans = NULL; } } else if (msg->response == 407) { /* Proxy Authentication Required */ if (transport->proxy.retries++ <= 30) { const gchar *proxy_hdr = sipmsg_find_header(msg, "Proxy-Authenticate"); if (proxy_hdr) { gchar *auth = NULL; if (!g_ascii_strncasecmp(proxy_hdr, "Digest", 6)) { auth = sip_sec_digest_authorization(sipe_private, proxy_hdr + 7, msg->method, msg->target); } else { guint i; transport->proxy.type = SIPE_AUTHENTICATION_TYPE_UNSET; for (i = 0; i < AUTH_PROTOCOLS; i++) { const gchar *protocol = auth_type_to_protocol[i]; if (protocol && !g_ascii_strncasecmp(proxy_hdr, protocol, strlen(protocol))) { SIPE_DEBUG_INFO("process_input_message: proxy authentication scheme '%s'", protocol); transport->proxy.type = i; transport->proxy.protocol = protocol; fill_auth(proxy_hdr, &transport->proxy); auth = auth_header(sipe_private, &transport->proxy, trans->msg); break; } } } if (auth) { gchar *resend; /* replace old proxy authentication with new one */ sipmsg_remove_header_now(trans->msg, "Proxy-Authorization"); sipmsg_add_header_now(trans->msg, "Proxy-Authorization", auth); g_free(auth); /* resend request with proxy authentication */ resend = sipmsg_to_string(trans->msg); send_sip_message(sipe_private->transport, resend); g_free(resend); /* Transaction not yet completed */ trans = NULL; } else SIPE_DEBUG_ERROR_NOFORMAT("process_input_message: can't generate proxy authentication. Giving up."); } else SIPE_DEBUG_ERROR_NOFORMAT("process_input_message: 407 response without 'Proxy-Authenticate' header. Giving up."); } else SIPE_DEBUG_ERROR_NOFORMAT("process_input_message: too many proxy authentication retries. Giving up."); } else { transport->registrar.retries = 0; transport->proxy.retries = 0; } /* Is transaction completed? */ if (trans) { if (trans->callback) { SIPE_DEBUG_INFO_NOFORMAT("process_input_message: we have a transaction callback"); /* call the callback to process response */ (trans->callback)(sipe_private, msg, trans); /* transport && trans no longer valid after redirect */ } /* * Redirect case: sipe_private->transport is * the new transport with empty queue */ if (sipe_private->transport->transactions) { SIPE_DEBUG_INFO("process_input_message: removing CSeq %d", transport->cseq); transactions_remove(sipe_private, trans); } } } else { SIPE_DEBUG_INFO_NOFORMAT("process_input_message: received response to unknown transaction"); notfound = TRUE; } } if (notfound) { SIPE_DEBUG_INFO("received a unknown sip message with method %s and response %d", method, msg->response); } } static void sip_transport_input(struct sipe_transport_connection *conn) { struct sipe_core_private *sipe_private = conn->user_data; struct sip_transport *transport = sipe_private->transport; gchar *cur = conn->buffer; /* according to the RFC remove CRLF at the beginning */ while (*cur == '\r' || *cur == '\n') { cur++; } if (cur != conn->buffer) sipe_utils_shrink_buffer(conn, cur); /* Received a full Header? */ transport->processing_input = TRUE; while (transport->processing_input && ((cur = strstr(conn->buffer, "\r\n\r\n")) != NULL)) { struct sipmsg *msg; guint remainder; cur += 2; cur[0] = '\0'; msg = sipmsg_parse_header(conn->buffer); cur += 2; remainder = conn->buffer_used - (cur - conn->buffer); if (msg && remainder >= (guint) msg->bodylen) { char *dummy = g_malloc(msg->bodylen + 1); memcpy(dummy, cur, msg->bodylen); dummy[msg->bodylen] = '\0'; msg->body = dummy; cur += msg->bodylen; sipe_utils_message_debug(conn, "SIP", conn->buffer, msg->body, FALSE); sipe_utils_shrink_buffer(conn, cur); } else { if (msg) { SIPE_DEBUG_INFO("sipe_transport_input: body too short (%d < %d, strlen %d) - ignoring message", remainder, msg->bodylen, (int)strlen(conn->buffer)); sipmsg_free(msg); } /* restore header for next try */ cur[-2] = '\r'; return; } /* Fatal header parse error? */ if (msg->response == SIPMSG_RESPONSE_FATAL_ERROR) { /* can't proceed -> drop connection */ sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_NETWORK, _("Corrupted message received")); transport->processing_input = FALSE; /* Verify the signature before processing it */ } else if (sip_sec_context_is_ready(transport->registrar.gssapi_context)) { struct sipmsg_breakdown msgbd; gchar *signature_input_str; gchar *rspauth; msgbd.msg = msg; sipmsg_breakdown_parse(&msgbd, transport->registrar.realm, transport->registrar.target, transport->registrar.protocol); signature_input_str = sipmsg_breakdown_get_string(transport->registrar.version, &msgbd); rspauth = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Authentication-Info"), "rspauth=\"", "\"", NULL); if (rspauth != NULL) { if (sip_sec_verify_signature(transport->registrar.gssapi_context, signature_input_str, rspauth)) { SIPE_DEBUG_INFO_NOFORMAT("sip_transport_input: signature of incoming message validated"); process_input_message(sipe_private, msg); /* transport is invalid after redirect */ } else { SIPE_DEBUG_INFO_NOFORMAT("sip_transport_input: signature of incoming message is invalid."); sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_NETWORK, _("Invalid message signature received")); transport->processing_input = FALSE; } } else if ((msg->response == 401) || sipe_strequal(msg->method, "REGISTER")) { /* a) Retry non-REGISTER requests with updated authentication */ /* b) We must always process REGISTER responses */ process_input_message(sipe_private, msg); } else { /* OCS sends provisional messages that are *not* signed */ if (msg->response >= 200) { /* We are not calling process_input_message(), so we need to drop the transaction here. */ struct transaction *trans = transactions_find(transport, msg); if (trans) transactions_remove(sipe_private, trans); } SIPE_DEBUG_INFO_NOFORMAT("sip_transport_input: message without authentication data - ignoring"); } g_free(signature_input_str); g_free(rspauth); sipmsg_breakdown_free(&msgbd); } else { process_input_message(sipe_private, msg); } sipmsg_free(msg); /* Redirect: old content of "transport" & "conn" is no longer valid */ transport = sipe_private->transport; conn = transport->connection; } } static void sip_transport_connected(struct sipe_transport_connection *conn) { struct sipe_core_private *sipe_private = conn->user_data; struct sip_transport *transport = sipe_private->transport; gchar *self_sip_uri = sip_uri_self(sipe_private); SIPE_LOG_INFO("sip_transport_connected: '%s:%u'(%p)", transport->server_name, transport->server_port, conn); while (sipe_private->lync_autodiscover_servers) sipe_private->lync_autodiscover_servers = sipe_lync_autodiscover_pop(sipe_private->lync_autodiscover_servers); sipe_private->service_data = NULL; sipe_private->address_data = NULL; /* * Initial keepalive timeout during REGISTER phase * * NOTE: 60 seconds is a guess. Needs more testing! */ transport->keepalive_timeout = 60; start_keepalive_timer(sipe_private, transport->keepalive_timeout); transport->ip_address = sipe_backend_transport_ip_address(conn); if (strchr(transport->ip_address, ':') != NULL) /* RFC2732: Format for Literal IPv6 Addresses in URL's */ transport->uri_address = g_strdup_printf("[%s]", transport->ip_address); else transport->uri_address = g_strdup(transport->ip_address); transport->sdp_marker = sipe_utils_ip_sdp_address_marker(transport->ip_address); transport->epid = sipe_get_epid(self_sip_uri, g_get_host_name(), transport->ip_address); g_free(self_sip_uri); do_register(sipe_private, FALSE); } static void resolve_next_lync(struct sipe_core_private *sipe_private); static void resolve_next_service(struct sipe_core_private *sipe_private, const struct sip_service_data *start); static void resolve_next_address(struct sipe_core_private *sipe_private, gboolean initial); static void sip_transport_error(struct sipe_transport_connection *conn, const gchar *msg) { struct sipe_core_private *sipe_private = conn->user_data; /* This failed attempt was based on a Lync Autodiscover result */ if (sipe_private->lync_autodiscover_servers) { resolve_next_lync(sipe_private); /* This failed attempt was based on a DNS SRV record */ } else if (sipe_private->service_data) { resolve_next_service(sipe_private, NULL); /* This failed attempt was based on a DNS A record */ } else if (sipe_private->address_data) { resolve_next_address(sipe_private, FALSE); } else { sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_NETWORK, msg); } } /* server_name must be g_alloc()'ed */ static void sipe_server_register(struct sipe_core_private *sipe_private, guint type, gchar *server_name, guint server_port) { sipe_connect_setup setup = { type, server_name, (server_port != 0) ? server_port : (type == SIPE_TRANSPORT_TLS) ? 5061 : 5060, sipe_private, sip_transport_connected, sip_transport_input, sip_transport_error }; struct sip_transport *transport = g_new0(struct sip_transport, 1); transport->auth_retry = TRUE; transport->server_name = server_name; transport->server_port = setup.server_port; transport->connection = sipe_backend_transport_connect(SIPE_CORE_PUBLIC, &setup); sipe_private->transport = transport; } struct sip_service_data { const char *protocol; const char *transport; guint type; }; /* * Autodiscover using DNS SRV records. See RFC2782/3263 * * Service list for AUTO */ static const struct sip_service_data service_autodetect[] = { { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS }, /* for internal TLS connections */ { "sipinternal", "tcp", SIPE_TRANSPORT_TCP }, /* for internal TCP connections */ { "sip", "tls", SIPE_TRANSPORT_TLS }, /* for external TLS connections */ { "sip", "tcp", SIPE_TRANSPORT_TCP }, /*.for external TCP connections */ { NULL, NULL, 0 } }; /* Service list for SSL/TLS */ static const struct sip_service_data service_tls[] = { { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS }, /* for internal TLS connections */ { "sip", "tls", SIPE_TRANSPORT_TLS }, /* for external TLS connections */ { NULL, NULL, 0 } }; /* Service list for TCP */ static const struct sip_service_data service_tcp[] = { { "sipinternal", "tcp", SIPE_TRANSPORT_TCP }, /* for internal TCP connections */ { "sip", "tcp", SIPE_TRANSPORT_TCP }, /*.for external TCP connections */ { NULL, NULL, 0 } }; static const struct sip_service_data *services[] = { service_autodetect, /* SIPE_TRANSPORT_AUTO */ service_tls, /* SIPE_TRANSPORT_TLS */ service_tcp /* SIPE_TRANSPORT_TCP */ }; struct sip_address_data { const char *prefix; guint port; }; /* * Autodiscover using DNS A records. This is an extension addded * by Microsoft. See http://support.microsoft.com/kb/2619522 */ static const struct sip_address_data addresses[] = { { "sipinternal", 5061 }, { "sipexternal", 443 }, /* * Our implementation supports only one port per host name. If the host name * resolves OK, we abort the search and try to connect. If we would know if we * are trying to connect from "Intranet" or "Internet" then we could choose * between those two ports. * * We drop port 5061 in order to cover the "Internet" case. * * { "sip", 5061 }, */ { "sip", 443 }, { NULL, 0 } }; static void sipe_core_dns_resolved(struct sipe_core_public *sipe_public, const gchar *hostname, guint port) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; gboolean service = sipe_private->service_data != NULL; sipe_private->dns_query = NULL; if (hostname) { gchar *host; guint type; if (service) { host = g_strdup(hostname); type = sipe_private->service_data->type; } else { /* DNS A resolver returns an IP address */ host = g_strdup_printf("%s.%s", sipe_private->address_data->prefix, sipe_private->public.sip_domain); port = sipe_private->address_data->port; type = sipe_private->transport_type; if (type == SIPE_TRANSPORT_AUTO) type = SIPE_TRANSPORT_TLS; } SIPE_DEBUG_INFO("sipe_core_dns_resolved - %s hostname: %s port: %d", service ? "SRV" : "A", hostname, port); sipe_server_register(sipe_private, type, host, port); } else { if (service) resolve_next_service(SIPE_CORE_PRIVATE, NULL); else resolve_next_address(SIPE_CORE_PRIVATE, FALSE); } } static void resolve_next_lync(struct sipe_core_private *sipe_private) { struct sipe_lync_autodiscover_data *lync_data = sipe_private->lync_autodiscover_servers->data; guint type = sipe_private->transport_type; if (lync_data) { /* Try to connect to next server on the list */ if (type == SIPE_TRANSPORT_AUTO) type = SIPE_TRANSPORT_TLS; sipe_server_register(sipe_private, type, g_strdup(lync_data->server), lync_data->port); } else { /* We tried all servers -> try DNS SRV next */ SIPE_LOG_INFO_NOFORMAT("no Lync Autodiscover servers found; trying SRV records next"); resolve_next_service(sipe_private, services[type]); } sipe_private->lync_autodiscover_servers = sipe_lync_autodiscover_pop(sipe_private->lync_autodiscover_servers); } static void resolve_next_service(struct sipe_core_private *sipe_private, const struct sip_service_data *start) { if (start) { sipe_private->service_data = start; } else { sipe_private->service_data++; if (sipe_private->service_data->protocol == NULL) { /* We tried all services */ sipe_private->service_data = NULL; /* Try A records list next */ SIPE_LOG_INFO_NOFORMAT("no SRV records found; trying A records next"); resolve_next_address(sipe_private, TRUE); return; } } /* Try to resolve next service */ sipe_private->dns_query = sipe_backend_dns_query_srv( SIPE_CORE_PUBLIC, sipe_private->service_data->protocol, sipe_private->service_data->transport, sipe_private->public.sip_domain, (sipe_dns_resolved_cb) sipe_core_dns_resolved, SIPE_CORE_PUBLIC); } static void resolve_next_address(struct sipe_core_private *sipe_private, gboolean initial) { gchar *hostname; if (initial) { sipe_private->address_data = addresses; } else { sipe_private->address_data++; if (sipe_private->address_data->prefix == NULL) { guint type = sipe_private->transport_type; /* We tried all addresss */ sipe_private->address_data = NULL; /* Try connecting to the SIP hostname directly */ SIPE_LOG_INFO_NOFORMAT("no SRV or A records found; using SIP domain as fallback"); if (type == SIPE_TRANSPORT_AUTO) type = SIPE_TRANSPORT_TLS; sipe_server_register(sipe_private, type, g_strdup(sipe_private->public.sip_domain), 0); return; } } /* Try to resolve next address */ hostname = g_strdup_printf("%s.%s", sipe_private->address_data->prefix, sipe_private->public.sip_domain); sipe_private->dns_query = sipe_backend_dns_query_a( SIPE_CORE_PUBLIC, hostname, sipe_private->address_data->port, (sipe_dns_resolved_cb) sipe_core_dns_resolved, SIPE_CORE_PUBLIC); g_free(hostname); } static void lync_autodiscover_cb(struct sipe_core_private *sipe_private, GSList *servers, SIPE_UNUSED_PARAMETER gpointer callback_data) { if (servers) { /* Lync Autodiscover succeeded */ SIPE_DEBUG_INFO_NOFORMAT("lync_autodiscover_cb: got server list"); sipe_private->lync_autodiscover_servers = servers; resolve_next_lync(sipe_private); } } /* * NOTE: this function can be called before sipe_core_allocate()! */ gboolean sipe_core_transport_sip_requires_password(guint authentication, gboolean sso) { return(sip_sec_requires_password(authentication, sso)); } void sipe_core_transport_sip_connect(struct sipe_core_public *sipe_public, guint transport, guint authentication, const gchar *server, const gchar *port) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; /* backend initialization is complete */ sipe_core_backend_initialized(sipe_private, authentication); /* * Initializing the certificate sub-system will trigger the generation * of a cryptographic key pair which takes time. If we do this after we * have connected to the server then there is a risk that we run into a * SIP connection timeout. So let's get this out of the way now... * * This is currently only needed if the user has selected TLS-DSK. */ if (sipe_private->authentication_type == SIPE_AUTHENTICATION_TYPE_TLS_DSK) sipe_certificate_init(sipe_private); if (server) { /* Use user specified server[:port] */ int port_number = 0; if (port) port_number = atoi(port); SIPE_LOG_INFO("sipe_core_connect: user specified SIP server %s:%d", server, port_number); sipe_server_register(sipe_private, transport, g_strdup(server), port_number); } else { /* Server auto-discovery */ /* Remember user specified transport type */ sipe_private->transport_type = transport; /* Start with Lync Autodiscover first */ sipe_lync_autodiscover_start(sipe_private, lync_autodiscover_cb, NULL); } } const gchar *sipe_core_transport_sip_server_name(struct sipe_core_public *sipe_public) { struct sip_transport *transport = SIPE_CORE_PRIVATE->transport; return(transport ? transport->server_name : NULL); } int sip_transaction_cseq(struct transaction *trans) { int cseq; g_return_val_if_fail(trans && trans->key, 0); sscanf(trans->key, "<%*[a-zA-Z0-9]><%d INVITE>", &cseq); return cseq; } const gchar *sip_transport_epid(struct sipe_core_private *sipe_private) { return(sipe_private->transport ? sipe_private->transport->epid : "0123456789ab"); } const gchar *sip_transport_ip_address(struct sipe_core_private *sipe_private) { return(sipe_private->transport ? sipe_private->transport->ip_address : "0.0.0.0"); } const gchar *sip_transport_sdp_address_marker(struct sipe_core_private *sipe_private) { return(sipe_private->transport ? sipe_private->transport->sdp_marker : "IP4"); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sip-transport.h ================================================ /** * @file sip-transport.h * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipmsg; struct sip_dialog; struct sipe_core_private; struct transaction; /* Transaction that can be associated with a SIP request */ typedef gboolean (*TransCallback) (struct sipe_core_private *, struct sipmsg *, struct transaction *); struct transaction_payload { GDestroyNotify destroy; void *data; }; struct transaction { TransCallback callback; TransCallback timeout_callback; /** Not yet perfect, but surely better then plain CSeq * Format is: * (RFC3261 17.2.3 for matching server transactions: Request-URI, To tag, From tag, Call-ID, CSeq, and top Via) */ gchar *key; gchar *timeout_key; struct sipmsg *msg; struct transaction_payload *payload; }; /* Send SIP response */ void sip_transport_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, guint code, const char *text, const char *body); /* Send SIP request */ struct transaction *sip_transport_request(struct sipe_core_private *sipe_private, const gchar *method, const gchar *url, const gchar *to, const gchar *addheaders, const gchar *body, struct sip_dialog *dialog, TransCallback callback); /* Send SIP request with timeout [in seconds] */ struct transaction *sip_transport_request_timeout(struct sipe_core_private *sipe_private, const gchar *method, const gchar *url, const gchar *to, const gchar *addheaders, const gchar *body, struct sip_dialog *dialog, TransCallback callback, guint timeout, TransCallback timeout_callback); /* Common SIP request types */ void sip_transport_ack(struct sipe_core_private *sipe_private, struct sip_dialog *dialog); void sip_transport_bye(struct sipe_core_private *sipe_private, struct sip_dialog *dialog); struct transaction *sip_transport_info(struct sipe_core_private *sipe_private, const gchar *addheaders, const gchar *body, struct sip_dialog *dialog, TransCallback callback); struct transaction *sip_transport_invite(struct sipe_core_private *sipe_private, const gchar *addheaders, const gchar *body, struct sip_dialog *dialog, TransCallback callback); struct transaction *sip_transport_service(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *addheaders, const gchar *body, TransCallback callback); void sip_transport_subscribe(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *addheaders, const gchar *body, struct sip_dialog *dialog, TransCallback callback); void sip_transport_update(struct sipe_core_private *sipe_private, struct sip_dialog *dialog, TransCallback callback); /* Misc. SIP transport stuff */ guint sip_transport_port(struct sipe_core_private *sipe_private); void sip_transport_deregister(struct sipe_core_private *sipe_private); void sip_transport_drop(struct sipe_core_private *sipe_private); void sip_transport_authentication_completed(struct sipe_core_private *sipe_private); int sip_transaction_cseq(struct transaction *trans); const gchar *sip_transport_epid(struct sipe_core_private *sipe_private); const gchar *sip_transport_ip_address(struct sipe_core_private *sipe_private); const gchar *sip_transport_sdp_address_marker(struct sipe_core_private *sipe_private); /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-appshare-client.h ================================================ /** * @file sipe-appshare-client.h * * pidgin-sipe * * Copyright (C) 2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_core_private; struct sipe_media_stream; struct sipmsg; struct sipe_rdp_client { gchar *cmdline; void *client_data; GSocketAddress *(*get_listen_address_cb)(struct sipe_rdp_client *client); gboolean (*launch_cb)(struct sipe_rdp_client *client, GSocketAddress *listen_address, struct sipe_media_stream *stream); void (*free_cb)(struct sipe_rdp_client *client); }; /* Client implementations */ void sipe_appshare_remmina_init(struct sipe_rdp_client *client); void sipe_appshare_xfreerdp_init(struct sipe_rdp_client *client); ================================================ FILE: src/core/sipe-appshare-remmina.c ================================================ /** * @file sipe-appshare-remmina.c * * pidgin-sipe * * Copyright (C) 2014-2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "sipe-appshare-client.h" #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-common.h" #include "sipe-media.h" struct remmina_data { gchar *config_file; }; static GSocketAddress * remmina_get_listen_address(SIPE_UNUSED_PARAMETER struct sipe_rdp_client *client) { GInetAddress *loopback; GSocketAddress *address; loopback = g_inet_address_new_loopback(G_SOCKET_FAMILY_IPV4); address = g_inet_socket_address_new(loopback, 0); g_object_unref(loopback); return address; } static gboolean remmina_launch(struct sipe_rdp_client *client, GSocketAddress *listen_address, struct sipe_media_stream *stream) { struct remmina_data *client_data = client->client_data; struct sipe_core_private *sipe_private; GInetAddress *address; gchar *address_string; gchar *alias; gchar *config_file; gchar *cmdline; guint16 port; GError *error = NULL; sipe_private = sipe_media_get_sipe_core_private(stream->call); address = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(listen_address)); address_string = g_inet_address_to_string(address); port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(listen_address)); alias = sipe_buddy_get_alias(sipe_private, stream->call->with); config_file = g_strdup_printf("[remmina]\n" "name=%s (Sipe desktop)\n" "protocol=RDP\n" "server=%s:%u\n" "security=rdp\n" "scale=1\n" "aspectscale=1\n" "viewmode=1\n" "colordepth=0\n" "disableautoreconnect=1\n", alias ? alias : stream->call->with, address_string, port); g_free(alias); g_free(address_string); client_data->config_file = g_strdup_printf("%s/sipe-appshare-%u-%p.remmina", g_get_user_runtime_dir(), getpid(), client); g_file_set_contents(client_data->config_file, config_file, strlen(config_file), &error); SIPE_DEBUG_INFO("Written .remmina file %s:\n%s", client_data->config_file, config_file); g_free(config_file); if (error) { SIPE_DEBUG_ERROR("Couldn't write remmina config file: %s", error->message); g_error_free(error); return FALSE; } cmdline = g_strdup_printf("%s -c %s", client->cmdline, client_data->config_file); SIPE_DEBUG_INFO("Launching remmina: %s", cmdline); g_spawn_command_line_async(cmdline, &error); g_free(cmdline); if (error) { SIPE_DEBUG_ERROR("Couldn't launch remmina: %s", error->message); g_error_free(error); return FALSE; } return TRUE; } static void remmina_free(struct sipe_rdp_client *client) { struct remmina_data *client_data = client->client_data; if (client_data->config_file) { g_unlink(client_data->config_file); g_free(client_data->config_file); } g_free(client_data); } void sipe_appshare_remmina_init(struct sipe_rdp_client *client) { client->client_data = g_new0(struct remmina_data, 1); client->get_listen_address_cb = remmina_get_listen_address; client->launch_cb = remmina_launch; client->free_cb = remmina_free; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-appshare-xfreerdp.c ================================================ /** * @file sipe-appshare-xfreerdp.c * * pidgin-sipe * * Copyright (C) 2014-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include "sipe-appshare-client.h" #include "sipe-backend.h" #include "sipe-common.h" struct xfreerdp_data { gchar *socket_path; }; static GSocketAddress * xfreerdp_get_listen_address(struct sipe_rdp_client *client) { struct xfreerdp_data *data = client->client_data; struct sockaddr_un address; data->socket_path = g_strdup_printf("%s/sipe-appshare-%u-%p", g_get_user_runtime_dir(), getpid(), client); g_unlink(data->socket_path); address.sun_family = AF_LOCAL; strncpy(address.sun_path, data->socket_path, sizeof (address.sun_path) - 1); address.sun_path[sizeof (address.sun_path) - 1] = '\0'; return g_socket_address_new_from_native(&address, sizeof (address)); } static gboolean xfreerdp_launch(struct sipe_rdp_client *client, SIPE_UNUSED_PARAMETER GSocketAddress *listen_address, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream) { struct xfreerdp_data *client_data = client->client_data; gchar *cmdline; GError *error = NULL; /* This assumes FreeRDP 2.x.x */ cmdline = g_strdup_printf("%s /v:%s /sec:rdp", client->cmdline, client_data->socket_path); g_spawn_command_line_async(cmdline, &error); g_free(cmdline); if (error) { SIPE_DEBUG_ERROR("Can't launch xfreerdp: %s", error->message); g_error_free(error); return FALSE; } return TRUE; } static void xfreerdp_free(struct sipe_rdp_client *client) { struct xfreerdp_data *client_data = client->client_data; if (client_data->socket_path) { g_unlink(client_data->socket_path); g_free(client_data->socket_path); } g_free(client_data); } void sipe_appshare_xfreerdp_init(struct sipe_rdp_client *client) { client->client_data = g_new0(struct xfreerdp_data, 1); client->get_listen_address_cb = xfreerdp_get_listen_address; client->launch_cb = xfreerdp_launch; client->free_cb = xfreerdp_free; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-appshare.c ================================================ /** * @file sipe-appshare.c * * pidgin-sipe * * Copyright (C) 2014-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef HAVE_APPSHARE_SERVER #include #include #include #include #endif // HAVE_APPSHARE_SERVER #include "sipmsg.h" #include "sipe-appshare-client.h" #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-chat.h" #include "sipe-common.h" #include "sipe-conf.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-appshare.h" #include "sipe-media.h" #include "sipe-nls.h" #include "sipe-schedule.h" #include "sipe-user.h" #include "sipe-utils.h" #include "sdpmsg.h" struct sipe_appshare { struct sipe_media_stream *stream; GSocket *socket; GIOChannel *channel; guint rdp_channel_readable_watch_id; guint rdp_channel_writable_watch_id; guint monitor_id; struct sipe_user_ask_ctx *ask_ctx; gchar rdp_channel_buffer[0x800]; gchar *rdp_channel_buffer_pos; gsize rdp_channel_buffer_len; struct sipe_rdp_client client; #ifdef HAVE_APPSHARE_SERVER rdpShadowServer *server; #endif // HAVE_APPSHARE_SERVER }; static void sipe_appshare_free(struct sipe_appshare *appshare) { if (appshare->rdp_channel_readable_watch_id != 0) { g_source_destroy(g_main_context_find_source_by_id(NULL, appshare->rdp_channel_readable_watch_id)); } if (appshare->rdp_channel_writable_watch_id != 0) { g_source_destroy(g_main_context_find_source_by_id(NULL, appshare->rdp_channel_writable_watch_id)); } if (appshare->channel) { GError *error = NULL; g_io_channel_shutdown(appshare->channel, TRUE, &error); if (error) { SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s", error->message); g_error_free(error); } g_io_channel_unref(appshare->channel); } if (appshare->socket) { g_object_unref(appshare->socket); } #ifdef HAVE_APPSHARE_SERVER if (appshare->server) { if (appshare->server->ipcSocket) { g_unlink(appshare->server->ipcSocket); } shadow_server_stop(appshare->server); shadow_server_uninit(appshare->server); shadow_server_free(appshare->server); } #endif // HAVE_APPSHARE_SERVER if (appshare->ask_ctx) { sipe_user_close_ask(appshare->ask_ctx); } g_free(appshare->client.cmdline); if (appshare->client.free_cb) { appshare->client.free_cb(&appshare->client); } g_free(appshare); } static gboolean rdp_channel_readable_cb(GIOChannel *channel, GIOCondition condition, gpointer data) { struct sipe_appshare *appshare = data; GError *error = NULL; gchar *buffer; gsize bytes_read; if (condition & G_IO_HUP) { struct sipe_media_call *call = appshare->stream->call; SIPE_DEBUG_INFO_NOFORMAT("Received HUP from RDP client."); sipe_backend_media_hangup(call->backend_private, TRUE); return FALSE; } buffer = g_malloc(2048); while (sipe_media_stream_is_writable(appshare->stream)) { GIOStatus status; status = g_io_channel_read_chars(channel, buffer, 2048, &bytes_read, &error); if (error) { struct sipe_media_call *call = appshare->stream->call; SIPE_DEBUG_ERROR("Error reading from RDP channel: %s", error->message); g_error_free(error); sipe_backend_media_hangup(call->backend_private, TRUE); g_free(buffer); return FALSE; } if (status == G_IO_STATUS_EOF) { struct sipe_media_call *call = appshare->stream->call; sipe_backend_media_hangup(call->backend_private, TRUE); g_free(buffer); return FALSE; } if (bytes_read == 0) { break; } sipe_media_stream_write(appshare->stream, (guint8 *)buffer, bytes_read); SIPE_DEBUG_INFO("Written: %" G_GSIZE_FORMAT "\n", bytes_read); } g_free(buffer); return TRUE; } static gboolean socket_connect_cb(SIPE_UNUSED_PARAMETER GIOChannel *channel, SIPE_UNUSED_PARAMETER GIOCondition condition, gpointer data) { struct sipe_appshare *appshare = data; GError *error = NULL; GSocket *data_socket; int fd; SIPE_DEBUG_INFO_NOFORMAT("RDP client has connected."); data_socket = g_socket_accept(appshare->socket, NULL, &error); if (error) { struct sipe_media_call *call = appshare->stream->call; SIPE_DEBUG_ERROR("Error accepting RDP client connection: %s", error->message); g_error_free(error); sipe_backend_media_hangup(call->backend_private, TRUE); return FALSE; } g_io_channel_shutdown(appshare->channel, TRUE, &error); if (error) { struct sipe_media_call *call = appshare->stream->call; SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s", error->message); g_error_free(error); g_object_unref(data_socket); sipe_backend_media_hangup(call->backend_private, TRUE); return FALSE; } g_io_channel_unref(appshare->channel); g_object_unref(appshare->socket); appshare->socket = data_socket; fd = g_socket_get_fd(appshare->socket); if (fd < 0) { struct sipe_media_call *call = appshare->stream->call; SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client connection socket"); sipe_backend_media_hangup(call->backend_private, TRUE); return FALSE; } appshare->channel = g_io_channel_unix_new(fd); // No encoding for binary data g_io_channel_set_encoding(appshare->channel, NULL, &error); if (error) { struct sipe_media_call *call = appshare->stream->call; SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s", error->message); g_error_free(error); sipe_backend_media_hangup(call->backend_private, TRUE); return FALSE; } appshare->rdp_channel_readable_watch_id = g_io_add_watch(appshare->channel, G_IO_IN | G_IO_HUP, rdp_channel_readable_cb, appshare); return FALSE; } static void launch_rdp_client(struct sipe_appshare *appshare) { struct sipe_rdp_client *client = &appshare->client; struct sipe_media_call *call = appshare->stream->call; GSocketAddress *address; GError *error = NULL; int fd; address = client->get_listen_address_cb(client); if (!address) { sipe_backend_media_hangup(call->backend_private, TRUE); return; } appshare->socket = g_socket_new(g_socket_address_get_family(address), G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); if (error) { SIPE_DEBUG_ERROR("Can't create RDP client listen socket: %s", error->message); g_error_free(error); g_object_unref(address); sipe_backend_media_hangup(call->backend_private, TRUE); return; } g_socket_set_blocking(appshare->socket, FALSE); g_socket_bind(appshare->socket, address, TRUE, &error); g_object_unref(address); if (error) { SIPE_DEBUG_ERROR("Can't bind to RDP client socket: %s", error->message); g_error_free(error); sipe_backend_media_hangup(call->backend_private, TRUE); return; } g_socket_listen(appshare->socket, &error); if (error) { SIPE_DEBUG_ERROR("Can't listen on RDP client socket: %s", error->message); g_error_free(error); sipe_backend_media_hangup(call->backend_private, TRUE); return; } fd = g_socket_get_fd(appshare->socket); if (fd < 0) { SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client listen socket"); sipe_backend_media_hangup(call->backend_private, TRUE); return; } appshare->channel = g_io_channel_unix_new(fd); appshare->rdp_channel_readable_watch_id = g_io_add_watch(appshare->channel, G_IO_IN, socket_connect_cb, appshare); address = g_socket_get_local_address(appshare->socket, &error); if (error) { SIPE_DEBUG_ERROR("Couldn't get appshare socket address: %s", error->message); g_error_free(error); sipe_backend_media_hangup(call->backend_private, TRUE); return; } if (!client->launch_cb(client, address, appshare->stream)) { SIPE_DEBUG_ERROR_NOFORMAT("Failed to launch RDP client."); sipe_backend_media_hangup(call->backend_private, TRUE); } SIPE_DEBUG_INFO_NOFORMAT("RDP client launched."); g_object_unref(address); } static gssize rdp_client_channel_write(struct sipe_appshare *appshare) { gsize bytes_written; GError *error = NULL; g_io_channel_write_chars(appshare->channel, appshare->rdp_channel_buffer_pos, appshare->rdp_channel_buffer_len, &bytes_written, &error); if (error) { SIPE_DEBUG_ERROR("Couldn't write data to RDP client: %s", error->message); g_error_free(error); return -1; } g_io_channel_flush(appshare->channel, &error); if (error) { if (g_error_matches(error, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_PIPE)) { /* Ignore broken pipe here and wait for the call to be * hung up upon G_IO_HUP in client_channel_cb(). */ g_error_free(error); return 0; } SIPE_DEBUG_ERROR("Couldn't flush RDP channel: %s", error->message); g_error_free(error); return -1; } appshare->rdp_channel_buffer_pos += bytes_written; appshare->rdp_channel_buffer_len -= bytes_written; return bytes_written; } static void delayed_hangup_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, gpointer data) { struct sipe_media_call *call = data; sipe_backend_media_hangup(call->backend_private, TRUE); } static gboolean rdp_channel_writable_cb(SIPE_UNUSED_PARAMETER GIOChannel *channel, SIPE_UNUSED_PARAMETER GIOCondition condition, gpointer data) { struct sipe_appshare *appshare = data; struct sipe_media_call *call = appshare->stream->call; if (rdp_client_channel_write(appshare) < 0) { sipe_backend_media_hangup(call->backend_private, TRUE); return FALSE; } if (appshare->rdp_channel_buffer_len == 0) { // Writing done, disconnect writable watch. appshare->rdp_channel_writable_watch_id = 0; return FALSE; } return TRUE; } static void read_cb(struct sipe_media_stream *stream) { struct sipe_appshare *appshare = sipe_media_stream_get_data(stream); gint bytes_read = 0; gssize bytes_written = 0; if (appshare->rdp_channel_writable_watch_id != 0) { // Data still in the buffer. Let the client read it first. return; } while (bytes_read == (gint)bytes_written) { bytes_read = sipe_backend_media_stream_read(stream, (guint8 *)appshare->rdp_channel_buffer, sizeof (appshare->rdp_channel_buffer)); if (bytes_read == 0) { return; } appshare->rdp_channel_buffer_pos = appshare->rdp_channel_buffer; appshare->rdp_channel_buffer_len = bytes_read; bytes_written = rdp_client_channel_write(appshare); if (bytes_written < 0) { /* Don't deallocate stream while in its read callback. * Schedule call hangup to be executed after we're back * in the message loop. */ sipe_schedule_seconds(sipe_media_get_sipe_core_private(stream->call), "appshare delayed hangup", stream->call->backend_private, 0, delayed_hangup_cb, NULL); return; } } if (bytes_read != (gint)bytes_written) { /* Schedule writing of the buffer's remainder to when * RDP channel becomes writable again. */ appshare->rdp_channel_writable_watch_id = g_io_add_watch(appshare->channel, G_IO_OUT, rdp_channel_writable_cb, appshare); } } static void writable_cb(struct sipe_media_stream *stream) { struct sipe_appshare *appshare = sipe_media_stream_get_data(stream); if (!appshare->socket) { launch_rdp_client(appshare); } } static void accept_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, gpointer data) { struct sipe_appshare *appshare = data; appshare->ask_ctx = NULL; sipe_backend_media_accept(appshare->stream->call->backend_private, TRUE); } static void decline_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, gpointer data) { struct sipe_appshare *appshare = data; appshare->ask_ctx = NULL; sipe_backend_media_hangup(appshare->stream->call->backend_private, TRUE); } static struct sipe_user_ask_ctx * ask_accept_applicationsharing(struct sipe_core_private *sipe_private, const gchar *from, SipeUserAskCb accept_cb, SipeUserAskCb decline_cb, gpointer user_data) { struct sipe_user_ask_ctx *ctx; gchar *alias = sipe_buddy_get_alias(sipe_private, from); gchar *ask_msg = g_strdup_printf(_("%s wants to start presenting"), alias ? alias : from); ctx = sipe_user_ask(sipe_private, ask_msg, _("Accept"), accept_cb, _("Decline"), decline_cb, user_data); g_free(ask_msg); g_free(alias); return ctx; } static struct sipe_appshare * initialize_appshare(struct sipe_media_stream *stream) { struct sipe_appshare *appshare; struct sipe_media_call *call; struct sipe_core_private *sipe_private; const gchar *cmdline; call = stream->call; sipe_private = sipe_media_get_sipe_core_private(call); appshare = g_new0(struct sipe_appshare, 1); appshare->stream = stream; sipe_media_stream_set_data(stream, appshare, (GDestroyNotify)sipe_appshare_free); cmdline = sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_RDP_CLIENT); if (is_empty(cmdline)) cmdline = "remmina"; appshare->client.cmdline = g_strdup(cmdline); if (strstr(cmdline, "xfreerdp")) { sipe_appshare_xfreerdp_init(&appshare->client); } else if (strstr(cmdline, "remmina")) { sipe_appshare_remmina_init(&appshare->client); } else { sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Application sharing error"), _("Unknown remote desktop client configured.")); sipe_backend_media_hangup(call->backend_private, TRUE); return NULL; } sipe_media_stream_add_extra_attribute(stream, "x-applicationsharing-session-id", "1"); sipe_media_stream_add_extra_attribute(stream, "x-applicationsharing-role", "viewer"); sipe_media_stream_add_extra_attribute(stream, "x-applicationsharing-media-type", "rdp"); stream->read_cb = read_cb; stream->writable_cb = writable_cb; return appshare; } void process_incoming_invite_appshare(struct sipe_core_private *sipe_private, struct sipmsg *msg) { struct sipe_media_call *call; struct sipe_media_stream *stream; struct sipe_appshare *appshare; struct sdpmsg *sdpmsg; GSList *i; sdpmsg = sdpmsg_parse_msg(msg->body); /* Skype for Business compatibility - ignore desktop video. */ i = sdpmsg->media; while (i) { struct sdpmedia *media = i->data; const gchar *label; i = i->next; label = sipe_utils_nameval_find(media->attributes, "label"); if (sipe_strequal(media->name, "video") && sipe_strequal(label, "applicationsharing-video")) { sdpmsg->media = g_slist_remove(sdpmsg->media, media); sdpmedia_free(media); } } call = process_incoming_invite_call_parsed_sdp(sipe_private, msg, sdpmsg); if (!call) { return; } stream = sipe_core_media_get_stream_by_id(call, "applicationsharing"); if (!stream) { sipe_backend_media_hangup(call->backend_private, TRUE); return; } appshare = initialize_appshare(stream); if (appshare) { gchar *from = sipmsg_parse_from_address(msg); appshare->ask_ctx = ask_accept_applicationsharing(sipe_private, from, accept_cb, decline_cb, appshare); g_free(from); } } static void connect_conference(struct sipe_core_private *sipe_private, struct sipe_chat_session *chat_session) { struct sipe_media_call *call; struct sipe_media_stream *stream; gchar * uri; chat_session->appshare_ask_ctx = NULL; uri = sipe_conf_build_uri(chat_session->id, "applicationsharing"); call = sipe_media_call_new(sipe_private, uri, NULL, SIPE_ICE_RFC_5245, SIPE_MEDIA_CALL_NO_UI); g_free(uri); stream = sipe_media_stream_add(call, "applicationsharing", SIPE_MEDIA_APPLICATION, SIPE_ICE_RFC_5245, TRUE, 0); if (!stream) { sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Application sharing error"), _("Couldn't connect application sharing")); sipe_backend_media_hangup(call->backend_private, FALSE); } sipe_media_stream_add_extra_attribute(stream, "connection", "new"); sipe_media_stream_add_extra_attribute(stream, "setup", "active"); initialize_appshare(stream); } void sipe_core_appshare_connect_conference(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session, gboolean user_must_accept) { if (user_must_accept) { const gchar *from; if (chat_session->appshare_ask_ctx) { // Accept dialog already opened. return; } if (chat_session->title) { from = chat_session->title; } else if (chat_session->organizer) { from = chat_session->organizer; } else { from = chat_session->id; } chat_session->appshare_ask_ctx = ask_accept_applicationsharing(SIPE_CORE_PRIVATE, from, (SipeUserAskCb)connect_conference, NULL, chat_session); } else { connect_conference(SIPE_CORE_PRIVATE, chat_session); } } sipe_appshare_role sipe_appshare_get_role(struct sipe_media_call *call) { struct sipe_media_stream *stream; g_return_val_if_fail(call, SIPE_APPSHARE_ROLE_NONE); stream = sipe_core_media_get_stream_by_id(call, "applicationsharing"); if (stream) { struct sipe_appshare *appshare; appshare = sipe_media_stream_get_data(stream); if (appshare) { #ifdef HAVE_APPSHARE_SERVER return appshare->server ? SIPE_APPSHARE_ROLE_PRESENTER : SIPE_APPSHARE_ROLE_VIEWER; #else return SIPE_APPSHARE_ROLE_VIEWER; #endif // HAVE_APPSHARE_SERVER } } return SIPE_APPSHARE_ROLE_NONE; } #ifdef HAVE_APPSHARE_SERVER /* Limit shared screen size to 2160p. Screen updates of larger monitors might * not fit into MultifragMaxRequestSize. */ #define APPSHARE_MAX_SCREEN_WIDTH 3840 #define APPSHARE_MAX_SCREEN_HEIGHT 2160 static void set_shared_display_area(rdpShadowServer *server, guint monitor_id) { MONITOR_DEF monitors[16]; MONITOR_DEF *monitor; UINT32 monitor_count; monitor_count = shadow_enum_monitors(monitors, 16); if (monitor_id >= monitor_count) { server->selectedMonitor = 0; return; } server->selectedMonitor = monitor_id; monitor = &monitors[monitor_id]; if ((monitor->right - monitor->left) > APPSHARE_MAX_SCREEN_WIDTH || (monitor->bottom - monitor->top) > APPSHARE_MAX_SCREEN_HEIGHT) { server->subRect.top = 0; server->subRect.left = 0; server->subRect.right = MIN(monitor->right - monitor->left, APPSHARE_MAX_SCREEN_WIDTH); server->subRect.bottom = MIN(monitor->bottom - monitor->top, APPSHARE_MAX_SCREEN_HEIGHT); server->shareSubRect = TRUE; SIPE_DEBUG_INFO("Cropping the shared screen to %dx%d", server->subRect.right, server->subRect.bottom); } } static void candidate_pairs_established_cb(struct sipe_media_stream *stream) { struct sipe_appshare *appshare; GSocketAddress *address; GError *error = NULL; struct sockaddr_un native; rdpShadowServer* server; const gchar *server_error = NULL; g_return_if_fail(sipe_strequal(stream->id, "applicationsharing")); appshare = sipe_media_stream_get_data(stream); server = shadow_server_new(); if(!server) { server_error = _("Could not create RDP server."); } else { server->ipcSocket = g_strdup_printf("%s/sipe-appshare-%u-%p", g_get_user_runtime_dir(), getpid(), stream); server->authentication = FALSE; server->mayInteract = FALSE; set_shared_display_area(server, appshare->monitor_id); /* Experimentally determined cap on multifrag max request size * Lync client would accept. Higher values result in a black * screen being displayed on the remote end. * * See related https://github.com/FreeRDP/FreeRDP/pull/3669. */ server->settings->MultifragMaxRequestSize = 0x3EFFFF; if(shadow_server_init(server) < 0) { server_error = _("Could not initialize RDP server."); } else if(shadow_server_start(server) < 0) { server_error = _("Could not start RDP server."); } } if (server_error) { struct sipe_core_private *sipe_private; sipe_private = sipe_media_get_sipe_core_private(stream->call); sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Application sharing error"), server_error); sipe_backend_media_hangup(stream->call->backend_private, TRUE); if (server) { shadow_server_uninit(server); shadow_server_free(server); } return; } appshare->server = server; appshare->socket = g_socket_new(G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); if (error) { SIPE_DEBUG_ERROR("Can't create RDP server socket: %s", error->message); g_error_free(error); sipe_backend_media_hangup(stream->call->backend_private, TRUE); return; } g_socket_set_blocking(appshare->socket, FALSE); native.sun_family = AF_LOCAL; strncpy(native.sun_path, server->ipcSocket, sizeof (native.sun_path) - 1); native.sun_path[sizeof (native.sun_path) - 1] = '\0'; address = g_socket_address_new_from_native(&native, sizeof native); g_socket_connect(appshare->socket, address, NULL, &error); if (error) { SIPE_DEBUG_ERROR("Can't connect to RDP server: %s", error->message); g_error_free(error); sipe_backend_media_hangup(stream->call->backend_private, TRUE); return; } appshare->channel = g_io_channel_unix_new(g_socket_get_fd(appshare->socket)); // No encoding for binary data g_io_channel_set_encoding(appshare->channel, NULL, &error); if (error) { SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s", error->message); g_error_free(error); sipe_backend_media_hangup(stream->call->backend_private, TRUE); return; } appshare->rdp_channel_readable_watch_id = g_io_add_watch(appshare->channel, G_IO_IN | G_IO_HUP, rdp_channel_readable_cb, appshare); // Appshare structure initialized; don't call this again. stream->candidate_pairs_established_cb = NULL; } static void stop_presenting_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, struct sipe_media_call *call) { struct sipe_media_stream *stream; stream = sipe_core_media_get_stream_by_id(call, "applicationsharing"); if (stream) { struct sipe_appshare *appshare; appshare = sipe_media_stream_get_data(stream); if (appshare) { appshare->ask_ctx = NULL; sipe_backend_media_hangup(call->backend_private, TRUE); } } } static struct sipe_user_ask_ctx * ask_end_presentation(struct sipe_core_private *sipe_private, const gchar *with, struct sipe_media_call *call) { struct sipe_user_ask_ctx *ctx; gchar *alias = sipe_buddy_get_alias(sipe_private, with); gchar *ask_msg = g_strdup_printf(_("Sharing desktop with %s"), alias ? alias : with); ctx = sipe_user_ask(sipe_private, ask_msg, _("Stop presenting"), (SipeUserAskCb)stop_presenting_cb, NULL, NULL, call); g_free(ask_msg); g_free(alias); return ctx; } static void monitor_selected_cb(struct sipe_core_private *sipe_private, gchar *with, guint monitor_id) { struct sipe_media_call *call; struct sipe_media_stream *stream; struct sipe_appshare *appshare; if (monitor_id == SIPE_CHOICE_CANCELLED) { g_free(with); return; } call = sipe_media_call_new(sipe_private, with, NULL, SIPE_ICE_RFC_5245, SIPE_MEDIA_CALL_INITIATOR | SIPE_MEDIA_CALL_NO_UI); stream = sipe_media_stream_add(call, "applicationsharing", SIPE_MEDIA_APPLICATION, SIPE_ICE_RFC_5245, TRUE, 0); if (!stream) { sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Application sharing error"), _("Couldn't initialize application sharing")); sipe_backend_media_hangup(call->backend_private, TRUE); g_free(with); return; } stream->candidate_pairs_established_cb = candidate_pairs_established_cb; stream->read_cb = read_cb; sipe_media_stream_add_extra_attribute(stream, "mid", "1"); sipe_media_stream_add_extra_attribute(stream, "x-applicationsharing-session-id", "1"); sipe_media_stream_add_extra_attribute(stream, "x-applicationsharing-role", "sharer"); sipe_media_stream_add_extra_attribute(stream, "x-applicationsharing-media-type", "rdp"); // These attributes are mandatory when sharing with a conference. sipe_media_stream_add_extra_attribute(stream, "setup", "active"); sipe_media_stream_add_extra_attribute(stream, "connection", "new"); appshare = g_new0(struct sipe_appshare, 1); appshare->stream = stream; appshare->monitor_id = monitor_id; appshare->ask_ctx = ask_end_presentation(sipe_private, with, call); sipe_media_stream_set_data(stream, appshare, (GDestroyNotify)sipe_appshare_free); g_free(with); } static void present_monitor_choice(struct sipe_core_public *sipe_public, const gchar *who) { MONITOR_DEF monitors[16]; int monitor_count; shadow_subsystem_set_entry_builtin("X11"); monitor_count = shadow_enum_monitors(monitors, 16); if (monitor_count == 1) { // Skip the choice, use the first (only) display right away. monitor_selected_cb(SIPE_CORE_PRIVATE, g_strdup(who), 0); } else { GSList *choices = NULL; int i; for (i = 0; i != monitor_count; ++i) { MONITOR_DEF *mon = &monitors[i]; gchar *str = g_strdup_printf("%dx%d @ [%d, %d]", mon->right - mon->left, mon->bottom - mon->top, mon->left, mon->top); choices = g_slist_append(choices, str); } sipe_user_ask_choice(SIPE_CORE_PRIVATE, _("Monitor to share"), choices, (SipeUserAskChoiceCb)monitor_selected_cb, g_strdup(who)); g_slist_free_full(choices, g_free); } } void sipe_core_appshare_share_desktop(struct sipe_core_public *sipe_public, const gchar *with) { present_monitor_choice(sipe_public, with); } void sipe_core_conf_share_desktop(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session) { gchar * uri; switch (sipe_core_conf_get_appshare_role(sipe_public, chat_session)) { case SIPE_APPSHARE_ROLE_PRESENTER: // We are already the presenting. return; case SIPE_APPSHARE_ROLE_VIEWER: { // Close RDP viewer before we start our own presentation. gchar *mcu_uri; struct sipe_media_call *call; mcu_uri = sipe_conf_build_uri(chat_session->id, "applicationsharing"); call = sipe_media_call_find(SIPE_CORE_PRIVATE, mcu_uri); g_free(mcu_uri); sipe_backend_media_hangup(call->backend_private, TRUE); break; } default: break; } uri = sipe_conf_build_uri(chat_session->id, "applicationsharing"); sipe_core_appshare_share_desktop(sipe_public, uri); g_free(uri); } void sipe_core_appshare_set_remote_control(struct sipe_media_call * call, gboolean enabled) { struct sipe_media_stream *stream; stream = sipe_core_media_get_stream_by_id(call, "applicationsharing"); if (stream) { struct sipe_appshare *appshare; appshare = sipe_media_stream_get_data(stream); if(appshare && appshare->server) { rdpShadowServer *server = appshare->server; int i; server->mayInteract = enabled; ArrayList_Lock(server->clients); for (i = 0; i < ArrayList_Count(server->clients); i++) { rdpShadowClient *client; client = ArrayList_GetItem(server->clients, i); client->mayInteract = enabled; } ArrayList_Unlock(server->clients); } } } gboolean sipe_core_appshare_get_remote_control(struct sipe_media_call * call) { struct sipe_media_stream *stream; stream = sipe_core_media_get_stream_by_id(call, "applicationsharing"); if (stream) { struct sipe_appshare *appshare; appshare = sipe_media_stream_get_data(stream); if(appshare && appshare->server) { return appshare->server->mayInteract; } } return FALSE; } #endif // HAVE_APPSHARE_SERVER /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-appshare.h ================================================ /** * @file sipe-appshare.h * * pidgin-sipe * * Copyright (C) 2014-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_core_private; struct sipe_media_call; struct sipmsg; void process_incoming_invite_appshare(struct sipe_core_private *sipe_private, struct sipmsg *msg); sipe_appshare_role sipe_appshare_get_role(struct sipe_media_call *call); ================================================ FILE: src/core/sipe-buddy.c ================================================ /** * @file sipe-buddy.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * GetUserPhoto operation * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-common.h" #include "sipmsg.h" #include "sip-csta.h" #include "sip-soap.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-cal.h" #include "sipe-chat.h" #include "sipe-conf.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-appshare.h" #include "sipe-digest.h" #include "sipe-group.h" #include "sipe-http.h" #include "sipe-im.h" #include "sipe-media.h" #include "sipe-nls.h" #include "sipe-ocs2005.h" #include "sipe-ocs2007.h" #include "sipe-schedule.h" #include "sipe-session.h" #include "sipe-status.h" #include "sipe-subscriptions.h" #include "sipe-svc.h" #include "sipe-ucs.h" #include "sipe-utils.h" #include "sipe-webticket.h" #include "sipe-xml.h" struct sipe_buddies { GHashTable *uri; GHashTable *exchange_key; /* Pending photo download HTTP requests */ GSList *pending_photo_requests; }; struct buddy_group_data { const struct sipe_group *group; gboolean is_obsolete; }; struct photo_response_data { gchar *who; gchar *photo_hash; struct sipe_http_request *request; }; static void buddy_fetch_photo(struct sipe_core_private *sipe_private, const gchar *uri); static void photo_response_data_free(struct photo_response_data *data); void sipe_buddy_add_keys(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy, const gchar *exchange_key, const gchar *change_key) { if (exchange_key) { buddy->exchange_key = g_strdup(exchange_key); g_hash_table_insert(sipe_private->buddies->exchange_key, buddy->exchange_key, buddy); } if (change_key) buddy->change_key = g_strdup(change_key); } struct sipe_buddy *sipe_buddy_add(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *exchange_key, const gchar *change_key) { /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */ gchar *normalized_uri = g_ascii_strdown(uri, -1); struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private, normalized_uri); if (!buddy) { buddy = g_new0(struct sipe_buddy, 1); buddy->name = normalized_uri; g_hash_table_insert(sipe_private->buddies->uri, buddy->name, buddy); sipe_buddy_add_keys(sipe_private, buddy, exchange_key, change_key); SIPE_DEBUG_INFO("sipe_buddy_add: Added buddy %s", normalized_uri); if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES)) { buddy->just_added = TRUE; sipe_subscribe_presence_single_cb(sipe_private, buddy->name); } buddy_fetch_photo(sipe_private, normalized_uri); normalized_uri = NULL; /* buddy takes ownership */ } else { SIPE_DEBUG_INFO("sipe_buddy_add: Buddy %s already exists", normalized_uri); buddy->is_obsolete = FALSE; } g_free(normalized_uri); return(buddy); } static gboolean is_buddy_in_group(struct sipe_buddy *buddy, const gchar *name) { if (buddy) { GSList *entry = buddy->groups; while (entry) { struct buddy_group_data *bgd = entry->data; if (sipe_strequal(bgd->group->name, name)) { bgd->is_obsolete = FALSE; return(TRUE); } entry = entry->next; } } return(FALSE); } void sipe_buddy_add_to_group(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy, struct sipe_group *group, const gchar *alias) { const gchar *uri = buddy->name; const gchar *group_name = group->name; sipe_backend_buddy bb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, group_name); if (!bb) { bb = sipe_backend_buddy_add(SIPE_CORE_PUBLIC, uri, alias, group_name); SIPE_DEBUG_INFO("sipe_buddy_add_to_group: created backend buddy '%s' with alias '%s'", uri, alias ? alias : ""); } if (!is_empty(alias)) { gchar *old_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, bb); if (sipe_strcase_equal(sipe_get_no_sip_uri(uri), old_alias)) { sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, bb, alias); SIPE_DEBUG_INFO("sipe_buddy_add_to_group: replaced alias for buddy '%s': old '%s' new '%s'", uri, old_alias, alias); } g_free(old_alias); } if (!is_buddy_in_group(buddy, group_name)) { sipe_buddy_insert_group(buddy, group); SIPE_DEBUG_INFO("sipe_buddy_add_to_group: added buddy %s to group %s", uri, group_name); } } static gint buddy_group_compare(gconstpointer a, gconstpointer b) { return(((const struct buddy_group_data *)a)->group->id - ((const struct buddy_group_data *)b)->group->id); } void sipe_buddy_insert_group(struct sipe_buddy *buddy, struct sipe_group *group) { struct buddy_group_data *bgd = g_new0(struct buddy_group_data, 1); bgd->group = group; buddy->groups = sipe_utils_slist_insert_unique_sorted(buddy->groups, bgd, buddy_group_compare, NULL); } static void buddy_group_free(gpointer data) { g_free(data); } static void buddy_group_remove(struct sipe_buddy *buddy, struct buddy_group_data *bgd) { buddy->groups = g_slist_remove(buddy->groups, bgd); buddy_group_free(bgd); } static void sipe_buddy_remove_group(struct sipe_buddy *buddy, const struct sipe_group *group) { GSList *entry = buddy->groups; struct buddy_group_data *bgd = NULL; while (entry) { bgd = entry->data; if (bgd->group == group) break; entry = entry->next; } buddy_group_remove(buddy, bgd); } void sipe_buddy_update_groups(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy, GSList *new_groups) { const gchar *uri = buddy->name; GSList *entry = buddy->groups; while (entry) { struct buddy_group_data *bgd = entry->data; const struct sipe_group *group = bgd->group; /* next buddy group */ entry = entry->next; /* old group NOT found in new list? */ if (g_slist_find(new_groups, group) == NULL) { sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, group->name); SIPE_DEBUG_INFO("sipe_buddy_update_groups: removing buddy %s from group '%s'", uri, group->name); /* this should never be NULL */ if (oldb) sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, oldb); buddy_group_remove(buddy, bgd); } } } gchar *sipe_buddy_groups_string(struct sipe_buddy *buddy) { guint i = 0; gchar *string; /* creating array from GList, converting guint to gchar * */ gchar **ids_arr = g_new(gchar *, g_slist_length(buddy->groups) + 1); GSList *entry = buddy->groups; if (!ids_arr) return(NULL); while (entry) { const struct sipe_group *group = ((struct buddy_group_data *) entry->data)->group; ids_arr[i] = g_strdup_printf("%u", group->id); entry = entry->next; i++; } ids_arr[i] = NULL; string = g_strjoinv(" ", ids_arr); g_strfreev(ids_arr); return(string); } void sipe_buddy_cleanup_local_list(struct sipe_core_private *sipe_private) { GSList *buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC, NULL, NULL); GSList *entry = buddies; SIPE_DEBUG_INFO("sipe_buddy_cleanup_local_list: overall %d backend buddies (including clones)", g_slist_length(buddies)); SIPE_DEBUG_INFO("sipe_buddy_cleanup_local_list: %d sipe buddies (unique)", sipe_buddy_count(sipe_private)); while (entry) { sipe_backend_buddy bb = entry->data; gchar *bname = sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC, bb); gchar *gname = sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC, bb); struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private, bname); if (!is_buddy_in_group(buddy, gname)) { SIPE_DEBUG_INFO("sipe_buddy_cleanup_local_list: REMOVING '%s' from local group '%s', as buddy is not in that group on remote contact list", bname, gname); sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, bb); } g_free(gname); g_free(bname); entry = entry->next; } g_slist_free(buddies); } struct sipe_buddy *sipe_buddy_find_by_uri(struct sipe_core_private *sipe_private, const gchar *uri) { if (!uri) return(NULL); return(g_hash_table_lookup(sipe_private->buddies->uri, uri)); } struct sipe_buddy *sipe_buddy_find_by_exchange_key(struct sipe_core_private *sipe_private, const gchar *exchange_key) { return(g_hash_table_lookup(sipe_private->buddies->exchange_key, exchange_key)); } void sipe_buddy_foreach(struct sipe_core_private *sipe_private, GHFunc callback, gpointer callback_data) { g_hash_table_foreach(sipe_private->buddies->uri, callback, callback_data); } static void buddy_free(struct sipe_buddy *buddy) { #ifndef _WIN32 /* * We are calling g_hash_table_foreach_steal(). That means that no * key/value deallocation functions are called. Therefore the glib * hash code does not touch the key (buddy->name) or value (buddy) * of the to-be-deleted hash node at all. It follows that we * * - MUST free the memory for the key ourselves and * - ARE allowed to do it in this function * * Conclusion: glib must be broken on the Windows platform if sipe * crashes with SIGTRAP when closing. You'll have to live * with the memory leak until this is fixed. */ g_free(buddy->name); #endif g_free(buddy->exchange_key); g_free(buddy->change_key); g_free(buddy->activity); g_free(buddy->meeting_subject); g_free(buddy->meeting_location); g_free(buddy->note); g_free(buddy->cal_start_time); g_free(buddy->cal_free_busy_base64); g_free(buddy->cal_free_busy); g_free(buddy->last_non_cal_activity); sipe_cal_free_working_hours(buddy->cal_working_hours); g_free(buddy->device_name); sipe_utils_slist_free_full(buddy->groups, buddy_group_free); g_free(buddy); } static gboolean buddy_free_cb(SIPE_UNUSED_PARAMETER gpointer key, gpointer buddy, SIPE_UNUSED_PARAMETER gpointer user_data) { buddy_free(buddy); /* We must return TRUE as the key/value have already been deleted */ return(TRUE); } void sipe_buddy_free(struct sipe_core_private *sipe_private) { struct sipe_buddies *buddies = sipe_private->buddies; g_hash_table_foreach_steal(buddies->uri, buddy_free_cb, NULL); /* core is being deallocated, remove all its pending photo requests */ while (buddies->pending_photo_requests) { struct photo_response_data *data = buddies->pending_photo_requests->data; buddies->pending_photo_requests = g_slist_remove(buddies->pending_photo_requests, data); photo_response_data_free(data); } g_hash_table_destroy(buddies->uri); g_hash_table_destroy(buddies->exchange_key); g_free(buddies); sipe_private->buddies = NULL; } static void buddy_set_obsolete_flag(SIPE_UNUSED_PARAMETER gpointer key, gpointer value, SIPE_UNUSED_PARAMETER gpointer user_data) { struct sipe_buddy *buddy = value; GSList *entry = buddy->groups; buddy->is_obsolete = TRUE; while (entry) { ((struct buddy_group_data *) entry->data)->is_obsolete = TRUE; entry = entry->next; } } void sipe_buddy_update_start(struct sipe_core_private *sipe_private) { g_hash_table_foreach(sipe_private->buddies->uri, buddy_set_obsolete_flag, NULL); } static gboolean buddy_check_obsolete_flag(SIPE_UNUSED_PARAMETER gpointer key, gpointer value, gpointer user_data) { struct sipe_core_private *sipe_private = user_data; struct sipe_buddy *buddy = value; const gchar *uri = buddy->name; if (buddy->is_obsolete) { /* all backend buddies in different groups */ GSList *buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC, uri, NULL); GSList *entry = buddies; SIPE_DEBUG_INFO("buddy_check_obsolete_flag: REMOVING %d backend buddies for '%s'", g_slist_length(buddies), uri); while (entry) { sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, entry->data); entry = entry->next; } g_slist_free(buddies); buddy_free(buddy); /* return TRUE as the key/value have already been deleted */ return(TRUE); } else { GSList *entry = buddy->groups; while (entry) { struct buddy_group_data *bgd = entry->data; /* next buddy group */ entry = entry->next; if (bgd->is_obsolete) { const struct sipe_group *group = bgd->group; sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, group->name); SIPE_DEBUG_INFO("buddy_check_obsolete_flag: removing buddy '%s' from group '%s'", uri, group->name); /* this should never be NULL */ if (oldb) sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, oldb); buddy_group_remove(buddy, bgd); } } return(FALSE); } } void sipe_buddy_update_finish(struct sipe_core_private *sipe_private) { g_hash_table_foreach_remove(sipe_private->buddies->uri, buddy_check_obsolete_flag, sipe_private); } gchar *sipe_core_buddy_status(struct sipe_core_public *sipe_public, const gchar *uri, guint activity, const gchar *status_text) { struct sipe_buddy *sbuddy; GString *status; if (!sipe_public) return NULL; /* happens on pidgin exit */ sbuddy = sipe_buddy_find_by_uri(SIPE_CORE_PRIVATE, uri); if (!sbuddy) return NULL; status = g_string_new(sbuddy->activity ? sbuddy->activity : (activity == SIPE_ACTIVITY_BUSY) || (activity == SIPE_ACTIVITY_BRB) ? status_text : NULL); if (sbuddy->is_mobile) { if (status->len) g_string_append(status, " - "); g_string_append(status, _("Mobile")); } if (sbuddy->note) { if (status->len) g_string_append(status, " - "); g_string_append(status, sbuddy->note); } /* return NULL instead of empty status text */ return(g_string_free(status, status->len ? FALSE : TRUE)); } gchar *sipe_buddy_get_alias(struct sipe_core_private *sipe_private, const gchar *with) { sipe_backend_buddy pbuddy; gchar *alias = NULL; if ((pbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, with, NULL))) { alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, pbuddy); } return alias; } void sipe_core_buddy_group(struct sipe_core_public *sipe_public, const gchar *who, const gchar *old_group_name, const gchar *new_group_name) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private, who); struct sipe_group *old_group = NULL; struct sipe_group *new_group; struct sipe_ucs_transaction *ucs_trans = NULL; SIPE_DEBUG_INFO("sipe_core_buddy_group: buddy '%s' old group '%s' new group '%s'", who ? who : "", old_group_name ? old_group_name : "", new_group_name ? new_group_name : ""); if (!buddy) /* buddy not in roaming list */ return; old_group = sipe_group_find_by_name(sipe_private, old_group_name); if (old_group) { sipe_buddy_remove_group(buddy, old_group); SIPE_DEBUG_INFO("sipe_core_buddy_group: buddy '%s' removed from old group '%s'", who, old_group_name); } new_group = sipe_group_find_by_name(sipe_private, new_group_name); if (new_group) { sipe_buddy_insert_group(buddy, new_group); SIPE_DEBUG_INFO("sipe_core_buddy_group: buddy '%s' added to new group '%s'", who, new_group_name); } if (sipe_ucs_is_migrated(sipe_private)) { /* UCS handling */ ucs_trans = sipe_ucs_transaction(sipe_private); if (new_group) { /* * 1. new buddy added to existing group * 2. existing buddy moved from old to existing group */ sipe_ucs_group_add_buddy(sipe_private, ucs_trans, new_group, buddy, buddy->name); if (old_group) sipe_ucs_group_remove_buddy(sipe_private, ucs_trans, old_group, buddy); } else if (old_group) { /* * 3. existing buddy removed from one of its groups * 4. existing buddy removed from last group */ sipe_ucs_group_remove_buddy(sipe_private, ucs_trans, old_group, buddy); if (g_slist_length(buddy->groups) < 1) sipe_buddy_remove(sipe_private, buddy); /* buddy no longer valid */ } /* non-UCS handling */ } else if (new_group) sipe_group_update_buddy(sipe_private, buddy); /* 5. buddy added to new group */ if (!new_group) sipe_group_create(sipe_private, ucs_trans, new_group_name, who); } void sipe_core_buddy_add(struct sipe_core_public *sipe_public, const gchar *uri, const gchar *group_name) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; if (!sipe_buddy_find_by_uri(sipe_private, uri)) sipe_buddy_add(sipe_private, uri, NULL, NULL); else SIPE_DEBUG_INFO("sipe_core_buddy_add: buddy %s already in internal list", uri); sipe_core_buddy_group(sipe_public, uri, NULL, group_name); } void sipe_buddy_remove(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy) { struct sipe_buddies *buddies = sipe_private->buddies; const gchar *uri = buddy->name; GSList *entry = buddy->groups; gchar *action_name = sipe_utils_presence_key(uri); sipe_schedule_cancel(sipe_private, action_name); g_free(action_name); /* If the buddy still has groups, we need to delete backend buddies */ while (entry) { const struct sipe_group *group = ((struct buddy_group_data *) entry->data)->group; sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, group->name); /* this should never be NULL */ if (oldb) sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, oldb); entry = entry->next; } g_hash_table_remove(buddies->uri, uri); if (buddy->exchange_key) g_hash_table_remove(buddies->exchange_key, buddy->exchange_key); buddy_free(buddy); } /** * Unassociates buddy from group first. * Then see if no groups left, removes buddy completely. * Otherwise updates buddy groups on server. */ void sipe_core_buddy_remove(struct sipe_core_public *sipe_public, const gchar *uri, const gchar *group_name) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private, uri); struct sipe_group *group = NULL; if (!buddy) return; if (group_name) { group = sipe_group_find_by_name(sipe_private, group_name); if (group) { sipe_buddy_remove_group(buddy, group); SIPE_DEBUG_INFO("sipe_core_buddy_remove: buddy '%s' removed from group '%s'", uri, group->name); } } if (g_slist_length(buddy->groups) < 1) { if (sipe_ucs_is_migrated(sipe_private)) { sipe_ucs_group_remove_buddy(sipe_private, NULL, group, buddy); } else { gchar *request = g_strdup_printf("%s", buddy->name); sip_soap_request(sipe_private, "deleteContact", request); g_free(request); } sipe_buddy_remove(sipe_private, buddy); } else { if (sipe_ucs_is_migrated(sipe_private)) { sipe_ucs_group_remove_buddy(sipe_private, NULL, group, buddy); } else /* updates groups on server */ sipe_group_update_buddy(sipe_private, buddy); } } void sipe_core_buddy_got_status(struct sipe_core_public *sipe_public, const gchar *uri, guint activity, time_t last_active) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_buddy *sbuddy = sipe_buddy_find_by_uri(sipe_private, uri); if (!sbuddy) return; /* Check if on 2005 system contact's calendar, * then set/preserve it. */ if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) { sipe_backend_buddy_set_status(sipe_public, uri, activity, last_active); } else { sipe_ocs2005_apply_calendar_status(sipe_private, sbuddy, sipe_status_activity_to_token(activity)); } } void sipe_core_buddy_tooltip_info(struct sipe_core_public *sipe_public, const gchar *uri, const gchar *status_name, gboolean is_online, struct sipe_backend_buddy_tooltip *tooltip) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; gchar *note = NULL; gboolean is_oof_note = FALSE; const gchar *activity = NULL; gchar *calendar = NULL; const gchar *meeting_subject = NULL; const gchar *meeting_location = NULL; gchar *access_text = NULL; #define SIPE_ADD_BUDDY_INFO(l, t) \ { \ gchar *tmp = g_markup_escape_text((t), -1); \ sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), tmp); \ g_free(tmp); \ } #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) \ sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), (t)) if (sipe_public) { /* happens on pidgin exit */ struct sipe_buddy *sbuddy = sipe_buddy_find_by_uri(sipe_private, uri); if (sbuddy) { note = sbuddy->note; is_oof_note = sbuddy->is_oof_note; activity = sbuddy->activity; calendar = sipe_cal_get_description(sbuddy); meeting_subject = sbuddy->meeting_subject; meeting_location = sbuddy->meeting_location; } if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) { gboolean is_group_access = FALSE; const int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", sipe_get_no_sip_uri(uri), &is_group_access); const char *access_level = sipe_ocs2007_access_level_name(container_id); access_text = is_group_access ? g_strdup(access_level) : g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, access_level); } } if (is_online) { const gchar *status_str = activity ? activity : status_name; SIPE_ADD_BUDDY_INFO(_("Status"), status_str); } if (is_online && !is_empty(calendar)) { SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar); } g_free(calendar); if (!is_empty(meeting_location)) { SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", uri, meeting_location); SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location); } if (!is_empty(meeting_subject)) { SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", uri, meeting_subject); SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject); } if (note) { gchar *note_italics = g_strdup_printf("%s", note); SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", uri, note); SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note ? _("Out of office note") : _("Note"), note_italics); g_free(note_italics); } if (access_text) { SIPE_ADD_BUDDY_INFO(_("Access level"), access_text); g_free(access_text); } } void sipe_buddy_update_property(struct sipe_core_private *sipe_private, const char *uri, sipe_buddy_info_fields propkey, char *property_value) { GSList *buddies, *entry; if (property_value) property_value = g_strstrip(property_value); entry = buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC, uri, NULL); /* all buddies in different groups */ while (entry) { gchar *prop_str; sipe_backend_buddy p_buddy = entry->data; /* for Display Name */ if (propkey == SIPE_BUDDY_INFO_DISPLAY_NAME) { gchar *alias; alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy); if (property_value && sipe_is_bad_alias(uri, alias)) { SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri, property_value); sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, p_buddy, property_value); } g_free(alias); alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC, p_buddy); if (!is_empty(property_value) && (!sipe_strequal(property_value, alias) || is_empty(alias)) ) { SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri, property_value); sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC, p_buddy, property_value); } g_free(alias); } /* for other properties */ else { if (!is_empty(property_value)) { prop_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, propkey); if (!prop_str || !sipe_strcase_equal(prop_str, property_value)) { sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC, p_buddy, propkey, property_value); } g_free(prop_str); } } entry = entry->next; } g_slist_free(buddies); } struct ms_dlx_data; struct ms_dlx_data { GSList *search_rows; gchar *other; guint max_returns; sipe_svc_callback *callback; struct sipe_svc_session *session; gchar *wsse_security; struct sipe_backend_search_token *token; /* must call ms_dlx_free() */ void (*failed_callback)(struct sipe_core_private *sipe_private, struct ms_dlx_data *mdd); }; static void free_search_rows(GSList *search_rows) { sipe_utils_slist_free_full(search_rows, g_free); } static void ms_dlx_free(struct ms_dlx_data *mdd) { free_search_rows(mdd->search_rows); sipe_svc_session_close(mdd->session); g_free(mdd->other); g_free(mdd->wsse_security); g_free(mdd); } #define SIPE_SOAP_SEARCH_ROW "" #define DLX_SEARCH_ITEM \ "" \ " %s" \ " %s" \ "" static gchar * prepare_buddy_search_query(GSList *query_rows, gboolean use_dlx) { gchar **attrs = g_new(gchar *, (g_slist_length(query_rows) / 2) + 1); guint i = 0; gchar *query = NULL; while (query_rows) { const gchar *attr; gchar *value; gchar *tmp = NULL; attr = query_rows->data; query_rows = g_slist_next(query_rows); value = query_rows->data; query_rows = g_slist_next(query_rows); if (!value) break; /* * Special value for SIP ID * * Active Directory seems only to be able to search for * SIP URIs. Make sure search string starts with "sip:". */ if (!attr) { attr = "msRTCSIP-PrimaryUserAddress"; if (!use_dlx) value = tmp = sip_uri(value); } attrs[i++] = g_markup_printf_escaped(use_dlx ? DLX_SEARCH_ITEM : SIPE_SOAP_SEARCH_ROW, attr, value); g_free(tmp); } attrs[i] = NULL; if (i) { query = g_strjoinv(NULL, attrs); SIPE_DEBUG_INFO("prepare_buddy_search_query: rows:\n%s", query ? query : ""); } g_strfreev(attrs); return query; } static void ms_dlx_webticket(struct sipe_core_private *sipe_private, const gchar *base_uri, const gchar *auth_uri, const gchar *wsse_security, SIPE_UNUSED_PARAMETER const gchar *failure_msg, gpointer callback_data) { struct ms_dlx_data *mdd = callback_data; if (wsse_security) { guint length = g_slist_length(mdd->search_rows); gchar *search; SIPE_DEBUG_INFO("ms_dlx_webticket: got ticket for %s", base_uri); if (length > 0) { /* complex search */ gchar *query = prepare_buddy_search_query(mdd->search_rows, TRUE); search = g_strdup_printf("" " %s" "", length / 2, query); g_free(query); } else { /* simple search */ search = g_strdup_printf("" " c,company,displayName,givenName,mail,mailNickname,msRTCSIP-PrimaryUserAddress,sn" " %s" " BeginsWith" "", mdd->other); } if (sipe_svc_ab_entry_request(sipe_private, mdd->session, auth_uri, wsse_security, search, mdd->max_returns, mdd->callback, mdd)) { /* keep webticket security token for potential further use */ g_free(mdd->wsse_security); mdd->wsse_security = g_strdup(wsse_security); /* callback data passed down the line */ mdd = NULL; } g_free(search); } else { /* no ticket: this will show the minmum information */ SIPE_DEBUG_ERROR("ms_dlx_webticket: no web ticket for %s", base_uri); } if (mdd) mdd->failed_callback(sipe_private, mdd); } static void ms_dlx_webticket_request(struct sipe_core_private *sipe_private, struct ms_dlx_data *mdd) { if (!sipe_webticket_request_with_port(sipe_private, mdd->session, sipe_private->dlx_uri, "AddressBookWebTicketBearer", ms_dlx_webticket, mdd)) { SIPE_DEBUG_ERROR("ms_dlx_webticket_request: couldn't request webticket for %s", sipe_private->dlx_uri); mdd->failed_callback(sipe_private, mdd); } } void sipe_buddy_search_contacts_finalize(struct sipe_core_private *sipe_private, struct sipe_backend_search_results *results, guint match_count, gboolean more) { gchar *secondary = g_strdup_printf( dngettext(PACKAGE_NAME, "Found %d contact%s:", "Found %d contacts%s:", match_count), match_count, more ? _(" (more matched your query)") : ""); sipe_backend_search_results_finalize(SIPE_CORE_PUBLIC, results, secondary, more); g_free(secondary); } static void search_ab_entry_response(struct sipe_core_private *sipe_private, const gchar *uri, SIPE_UNUSED_PARAMETER const gchar *raw, sipe_xml *soap_body, gpointer callback_data) { struct ms_dlx_data *mdd = callback_data; if (soap_body) { const sipe_xml *node; struct sipe_backend_search_results *results; GHashTable *found; SIPE_DEBUG_INFO("search_ab_entry_response: received valid SOAP message from service %s", uri); /* any matches? */ node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry"); if (!node) { /* try again with simple search, if possible */ if (mdd->other && mdd->search_rows) { SIPE_DEBUG_INFO_NOFORMAT("search_ab_entry_response: no matches, retrying with simple search"); /* throw away original search query */ free_search_rows(mdd->search_rows); mdd->search_rows = NULL; ms_dlx_webticket_request(sipe_private, mdd); /* callback data passed down the line */ return; } else { SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: no matches"); sipe_backend_search_failed(SIPE_CORE_PUBLIC, mdd->token, _("No contacts found")); ms_dlx_free(mdd); return; } } /* OK, we found something - show the results to the user */ results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC, mdd->token); if (!results) { SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: Unable to display the search results."); sipe_backend_search_failed(SIPE_CORE_PUBLIC, mdd->token, _("Unable to display the search results")); ms_dlx_free(mdd); return; } /* SearchAbEntryResult can contain duplicates */ found = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); for (/* initialized above */ ; node; node = sipe_xml_twin(node)) { const sipe_xml *attrs; gchar *sip_uri = NULL; gchar *displayname = NULL; gchar *company = NULL; gchar *country = NULL; gchar *email = NULL; for (attrs = sipe_xml_child(node, "Attributes/Attribute"); attrs; attrs = sipe_xml_twin(attrs)) { gchar *name = sipe_xml_data(sipe_xml_child(attrs, "Name")); gchar *value = sipe_xml_data(sipe_xml_child(attrs, "Value")); if (!is_empty(value)) { if (sipe_strcase_equal(name, "msrtcsip-primaryuseraddress")) { g_free(sip_uri); sip_uri = value; value = NULL; } else if (sipe_strcase_equal(name, "displayname")) { g_free(displayname); displayname = value; value = NULL; } else if (sipe_strcase_equal(name, "mail")) { g_free(email); email = value; value = NULL; } else if (sipe_strcase_equal(name, "company")) { g_free(company); company = value; value = NULL; } else if (sipe_strcase_equal(name, "country")) { g_free(country); country = value; value = NULL; } } g_free(value); g_free(name); } if (sip_uri && !g_hash_table_lookup(found, sip_uri)) { gchar **uri_parts = g_strsplit(sip_uri, ":", 2); sipe_backend_search_results_add(SIPE_CORE_PUBLIC, results, uri_parts[1], displayname, company, country, email); g_strfreev(uri_parts); g_hash_table_insert(found, sip_uri, (gpointer) TRUE); sip_uri = NULL; } g_free(email); g_free(country); g_free(company); g_free(displayname); g_free(sip_uri); } sipe_buddy_search_contacts_finalize(sipe_private, results, g_hash_table_size(found), FALSE); g_hash_table_destroy(found); ms_dlx_free(mdd); } else { mdd->failed_callback(sipe_private, mdd); } } static gboolean process_search_contact_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans) { struct sipe_backend_search_token *token = trans->payload->data; struct sipe_backend_search_results *results; sipe_xml *searchResults; const sipe_xml *mrow; guint match_count = 0; gboolean more = FALSE; /* valid response? */ if (msg->response != 200) { SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)", msg->response); sipe_backend_search_failed(SIPE_CORE_PUBLIC, token, _("Contact search failed")); return(FALSE); } SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg->body ? msg->body : ""); /* valid XML? */ searchResults = sipe_xml_parse(msg->body, msg->bodylen); if (!searchResults) { SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults"); sipe_backend_search_failed(SIPE_CORE_PUBLIC, token, _("Contact search failed")); return(FALSE); } /* any matches? */ mrow = sipe_xml_child(searchResults, "Body/Array/row"); if (!mrow) { SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches"); sipe_backend_search_failed(SIPE_CORE_PUBLIC, token, _("No contacts found")); sipe_xml_free(searchResults); return(FALSE); } /* OK, we found something - show the results to the user */ results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC, trans->payload->data); if (!results) { SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results."); sipe_backend_search_failed(SIPE_CORE_PUBLIC, token, _("Unable to display the search results")); sipe_xml_free(searchResults); return FALSE; } for (/* initialized above */ ; mrow; mrow = sipe_xml_twin(mrow)) { gchar **uri_parts = g_strsplit(sipe_xml_attribute(mrow, "uri"), ":", 2); sipe_backend_search_results_add(SIPE_CORE_PUBLIC, results, uri_parts[1], sipe_xml_attribute(mrow, "displayName"), sipe_xml_attribute(mrow, "company"), sipe_xml_attribute(mrow, "country"), sipe_xml_attribute(mrow, "email")); g_strfreev(uri_parts); match_count++; } if ((mrow = sipe_xml_child(searchResults, "Body/directorySearch/moreAvailable")) != NULL) { char *data = sipe_xml_data(mrow); more = (g_ascii_strcasecmp(data, "true") == 0); g_free(data); } sipe_buddy_search_contacts_finalize(sipe_private, results, match_count, more); sipe_xml_free(searchResults); return(TRUE); } static void search_soap_request(struct sipe_core_private *sipe_private, GDestroyNotify destroy, void *data, guint max, SoapTransCallback callback, GSList *search_rows) { gchar *query = prepare_buddy_search_query(search_rows, FALSE); struct transaction_payload *payload = g_new0(struct transaction_payload, 1); payload->destroy = destroy; payload->data = data; sip_soap_directory_search(sipe_private, max, query, callback, payload); g_free(query); } static void search_ab_entry_failed(struct sipe_core_private *sipe_private, struct ms_dlx_data *mdd) { /* error using [MS-DLX] server, retry using Active Directory */ if (mdd->search_rows) search_soap_request(sipe_private, NULL, mdd->token, 100, process_search_contact_response, mdd->search_rows); ms_dlx_free(mdd); } void sipe_core_buddy_search(struct sipe_core_public *sipe_public, struct sipe_backend_search_token *token, const gchar *given_name, const gchar *surname, const gchar *email, const gchar *sipid, const gchar *company, const gchar *country) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; /* Lync 2013 or newer: use UCS if contacts are migrated */ if (SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013) && sipe_ucs_is_migrated(sipe_private)) { sipe_ucs_search(sipe_private, token, given_name, surname, email, sipid, company, country); } else { GSList *query_rows = NULL; guint count = 0; const gchar *simple = NULL; #define ADD_QUERY_ROW(attr, val) \ if (val) { \ query_rows = g_slist_append(query_rows, g_strdup(attr)); \ query_rows = g_slist_append(query_rows, g_strdup(val)); \ simple = val; \ count++; \ } ADD_QUERY_ROW("givenName", given_name); ADD_QUERY_ROW("sn", surname); ADD_QUERY_ROW("mail", email); /* prepare_buddy_search_query() interprets NULL as SIP ID */ ADD_QUERY_ROW(NULL, sipid); ADD_QUERY_ROW("company", company); ADD_QUERY_ROW("c", country); if (query_rows) { if (sipe_private->dlx_uri != NULL) { struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1); mdd->search_rows = query_rows; /* user entered only one search string, remember that one */ if (count == 1) mdd->other = g_strdup(simple); mdd->max_returns = 100; mdd->callback = search_ab_entry_response; mdd->failed_callback = search_ab_entry_failed; mdd->session = sipe_svc_session_start(); mdd->token = token; ms_dlx_webticket_request(sipe_private, mdd); } else { /* no [MS-DLX] server, use Active Directory search instead */ search_soap_request(sipe_private, NULL, token, 100, process_search_contact_response, query_rows); free_search_rows(query_rows); } } else sipe_backend_search_failed(sipe_public, token, _("Invalid contact search query")); } } static void get_info_finalize(struct sipe_core_private *sipe_private, struct sipe_backend_buddy_info *info, const gchar *uri, const gchar *server_alias, const gchar *email) { sipe_backend_buddy bbuddy; struct sipe_buddy *sbuddy; gchar *alias; gchar *value; if (!info) { info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC); } else { sipe_backend_buddy_info_break(SIPE_CORE_PUBLIC, info); } if (!info) return; bbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL); if (is_empty(server_alias)) { value = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC, bbuddy); if (value) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_DISPLAY_NAME, value); } } else { value = g_strdup(server_alias); } /* present alias if it differs from server alias */ alias = sipe_backend_buddy_get_local_alias(SIPE_CORE_PUBLIC, bbuddy); if (alias && !sipe_strequal(alias, value)) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_ALIAS, alias); } g_free(alias); g_free(value); if (is_empty(email)) { value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, bbuddy, SIPE_BUDDY_INFO_EMAIL); if (value) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_EMAIL, value); g_free(value); } } value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, bbuddy, SIPE_BUDDY_INFO_SITE); if (value) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_SITE, value); g_free(value); } sbuddy = sipe_buddy_find_by_uri(sipe_private, uri); if (sbuddy && sbuddy->device_name) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_DEVICE, sbuddy->device_name); } sipe_backend_buddy_info_finalize(SIPE_CORE_PUBLIC, info, uri); } static void get_info_ab_entry_response(struct sipe_core_private *sipe_private, const gchar *uri, SIPE_UNUSED_PARAMETER const gchar *raw, sipe_xml *soap_body, gpointer callback_data) { struct ms_dlx_data *mdd = callback_data; struct sipe_backend_buddy_info *info = NULL; gchar *server_alias = NULL; gchar *email = NULL; if (soap_body) { const sipe_xml *node; SIPE_DEBUG_INFO("get_info_ab_entry_response: received valid SOAP message from service %s", uri); info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC); for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute"); node; node = sipe_xml_twin(node)) { gchar *name = sipe_xml_data(sipe_xml_child(node, "Name")); gchar *value = sipe_xml_data(sipe_xml_child(node, "Value")); const sipe_xml *values = sipe_xml_child(node, "Values"); /* Single value entries */ if (!is_empty(value)) { if (sipe_strcase_equal(name, "displayname")) { g_free(server_alias); server_alias = value; value = NULL; sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias); } else if (sipe_strcase_equal(name, "mail")) { g_free(email); email = value; value = NULL; sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_EMAIL, email); } else if (sipe_strcase_equal(name, "title")) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_JOB_TITLE, value); } else if (sipe_strcase_equal(name, "company")) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_COMPANY, value); } else if (sipe_strcase_equal(name, "country")) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_COUNTRY, value); } } else if (values) { gchar *first = sipe_xml_data(sipe_xml_child(values, "string")); if (sipe_strcase_equal(name, "telephonenumber")) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_WORK_PHONE, first); } g_free(first); } g_free(value); g_free(name); } } /* this will show the minmum information */ get_info_finalize(sipe_private, info, mdd->other, server_alias, email); g_free(email); g_free(server_alias); ms_dlx_free(mdd); } static gboolean process_get_info_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans) { const gchar *uri = trans->payload->data; struct sipe_backend_buddy_info *info = NULL; gchar *server_alias = NULL; gchar *email = NULL; SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri, sipe_private->username); if (msg->response != 200) { SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg->response); } else { sipe_xml *searchResults; const sipe_xml *mrow; SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg->body ? msg->body : ""); searchResults = sipe_xml_parse(msg->body, msg->bodylen); if (!searchResults) { SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults"); } else if ((mrow = sipe_xml_child(searchResults, "Body/Array/row"))) { const gchar *value; gchar *phone_number; info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC); server_alias = g_strdup(sipe_xml_attribute(mrow, "displayName")); email = g_strdup(sipe_xml_attribute(mrow, "email")); phone_number = g_strdup(sipe_xml_attribute(mrow, "phone")); /* * For 2007 system we will take this from ContactCard - * it has cleaner tel: URIs at least */ if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) { char *tel_uri = sip_to_tel_uri(phone_number); /* trims its parameters, so call first */ sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, phone_number); g_free(tel_uri); sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri); } if (!is_empty(server_alias)) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias); } if ((value = sipe_xml_attribute(mrow, "title")) && strlen(value) > 0) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_JOB_TITLE, value); } if ((value = sipe_xml_attribute(mrow, "office")) && strlen(value) > 0) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_OFFICE, value); } if (!is_empty(phone_number)) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_WORK_PHONE, phone_number); } g_free(phone_number); if ((value = sipe_xml_attribute(mrow, "company")) && strlen(value) > 0) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_COMPANY, value); } if ((value = sipe_xml_attribute(mrow, "city")) && strlen(value) > 0) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_CITY, value); } if ((value = sipe_xml_attribute(mrow, "state")) && strlen(value) > 0) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_STATE, value); } if ((value = sipe_xml_attribute(mrow, "country")) && strlen(value) > 0) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_COUNTRY, value); } if (!is_empty(email)) { sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC, info, SIPE_BUDDY_INFO_EMAIL, email); } } sipe_xml_free(searchResults); } /* this will show the minmum information */ get_info_finalize(sipe_private, info, uri, server_alias, email); g_free(server_alias); g_free(email); return TRUE; } static void get_info_ab_entry_failed(struct sipe_core_private *sipe_private, struct ms_dlx_data *mdd) { /* error using [MS-DLX] server, retry using Active Directory */ search_soap_request(sipe_private, g_free, mdd->other, 1, process_get_info_response, mdd->search_rows); mdd->other = NULL; ms_dlx_free(mdd); } static GSList *search_rows_for_uri(const gchar *uri) { /* prepare_buddy_search_query() interprets NULL as SIP ID */ GSList *l = g_slist_append(NULL, NULL); return(g_slist_append(l, g_strdup(uri))); } void sipe_core_buddy_get_info(struct sipe_core_public *sipe_public, const gchar *who) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; GSList *search_rows = search_rows_for_uri(who); if (sipe_private->dlx_uri) { struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1); mdd->search_rows = search_rows; mdd->other = g_strdup(who); mdd->max_returns = 1; mdd->callback = get_info_ab_entry_response; mdd->failed_callback = get_info_ab_entry_failed; mdd->session = sipe_svc_session_start(); ms_dlx_webticket_request(sipe_private, mdd); } else { /* no [MS-DLX] server, use Active Directory search instead */ search_soap_request(sipe_private, g_free, g_strdup(who), 1, process_get_info_response, search_rows); free_search_rows(search_rows); } } static void photo_response_data_free(struct photo_response_data *data) { g_free(data->who); g_free(data->photo_hash); if (data->request) { sipe_http_request_cancel(data->request); } g_free(data); } static void photo_response_data_remove(struct sipe_core_private *sipe_private, struct photo_response_data *data) { data->request = NULL; sipe_private->buddies->pending_photo_requests = g_slist_remove(sipe_private->buddies->pending_photo_requests, data); photo_response_data_free(data); } static void process_buddy_photo_response(struct sipe_core_private *sipe_private, guint status, GSList *headers, const char *body, gpointer data) { struct photo_response_data *rdata = (struct photo_response_data *) data; if (status == SIPE_HTTP_STATUS_OK) { const gchar *len_str = sipe_utils_nameval_find(headers, "Content-Length"); if (len_str) { gsize photo_size = atoi(len_str); gpointer photo = g_new(char, photo_size); if (photo) { memcpy(photo, body, photo_size); sipe_backend_buddy_set_photo(SIPE_CORE_PUBLIC, rdata->who, photo, photo_size, rdata->photo_hash); } } } photo_response_data_remove(sipe_private, rdata); } static void process_get_user_photo_response(struct sipe_core_private *sipe_private, guint status, SIPE_UNUSED_PARAMETER GSList *headers, const gchar *body, gpointer data) { struct photo_response_data *rdata = (struct photo_response_data *) data; if ((status == SIPE_HTTP_STATUS_OK) && body) { sipe_xml *xml = sipe_xml_parse(body, strlen(body)); const sipe_xml *node = sipe_xml_child(xml, "Body/GetUserPhotoResponse/PictureData"); if (node) { gchar *base64; gsize photo_size; guchar *photo; /* decode photo data */ base64 = sipe_xml_data(node); photo = g_base64_decode(base64, &photo_size); g_free(base64); /* EWS doesn't provide a hash -> calculate SHA-1 digest */ if (!rdata->photo_hash) { guchar digest[SIPE_DIGEST_SHA1_LENGTH]; sipe_digest_sha1(photo, photo_size, digest); /* rdata takes ownership of digest string */ rdata->photo_hash = buff_to_hex_str(digest, SIPE_DIGEST_SHA1_LENGTH); } /* backend frees "photo" */ sipe_backend_buddy_set_photo(SIPE_CORE_PUBLIC, rdata->who, photo, photo_size, rdata->photo_hash); } sipe_xml_free(xml); } photo_response_data_remove(sipe_private, rdata); } static gchar *create_x_ms_webticket_header(const gchar *wsse_security) { gchar *assertion = sipe_xml_extract_raw(wsse_security, "Assertion", TRUE); gchar *wsse_security_base64; gchar *x_ms_webticket_header; if (!assertion) { return NULL; } wsse_security_base64 = g_base64_encode((const guchar *)assertion, strlen(assertion)); x_ms_webticket_header = g_strdup_printf("X-MS-WebTicket: opaque=%s\r\n", wsse_security_base64); g_free(assertion); g_free(wsse_security_base64); return x_ms_webticket_header; } /* see also sipe_ucs_http_request() */ static struct sipe_http_request *get_user_photo_request(struct sipe_core_private *sipe_private, struct photo_response_data *data, const gchar *ews_url, const gchar *email) { gchar *soap = g_strdup_printf("\r\n" "" " " " " " " " " " " " %s" " HR48x48" " " " " "", email); struct sipe_http_request *request = sipe_http_request_post(sipe_private, ews_url, NULL, soap, "text/xml; charset=UTF-8", process_get_user_photo_response, data); g_free(soap); if (request) { sipe_core_email_authentication(sipe_private, request); sipe_http_request_allow_redirect(request); } else { SIPE_DEBUG_ERROR_NOFORMAT("get_user_photo_request: failed to create HTTP connection"); } return(request); } static void photo_response_data_finalize(struct sipe_core_private *sipe_private, struct photo_response_data *data, const gchar *uri, const gchar *photo_hash) { if (data->request) { data->who = g_strdup(uri); data->photo_hash = g_strdup(photo_hash); sipe_private->buddies->pending_photo_requests = g_slist_append(sipe_private->buddies->pending_photo_requests, data); sipe_http_request_ready(data->request); } else { photo_response_data_free(data); } } void sipe_buddy_update_photo(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *photo_hash, const gchar *photo_url, const gchar *headers) { const gchar *photo_hash_old = sipe_backend_buddy_get_photo_hash(SIPE_CORE_PUBLIC, uri); if (!sipe_strequal(photo_hash, photo_hash_old)) { struct photo_response_data *data = g_new0(struct photo_response_data, 1); SIPE_DEBUG_INFO("sipe_buddy_update_photo: who '%s' url '%s' hash '%s'", uri, photo_url, photo_hash); /* Photo URL is embedded XML? */ if (g_str_has_prefix(photo_url, "<") && g_str_has_suffix(photo_url, ">")) { /* add dummy root to embedded XML string */ gchar *tmp = g_strdup_printf("%s", photo_url); sipe_xml *xml = sipe_xml_parse(tmp, strlen(tmp)); g_free(tmp); if (xml) { gchar *ews_url = sipe_xml_data(sipe_xml_child(xml, "ewsUrl")); gchar *email = sipe_xml_data(sipe_xml_child(xml, "primarySMTP")); if (!is_empty(ews_url) && !is_empty(email)) { /* * Workaround for missing Office 365 buddy icons * * (All?) Office 365 contact cards have the following * XML embedded as the photo URI XML node text: * * https://outlook.office365.com/EWS/Exchange.asmx/WSSecurity * user@company.com * * The simple HTTP request by get_user_photo_request() * is rejected with 401. But the response contains * * WWW-Authenticate: Basic Realm="" * * to which the HTTP transport answers with a retry * using Basic authentication. That in turn is rejected * with 500 and thus the buddy icon retrieval fails. * * As a quick workaround strip the trailing "/WSSecurity" * from the URL. The HTTP request for the buddy icon * retrieval will work with this stripped URL. * * @TODO: this is probably not the correct approach. * get_user_photo_request() should be updated * to support also a webticket request. */ gchar *tmp = g_strrstr(ews_url, "/WSSecurity"); if (tmp) *tmp = '\0'; data->request = get_user_photo_request(sipe_private, data, ews_url, email); } g_free(email); g_free(ews_url); sipe_xml_free(xml); } } else { data->request = sipe_http_request_get(sipe_private, photo_url, headers, process_buddy_photo_response, data); } photo_response_data_finalize(sipe_private, data, uri, photo_hash); } } static void get_photo_ab_entry_response(struct sipe_core_private *sipe_private, const gchar *uri, SIPE_UNUSED_PARAMETER const gchar *raw, sipe_xml *soap_body, gpointer callback_data) { struct ms_dlx_data *mdd = callback_data; gchar *photo_rel_path = NULL; gchar *photo_hash = NULL; if (soap_body) { const sipe_xml *node; SIPE_DEBUG_INFO("get_photo_ab_entry_response: received valid SOAP message from service %s", uri); for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute"); node; node = sipe_xml_twin(node)) { gchar *name = sipe_xml_data(sipe_xml_child(node, "Name")); gchar *value = sipe_xml_data(sipe_xml_child(node, "Value")); if (!is_empty(value)) { if (sipe_strcase_equal(name, "PhotoRelPath")) { g_free(photo_rel_path); photo_rel_path = value; value = NULL; } else if (sipe_strcase_equal(name, "PhotoHash")) { g_free(photo_hash); photo_hash = value; value = NULL; } } g_free(value); g_free(name); } } if (sipe_private->addressbook_uri && photo_rel_path && photo_hash) { gchar *photo_url = g_strdup_printf("%s/%s", sipe_private->addressbook_uri, photo_rel_path); gchar *x_ms_webticket_header = create_x_ms_webticket_header(mdd->wsse_security); sipe_buddy_update_photo(sipe_private, mdd->other, photo_hash, photo_url, x_ms_webticket_header); g_free(x_ms_webticket_header); g_free(photo_url); } g_free(photo_rel_path); g_free(photo_hash); ms_dlx_free(mdd); } static void get_photo_ab_entry_failed(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, struct ms_dlx_data *mdd) { ms_dlx_free(mdd); } static void buddy_fetch_photo(struct sipe_core_private *sipe_private, const gchar *uri) { if (sipe_backend_uses_photo()) { /* Lync 2013 or newer: use UCS if contacts are migrated */ if (SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013) && sipe_ucs_is_migrated(sipe_private)) { struct photo_response_data *data = g_new0(struct photo_response_data, 1); data->request = get_user_photo_request(sipe_private, data, sipe_ucs_ews_url(sipe_private), sipe_get_no_sip_uri(uri)); photo_response_data_finalize(sipe_private, data, uri, /* there is no hash */ NULL); /* Lync 2010: use [MS-DLX] */ } else if (sipe_private->dlx_uri && sipe_private->addressbook_uri) { struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1); mdd->search_rows = search_rows_for_uri(uri); mdd->other = g_strdup(uri); mdd->max_returns = 1; mdd->callback = get_photo_ab_entry_response; mdd->failed_callback = get_photo_ab_entry_failed; mdd->session = sipe_svc_session_start(); ms_dlx_webticket_request(sipe_private, mdd); } } } static void buddy_refresh_photos_cb(gpointer uri, SIPE_UNUSED_PARAMETER gpointer value, gpointer sipe_private) { buddy_fetch_photo(sipe_private, uri); } void sipe_buddy_refresh_photos(struct sipe_core_private *sipe_private) { g_hash_table_foreach(sipe_private->buddies->uri, buddy_refresh_photos_cb, sipe_private); } /* Buddy menu callbacks*/ void sipe_core_buddy_new_chat(struct sipe_core_public *sipe_public, const gchar *who) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; /* 2007+ conference */ if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) { sipe_conf_add(sipe_private, who); /* 2005- multiparty chat */ } else { gchar *self = sip_uri_self(sipe_private); struct sip_session *session; session = sipe_session_add_chat(sipe_private, NULL, TRUE, self); session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC, session->chat_session, session->chat_session->title, self); g_free(self); sipe_im_invite(sipe_private, session, who, NULL, NULL, NULL, FALSE); } } void sipe_core_buddy_send_email(struct sipe_core_public *sipe_public, const gchar *who) { sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public, who, NULL); gchar *email = sipe_backend_buddy_get_string(sipe_public, buddy, SIPE_BUDDY_INFO_EMAIL); if (email) { gchar *command_line = g_strdup_printf( #ifdef _WIN32 "cmd /c start" #else "xdg-email" #endif " mailto:%s", email); g_free(email); SIPE_DEBUG_INFO("sipe_core_buddy_send_email: going to call email client: %s", command_line); g_spawn_command_line_async(command_line, NULL); g_free(command_line); } else { SIPE_DEBUG_INFO("sipe_core_buddy_send_email: no email address stored for buddy=%s", who); } } /* Buddy menu */ static struct sipe_backend_buddy_menu *buddy_menu_phone(struct sipe_core_public *sipe_public, struct sipe_backend_buddy_menu *menu, sipe_backend_buddy buddy, sipe_buddy_info_fields id_phone, sipe_buddy_info_fields id_display, const gchar *type) { gchar *phone = sipe_backend_buddy_get_string(sipe_public, buddy, id_phone); if (phone) { gchar *display = sipe_backend_buddy_get_string(sipe_public, buddy, id_display); gchar *tmp = NULL; gchar *label = g_strdup_printf("%s %s", type, display ? display : (tmp = sip_tel_uri_denormalize(phone))); menu = sipe_backend_buddy_menu_add(sipe_public, menu, label, SIPE_BUDDY_MENU_MAKE_CALL, phone); g_free(tmp); g_free(label); g_free(display); g_free(phone); } return(menu); } struct sipe_backend_buddy_menu *sipe_core_buddy_create_menu(struct sipe_core_public *sipe_public, const gchar *buddy_name, struct sipe_backend_buddy_menu *menu) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public, buddy_name, NULL); gchar *self = sip_uri_self(sipe_private); SIPE_SESSION_FOREACH { if (!sipe_strcase_equal(self, buddy_name) && session->chat_session) { struct sipe_chat_session *chat_session = session->chat_session; gboolean is_conf = (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE); if (sipe_backend_chat_find(chat_session->backend, buddy_name)) { gboolean conf_op = sipe_backend_chat_is_operator(chat_session->backend, self); if (is_conf && /* Not conf OP */ !sipe_backend_chat_is_operator(chat_session->backend, buddy_name) && /* We are a conf OP */ conf_op) { gchar *label = g_strdup_printf(_("Make leader of '%s'"), chat_session->title); menu = sipe_backend_buddy_menu_add(sipe_public, menu, label, SIPE_BUDDY_MENU_MAKE_CHAT_LEADER, chat_session); g_free(label); } if (is_conf && /* We are a conf OP */ conf_op) { gchar *label = g_strdup_printf(_("Remove from '%s'"), chat_session->title); menu = sipe_backend_buddy_menu_add(sipe_public, menu, label, SIPE_BUDDY_MENU_REMOVE_FROM_CHAT, chat_session); g_free(label); } } else { if (!is_conf || (is_conf && !session->locked)) { gchar *label = g_strdup_printf(_("Invite to '%s'"), chat_session->title); menu = sipe_backend_buddy_menu_add(sipe_public, menu, label, SIPE_BUDDY_MENU_INVITE_TO_CHAT, chat_session); g_free(label); } } } } SIPE_SESSION_FOREACH_END; g_free(self); menu = sipe_backend_buddy_menu_add(sipe_public, menu, _("New chat"), SIPE_BUDDY_MENU_NEW_CHAT, NULL); /* add buddy's phone numbers if we have call control */ if (sip_csta_is_idle(sipe_private)) { /* work phone */ menu = buddy_menu_phone(sipe_public, menu, buddy, SIPE_BUDDY_INFO_WORK_PHONE, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, _("Work")); /* mobile phone */ menu = buddy_menu_phone(sipe_public, menu, buddy, SIPE_BUDDY_INFO_MOBILE_PHONE, SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY, _("Mobile")); /* home phone */ menu = buddy_menu_phone(sipe_public, menu, buddy, SIPE_BUDDY_INFO_HOME_PHONE, SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY, _("Home")); /* other phone */ menu = buddy_menu_phone(sipe_public, menu, buddy, SIPE_BUDDY_INFO_OTHER_PHONE, SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY, _("Other")); /* custom1 phone */ menu = buddy_menu_phone(sipe_public, menu, buddy, SIPE_BUDDY_INFO_CUSTOM1_PHONE, SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY, _("Custom1")); } { gchar *email = sipe_backend_buddy_get_string(sipe_public, buddy, SIPE_BUDDY_INFO_EMAIL); if (email) { menu = sipe_backend_buddy_menu_add(sipe_public, menu, _("Send email..."), SIPE_BUDDY_MENU_SEND_EMAIL, NULL); g_free(email); } } #ifdef HAVE_APPSHARE_SERVER { struct sipe_media_call *call = sipe_media_call_find(SIPE_CORE_PRIVATE, buddy_name); if (call && sipe_appshare_get_role(call) == SIPE_APPSHARE_ROLE_PRESENTER) { /* We're already presenting to this buddy. */ if (sipe_core_appshare_get_remote_control(call)) { menu = sipe_backend_buddy_menu_add(sipe_public, menu, _("Take desktop control"), SIPE_BUDDY_MENU_TAKE_DESKTOP_CONTROL, call); } else { menu = sipe_backend_buddy_menu_add(sipe_public, menu, _("Give desktop control"), SIPE_BUDDY_MENU_GIVE_DESKTOP_CONTROL, call); } } else { menu = sipe_backend_buddy_menu_add(sipe_public, menu, _("Share my desktop"), SIPE_BUDDY_MENU_SHARE_DESKTOP, NULL); } } #endif // HAVE_APPSHARE_SERVER /* access level control */ if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) menu = sipe_backend_buddy_sub_menu_add(sipe_public, menu, _("Access level"), sipe_ocs2007_access_control_menu(sipe_private, buddy_name)); return(menu); } guint sipe_buddy_count(struct sipe_core_private *sipe_private) { return(g_hash_table_size(sipe_private->buddies->uri)); } static guint sipe_ht_hash_nick(const char *nick) { char *lc = g_utf8_strdown(nick, -1); guint bucket = g_str_hash(lc); g_free(lc); return bucket; } static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2) { char *nick1_norm = NULL; char *nick2_norm = NULL; gboolean equal; if (nick1 == NULL && nick2 == NULL) return TRUE; if (nick1 == NULL || nick2 == NULL || !g_utf8_validate(nick1, -1, NULL) || !g_utf8_validate(nick2, -1, NULL)) return FALSE; nick1_norm = g_utf8_casefold(nick1, -1); nick2_norm = g_utf8_casefold(nick2, -1); equal = g_utf8_collate(nick1_norm, nick2_norm) == 0; g_free(nick2_norm); g_free(nick1_norm); return equal; } void sipe_buddy_init(struct sipe_core_private *sipe_private) { struct sipe_buddies *buddies = g_new0(struct sipe_buddies, 1); buddies->uri = g_hash_table_new((GHashFunc) sipe_ht_hash_nick, (GEqualFunc) sipe_ht_equals_nick); buddies->exchange_key = g_hash_table_new(g_str_hash, g_str_equal); sipe_private->buddies = buddies; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-buddy.h ================================================ /** * @file sipe-buddy.h * * pidgin-sipe * * Copyright (C) 2010-2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_backend_search_results; struct sipe_cal_working_hours; struct sipe_core_private; struct sipe_group; struct sipe_buddy { gchar *name; gchar *exchange_key; gchar *change_key; gchar *activity; gchar *meeting_subject; gchar *meeting_location; /* Sipe internal format for Note is HTML. * All incoming plain text should be html-escaped * for example by g_markup_escape_text() */ gchar *note; gboolean is_oof_note; gboolean is_mobile; time_t note_since; /* Calendar related fields */ gchar *cal_start_time; int cal_granularity; gchar *cal_free_busy_base64; gchar *cal_free_busy; time_t cal_free_busy_published; /* for 2005 systems */ int user_avail; time_t user_avail_since; time_t activity_since; const char *last_non_cal_status_id; gchar *last_non_cal_activity; struct sipe_cal_working_hours *cal_working_hours; gchar *device_name; GSList *groups; /** flag to control sending 'context' element in 2007 subscriptions */ gboolean just_added; gboolean is_obsolete; }; /** * Adds UCS Exchange/Change keys to a @c sipe_buddy structure * * @param sipe_private SIPE core data * @param buddy sipe_buddy data structure * @param exchange_key Exchange key (may be @c NULL) * @param change_key Change key (may be @c NULL) */ void sipe_buddy_add_keys(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy, const gchar *exchange_key, const gchar *change_key); /** * Creates @c sipe_buddy structure for a new buddy and adds it into the buddy * list of given account. If buddy is already in the list, its existing * structure is returned. * * @param sipe_private SIPE core data * @param uri SIP URI of a buddy * @param exchange_key Exchange key (may be @c NULL) * @param change_key Change key (may be @c NULL) * * @return @c sipe_buddy structure */ struct sipe_buddy *sipe_buddy_add(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *exchange_key, const gchar *change_key); /** * Add buddy to a group. * * @param sipe_private SIPE core data * @param buddy sipe_buddy data structure * @param group sipe_group data structure * @param alias alias for the buddy in that group (may be @c NULL) */ void sipe_buddy_add_to_group(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy, struct sipe_group *group, const gchar *alias); /** * Insert a group to buddy group list * * @param buddy sipe_buddy data structure * @param group sipe_group data structure */ void sipe_buddy_insert_group(struct sipe_buddy *buddy, struct sipe_group *group); /** * Update group list for a buddy * * @param sipe_private SIPE core data * @param buddy sipe_buddy data structure * @param group list with new sipe_group data structures */ void sipe_buddy_update_groups(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy, GSList *new_groups); /** * Returns string of group IDs the buddy belongs to, e.g. "2 4 7 8" * * @param buddy sipe_buddy data structure * * @result group string. Must be @c g_free()'d after use. */ gchar *sipe_buddy_groups_string(struct sipe_buddy *buddy); /** * Remove entries from local buddy list that do not have corresponding entries * in the ones in the contact list sent by the server * * @param sipe_private SIPE core data */ void sipe_buddy_cleanup_local_list(struct sipe_core_private *sipe_private); /** * Prepare buddy list for an update * * @param sipe_private SIPE core data */ void sipe_buddy_update_start(struct sipe_core_private *sipe_private); /** * Finish buddy list update. This will remove obsolete buddies. * * @param sipe_private SIPE core data */ void sipe_buddy_update_finish(struct sipe_core_private *sipe_private); /** * Find buddy by URI * * @param sipe_private SIPE core data * @param uri SIP URI of a buddy (may be @c NULL) * * @return @c sipe_buddy structure or @c NULL */ struct sipe_buddy *sipe_buddy_find_by_uri(struct sipe_core_private *sipe_private, const gchar *uri); /** * Find buddy by Exchange Key * * @param sipe_private SIPE core data * @param uri Exchange Key of a buddy * * @return @c sipe_buddy structure */ struct sipe_buddy *sipe_buddy_find_by_exchange_key(struct sipe_core_private *sipe_private, const gchar *exchange_key); /** * Iterate buddy list * * @param sipe_private SIPE core data * @param callback function to call on each buddy * @param callback_data user data for the callback */ void sipe_buddy_foreach(struct sipe_core_private *sipe_private, GHFunc callback, gpointer callback_data); /** * Cancels buddy subscriptions and then deletes the buddy * * @param sipe_private SIPE core data * @param buddy @c sipe_buddy structure to remove */ void sipe_buddy_remove(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy); /** * Tries to retrieve a real user's name associated with given SIP URI. * * Result must be g_free'd after use. * * @param sipe_private SIPE core data * @param with a SIP URI * * @return Name of the user if the URI is found in buddy list, otherwise @c NULL */ gchar *sipe_buddy_get_alias(struct sipe_core_private *sipe_private, const gchar *with); /** * Update the value of a buddy property with given SIP URI * * @param sipe_private SIPE core data * @param uri a SIP URI * @param propkey property id (see sipe-backend.h) * @param property_value new value for the property */ void sipe_buddy_update_property(struct sipe_core_private *sipe_private, const gchar *uri, sipe_buddy_info_fields propkey, gchar *property_value); /** * Update the buddy photo with given SIP URI. If hash is the same * as the cached one then the fetching of the photo is skipped. * * @param sipe_private SIPE core data * @param uri a SIP URI * @param photo_hash hash value for the photo data * @param photo_url HTTP URL where to get the photo data * @param headers additional HTTP headers (may be @c NULL) */ void sipe_buddy_update_photo(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *photo_hash, const gchar *photo_url, const gchar *headers); /** * Triggers a download of all buddy photos that were changed on the server. * * @param sipe_private SIPE core data */ void sipe_buddy_refresh_photos(struct sipe_core_private *sipe_private); /** * Finalize the search results and display results to user. * * @param sipe_private SIPE core data * @param results opaque results handle for backend * @param match_count number of matches found * @param more @c TRUE if there are more matches available */ void sipe_buddy_search_contacts_finalize(struct sipe_core_private *sipe_private, struct sipe_backend_search_results *results, guint match_count, gboolean more); /** * Number of buddies * * @param sipe_private SIPE core data */ guint sipe_buddy_count(struct sipe_core_private *sipe_private); /** * Initialize buddy data * * @param sipe_private SIPE core data */ void sipe_buddy_init(struct sipe_core_private *sipe_private); /** * Free buddy data * * @param sipe_private SIPE core data */ void sipe_buddy_free(struct sipe_core_private *sipe_private); ================================================ FILE: src/core/sipe-cal.c ================================================ /** * @file sipe-cal.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-common.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-cal.h" #include "sipe-http.h" #include "sipe-nls.h" #include "sipe-ocs2005.h" #include "sipe-ocs2007.h" #include "sipe-schedule.h" #include "sipe-utils.h" #include "sipe-xml.h" /* Calendar backends */ #ifdef _WIN32 #include "sipe-domino.h" #endif #include "sipe-ews.h" #define TIME_NULL (time_t)-1 #define IS(time) (time != TIME_NULL) /* http://msdn.microsoft.com/en-us/library/aa565001.aspx 480 0 1 11 Sunday -60 2 3 Sunday Monday Tuesday Wednesday Thursday Friday 600 1140 Desc: int short short Sunday or Monday or Tuesday or Wednesday or Thursday or Friday or Saturday string */ struct sipe_cal_std_dst { int bias; /* Ex.: -60 */ gchar *time; /* hh:mm:ss, 02:00:00 */ int day_order; /* 1..5 */ int month; /* 1..12 */ gchar *day_of_week; /* Sunday or Monday or Tuesday or Wednesday or Thursday or Friday or Saturday */ gchar *year; /* YYYY */ time_t switch_time; }; struct sipe_cal_working_hours { int bias; /* Ex.: 480 */ struct sipe_cal_std_dst std; /* StandardTime */ struct sipe_cal_std_dst dst; /* DaylightTime */ gchar *days_of_week; /* Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday separated by space */ int start_time; /* 0...1440 */ int end_time; /* 0...1440 */ gchar *tz; /* aggregated timezone string as in TZ environment variable. Ex.: TST+8TDT+7,M3.2.0/02:00:00,M11.1.0/02:00:00 */ /** separate simple strings for Windows platform as the proper TZ does not work there. * anyway, dynamic timezones would't work with just TZ */ gchar *tz_std; /* Ex.: TST8 */ gchar *tz_dst; /* Ex.: TDT7 */ }; /* not for translation, a part of XML Schema definitions */ static const char *wday_names[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; static int sipe_cal_get_wday(char *wday_name) { int i; if (!wday_name) return -1; for (i = 0; i < 7; i++) { if (sipe_strequal(wday_names[i], wday_name)) { return i; } } return -1; } void sipe_cal_event_free(struct sipe_cal_event* cal_event) { if (!cal_event) return; g_free(cal_event->subject); g_free(cal_event->location); g_free(cal_event); } void sipe_cal_events_free(GSList *cal_events) { if (!cal_events) return; sipe_utils_slist_free_full(cal_events, (GDestroyNotify) sipe_cal_event_free); } void sipe_cal_calendar_free(struct sipe_calendar *cal) { if (cal) { g_free(cal->email); g_free(cal->legacy_dn); g_free(cal->as_url); g_free(cal->oof_url); g_free(cal->oab_url); g_free(cal->domino_url); g_free(cal->oof_state); g_free(cal->oof_note); g_free(cal->free_busy); g_free(cal->working_hours_xml_str); sipe_cal_events_free(cal->cal_events); if (cal->request) sipe_http_request_cancel(cal->request); sipe_http_session_close(cal->session); g_free(cal); } } void sipe_cal_calendar_init(struct sipe_core_private *sipe_private) { if (!sipe_private->calendar) { struct sipe_calendar *cal; const char *value; sipe_private->calendar = cal = g_new0(struct sipe_calendar, 1); cal->sipe_private = sipe_private; cal->email = g_strdup(sipe_private->email); /* user specified a service URL? */ value = sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_EMAIL_URL); if (!is_empty(value)) { cal->as_url = g_strdup(value); cal->oof_url = g_strdup(value); cal->domino_url = g_strdup(value); } } return; } void sipe_cal_event_debug(const struct sipe_cal_event *cal_event, const gchar *label) { GString* str = g_string_new(NULL); const gchar *status = ""; switch(cal_event->cal_status) { case SIPE_CAL_FREE: status = "SIPE_CAL_FREE"; break; case SIPE_CAL_TENTATIVE: status = "SIPE_CAL_TENTATIVE"; break; case SIPE_CAL_BUSY: status = "SIPE_CAL_BUSY"; break; case SIPE_CAL_OOF: status = "SIPE_CAL_OOF"; break; case SIPE_CAL_NO_DATA: status = "SIPE_CAL_NO_DATA"; break; } g_string_append_printf(str, "\tstart_time: %s\n", IS(cal_event->start_time) ? sipe_utils_time_to_debug_str(localtime(&cal_event->start_time)) : ""); g_string_append_printf(str, "\tend_time : %s\n", IS(cal_event->end_time) ? sipe_utils_time_to_debug_str(localtime(&cal_event->end_time)) : ""); g_string_append_printf(str, "\tcal_status: %s\n", status); g_string_append_printf(str, "\tsubject : %s\n", cal_event->subject ? cal_event->subject : ""); g_string_append_printf(str, "\tlocation : %s\n", cal_event->location ? cal_event->location : ""); /* last line must have no line break */ g_string_append_printf(str, "\tis_meeting: %s", cal_event->is_meeting ? "TRUE" : "FALSE"); SIPE_DEBUG_INFO("%s%s", label, str->str); g_string_free(str, TRUE); } char * sipe_cal_event_hash(struct sipe_cal_event* event) { /* no end_time as it dos not get published */ /* no cal_status as it can change on publication */ return g_strdup_printf("<%d><%s><%s><%d>", (int)event->start_time, event->subject ? event->subject : "", event->location ? event->location : "", event->is_meeting); } #define ENVIRONMENT_TIMEZONE "TZ" static gchar * sipe_switch_tz(const char *tz) { gchar *tz_orig; tz_orig = g_strdup(g_getenv(ENVIRONMENT_TIMEZONE)); g_setenv(ENVIRONMENT_TIMEZONE, tz, TRUE); tzset(); return(tz_orig); } static void sipe_reset_tz(gchar *tz_orig) { if (tz_orig) { g_setenv(ENVIRONMENT_TIMEZONE, tz_orig, TRUE); g_free(tz_orig); } else { g_unsetenv(ENVIRONMENT_TIMEZONE); } tzset(); } /** * Converts struct tm to Epoch time_t considering timezone. * * @param tz as defined for TZ environment variable. * * Reference: see timegm(3) - Linux man page */ time_t sipe_mktime_tz(struct tm *tm, const char* tz) { time_t ret; gchar *tz_orig; tz_orig = sipe_switch_tz(tz); ret = mktime(tm); sipe_reset_tz(tz_orig); return ret; } /** * Converts Epoch time_t to struct tm considering timezone. * * @param tz as defined for TZ environment variable. * * Reference: see timegm(3) - Linux man page */ static struct tm * sipe_localtime_tz(const time_t *time, const char* tz) { struct tm *ret; gchar *tz_orig; tz_orig = sipe_switch_tz(tz); ret = localtime(time); sipe_reset_tz(tz_orig); return ret; } void sipe_cal_free_working_hours(struct sipe_cal_working_hours *wh) { if (!wh) return; g_free(wh->std.time); g_free(wh->std.day_of_week); g_free(wh->std.year); g_free(wh->dst.time); g_free(wh->dst.day_of_week); g_free(wh->dst.year); g_free(wh->days_of_week); g_free(wh->tz); g_free(wh->tz_std); g_free(wh->tz_dst); g_free(wh); } /** * Returns time_t of daylight savings time start/end * in the provided timezone or otherwise * (time_t)-1 if no daylight savings time. */ static time_t sipe_cal_get_std_dst_time(time_t now, int bias, struct sipe_cal_std_dst* std_dst, struct sipe_cal_std_dst* dst_std) { struct tm switch_tm; time_t res = TIME_NULL; struct tm *gm_now_tm; gchar **time_arr; if (std_dst->month == 0) return TIME_NULL; gm_now_tm = gmtime(&now); time_arr = g_strsplit(std_dst->time, ":", 0); switch_tm.tm_sec = atoi(time_arr[2]); switch_tm.tm_min = atoi(time_arr[1]); switch_tm.tm_hour = atoi(time_arr[0]); g_strfreev(time_arr); switch_tm.tm_mday = std_dst->year ? std_dst->day_order : 1 /* to adjust later */ ; switch_tm.tm_mon = std_dst->month - 1; switch_tm.tm_year = std_dst->year ? atoi(std_dst->year) - 1900 : gm_now_tm->tm_year; switch_tm.tm_isdst = 0; /* to set tm_wday */ res = sipe_mktime_tz(&switch_tm, "UTC"); /* if not dynamic, calculate right tm_mday */ if (!std_dst->year) { int switch_wday = sipe_cal_get_wday(std_dst->day_of_week); int needed_month; /* get first desired wday in the month */ int delta = switch_wday >= switch_tm.tm_wday ? (switch_wday - switch_tm.tm_wday) : (switch_wday + 7 - switch_tm.tm_wday); switch_tm.tm_mday = 1 + delta; /* try nth order */ switch_tm.tm_mday += (std_dst->day_order - 1) * 7; needed_month = switch_tm.tm_mon; /* to set settle date if ahead of allowed month dates */ res = sipe_mktime_tz(&switch_tm, "UTC"); if (needed_month != switch_tm.tm_mon) { /* moving 1 week back to stay within required month */ switch_tm.tm_mday -= 7; /* to fix date again */ res = sipe_mktime_tz(&switch_tm, "UTC"); } } /* note: bias is taken from "switch to" structure */ return res + (bias + dst_std->bias)*60; } static void sipe_cal_parse_std_dst(const sipe_xml *xn_std_dst_time, struct sipe_cal_std_dst *std_dst) { const sipe_xml *node; gchar *tmp; if (!xn_std_dst_time) return; if (!std_dst) return; /* 0 1 11 2009 Sunday */ if ((node = sipe_xml_child(xn_std_dst_time, "Bias"))) { std_dst->bias = atoi(tmp = sipe_xml_data(node)); g_free(tmp); } if ((node = sipe_xml_child(xn_std_dst_time, "Time"))) { std_dst->time = sipe_xml_data(node); } if ((node = sipe_xml_child(xn_std_dst_time, "DayOrder"))) { std_dst->day_order = atoi(tmp = sipe_xml_data(node)); g_free(tmp); } if ((node = sipe_xml_child(xn_std_dst_time, "Month"))) { std_dst->month = atoi(tmp = sipe_xml_data(node)); g_free(tmp); } if ((node = sipe_xml_child(xn_std_dst_time, "DayOfWeek"))) { std_dst->day_of_week = sipe_xml_data(node); } if ((node = sipe_xml_child(xn_std_dst_time, "Year"))) { std_dst->year = sipe_xml_data(node); } } void sipe_cal_parse_working_hours(const sipe_xml *xn_working_hours, struct sipe_buddy *buddy) { const sipe_xml *xn_bias; const sipe_xml *xn_timezone; const sipe_xml *xn_working_period; const sipe_xml *xn_standard_time; const sipe_xml *xn_daylight_time; gchar *tmp; time_t now = time(NULL); struct sipe_cal_std_dst* std; struct sipe_cal_std_dst* dst; if (!xn_working_hours) return; /* 480 ... Monday Tuesday Wednesday Thursday Friday 600 1140 */ sipe_cal_free_working_hours(buddy->cal_working_hours); buddy->cal_working_hours = g_new0(struct sipe_cal_working_hours, 1); xn_timezone = sipe_xml_child(xn_working_hours, "TimeZone"); xn_bias = sipe_xml_child(xn_timezone, "Bias"); if (xn_bias) { buddy->cal_working_hours->bias = atoi(tmp = sipe_xml_data(xn_bias)); g_free(tmp); } xn_standard_time = sipe_xml_child(xn_timezone, "StandardTime"); xn_daylight_time = sipe_xml_child(xn_timezone, "DaylightTime"); std = &((*buddy->cal_working_hours).std); dst = &((*buddy->cal_working_hours).dst); sipe_cal_parse_std_dst(xn_standard_time, std); sipe_cal_parse_std_dst(xn_daylight_time, dst); xn_working_period = sipe_xml_child(xn_working_hours, "WorkingPeriodArray/WorkingPeriod"); if (xn_working_period) { /* NOTE: this can be NULL! */ buddy->cal_working_hours->days_of_week = sipe_xml_data(sipe_xml_child(xn_working_period, "DayOfWeek")); buddy->cal_working_hours->start_time = atoi(tmp = sipe_xml_data(sipe_xml_child(xn_working_period, "StartTimeInMinutes"))); g_free(tmp); buddy->cal_working_hours->end_time = atoi(tmp = sipe_xml_data(sipe_xml_child(xn_working_period, "EndTimeInMinutes"))); g_free(tmp); } std->switch_time = sipe_cal_get_std_dst_time(now, buddy->cal_working_hours->bias, std, dst); dst->switch_time = sipe_cal_get_std_dst_time(now, buddy->cal_working_hours->bias, dst, std); /* TST8TDT7,M3.2.0/02:00:00,M11.1.0/02:00:00 */ buddy->cal_working_hours->tz = g_strdup_printf("TST%dTDT%d,M%d.%d.%d/%s,M%d.%d.%d/%s", (buddy->cal_working_hours->bias + buddy->cal_working_hours->std.bias) / 60, (buddy->cal_working_hours->bias + buddy->cal_working_hours->dst.bias) / 60, buddy->cal_working_hours->dst.month, buddy->cal_working_hours->dst.day_order, sipe_cal_get_wday(buddy->cal_working_hours->dst.day_of_week), buddy->cal_working_hours->dst.time, buddy->cal_working_hours->std.month, buddy->cal_working_hours->std.day_order, sipe_cal_get_wday(buddy->cal_working_hours->std.day_of_week), buddy->cal_working_hours->std.time ); /* TST8 */ buddy->cal_working_hours->tz_std = g_strdup_printf("TST%d", (buddy->cal_working_hours->bias + buddy->cal_working_hours->std.bias) / 60); /* TDT7 */ buddy->cal_working_hours->tz_dst = g_strdup_printf("TDT%d", (buddy->cal_working_hours->bias + buddy->cal_working_hours->dst.bias) / 60); } struct sipe_cal_event* sipe_cal_get_event(GSList *cal_events, time_t time_in_question) { GSList *entry = cal_events; struct sipe_cal_event* cal_event; struct sipe_cal_event* res = NULL; if (!cal_events || !IS(time_in_question)) return NULL; while (entry) { cal_event = entry->data; /* event is in the past or in the future */ if (cal_event->start_time > time_in_question || cal_event->end_time <= time_in_question) { entry = entry->next; continue; } if (!res) { res = cal_event; } else { int res_status = (res->cal_status == SIPE_CAL_NO_DATA) ? -1 : res->cal_status; int cal_status = (cal_event->cal_status == SIPE_CAL_NO_DATA) ? -1 : cal_event->cal_status; if (res_status < cal_status) { res = cal_event; } } entry = entry->next; } return res; } static int sipe_cal_get_status0(const gchar *free_busy, time_t cal_start, int granularity, time_t time_in_question, int *index) { int res = SIPE_CAL_NO_DATA; int shift; time_t cal_end = cal_start + strlen(free_busy)*granularity*60 - 1; if (!(time_in_question >= cal_start && time_in_question <= cal_end)) return res; shift = (time_in_question - cal_start) / (granularity*60); if (index) { *index = shift; } res = free_busy[shift] - '0'; return res; } /** * Returns time when current calendar state started */ static time_t sipe_cal_get_since_time(const gchar *free_busy, time_t calStart, int granularity, int index, int current_state) { int i; if ((index < 0) || ((size_t)(index + 1) > strlen(free_busy))) return 0; for (i = index; i >= 0; i--) { int temp_status = free_busy[i] - '0'; if (current_state != temp_status) { return calStart + (i + 1)*granularity*60; } } return calStart; } static char* sipe_cal_get_free_busy(struct sipe_buddy *buddy); int sipe_cal_get_status(struct sipe_buddy *buddy, time_t time_in_question, time_t *since) { time_t cal_start; const char* free_busy; int ret = SIPE_CAL_NO_DATA; time_t state_since; int index = -1; if (!buddy || !buddy->cal_start_time || !buddy->cal_granularity) { SIPE_DEBUG_INFO("sipe_cal_get_status: no calendar data1 for %s, exiting", buddy ? (buddy->name ? buddy->name : "") : ""); return SIPE_CAL_NO_DATA; } if (!(free_busy = sipe_cal_get_free_busy(buddy))) { SIPE_DEBUG_INFO("sipe_cal_get_status: no calendar data2 for %s, exiting", buddy->name); return SIPE_CAL_NO_DATA; } SIPE_DEBUG_INFO("sipe_cal_get_description: buddy->cal_free_busy=\n%s", free_busy); cal_start = sipe_utils_str_to_time(buddy->cal_start_time); ret = sipe_cal_get_status0(free_busy, cal_start, buddy->cal_granularity, time_in_question, &index); state_since = sipe_cal_get_since_time(free_busy, cal_start, buddy->cal_granularity, index, ret); if (since) *since = state_since; return ret; } static time_t sipe_cal_get_switch_time(const gchar *free_busy, time_t calStart, int granularity, int index, int current_state, int *to_state) { size_t i; time_t ret = TIME_NULL; if ((index < 0) || ((size_t) (index + 1) > strlen(free_busy))) { *to_state = SIPE_CAL_NO_DATA; return ret; } for (i = index + 1; i < strlen(free_busy); i++) { int temp_status = free_busy[i] - '0'; if (current_state != temp_status) { *to_state = temp_status; return calStart + i*granularity*60; } } return ret; } static const char* sipe_cal_get_tz(struct sipe_cal_working_hours *wh, time_t time_in_question) { time_t dst_switch_time = (*wh).dst.switch_time; time_t std_switch_time = (*wh).std.switch_time; gboolean is_dst = FALSE; /* No daylight savings */ if (dst_switch_time == TIME_NULL) { return wh->tz_std; } if (dst_switch_time < std_switch_time) { /* North hemosphere - Europe, US */ if (time_in_question >= dst_switch_time && time_in_question < std_switch_time) { is_dst = TRUE; } } else { /* South hemisphere - Australia */ if (time_in_question >= dst_switch_time || time_in_question < std_switch_time) { is_dst = TRUE; } } if (is_dst) { return wh->tz_dst; } else { return wh->tz_std; } } static time_t sipe_cal_mktime_of_day(struct tm *sample_today_tm, const int shift_minutes, const char *tz) { sample_today_tm->tm_sec = 0; sample_today_tm->tm_min = shift_minutes % 60; sample_today_tm->tm_hour = shift_minutes / 60; return sipe_mktime_tz(sample_today_tm, tz); } /** * Returns work day start and end in Epoch time * considering the initial values are provided * in contact's local time zone. */ static void sipe_cal_get_today_work_hours(struct sipe_cal_working_hours *wh, time_t *start, time_t *end, time_t *next_start) { time_t now = time(NULL); const char *tz = sipe_cal_get_tz(wh, now); struct tm *remote_now_tm = sipe_localtime_tz(&now, tz); if (!(wh->days_of_week && strstr(wh->days_of_week, wday_names[remote_now_tm->tm_wday]))) { /* not a work day */ *start = TIME_NULL; *end = TIME_NULL; *next_start = TIME_NULL; return; } *end = sipe_cal_mktime_of_day(remote_now_tm, wh->end_time, tz); if (now < *end) { *start = sipe_cal_mktime_of_day(remote_now_tm, wh->start_time, tz); *next_start = TIME_NULL; } else { /* calculate start of tomorrow's work day if any */ time_t tom = now + 24*60*60; struct tm *remote_tom_tm = sipe_localtime_tz(&tom, sipe_cal_get_tz(wh, tom)); if (!(wh->days_of_week && strstr(wh->days_of_week, wday_names[remote_tom_tm->tm_wday]))) { /* not a work day */ *next_start = TIME_NULL; } *next_start = sipe_cal_mktime_of_day(remote_tom_tm, wh->start_time, sipe_cal_get_tz(wh, tom)); *start = TIME_NULL; } } static int sipe_cal_is_in_work_hours(const time_t time_in_question, const time_t start, const time_t end) { return !((time_in_question >= end) || (IS(start) && time_in_question < start)); } /** * Returns time closest to now. Choses only from times ahead of now. * Returns TIME_NULL otherwise. */ static time_t sipe_cal_get_until(const time_t now, const time_t switch_time, const time_t start, const time_t end, const time_t next_start) { time_t ret = TIME_NULL; int min_diff = now - ret; if (IS(switch_time) && switch_time > now && (switch_time - now) < min_diff) { min_diff = switch_time - now; ret = switch_time; } if (IS(start) && start > now && (start - now) < min_diff) { min_diff = start - now; ret = start; } if (IS(end) && end > now && (end - now) < min_diff) { min_diff = end - now; ret = end; } if (IS(next_start) && next_start > now && (next_start - now) < min_diff) { min_diff = next_start - now; ret = next_start; } return ret; } static char* sipe_cal_get_free_busy(struct sipe_buddy *buddy) { /* do lazy decode if necessary */ if (!buddy->cal_free_busy && buddy->cal_free_busy_base64) { gsize cal_dec64_len; guchar *cal_dec64; gsize i; int j = 0; cal_dec64 = g_base64_decode(buddy->cal_free_busy_base64, &cal_dec64_len); buddy->cal_free_busy = g_malloc0(cal_dec64_len * 4 + 1); /* http://msdn.microsoft.com/en-us/library/dd941537%28office.13%29.aspx 00, Free (Fr) 01, Tentative (Te) 10, Busy (Bu) 11, Out of facility (Oo) http://msdn.microsoft.com/en-us/library/aa566048.aspx 0 Free 1 Tentative 2 Busy 3 Out of Office (OOF) 4 No data */ for (i = 0; i < cal_dec64_len; i++) { #define TWO_BIT_MASK 0x03 char tmp = cal_dec64[i]; buddy->cal_free_busy[j++] = (tmp & TWO_BIT_MASK) + '0'; buddy->cal_free_busy[j++] = ((tmp >> 2) & TWO_BIT_MASK) + '0'; buddy->cal_free_busy[j++] = ((tmp >> 4) & TWO_BIT_MASK) + '0'; buddy->cal_free_busy[j++] = ((tmp >> 6) & TWO_BIT_MASK) + '0'; } buddy->cal_free_busy[j++] = '\0'; g_free(cal_dec64); } return buddy->cal_free_busy; } char * sipe_cal_get_freebusy_base64(const char* freebusy_hex) { guint i = 0; guint j = 0; guint shift_factor = 0; guint len, res_len; guchar *res; gchar *res_base64; if (!freebusy_hex) return NULL; len = strlen(freebusy_hex); res_len = len / 4 + 1; res = g_malloc0(res_len); while (i < len) { res[j] |= (freebusy_hex[i++] - '0') << shift_factor; shift_factor += 2; if (shift_factor == 8) { shift_factor = 0; j++; } } res_base64 = g_base64_encode(res, shift_factor ? res_len : res_len - 1); g_free(res); return res_base64; } char * sipe_cal_get_description(struct sipe_buddy *buddy) { time_t cal_start; time_t cal_end; int current_cal_state; time_t now = time(NULL); time_t start = TIME_NULL; time_t end = TIME_NULL; time_t next_start = TIME_NULL; time_t switch_time; int to_state = SIPE_CAL_NO_DATA; time_t until = TIME_NULL; int index = 0; gboolean has_working_hours = (buddy->cal_working_hours != NULL); const char *free_busy; const char *cal_states[] = {_("Free"), _("Tentative"), _("Busy"), _("Out of office"), _("No data")}; if (buddy->cal_granularity != 15) { SIPE_DEBUG_INFO("sipe_cal_get_description: granularity %d is unsupported, exiting.", buddy->cal_granularity); return NULL; } /* to lazy load if needed */ free_busy = sipe_cal_get_free_busy(buddy); SIPE_DEBUG_INFO("sipe_cal_get_description: buddy->cal_free_busy=\n%s", free_busy ? free_busy : ""); if (!buddy->cal_free_busy || !buddy->cal_granularity || !buddy->cal_start_time) { SIPE_DEBUG_INFO_NOFORMAT("sipe_cal_get_description: no calendar data, exiting"); return NULL; } cal_start = sipe_utils_str_to_time(buddy->cal_start_time); cal_end = cal_start + 60 * (buddy->cal_granularity) * strlen(buddy->cal_free_busy); current_cal_state = sipe_cal_get_status0(free_busy, cal_start, buddy->cal_granularity, time(NULL), &index); if (current_cal_state == SIPE_CAL_NO_DATA) { SIPE_DEBUG_INFO_NOFORMAT("sipe_cal_get_description: calendar is undefined for present moment, exiting."); return NULL; } switch_time = sipe_cal_get_switch_time(free_busy, cal_start, buddy->cal_granularity, index, current_cal_state, &to_state); SIPE_DEBUG_INFO_NOFORMAT("\n* Calendar *"); if (buddy->cal_working_hours) { sipe_cal_get_today_work_hours(buddy->cal_working_hours, &start, &end, &next_start); SIPE_DEBUG_INFO("Remote now timezone : %s", sipe_cal_get_tz(buddy->cal_working_hours, now)); SIPE_DEBUG_INFO("std.switch_time(GMT): %s", IS((*buddy->cal_working_hours).std.switch_time) ? sipe_utils_time_to_debug_str(gmtime(&((*buddy->cal_working_hours).std.switch_time))) : ""); SIPE_DEBUG_INFO("dst.switch_time(GMT): %s", IS((*buddy->cal_working_hours).dst.switch_time) ? sipe_utils_time_to_debug_str(gmtime(&((*buddy->cal_working_hours).dst.switch_time))) : ""); SIPE_DEBUG_INFO("Remote now time : %s", sipe_utils_time_to_debug_str(sipe_localtime_tz(&now, sipe_cal_get_tz(buddy->cal_working_hours, now)))); SIPE_DEBUG_INFO("Remote start time : %s", IS(start) ? sipe_utils_time_to_debug_str(sipe_localtime_tz(&start, sipe_cal_get_tz(buddy->cal_working_hours, start))) : ""); SIPE_DEBUG_INFO("Remote end time : %s", IS(end) ? sipe_utils_time_to_debug_str(sipe_localtime_tz(&end, sipe_cal_get_tz(buddy->cal_working_hours, end))) : ""); SIPE_DEBUG_INFO("Rem. next_start time: %s", IS(next_start) ? sipe_utils_time_to_debug_str(sipe_localtime_tz(&next_start, sipe_cal_get_tz(buddy->cal_working_hours, next_start))) : ""); SIPE_DEBUG_INFO("Remote switch time : %s", IS(switch_time) ? sipe_utils_time_to_debug_str(sipe_localtime_tz(&switch_time, sipe_cal_get_tz(buddy->cal_working_hours, switch_time))) : ""); } else { SIPE_DEBUG_INFO("Local now time : %s", sipe_utils_time_to_debug_str(localtime(&now))); SIPE_DEBUG_INFO("Local switch time : %s", IS(switch_time) ? sipe_utils_time_to_debug_str(localtime(&switch_time)) : ""); } SIPE_DEBUG_INFO("Calendar End (GMT) : %s", sipe_utils_time_to_debug_str(gmtime(&cal_end))); SIPE_DEBUG_INFO("current cal state : %s", cal_states[current_cal_state]); SIPE_DEBUG_INFO("switch cal state : %s", cal_states[to_state] ); /* Calendar: string calculations */ /* ALGORITHM (don't delete) (c)2009,2010 pier11 SOD = Start of Work Day EOD = End of Work Day NSOD = Start of tomorrow's Work Day SW = Calendar status switch time if current_cal_state == Free until = min_t of SOD, EOD, NSOD, SW (min_t(x) = min(x-now) where x>now only) else until = SW if (!until && (cal_period_end > now + 8H)) until = cal_period_end if (!until) return "Currently %", current_cal_state if (until - now > 8H) if (current_cal_state == Free && (work_hours && !in work_hours(now))) return "Outside of working hours for next 8 hours" else return "%s for next 8 hours", current_cal_state if (current_cal_state == Free) if (work_hours && until !in work_hours(now)) "Not working" else "%s", current_cal_state " until %.2d:%.2d", until else "Currently %", current_cal_state if (work_hours && until !in work_hours(until)) ". Outside of working hours at at %.2d:%.2d", until else ". %s at %.2d:%.2d", to_state, until */ if (current_cal_state < 1) { /* Free */ until = sipe_cal_get_until(now, switch_time, start, end, next_start); } else { until = switch_time; } if (!IS(until) && (cal_end - now > 8*60*60)) until = cal_end; if (!IS(until)) { return g_strdup_printf(_("Currently %s"), cal_states[current_cal_state]); } if (until - now > 8*60*60) { /* Free & outside work hours */ if (current_cal_state < 1 && has_working_hours && !sipe_cal_is_in_work_hours(now, start, end)) { return g_strdup(_("Outside of working hours for next 8 hours")); } else { return g_strdup_printf(_("%s for next 8 hours"), cal_states[current_cal_state]); } } if (current_cal_state < 1) { /* Free */ const char *tmp; struct tm *until_tm = localtime(&until); if (has_working_hours && !sipe_cal_is_in_work_hours(now, start, end)) { tmp = _("Not working"); } else { tmp = cal_states[current_cal_state]; } return g_strdup_printf(_("%s until %.2d:%.2d"), tmp, until_tm->tm_hour, until_tm->tm_min); } else { /* Tentative or Busy or OOF */ char *tmp; char *res; struct tm *until_tm = localtime(&until); tmp = g_strdup_printf(_("Currently %s"), cal_states[current_cal_state]); if (has_working_hours && !sipe_cal_is_in_work_hours(until, start, end)) { res = g_strdup_printf(_("%s. Outside of working hours at %.2d:%.2d"), tmp, until_tm->tm_hour, until_tm->tm_min); g_free(tmp); return res; } else { res = g_strdup_printf(_("%s. %s at %.2d:%.2d"), tmp, cal_states[to_state], until_tm->tm_hour, until_tm->tm_min); g_free(tmp); return res; } } /* End of - Calendar: string calculations */ } #define UPDATE_CALENDAR_INTERVAL (15*60) /* 15 min, default granularity for Exchange */ #define UPDATE_CALENDAR_OFFSET 30 /* 30 seconds before next interval starts */ static void sipe_cal_update_cb(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER gpointer data) { sipe_core_update_calendar(SIPE_CORE_PUBLIC); } void sipe_core_update_calendar(struct sipe_core_public *sipe_public) { time_t now, offset; SIPE_LOG_INFO_NOFORMAT("sipe_core_update_calendar: started."); /* Do in parallel. * If failed, the branch will be disabled for subsequent calls. * Can't rely that user turned the functionality on in account settings. */ sipe_ews_update_calendar(SIPE_CORE_PRIVATE); #ifdef _WIN32 /* @TODO: UNIX integration missing */ sipe_domino_update_calendar(SIPE_CORE_PRIVATE); #endif /* how long, in seconds, until the next calendar interval starts? */ now = time(NULL); offset = (now / UPDATE_CALENDAR_INTERVAL + 1) * UPDATE_CALENDAR_INTERVAL - now; /* ensure that the update after the initial one is not too soon */ if (offset <= (UPDATE_CALENDAR_INTERVAL / 2)) offset += UPDATE_CALENDAR_INTERVAL; /* schedule next update before a new calendar interval starts */ sipe_schedule_seconds(SIPE_CORE_PRIVATE, "<+update-calendar>", NULL, offset - UPDATE_CALENDAR_OFFSET, sipe_cal_update_cb, NULL); SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: finished."); } void sipe_cal_presence_publish(struct sipe_core_private *sipe_private, gboolean do_publish_calendar) { if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) { if (do_publish_calendar) sipe_ocs2007_presence_publish(sipe_private, NULL); else sipe_ocs2007_category_publish(sipe_private, FALSE); } else { sipe_ocs2005_presence_publish(sipe_private, do_publish_calendar); } } void sipe_cal_delayed_calendar_update(struct sipe_core_private *sipe_private) { #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */ /* only start periodic calendar updating if user hasn't disabled it */ if (!SIPE_CORE_PUBLIC_FLAG_IS(DONT_PUBLISH)) sipe_schedule_seconds(sipe_private, "<+update-calendar>", NULL, UPDATE_CALENDAR_DELAY, sipe_cal_update_cb, NULL); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-cal.h ================================================ /** * @file sipe-cal.h * * pidgin-sipe * * Copyright (C) 2010-2015 SIPE Project * Copyright (C) 2009 pier11 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_buddy; struct sipe_core_private; struct sipe_http_request; struct sipe_http_session; struct _sipe_xml; /* Calendar statuses */ #define SIPE_CAL_FREE 0 #define SIPE_CAL_TENTATIVE 1 #define SIPE_CAL_BUSY 2 #define SIPE_CAL_OOF 3 #define SIPE_CAL_NO_DATA 4 /* Default granularity of FreeBusy data is 15 minutes */ #define SIPE_FREE_BUSY_GRANULARITY_SEC (15*60) /* FreeBusy stream duration in seconds. Defaults to 4 days */ #define SIPE_FREE_BUSY_PERIOD_SEC (4*(24*60*60)) struct sipe_cal_event { time_t start_time; time_t end_time; /* SIPE_CAL_* */ int cal_status; char *subject; char *location; int is_meeting; }; /** For extracting our Calendar information from * external sources like Exchange, Lotus Domino. */ struct sipe_calendar { struct sipe_core_private *sipe_private; int state; char *email; char *legacy_dn; int is_ews_disabled; int is_domino_disabled; int is_updated; gboolean retry; char *as_url; char *oof_url; char *oab_url; char *domino_url; char *oof_state; /* Enabled, Disabled, Scheduled */ char *oof_note; time_t oof_start; time_t oof_end; time_t updated; gboolean published; struct sipe_http_session *session; struct sipe_http_request *request; time_t fb_start; /* hex form */ char *free_busy; char *working_hours_xml_str; GSList *cal_events; }; void sipe_cal_event_free(struct sipe_cal_event* cal_event); void sipe_cal_events_free(GSList *cal_events); void sipe_cal_calendar_free(struct sipe_calendar *cal); void sipe_cal_calendar_init(struct sipe_core_private *sipe_private); /** * Returns hash of Calendar Event for comparison. * * Must be g_free()'d after use. */ char * sipe_cal_event_hash(struct sipe_cal_event* event); /** * Dump calendar event in human readable form to debug log. */ void sipe_cal_event_debug(const struct sipe_cal_event *cal_event, const gchar *label); /** * Converts struct tm to Epoch time_t considering timezone. * * @param tz as defined for TZ environment variable. * * Reference: see timegm(3) - Linux man page */ time_t sipe_mktime_tz(struct tm *tm, const char* tz); /** * Converts hex representation of freebusy string as * returned by Exchange Web Services to * condenced and base64 encoded form * * Must be g_free()'d after use. */ char * sipe_cal_get_freebusy_base64(const char* freebusy_hex); /** Contains buddy's working hours information */ struct sipe_cal_working_hours; /** * Parses Working Hours from passed XML piece * and creates/fills struct sipe_cal_working_hours in struct sipe_buddy */ void sipe_cal_parse_working_hours(const struct _sipe_xml *xn_working_hours, struct sipe_buddy *buddy); /** * Frees struct sipe_cal_working_hours */ void sipe_cal_free_working_hours(struct sipe_cal_working_hours *wh); /** * Returns user calendar information in text form. * Example: "Currently Busy. Free at 13:00" */ char * sipe_cal_get_description(struct sipe_buddy *buddy); /** * Returns calendar status SIPE_CAL_* at time specified. * Returns SIPE_CAL_NO_DATA if no calendar data availible. * * @param since (out) Returns beginning time of the status. */ int sipe_cal_get_status(struct sipe_buddy *buddy, time_t time_in_question, time_t *since); /** * Returns calendar event at time in question. * If conflict, takes last event in the following * priority order: OOF, Busy, Tentative. */ struct sipe_cal_event* sipe_cal_get_event(GSList *cal_events, time_t time_in_question); /** * Publish presence information */ void sipe_cal_presence_publish(struct sipe_core_private *sipe_private, gboolean do_publish_calendar); /** * Schedule calendar update */ void sipe_cal_delayed_calendar_update(struct sipe_core_private *sipe_private); ================================================ FILE: src/core/sipe-cert-crypto-nss.c ================================================ /** * @file sipe-cert-crypto-nss.c * * pidgin-sipe * * Copyright (C) 2011-2012 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * Certificate routines implementation based on NSS. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifdef HAVE_VALGRIND #include #endif /* * Work around a compiler error in NSS 3.13.x. Let's hope they fix it for * 3.14.x. See also: https://bugzilla.mozilla.org/show_bug.cgi?id=702090 */ #include "nss.h" #if (NSS_VMAJOR == 3) && (NSS_VMINOR == 13) #define __GNUC_MINOR __GNUC_MINOR__ #endif #include "cert.h" #include "cryptohi.h" #include "keyhi.h" #include "pk11pub.h" #include "sipe-backend.h" #include "sipe-cert-crypto.h" struct sipe_cert_crypto { SECKEYPrivateKey *private; SECKEYPublicKey *public; }; /* * This data structure is used in two different modes * * a) certificate generated by the server from our Certificate Request * * key_pair.private - reference to client private key, don't free! * key_pair.public - reference to client public key, don't free! * decoded - certificate as NSS data structure, must be freed * raw - certificate as DER encoded binary, must be freed * length - length of DER binary * * b) server certificate * * key_pair.private - NULL * key_pair.public - reference to server public key, must be freed! * decoded - certificate as NSS data structure, must be freed * raw - NULL * length - modulus length of server public key */ struct certificate_nss { struct sipe_cert_crypto key_pair; CERTCertificate *decoded; guchar *raw; gsize length; }; struct sipe_cert_crypto *sipe_cert_crypto_init(void) { PK11SlotInfo *slot = PK11_GetInternalKeySlot(); if (slot) { PK11RSAGenParams rsaParams; struct sipe_cert_crypto *scc = g_new0(struct sipe_cert_crypto, 1); /* RSA parameters - should those be configurable? */ #ifdef HAVE_VALGRIND /* * valgrind makes key pair generation extremely slow. At least * on my system it takes longer for the default key size than * the SIP server timeout and our next message will fail with * * Read error: Connection reset by peer (104) * * Let's reduce the key size when we detect valgrind. */ if (RUNNING_ON_VALGRIND) { rsaParams.keySizeInBits = 1024; SIPE_DEBUG_INFO("sipe_cert_crypto_init: running on valgrind, reducing RSA key size to %d bits", rsaParams.keySizeInBits); } else #endif rsaParams.keySizeInBits = 2048; rsaParams.pe = 65537; SIPE_DEBUG_INFO_NOFORMAT("sipe_cert_crypto_init: generate key pair, this might take a while..."); scc->private = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams, &scc->public, PR_FALSE, /* not permanent */ PR_TRUE, /* sensitive */ NULL); if (scc->private) { SIPE_DEBUG_INFO_NOFORMAT("sipe_cert_crypto_init: key pair generated"); PK11_FreeSlot(slot); return(scc); } SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_init: key generation failed"); g_free(scc); PK11_FreeSlot(slot); } return(NULL); } void sipe_cert_crypto_free(struct sipe_cert_crypto *scc) { if (scc) { if (scc->public) SECKEY_DestroyPublicKey(scc->public); if (scc->private) SECKEY_DestroyPrivateKey(scc->private); g_free(scc); } } static gchar *sign_cert_or_certreq(CERTCertificate *cert, CERTCertificateRequest *certreq, SECKEYPrivateKey *private) { PRArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); gchar *base64 = NULL; if (arena) { SECItem *encoding = SEC_ASN1EncodeItem(arena, NULL, cert ? (void *) cert : (void *) certreq, cert ? SEC_ASN1_GET(CERT_CertificateTemplate) : SEC_ASN1_GET(CERT_CertificateRequestTemplate)); if (encoding) { SECOidTag signtag = SEC_GetSignatureAlgorithmOidTag(private->keyType, SEC_OID_UNKNOWN); if (signtag != SEC_OID_UNKNOWN) { SECItem raw; if (!SEC_DerSignData(arena, &raw, encoding->data, encoding->len, private, signtag)) { SIPE_DEBUG_INFO_NOFORMAT("sign_cert_or_certreq: successfully signed"); base64 = g_base64_encode(raw.data, raw.len); } else { SIPE_DEBUG_ERROR_NOFORMAT("sign_cert_or_certreq: signing failed"); } } else { SIPE_DEBUG_ERROR_NOFORMAT("sign_cert_or_certreq: can't find signature algorithm"); } /* all memory allocated from "arena" SECITEM_FreeItem(encoding, PR_TRUE); */ } else { SIPE_DEBUG_ERROR_NOFORMAT("sign_cert_or_certreq: can't ASN.1 encode data"); } PORT_FreeArena(arena, PR_TRUE); } else { SIPE_DEBUG_ERROR_NOFORMAT("sign_cert_or_certreq: can't allocate memory"); } return(base64); } static CERTCertificateRequest *generate_request(struct sipe_cert_crypto *scc, const gchar *subject) { SECItem *pkd; CERTCertificateRequest *certreq = NULL; if (!scc || !subject) return(NULL); pkd = SECKEY_EncodeDERSubjectPublicKeyInfo(scc->public); if (pkd) { CERTSubjectPublicKeyInfo *spki = SECKEY_DecodeDERSubjectPublicKeyInfo(pkd); if (spki) { gchar *cn = g_strdup_printf("CN=%s", subject); CERTName *name = CERT_AsciiToName(cn); g_free(cn); if (name) { certreq = CERT_CreateCertificateRequest(name, spki, NULL); if (!certreq) { SIPE_DEBUG_ERROR_NOFORMAT("generate_request: certreq creation failed"); } CERT_DestroyName(name); } else { SIPE_DEBUG_ERROR_NOFORMAT("generate_request: subject name creation failed"); } SECKEY_DestroySubjectPublicKeyInfo(spki); } else { SIPE_DEBUG_ERROR_NOFORMAT("generate_request: DER decode public key info failed"); } SECITEM_FreeItem(pkd, PR_TRUE); } else { SIPE_DEBUG_ERROR_NOFORMAT("generate_request: DER encode failed"); } return(certreq); } gchar *sipe_cert_crypto_request(struct sipe_cert_crypto *scc, const gchar *subject) { gchar *base64 = NULL; CERTCertificateRequest *certreq = generate_request(scc, subject); if (certreq) { base64 = sign_cert_or_certreq(NULL, certreq, scc->private); CERT_DestroyCertificateRequest(certreq); } return(base64); } void sipe_cert_crypto_destroy(gpointer certificate) { struct certificate_nss *cn = certificate; if (cn) { /* imported server certificate - mode (b) */ if (!cn->raw && cn->key_pair.public) SECKEY_DestroyPublicKey(cn->key_pair.public); if (cn->decoded) CERT_DestroyCertificate(cn->decoded); g_free(cn->raw); g_free(cn); } } /* generates certificate_nss in mode (a) */ gpointer sipe_cert_crypto_decode(struct sipe_cert_crypto *scc, const gchar *base64) { struct certificate_nss *cn = g_new0(struct certificate_nss, 1); cn->raw = g_base64_decode(base64, &cn->length); cn->decoded = CERT_DecodeCertFromPackage((char *) cn->raw, cn->length); if (!cn->decoded) { sipe_cert_crypto_destroy(cn); return(NULL); } cn->key_pair = *scc; return(cn); } /* generates certificate_nss in mode (b) */ gpointer sipe_cert_crypto_import(const guchar *raw, gsize length) { struct certificate_nss *cn = g_new0(struct certificate_nss, 1); /* cn->raw not needed as this is a server certificate */ cn->decoded = CERT_DecodeCertFromPackage((char *) raw, length); if (!cn->decoded) { sipe_cert_crypto_destroy(cn); return(NULL); } cn->key_pair.public = CERT_ExtractPublicKey(cn->decoded); if (!cn->key_pair.public) { sipe_cert_crypto_destroy(cn); return(NULL); } cn->length = SECKEY_PublicKeyStrength(cn->key_pair.public); return(cn); } gboolean sipe_cert_crypto_valid(gpointer certificate, guint offset) { struct certificate_nss *cn = certificate; SECCertTimeValidity validity; if (!cn) return(FALSE); validity = CERT_CheckCertValidTimes(cn->decoded, /* PRTime unit is microseconds */ PR_Now() + offset * PR_USEC_PER_SEC, PR_FALSE); return((validity == secCertTimeValid) || /* * From certt.h: "validity could not be decoded from the * cert, most likely because it was NULL" * * Let's assume if the server sends us such a certificate * that it must be valid then... */ (validity == secCertTimeUndetermined)); } guint sipe_cert_crypto_expires(gpointer certificate) { struct certificate_nss *cn = certificate; PRTime now, notAfter; if (!cn || (CERT_GetCertTimes(cn->decoded, &now, /* can't be NULL */ ¬After) != SECSuccess)) return(0); /* Sanity check */ now = PR_Now(); if (notAfter < now) return(0); /* PRTime unit is microseconds */ return((notAfter - now) / PR_USEC_PER_SEC); } gsize sipe_cert_crypto_raw_length(gpointer certificate) { return(((struct certificate_nss *) certificate)->length); } const guchar *sipe_cert_crypto_raw(gpointer certificate) { return(((struct certificate_nss *) certificate)->raw); } gpointer sipe_cert_crypto_public_key(gpointer certificate) { return(((struct certificate_nss *) certificate)->key_pair.public); } gsize sipe_cert_crypto_modulus_length(gpointer certificate) { return(((struct certificate_nss *) certificate)->length); } gpointer sipe_cert_crypto_private_key(gpointer certificate) { return(((struct certificate_nss *) certificate)->key_pair.private); } /* Create test certificate for internal key pair (ONLY USE FOR TEST CODE!!!) */ gpointer sipe_cert_crypto_test_certificate(struct sipe_cert_crypto *scc) { CERTCertificateRequest *certreq = generate_request(scc, "test@test.com"); struct certificate_nss *cn = NULL; if (certreq) { /* self-signed */ CERTName *issuer = CERT_AsciiToName("CN=test@test.com"); if (issuer) { /* we really don't need this certificate for long... */ CERTValidity *validity = CERT_CreateValidity(PR_Now(), PR_Now() + 600 * PR_USEC_PER_SEC); if (validity) { CERTCertificate *certificate = CERT_CreateCertificate(1, issuer, validity, certreq); if (certificate) { SECOidTag signtag = SEC_GetSignatureAlgorithmOidTag(scc->private->keyType, SEC_OID_UNKNOWN); if ((signtag != SEC_OID_UNKNOWN) && (SECOID_SetAlgorithmID(certificate->arena, &certificate->signature, signtag, 0) == SECSuccess)) { gchar *base64 = sign_cert_or_certreq(certificate, NULL, scc->private); if (base64) { cn = sipe_cert_crypto_decode(scc, base64); if (!cn) { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: certificate decode failed"); } g_free(base64); } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: certificate signing failed"); } } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: setting certificate signature algorithm ID failed"); } CERT_DestroyCertificate(certificate); } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: certificate creation failed"); } CERT_DestroyValidity(validity); } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: validity creation failed"); } CERT_DestroyName(issuer); } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: issuer name creation failed"); } CERT_DestroyCertificateRequest(certreq); } return(cn); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-cert-crypto-openssl.c ================================================ /** * @file sipe-cert-crypto-openssl.c * * pidgin-sipe * * Copyright (C) 2013-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * Certificate routines implementation based on OpenSSL. */ #include #include #include #include #include #include #include "sipe-backend.h" #include "sipe-cert-crypto.h" struct sipe_cert_crypto { RSA *key; }; /* * This data structure is used in two different modes * * a) certificate generated by the server from our Certificate Request * * key - reference to client RSA key, don't free! * decoded - certificate as OpenSSL data structure, must be freed * raw - certificate as DER encoded binary, must be freed * length - length of DER binary * * b) server certificate * * key - reference to server public key, must be freed * decoded - certificate as OpenSSL data structure, must be freed * raw - NULL * length - modulus length of server public key */ struct certificate_openssl { RSA *key; EVP_PKEY *public; X509 *decoded; guchar *raw; gsize length; }; struct sipe_cert_crypto *sipe_cert_crypto_init(void) { struct sipe_cert_crypto *scc = g_new0(struct sipe_cert_crypto, 1); /* allocate memory for RSA key */ scc->key = RSA_new(); if (scc->key) { BIGNUM *e = BN_new(); if (e) { /* RSA parameters - should those be configurable? */ if (BN_set_word(e, RSA_F4)) { SIPE_DEBUG_INFO_NOFORMAT("sipe_cert_crypto_init: generate key pair, this might take a while..."); if (RSA_generate_key_ex(scc->key, 2048, e, NULL)) { SIPE_DEBUG_INFO_NOFORMAT("sipe_cert_crypto_init: key pair generated"); BN_free(e); return(scc); } else SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_init: key generation failed"); } else SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_init: big number initialization failed"); BN_free(e); } else SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_init: memory allocation for big number failed"); } else SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_init: memory allocation for RSA key failed"); sipe_cert_crypto_free(scc); return(NULL); } void sipe_cert_crypto_free(struct sipe_cert_crypto *scc) { if (scc) { if (scc->key) RSA_free(scc->key); g_free(scc); } } gchar *sipe_cert_crypto_request(struct sipe_cert_crypto *scc, const gchar *subject) { gchar *base64 = NULL; EVP_PKEY *pkey; if (!scc || !subject) return(NULL); if ((pkey = EVP_PKEY_new()) != NULL) { X509_REQ *x509_req; if ((x509_req = X509_REQ_new()) != NULL) { X509_NAME *name; EVP_PKEY_set1_RSA(pkey, scc->key); X509_REQ_set_version(x509_req, 2); X509_REQ_set_pubkey(x509_req, pkey); name = X509_REQ_get_subject_name(x509_req); X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (guchar *) subject, -1, -1, 0); if (X509_REQ_sign(x509_req, pkey, EVP_sha1())) { gsize length; guchar *buf, *tmp; /* * Encode into DER format * * NOTE: i2d_X509(a, b) autoincrements b! */ length = i2d_X509_REQ(x509_req, NULL); tmp = buf = g_malloc(length); i2d_X509_REQ(x509_req, &tmp); base64 = g_base64_encode(buf, length); g_free(buf); } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_request: can't sign certificate request"); } X509_REQ_free(x509_req); } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_request: can't create x509 request data structure"); } EVP_PKEY_free(pkey); } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_request: can't create private key data structure"); } return(base64); } void sipe_cert_crypto_destroy(gpointer certificate) { struct certificate_openssl *co = certificate; if (co) { /* imported server certificate - mode (b) */ if (!co->raw && co->key) RSA_free(co->key); if (co->decoded) X509_free(co->decoded); g_free(co->raw); g_free(co); } } /* generates certificate_openssl in mode (a) */ gpointer sipe_cert_crypto_decode(struct sipe_cert_crypto *scc, const gchar *base64) { struct certificate_openssl *co = g_new0(struct certificate_openssl, 1); const guchar *tmp; /* NOTE: d2i_X509(NULL, &in, len) autoincrements "in" */ tmp = co->raw = g_base64_decode(base64, &co->length); co->decoded = d2i_X509(NULL, &tmp, co->length); if (!co->decoded) { sipe_cert_crypto_destroy(co); return(NULL); } co->key = scc->key; return(co); } /* generates certificate_openssl in mode (b) */ gpointer sipe_cert_crypto_import(const guchar *raw, gsize length) { struct certificate_openssl *co = g_new0(struct certificate_openssl, 1); EVP_PKEY *pkey; /* co->raw not needed as this is a server certificate */ /* NOTE: d2i_X509(NULL, in, len) autoincrements "in" */ co->decoded = d2i_X509(NULL, &raw, length); if (!co->decoded) { sipe_cert_crypto_destroy(co); return(NULL); } pkey = X509_get_pubkey(co->decoded); if (!pkey) { sipe_cert_crypto_destroy(co); return(NULL); } co->key = EVP_PKEY_get1_RSA(pkey); co->length = EVP_PKEY_size(pkey); EVP_PKEY_free(pkey); if (!co->key) { sipe_cert_crypto_destroy(co); return(NULL); } return(co); } gboolean sipe_cert_crypto_valid(gpointer certificate, guint offset) { struct certificate_openssl *co = certificate; time_t compare = time(NULL) + offset; return(co && (X509_cmp_time(X509_get_notAfter(co->decoded), &compare) > 0)); } guint sipe_cert_crypto_expires(gpointer certificate) { struct certificate_openssl *co = certificate; guint min; guint max; /* make sure certificate hasn't expired already */ if (!sipe_cert_crypto_valid(co, 0)) return(0); /* * I can't believe this, but it's true... * * OpenSSL doesn't have a public API to convert an ASN1_TIME * to seconds since epoch :-( * * @TODO: latest OpenSSL API has ASN1_TIME_diff() * * <30000 seconds (~8 hours) seems to be the most common expiration * value. Run a bisect to determine the real expiration value. */ min = 0; max = 30000; while (1) { guint offset = (max - min) / 2 + min; if (offset == min) { break; } else if (sipe_cert_crypto_valid(co, offset)) { min = offset; } else { max = offset; } } return(min); } gsize sipe_cert_crypto_raw_length(gpointer certificate) { return(((struct certificate_openssl *) certificate)->length); } const guchar *sipe_cert_crypto_raw(gpointer certificate) { return(((struct certificate_openssl *) certificate)->raw); } gpointer sipe_cert_crypto_public_key(gpointer certificate) { return(((struct certificate_openssl *) certificate)->key); } gsize sipe_cert_crypto_modulus_length(gpointer certificate) { return(((struct certificate_openssl *) certificate)->length); } gpointer sipe_cert_crypto_private_key(gpointer certificate) { return(((struct certificate_openssl *) certificate)->key); } /* Create test certificate for internal key pair (ONLY USE FOR TEST CODE!!!) */ gpointer sipe_cert_crypto_test_certificate(struct sipe_cert_crypto *scc) { struct certificate_openssl *co = NULL; EVP_PKEY *pkey; if ((pkey = EVP_PKEY_new()) != NULL) { X509 *x509; if ((x509 = X509_new()) != NULL) { X509_NAME *name; EVP_PKEY_set1_RSA(pkey, scc->key); X509_set_version(x509, 2); ASN1_INTEGER_set(X509_get_serialNumber(x509), 0); X509_gmtime_adj(X509_get_notBefore(x509), 0); X509_gmtime_adj(X509_get_notAfter(x509), (long) 60*60*24); X509_set_pubkey(x509, pkey); name = X509_get_subject_name(x509); X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (guchar *) "test@test.com", -1, -1, 0); X509_set_issuer_name(x509, name); if (X509_sign(x509, pkey, EVP_sha1())) { guchar *buf; co = g_new0(struct certificate_openssl, 1); co->decoded = x509; co->key = scc->key; /* * Encode into DER format * * NOTE: i2d_X509(a, b) autoincrements b! */ co->length = i2d_X509(x509, NULL); co->raw = buf = g_malloc(co->length); i2d_X509(x509, &buf); } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: can't sign certificate"); X509_free(x509); } } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: can't create x509 data structure"); } EVP_PKEY_free(pkey); } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: can't create private key data structure"); } return(co); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-cert-crypto.h ================================================ /** * @file sipe-cert-crypto.h * * pidgin-sipe * * Copyright (C) 2011-12 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Internal interface between sipe-certificate/sipe-tls * and the certificate crypto implementation */ /* * Interface dependencies: * * */ /* Forward declarations */ struct sipe_cert_crypto; /** * Free certificate crypto backend data * * @return opaque pointer to backend private data */ struct sipe_cert_crypto *sipe_cert_crypto_init(void); /** * Free certificate crypto backend data * * @param scc opaque pointer to backend private data */ void sipe_cert_crypto_free(struct sipe_cert_crypto *scc); /** * Create a certificate request as Base64 encoded string * * @param scc opaque pointer to backend private data * @param subject subject for certificate request * * @return Base64 encoded string. Must be @g_free'd() */ gchar *sipe_cert_crypto_request(struct sipe_cert_crypto *scc, const gchar *subject); /** * Destroy certificate (this is a @GDestroyNotify) * * @param certificate opaque pointer to backend certificate structure * May be @c NULL */ void sipe_cert_crypto_destroy(gpointer certificate); /** * Decode a client certificate from Base64 string * * @param base64 Base64 encoded DER data * * @return opaque pointer to certificate. Must be @sipe_cert_crypto_destroy()'d. */ gpointer sipe_cert_crypto_decode(struct sipe_cert_crypto *scc, const gchar *base64); /** * Import a server certificate from DER data * * @param raw DER data * @param length length of DER data * * @return opaque pointer to certificate. Must be @sipe_cert_crypto_destroy()'d. */ gpointer sipe_cert_crypto_import(const guchar *raw, gsize length); /** * Check if certificate is valid until @c offset seconds from now * * @param certificate opaque pointer to backend certificate structure * @param offset seconds from now * * @return @c TRUE if certificate is still valid at that time */ gboolean sipe_cert_crypto_valid(gpointer certificate, guint offset); /** * Return how many seconds until the certificate expires * * @param certificate opaque pointer to backend certificate structure * * @return offset in seconds */ guint sipe_cert_crypto_expires(gpointer certificate); /** * Return length of certificate in DER form * * @param certificate opaque pointer to backend certificate structure * * @return length in bytes */ gsize sipe_cert_crypto_raw_length(gpointer certificate); /** * Return certificate in DER form * * @param certificate opaque pointer to backend certificate structure * * @return pointer to DER data */ const guchar *sipe_cert_crypto_raw(gpointer certificate); /** * Get public key for certificate * * @param certificate opaque pointer to backend certificate structure * * @return opaque pointer to backend public key structure */ gpointer sipe_cert_crypto_public_key(gpointer certificate); /** * Get public key modulus length for server certificate * * @param certificate opaque pointer to backend certificate structure * * @return server public key strength */ gsize sipe_cert_crypto_modulus_length(gpointer certificate); /** * Get private key for client certificate * * @param certificate opaque pointer to backend certificate structure * * @return opaque pointer to backend private key structure */ gpointer sipe_cert_crypto_private_key(gpointer certificate); /** * Create test certificate for internal key pair (ONLY USE FOR TEST CODE!!!) * * @param scc opaque pointer to backend private data * * @return opaque pointer to backend certificate structure */ gpointer sipe_cert_crypto_test_certificate(struct sipe_cert_crypto *scc); ================================================ FILE: src/core/sipe-certificate.c ================================================ /** * @file sipe-certificate.c * * pidgin-sipe * * Copyright (C) 2011-2016 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Specification references: * * - [MS-SIPAE]: http://msdn.microsoft.com/en-us/library/cc431510.aspx * - [MS-OCAUTHWS]: http://msdn.microsoft.com/en-us/library/ff595592.aspx * - MS Tech-Ed Europe 2010 "UNC310: Microsoft Lync 2010 Technology Explained" * http://ecn.channel9.msdn.com/o9/te/Europe/2010/pptx/unc310.pptx */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "sipe-common.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-certificate.h" #include "sipe-cert-crypto.h" #include "sipe-nls.h" #include "sipe-svc.h" #include "sipe-webticket.h" #include "sipe-xml.h" struct sipe_certificate { GHashTable *certificates; struct sipe_cert_crypto *backend; }; struct certificate_callback_data { gchar *target; struct sipe_svc_session *session; }; static void callback_data_free(struct certificate_callback_data *ccd) { if (ccd) { sipe_svc_session_close(ccd->session); g_free(ccd->target); g_free(ccd); } } void sipe_certificate_free(struct sipe_core_private *sipe_private) { struct sipe_certificate *sc = sipe_private->certificate; if (sc) { g_hash_table_destroy(sc->certificates); sipe_cert_crypto_free(sc->backend); g_free(sc); } } gboolean sipe_certificate_init(struct sipe_core_private *sipe_private) { struct sipe_certificate *sc; struct sipe_cert_crypto *ssc; if (sipe_private->certificate) return(TRUE); ssc = sipe_cert_crypto_init(); if (!ssc) { SIPE_DEBUG_ERROR_NOFORMAT("sipe_certificate_init: crypto backend init FAILED!"); return(FALSE); } sc = g_new0(struct sipe_certificate, 1); sc->certificates = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, sipe_cert_crypto_destroy); sc->backend = ssc; SIPE_DEBUG_INFO_NOFORMAT("sipe_certificate_init: DONE"); sipe_private->certificate = sc; return(TRUE); } static gchar *create_certreq(struct sipe_core_private *sipe_private, const gchar *subject) { gchar *base64; if (!sipe_certificate_init(sipe_private)) return(NULL); SIPE_DEBUG_INFO_NOFORMAT("create_req: generating new certificate request"); base64 = sipe_cert_crypto_request(sipe_private->certificate->backend, subject); if (base64) { GString *format = g_string_new(NULL); gsize count = strlen(base64); const gchar *p = base64; /* Base64 needs to be formated correctly */ #define CERTREQ_BASE64_LINE_LENGTH 76 while (count > 0) { gsize chunk = count > CERTREQ_BASE64_LINE_LENGTH ? CERTREQ_BASE64_LINE_LENGTH : count; g_string_append_len(format, p, chunk); if (chunk == CERTREQ_BASE64_LINE_LENGTH) g_string_append(format, "\r\n"); count -= chunk; p += chunk; } /* swap Base64 buffers */ g_free(base64); base64 = format->str; g_string_free(format, FALSE); } return(base64); } static void add_certificate(struct sipe_core_private *sipe_private, const gchar *target, gpointer certificate) { struct sipe_certificate *sc = sipe_private->certificate; g_hash_table_insert(sc->certificates, g_strdup(target), certificate); } gpointer sipe_certificate_tls_dsk_find(struct sipe_core_private *sipe_private, const gchar *target) { struct sipe_certificate *sc = sipe_private->certificate; gpointer certificate; if (!target || !sc) return(NULL); certificate = g_hash_table_lookup(sc->certificates, target); /* Let's make sure the certificate is still valid for another hour */ if (!sipe_cert_crypto_valid(certificate, 60 * 60)) { SIPE_DEBUG_ERROR("sipe_certificate_tls_dsk_find: certificate for '%s' is invalid", target); return(NULL); } return(certificate); } static void certificate_failure(struct sipe_core_private *sipe_private, const gchar *format, const gchar *parameter, const gchar *failure_info) { gchar *tmp = g_strdup_printf(format, parameter); if (failure_info) { gchar *tmp2 = g_strdup_printf("%s\n(%s)", tmp, failure_info); g_free(tmp); tmp = tmp2; } sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED, tmp); g_free(tmp); } static void get_and_publish_cert(struct sipe_core_private *sipe_private, const gchar *uri, SIPE_UNUSED_PARAMETER const gchar *raw, sipe_xml *soap_body, gpointer callback_data) { struct certificate_callback_data *ccd = callback_data; gboolean success = (uri == NULL); /* abort case */ if (soap_body) { gchar *cert_base64 = sipe_xml_data(sipe_xml_child(soap_body, "Body/GetAndPublishCertResponse/RequestSecurityTokenResponse/RequestedSecurityToken/BinarySecurityToken")); SIPE_DEBUG_INFO("get_and_publish_cert: received valid SOAP message from service %s", uri); if (cert_base64) { gpointer opaque = sipe_cert_crypto_decode(sipe_private->certificate->backend, cert_base64); SIPE_DEBUG_INFO_NOFORMAT("get_and_publish_cert: found certificate"); if (opaque) { add_certificate(sipe_private, ccd->target, opaque); SIPE_DEBUG_INFO("get_and_publish_cert: certificate for target '%s' added", ccd->target); /* Let's try this again... */ sip_transport_authentication_completed(sipe_private); success = TRUE; } g_free(cert_base64); } } if (!success) { certificate_failure(sipe_private, _("Certificate request to %s failed"), uri, NULL); } callback_data_free(ccd); } static void certprov_webticket(struct sipe_core_private *sipe_private, const gchar *base_uri, const gchar *auth_uri, const gchar *wsse_security, const gchar *failure_msg, gpointer callback_data) { struct certificate_callback_data *ccd = callback_data; if (wsse_security) { /* Got a Web Ticket for Certificate Provisioning Service */ gchar *certreq_base64 = create_certreq(sipe_private, sipe_private->username); SIPE_DEBUG_INFO("certprov_webticket: got ticket for %s", base_uri); if (certreq_base64) { SIPE_DEBUG_INFO_NOFORMAT("certprov_webticket: created certificate request"); if (sipe_svc_get_and_publish_cert(sipe_private, ccd->session, auth_uri, wsse_security, certreq_base64, get_and_publish_cert, ccd)) /* callback data passed down the line */ ccd = NULL; g_free(certreq_base64); } if (ccd) { certificate_failure(sipe_private, _("Certificate request to %s failed"), base_uri, NULL); } } else if (auth_uri) { certificate_failure(sipe_private, _("Web ticket request to %s failed"), base_uri, failure_msg); } if (ccd) callback_data_free(ccd); } gboolean sipe_certificate_tls_dsk_generate(struct sipe_core_private *sipe_private, const gchar *target, const gchar *uri) { struct certificate_callback_data *ccd = g_new0(struct certificate_callback_data, 1); gboolean ret; ccd->session = sipe_svc_session_start(); ret = sipe_webticket_request_with_port(sipe_private, ccd->session, uri, "CertProvisioningServiceWebTicketProof_SHA1", certprov_webticket, ccd); if (ret) { ccd->target = g_strdup(target); } else { callback_data_free(ccd); } return(ret); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-certificate.h ================================================ /** * @file sipe-certificate.h * * pidgin-sipe * * Copyright (C) 2011 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Interface dependencies: * * */ /* Forward declarations */ struct sipe_core_private; /** * Find TLS-DSK user certificate for a given target * * @param sipe_private SIPE core private data * @param target target name from authentication header * * @return opaque pointer to the certificate. The caller does not own * the certificate, i.e. he must not free it! */ gpointer sipe_certificate_tls_dsk_find(struct sipe_core_private *sipe_private, const gchar *target); /** * Trigger the generation of TLS-DSK user certificate for a given target * * @param sipe_private SIPE core private data * @param target target name from authentication header * @param uri URI for the Certificate Provisioning Service * @return @c TRUE if certificate generation was triggered */ gboolean sipe_certificate_tls_dsk_generate(struct sipe_core_private *sipe_private, const gchar *target, const gchar *uri); /** * Initialize certificate data * * @param sipe_private SIPE core private data */ gboolean sipe_certificate_init(struct sipe_core_private *sipe_private); /** * Free certificate data * * @param sipe_private SIPE core private data */ void sipe_certificate_free(struct sipe_core_private *sipe_private); ================================================ FILE: src/core/sipe-chat.c ================================================ /** * @file sipe-chat.c * * pidgin-sipe * * Copyright (C) 2009-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-common.h" #include "sipmsg.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-chat.h" #include "sipe-conf.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-dialog.h" #include "sipe-groupchat.h" #include "sipe-im.h" #include "sipe-nls.h" #include "sipe-schedule.h" #include "sipe-session.h" #include "sipe-user.h" #include "sipe-utils.h" #include "sipe-xml.h" /** * Invite @who to chat * * @param sipe_private SIPE core private data * @param session SIPE session for chat * @param who URI whom to invite to chat. */ static void sipe_invite_to_chat(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *who); static GList *chat_sessions = NULL; struct sipe_chat_session *sipe_chat_create_session(guint type, const gchar *id, const gchar *title) { struct sipe_chat_session *session = g_new0(struct sipe_chat_session, 1); if (id) session->id = g_strdup(id); session->title = g_strdup(title); session->type = type; chat_sessions = g_list_prepend(chat_sessions, session); return(session); } void sipe_chat_remove_session(struct sipe_chat_session *session) { chat_sessions = g_list_remove(chat_sessions, session); sipe_backend_chat_session_destroy(session->backend); g_free(session->title); g_free(session->id); g_free(session->join_url); g_free(session->dial_in_conf_id); g_free(session->organizer); if (session->appshare_ask_ctx) { sipe_user_close_ask(session->appshare_ask_ctx); } g_free(session); } void sipe_chat_destroy(void) { while (chat_sessions) { struct sipe_chat_session *chat_session = chat_sessions->data; SIPE_DEBUG_INFO("sipe_chat_destroy: '%s' (%s)", chat_session->title, chat_session->id); sipe_chat_remove_session(chat_session); } } const gchar *sipe_core_chat_id(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session) { return(chat_session->id); } guint sipe_core_chat_type(struct sipe_chat_session *chat_session) { return(chat_session ? chat_session->type : SIPE_CHAT_TYPE_UNKNOWN); } void sipe_core_chat_invite(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session, const char *name) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; SIPE_DEBUG_INFO("sipe_core_chat_create: who '%s'", name); switch (chat_session->type) { case SIPE_CHAT_TYPE_MULTIPARTY: case SIPE_CHAT_TYPE_CONFERENCE: { struct sip_session *session = sipe_session_find_chat(sipe_private, chat_session); if (session) { gchar *uri = sip_uri(name); sipe_invite_to_chat(sipe_private, session, uri); g_free(uri); } } break; case SIPE_CHAT_TYPE_GROUPCHAT: /* @TODO */ SIPE_DEBUG_INFO_NOFORMAT("GROUP CHAT: INVITE NOT IMPLEMENTED!"); break; default: break; } } void sipe_core_chat_rejoin(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; SIPE_DEBUG_INFO("sipe_core_chat_rejoin: '%s'", chat_session->title); switch (chat_session->type) { case SIPE_CHAT_TYPE_MULTIPARTY: { struct sip_session *session = sipe_session_add_chat(sipe_private, chat_session, TRUE, NULL); gchar *self = sip_uri_self(sipe_private); sipe_invite_to_chat(sipe_private, session, self); sipe_backend_chat_rejoin(SIPE_CORE_PUBLIC, chat_session->backend, self, chat_session->title); g_free(self); } break; case SIPE_CHAT_TYPE_CONFERENCE: sipe_conf_create(sipe_private, chat_session, NULL); break; case SIPE_CHAT_TYPE_GROUPCHAT: sipe_groupchat_rejoin(sipe_private, chat_session); break; default: break; } } void sipe_core_chat_leave(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; SIPE_DEBUG_INFO("sipe_core_chat_leave: '%s'", chat_session->title); switch (chat_session->type) { case SIPE_CHAT_TYPE_MULTIPARTY: case SIPE_CHAT_TYPE_CONFERENCE: { struct sip_session *session = sipe_session_find_chat(sipe_private, chat_session); if (session) { sipe_session_close(sipe_private, session); } } break; case SIPE_CHAT_TYPE_GROUPCHAT: sipe_groupchat_leave(sipe_private, chat_session); break; default: break; } } void sipe_core_chat_send(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session, const char *what) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; SIPE_DEBUG_INFO("sipe_core_chat_send: '%s' to '%s'", what, chat_session->title); switch (chat_session->type) { case SIPE_CHAT_TYPE_MULTIPARTY: case SIPE_CHAT_TYPE_CONFERENCE: { struct sip_session *session = sipe_session_find_chat(sipe_private, chat_session); if (session) { sipe_session_enqueue_message(session, what, NULL); sipe_im_process_queue(sipe_private, session); } } break; case SIPE_CHAT_TYPE_GROUPCHAT: sipe_groupchat_send(sipe_private, chat_session, what); break; default: break; } } gchar * sipe_chat_get_name(void) { /** * A non-volatile ID counter. * Should survive connection drop & reconnect. */ static guint chat_seq = 0; /* Generate next ID */ gchar *chat_name = g_strdup_printf(_("Chat #%d"), ++chat_seq); SIPE_DEBUG_INFO("sipe_chat_get_name: added new: %s", chat_name); return chat_name; } static void sipe_refer(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *who) { gchar *hdr; gchar *contact; struct sip_dialog *dialog = sipe_dialog_find(session, session->chat_session->id); const char *ourtag = dialog && dialog->ourtag ? dialog->ourtag : NULL; contact = get_contact(sipe_private); hdr = g_strdup_printf( "Contact: %s\r\n" "Refer-to: <%s>\r\n" "Referred-By: %s%s;epid=%s\r\n" "Require: com.microsoft.rtc-multiparty\r\n", contact, who, sipe_private->username, ourtag ? ";tag=" : "", ourtag ? ourtag : "", sip_transport_epid(sipe_private)); sip_transport_request(sipe_private, "REFER", session->chat_session->id, session->chat_session->id, hdr, NULL, dialog, NULL); g_free(hdr); g_free(contact); } static gboolean sipe_is_election_finished(struct sip_session *session) { gboolean res = TRUE; SIPE_DIALOG_FOREACH { if (dialog->election_vote == 0) { res = FALSE; break; } } SIPE_DIALOG_FOREACH_END; if (res) { session->is_voting_in_progress = FALSE; } return res; } static gboolean process_info_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans); static void sipe_send_election_set_rm(struct sipe_core_private *sipe_private, struct sip_dialog *dialog) { const gchar *hdr = "Content-Type: application/x-ms-mim\r\n"; gchar *body = g_strdup_printf( "\r\n" "" "\r\n", sipe_private->username); sip_transport_info(sipe_private, hdr, body, dialog, process_info_response); g_free(body); } void sipe_process_pending_invite_queue(struct sipe_core_private *sipe_private, struct sip_session *session) { gchar *invitee; GSList *entry = session->pending_invite_queue; while (entry) { invitee = entry->data; sipe_invite_to_chat(sipe_private, session, invitee); entry = session->pending_invite_queue = g_slist_remove(session->pending_invite_queue, invitee); g_free(invitee); } } void sipe_chat_set_roster_manager(struct sip_session *session, const gchar *roster_manager) { struct sipe_chat_session *chat_session = session->chat_session; g_free(chat_session->id); chat_session->id = NULL; if (roster_manager) chat_session->id = g_strdup(roster_manager); } static void sipe_election_result(struct sipe_core_private *sipe_private, void *sess) { struct sip_session *session = (struct sip_session *)sess; const gchar *rival = NULL; if (session->chat_session->id) { SIPE_DEBUG_INFO( "sipe_election_result: RM has already been elected in the meantime. It is %s", session->chat_session->id); return; } session->is_voting_in_progress = FALSE; SIPE_DIALOG_FOREACH { if (dialog->election_vote < 0) { rival = dialog->with; break; } } SIPE_DIALOG_FOREACH_END; if (rival) { SIPE_DEBUG_INFO("sipe_election_result: we loose RM election to %s", rival); } else { gchar *self = sip_uri_self(sipe_private); SIPE_DEBUG_INFO_NOFORMAT("sipe_election_result: we have won RM election!"); sipe_chat_set_roster_manager(session, self); g_free(self); SIPE_DIALOG_FOREACH { /* send SetRM to each chat participant*/ sipe_send_election_set_rm(sipe_private, dialog); } SIPE_DIALOG_FOREACH_END; } session->bid = 0; sipe_process_pending_invite_queue(sipe_private, session); } static gboolean process_info_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { const gchar *contenttype = sipmsg_find_content_type_header(msg); const gchar *callid = sipmsg_find_call_id_header(msg); struct sip_dialog *dialog; struct sip_session *session; session = sipe_session_find_chat_by_callid(sipe_private, callid); if (!session) { SIPE_DEBUG_INFO("process_info_response: failed find dialog for callid %s, exiting.", callid); return FALSE; } if (msg->response == 200 && g_str_has_prefix(contenttype, "application/x-ms-mim")) { sipe_xml *xn_action = sipe_xml_parse(msg->body, msg->bodylen); const sipe_xml *xn_request_rm_response = sipe_xml_child(xn_action, "RequestRMResponse"); const sipe_xml *xn_set_rm_response = sipe_xml_child(xn_action, "SetRMResponse"); if (xn_request_rm_response) { const char *with = sipe_xml_attribute(xn_request_rm_response, "uri"); const char *allow = sipe_xml_attribute(xn_request_rm_response, "allow"); dialog = sipe_dialog_find(session, with); if (!dialog) { SIPE_DEBUG_INFO("process_info_response: failed find dialog for %s, exiting.", with); sipe_xml_free(xn_action); return FALSE; } if (allow && !g_ascii_strcasecmp(allow, "true")) { SIPE_DEBUG_INFO("process_info_response: %s has voted PRO", with); dialog->election_vote = 1; } else if (allow && !g_ascii_strcasecmp(allow, "false")) { SIPE_DEBUG_INFO("process_info_response: %s has voted CONTRA", with); dialog->election_vote = -1; } if (sipe_is_election_finished(session)) { sipe_election_result(sipe_private, session); } } else if (xn_set_rm_response) { } sipe_xml_free(xn_action); } return TRUE; } static void sipe_send_election_request_rm(struct sipe_core_private *sipe_private, struct sip_dialog *dialog, int bid) { const gchar *hdr = "Content-Type: application/x-ms-mim\r\n"; gchar *body = g_strdup_printf( "\r\n" "" "\r\n", sipe_private->username, bid); sip_transport_info(sipe_private, hdr, body, dialog, process_info_response); g_free(body); } static void sipe_election_start(struct sipe_core_private *sipe_private, struct sip_session *session) { if (session->is_voting_in_progress) { SIPE_DEBUG_INFO_NOFORMAT("sipe_election_start: other election is in progress, exiting."); return; } else { session->is_voting_in_progress = TRUE; } session->bid = rand(); SIPE_DEBUG_INFO("sipe_election_start: RM election has initiated. Our bid=%d", session->bid); SIPE_DIALOG_FOREACH { /* reset election_vote for each chat participant */ dialog->election_vote = 0; /* send RequestRM to each chat participant*/ sipe_send_election_request_rm(sipe_private, dialog, session->bid); } SIPE_DIALOG_FOREACH_END; sipe_schedule_seconds(sipe_private, "<+election-result>", session, 15, sipe_election_result, NULL); } static void sipe_invite_to_chat(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *who) { /* a conference */ if (session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) { sipe_invite_conf(sipe_private, session, who); } else /* a multi-party chat */ { gchar *self = sip_uri_self(sipe_private); if (session->chat_session->id) { if (sipe_strcase_equal(session->chat_session->id, self)) { sipe_im_invite(sipe_private, session, who, NULL, NULL, NULL, FALSE); } else { sipe_refer(sipe_private, session, who); } } else { SIPE_DEBUG_INFO_NOFORMAT("sipe_invite_to_chat: no RM available"); session->pending_invite_queue = sipe_utils_slist_insert_unique_sorted(session->pending_invite_queue, g_strdup(who), (GCompareFunc)strcmp, g_free); sipe_election_start(sipe_private, session); } g_free(self); } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-chat.h ================================================ /** * @file sipe-chat.h * * pidgin-sipe * * Copyright (C) 2009-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_core_private; struct sip_session; struct sipe_backend_chat_session; enum sipe_chat_type { SIPE_CHAT_TYPE_UNKNOWN = 0, SIPE_CHAT_TYPE_MULTIPARTY, SIPE_CHAT_TYPE_CONFERENCE, SIPE_CHAT_TYPE_GROUPCHAT }; struct sipe_chat_session { struct sipe_backend_chat_session *backend; /* * Chat identifier (must be unique per account) * * 2007 Group chat: channel URI * 2007 Conference: focus URI * 2005 multiparty chat: roster manager SIP URI */ gchar *id; /* Human readable chat identifier (can have duplicates) */ gchar *title; /* SIPE_CHAT_TYPE_xxx */ guint type; gchar *join_url; gchar *dial_in_conf_id; gchar *organizer; struct sipe_user_ask_ctx *appshare_ask_ctx; }; /** * Create a new chat session * * @param session */ struct sipe_chat_session * sipe_chat_create_session(guint type, const gchar *id, const gchar *title); /** * Remove a chat session * * @param session */ void sipe_chat_remove_session(struct sipe_chat_session *session); /** * Release resources on unload */ void sipe_chat_destroy(void); /** * Generate a name for a new private chat. * * @return chat name. Must be g_free()'d after use */ gchar * sipe_chat_get_name(void); /** * * * @param sipe_private SIPE core private data * @param session SIPE session for chat */ void sipe_process_pending_invite_queue(struct sipe_core_private *sipe_private, struct sip_session *session); /** * Set roster manager URI for a multiparty chat * * @param session SIPE session for chat * @param roster_manager New roster manager URI or NULL */ void sipe_chat_set_roster_manager(struct sip_session *session, const gchar *roster_manager); ================================================ FILE: src/core/sipe-conf.c ================================================ /** * @file sipe-conf.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * Documentation references: * * Microsoft DevNet: [MS-CONFIM]: Centralized Conference Control Protocol: * Instant Messaging Extensions * * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-common.h" #include "sipmsg.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-chat.h" #include "sipe-conf.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-appshare.h" #include "sipe-dialog.h" #include "sipe-http.h" #include "sipe-im.h" #include "sipe-media.h" #include "sipe-nls.h" #include "sipe-session.h" #include "sipe-subscriptions.h" #include "sipe-user.h" #include "sipe-utils.h" #include "sipe-xml.h" /** * Invite counterparty to join conference. * @param focus_uri (%s) * @param subject (%s) of conference */ #define SIPE_SEND_CONF_INVITE \ ""\ "%s"\ "%s"\ ""\ ""\ ""\ "" static gboolean sipe_conf_check_for_lync_url(struct sipe_core_private *sipe_private, gchar *uri); static struct transaction * cccp_request(struct sipe_core_private *sipe_private, const gchar *method, const gchar *with, struct sip_dialog *dialog, TransCallback callback, const gchar *body, ...) { gchar *headers; gchar *request; gchar *request_body; gchar *self = sip_uri_self(sipe_private); va_list args; struct transaction *trans; headers = g_strdup_printf( "Supported: ms-sender\r\n" "Contact: %s\r\n" "Content-Type: application/cccp+xml\r\n", sipe_private->contact); /* TODO: put request_id to queue to further compare with incoming one */ request = g_strdup_printf( "" "" "%s" "", with, self, sipe_private->cccp_request_id++, body); g_free(self); va_start(args, body); request_body = g_strdup_vprintf(request, args); va_end(args); g_free(request); trans = sip_transport_request(sipe_private, method, with, with, headers, request_body, dialog, callback); g_free(headers); g_free(request_body); return trans; } static gboolean process_conf_get_capabilities(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { if (msg->response >= 400) { SIPE_DEBUG_INFO_NOFORMAT("process_conf_get_capabilities: " "getConferencingCapabilities failed."); return FALSE; } if (msg->response == 200) { sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen); const sipe_xml *node; gchar *default_region; if (!sipe_strequal("success", sipe_xml_attribute(xn_response, "code"))) { return TRUE; } node = sipe_xml_child(xn_response, "getConferencingCapabilities/mcu-types/mcuType"); for (;node; node = sipe_xml_twin(node)) { sipe_private->conf_mcu_types = g_slist_append(sipe_private->conf_mcu_types, sipe_xml_data(node)); } g_hash_table_remove_all(sipe_private->access_numbers); node = sipe_xml_child(xn_response, "getConferencingCapabilities/pstn-bridging/access-numbers/region"); for (;node; node = sipe_xml_twin(node)) { gchar *name = g_strdup(sipe_xml_attribute(node, "name")); gchar *number = sipe_xml_data(sipe_xml_child(node, "access-number/number")); if (name && number) { g_hash_table_insert(sipe_private->access_numbers, name, number); } } node = sipe_xml_child(xn_response, "getConferencingCapabilities/pstn-bridging/access-numbers/default-region"); default_region = sipe_xml_data(node); if (default_region) { sipe_private->default_access_number = g_hash_table_lookup(sipe_private->access_numbers, default_region); } g_free(default_region); sipe_xml_free(xn_response); } return TRUE; } void sipe_conf_get_capabilities(struct sipe_core_private *sipe_private) { cccp_request(sipe_private, "SERVICE", sipe_private->focus_factory_uri, NULL, process_conf_get_capabilities, ""); } gboolean sipe_conf_supports_mcu_type(struct sipe_core_private *sipe_private, const gchar *type) { return g_slist_find_custom(sipe_private->conf_mcu_types, type, (GCompareFunc)g_strcmp0) != NULL; } /** * Generates random GUID. * This method is borrowed from pidgin's msnutils.c */ static char * rand_guid() { return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X", rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111); } /** Invite us to the focus callback */ static gboolean process_invite_conf_focus_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { struct sip_session *session = NULL; char *focus_uri = sipmsg_parse_to_address(msg); session = sipe_session_find_conference(sipe_private, focus_uri); if (!session) { SIPE_DEBUG_INFO("process_invite_conf_focus_response: unable to find conf session with focus=%s", focus_uri); g_free(focus_uri); return FALSE; } if (!session->focus_dialog) { SIPE_DEBUG_INFO_NOFORMAT("process_invite_conf_focus_response: session's focus_dialog is NULL"); g_free(focus_uri); return FALSE; } sipe_dialog_parse(session->focus_dialog, msg, TRUE); if (msg->response >= 200) { /* send ACK to focus */ session->focus_dialog->cseq = 0; sip_transport_ack(sipe_private, session->focus_dialog); session->focus_dialog->outgoing_invite = NULL; session->focus_dialog->is_established = TRUE; } if (msg->response >= 400) { gchar *reason = sipmsg_get_ms_diagnostics_reason(msg); SIPE_DEBUG_INFO_NOFORMAT("process_invite_conf_focus_response: INVITE response is not 200. Failed to join focus."); sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Failed to join the conference"), reason ? reason : _("no reason given")); g_free(reason); sipe_session_remove(sipe_private, session); g_free(focus_uri); return FALSE; } else if (msg->response == 200) { sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen); const gchar *code = sipe_xml_attribute(xn_response, "code"); if (sipe_strequal(code, "success")) { /* subscribe to focus */ sipe_subscribe_conference(sipe_private, session->chat_session->id, FALSE); #ifdef HAVE_VV if (session->is_call) sipe_core_media_connect_conference(SIPE_CORE_PUBLIC, session->chat_session, FALSE); #endif } sipe_xml_free(xn_response); } g_free(focus_uri); return TRUE; } static gchar * parse_ocs_focus_uri(const gchar *uri) { const gchar *confkey; size_t uri_len; if (!uri) return NULL; // URI can have this prefix if it was typed in by the user if (g_str_has_prefix(uri, "meet:") || g_str_has_prefix(uri, "conf:")) { uri += 5; } uri_len = strlen(uri); if (!uri || !g_str_has_prefix(uri, "sip:") || uri_len == 4 || g_strstr_len(uri, -1, "%")) { return NULL; } confkey = g_strstr_len(uri, -1, "?"); if (confkey) { /* TODO: Investigate how conf-key field should be used, * ignoring for now */ uri_len = confkey - uri; } return g_strndup(uri, uri_len); } static gchar * extract_uri_from_html(const gchar *body, const gchar *prefix, guint prefix_skip_chars) { gchar *uri = NULL; const gchar *start = g_strstr_len(body, -1, prefix); if (start) { const gchar *end; start += prefix_skip_chars; end = strchr(start, '"'); if (end) { gchar *html = g_strndup(start, end - start); /* decode HTML entities */ gchar *html_unescaped = sipe_backend_markup_strip_html(html); g_free(html); if (!is_empty(html_unescaped)) { uri = sipe_utils_uri_unescape(html_unescaped); } g_free(html_unescaped); } } return uri; } static void sipe_conf_lync_url_cb(struct sipe_core_private *sipe_private, guint status, SIPE_UNUSED_PARAMETER GSList *headers, const gchar *body, gpointer callback_data) { gchar *uri = callback_data; if (status != (guint) SIPE_HTTP_STATUS_ABORTED) { gchar *focus_uri = NULL; if (body) { /* * Extract focus URI from HTML, e.g. * * */ gchar *uri = extract_uri_from_html(body, "href=\"conf", 6); focus_uri = parse_ocs_focus_uri(uri); g_free(uri); } if (focus_uri) { SIPE_DEBUG_INFO("sipe_conf_lync_url_cb: found focus URI" " '%s'", focus_uri); sipe_conf_create(sipe_private, NULL, focus_uri); g_free(focus_uri); } else { /* * If present, domainOwnerJoinLauncherUrl redirects to * a page from where we still may extract the focus URI. */ gchar *launcher_url = NULL; static const gchar *launcher_url_prefix[] = { "var domainOwnerJoinLauncherUrl = \"", "sb-data-domainOwnerJoinLauncherUrl=\"", NULL }; const gchar **p; SIPE_DEBUG_INFO("sipe_conf_lync_url_cb: no focus URI " "found from URL '%s'", uri); for (p = launcher_url_prefix; !launcher_url && *p; ++p) { launcher_url = extract_uri_from_html(body, *p, strlen(*p)); } if (launcher_url && sipe_conf_check_for_lync_url(sipe_private, launcher_url)) { SIPE_DEBUG_INFO("sipe_conf_lync_url_cb: retrying with URL '%s'", launcher_url); /* Ownership taken by sipe_conf_check_for_lync_url() */ launcher_url = NULL; } else { gchar *error; error = g_strdup_printf(_("Can't find a conference URI on this page:\n\n%s"), uri); sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Failed to join the conference"), error); g_free(error); } g_free(launcher_url); } } g_free(uri); } static gboolean sipe_conf_check_for_lync_url(struct sipe_core_private *sipe_private, gchar *uri) { struct sipe_http_request *request; if (!(g_str_has_prefix(uri, "https://") || g_str_has_prefix(uri, "http://"))) return(FALSE); /* URL points to a HTML page with the conference focus URI */ request = sipe_http_request_get(sipe_private, uri, NULL, sipe_conf_lync_url_cb, uri); if (request) { sipe_http_request_ready(request); return(TRUE); } return(FALSE); } static void sipe_conf_uri_error(struct sipe_core_private *sipe_private, const gchar *uri) { gchar *error = g_strdup_printf(_("\"%s\" is not a valid conference URI"), uri ? uri : ""); sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Failed to join the conference"), error); g_free(error); } void sipe_core_conf_create(struct sipe_core_public *sipe_public, const gchar *uri, const gchar *organizer, const gchar *meeting_id) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; /* SIP URI or HTTP URL */ if (uri) { gchar *uri_ue = sipe_utils_uri_unescape(uri); SIPE_DEBUG_INFO("sipe_core_conf_create: URI '%s' unescaped '%s'", uri, uri_ue ? uri_ue : ""); /* takes ownership of "uri_ue" if successful */ if (!sipe_conf_check_for_lync_url(sipe_private, uri_ue)) { gchar *focus_uri = parse_ocs_focus_uri(uri_ue); if (focus_uri) { sipe_conf_create(sipe_private, NULL, focus_uri); g_free(focus_uri); } else sipe_conf_uri_error(sipe_private, uri); g_free(uri_ue); } /* Organizer email and meeting ID */ } else if (organizer && meeting_id) { gchar *tmp = g_strdup_printf("sip:%s;gruu;opaque=app:conf:focus:id:%s", organizer, meeting_id); gchar *focus_uri = parse_ocs_focus_uri(tmp); SIPE_DEBUG_INFO("sipe_core_conf_create: organizer '%s' meeting ID '%s'", organizer, meeting_id); if (focus_uri) { sipe_conf_create(sipe_private, NULL, focus_uri); g_free(focus_uri); } else sipe_conf_uri_error(sipe_private, tmp); g_free(tmp); } else { sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Failed to join the conference"), _("Incomplete conference information provided")); } } /** Create new session with Focus URI */ struct sip_session * sipe_conf_create(struct sipe_core_private *sipe_private, struct sipe_chat_session *chat_session, const gchar *focus_uri) { /* addUser request to the focus. * * focus_URI, from, endpoint_GUID */ static const gchar CCCP_ADD_USER[] = "" "" "" "" "attendee" "" "" "" ""; gchar *self; struct sip_session *session = sipe_session_add_chat(sipe_private, chat_session, FALSE, focus_uri); session->focus_dialog = g_new0(struct sip_dialog, 1); session->focus_dialog->callid = gencallid(); session->focus_dialog->with = g_strdup(session->chat_session->id); session->focus_dialog->endpoint_GUID = rand_guid(); session->focus_dialog->ourtag = gentag(); self = sip_uri_self(sipe_private); session->focus_dialog->outgoing_invite = cccp_request(sipe_private, "INVITE", session->focus_dialog->with, session->focus_dialog, process_invite_conf_focus_response, CCCP_ADD_USER, session->focus_dialog->with, self, session->focus_dialog->endpoint_GUID); /* Rejoin existing session? */ if (chat_session) { SIPE_DEBUG_INFO("sipe_conf_create: rejoin '%s' (%s)", chat_session->title, chat_session->id); sipe_backend_chat_rejoin(SIPE_CORE_PUBLIC, chat_session->backend, self, chat_session->title); } g_free(self); return(session); } /** Modify User Role */ void sipe_conf_modify_user_role(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar* who) { /* modifyUserRoles request to the focus. Makes user a leader. * * focus_uri (%s) * who (%s) */ static const gchar CCCP_MODIFY_USER_ROLES[] = "" "" "" "presenter" "" ""; if (!session->focus_dialog || !session->focus_dialog->is_established) { SIPE_DEBUG_INFO_NOFORMAT("sipe_conf_modify_user_role: no dialog with focus, exiting."); return; } cccp_request(sipe_private, "INFO", session->focus_dialog->with, session->focus_dialog, NULL, CCCP_MODIFY_USER_ROLES, session->focus_dialog->with, who); } /** * Check conference lock status */ sipe_chat_lock_status sipe_core_chat_lock_status(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; sipe_chat_lock_status status = SIPE_CHAT_LOCK_STATUS_NOT_ALLOWED; if (chat_session && (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE)) { struct sip_session *session = sipe_session_find_chat(sipe_private, chat_session); if (session) { gchar *self = sip_uri_self(sipe_private); /* Only operators are allowed to change the lock status */ if (sipe_backend_chat_is_operator(chat_session->backend, self)) { status = session->locked ? SIPE_CHAT_LOCK_STATUS_LOCKED : SIPE_CHAT_LOCK_STATUS_UNLOCKED; } g_free(self); } } return(status); } /** * Modify Conference Lock * Sends request to Focus. * INFO method is a carrier of application/cccp+xml */ void sipe_core_chat_modify_lock(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session, const gboolean locked) { /* modifyConferenceLock request to the focus. Locks/unlocks conference. * * focus_uri (%s) * locked (%s) "true" or "false" values applicable */ static const gchar CCCP_MODIFY_CONFERENCE_LOCK[] = "" "" "%s" ""; struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sip_session *session = sipe_session_find_chat(sipe_private, chat_session); if (!session) return; if (!session->focus_dialog || !session->focus_dialog->is_established) { SIPE_DEBUG_INFO_NOFORMAT("sipe_conf_modify_conference_lock: no dialog with focus, exiting."); return; } cccp_request(sipe_private, "INFO", session->focus_dialog->with, session->focus_dialog, NULL, CCCP_MODIFY_CONFERENCE_LOCK, session->focus_dialog->with, locked ? "true" : "false"); } /** Modify Delete User */ void sipe_conf_delete_user(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar* who) { /* deleteUser request to the focus. Removes a user from the conference. * * focus_uri (%s) * who (%s) */ static const gchar CCCP_DELETE_USER[] = "" "" ""; if (!session->focus_dialog || !session->focus_dialog->is_established) { SIPE_DEBUG_INFO_NOFORMAT("sipe_conf_delete_user: no dialog with focus, exiting."); return; } cccp_request(sipe_private, "INFO", session->focus_dialog->with, session->focus_dialog, NULL, CCCP_DELETE_USER, session->focus_dialog->with, who); } void sipe_conf_announce_audio_mute_state(struct sipe_core_private *sipe_private, struct sip_session *session, gboolean is_muted) { // See [MS-CONFAV] 3.2.5.4 and 4.3 static const gchar CCCP_MODIFY_ENDPOINT_MEDIA[] = "" "" "" "audio" "%s" "" "%s" "" "" ""; gchar *mcu_uri = sipe_conf_build_uri(session->focus_dialog->with, "audio-video"); gchar *self = sip_uri_self(sipe_private); cccp_request(sipe_private, "INFO", session->focus_dialog->with, session->focus_dialog, NULL, CCCP_MODIFY_ENDPOINT_MEDIA, mcu_uri, session->focus_dialog->with, self, session->audio_video_entity, session->audio_media_id, session->audio_media_id, is_muted ? "recvonly" : "sendrecv", is_muted ? "block" : "unblock"); g_free(mcu_uri); g_free(self); } /** Invite counterparty to join conference callback */ static gboolean process_invite_conf_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { struct sip_dialog *dialog = g_new0(struct sip_dialog, 1); dialog->callid = g_strdup(sipmsg_find_call_id_header(msg)); dialog->cseq = sipmsg_parse_cseq(msg); dialog->with = sipmsg_parse_to_address(msg); sipe_dialog_parse(dialog, msg, TRUE); if (msg->response >= 200) { /* send ACK to counterparty */ dialog->cseq--; sip_transport_ack(sipe_private, dialog); dialog->outgoing_invite = NULL; dialog->is_established = TRUE; } if (msg->response >= 400) { SIPE_DEBUG_INFO("process_invite_conf_response: INVITE response is not 200. Failed to invite %s.", dialog->with); /* @TODO notify user of failure to invite counterparty */ sipe_dialog_free(dialog); return FALSE; } if (msg->response >= 200) { struct sip_session *session = sipe_session_find_im(sipe_private, dialog->with); struct sip_dialog *im_dialog = sipe_dialog_find(session, dialog->with); /* close IM session to counterparty */ if (im_dialog) { sip_transport_bye(sipe_private, im_dialog); sipe_dialog_remove(session, dialog->with); } } sipe_dialog_free(dialog); return TRUE; } /** * Invites counterparty to join conference. */ void sipe_invite_conf(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar* who) { gchar *hdr; gchar *contact; gchar *body; struct sip_dialog *dialog = NULL; /* It will be short lived special dialog. * Will not be stored in session. */ dialog = g_new0(struct sip_dialog, 1); dialog->callid = gencallid(); dialog->with = g_strdup(who); dialog->ourtag = gentag(); contact = get_contact(sipe_private); hdr = g_strdup_printf( "Supported: ms-sender\r\n" "Contact: %s\r\n" "Content-Type: application/ms-conf-invite+xml\r\n", contact); g_free(contact); body = g_strdup_printf( SIPE_SEND_CONF_INVITE, session->chat_session->id, session->subject ? session->subject : "" ); sip_transport_invite(sipe_private, hdr, body, dialog, process_invite_conf_response); sipe_dialog_free(dialog); g_free(body); g_free(hdr); } /** Create conference callback */ static gboolean process_conf_add_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans) { if (msg->response >= 400) { SIPE_DEBUG_INFO_NOFORMAT("process_conf_add_response: SERVICE response is not 200. Failed to create conference."); /* @TODO notify user of failure to create conference */ return FALSE; } if (msg->response == 200) { sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen); if (sipe_strequal("success", sipe_xml_attribute(xn_response, "code"))) { gchar *who = trans->payload->data; const sipe_xml *xn_conference_info = sipe_xml_child(xn_response, "addConference/conference-info"); struct sip_session *session = sipe_conf_create(sipe_private, NULL, sipe_xml_attribute(xn_conference_info, "entity")); SIPE_DEBUG_INFO("process_conf_add_response: session->focus_uri=%s", session->chat_session->id); session->pending_invite_queue = sipe_utils_slist_insert_unique_sorted(session->pending_invite_queue, g_strdup(who), (GCompareFunc)strcmp, g_free); } sipe_xml_free(xn_response); } return TRUE; } /** * Creates conference. */ void sipe_conf_add(struct sipe_core_private *sipe_private, const gchar* who) { gchar *conference_id; struct transaction *trans; time_t expiry = time(NULL) + 7*60*60; /* 7 hours */ char *expiry_time; /* addConference request to the focus factory. * * conference_id (%s) Ex.: 8386E6AEAAA41E4AA6627BA76D43B6D1 * expiry_time (%s) Ex.: 2009-07-13T17:57:09Z * conference_view (%s) Ex.: */ static const gchar CCCP_ADD_CONFERENCE[] = "" "" "" "" "%s" "%s" "openAuthenticated" "" "%s" "" ""; static const gchar *DESIRED_MCU_TYPES[] = { "chat", #ifdef HAVE_VV "audio-video", #endif #ifdef HAVE_APPSHARE "applicationsharing", #endif NULL }; GString *conference_view = g_string_new(""); const gchar **type; for (type = DESIRED_MCU_TYPES; *type; ++type ) { if (sipe_conf_supports_mcu_type(sipe_private, *type)) { g_string_append(conference_view, ""); } } expiry_time = sipe_utils_time_to_str(expiry); conference_id = genconfid(); trans = cccp_request(sipe_private, "SERVICE", sipe_private->focus_factory_uri, NULL, process_conf_add_response, CCCP_ADD_CONFERENCE, conference_id, expiry_time, conference_view->str); g_free(conference_id); g_free(expiry_time); g_string_free(conference_view, TRUE); if (trans) { struct transaction_payload *payload = g_new0(struct transaction_payload, 1); payload->destroy = g_free; payload->data = g_strdup(who); trans->payload = payload; } } static void accept_incoming_invite_conf(struct sipe_core_private *sipe_private, gchar *focus_uri, gboolean audio, struct sipmsg *msg) { struct sip_session *session; /* acknowledge invite */ sipmsg_update_to_header_tag(msg); sip_transport_response(sipe_private, msg, 200, "OK", NULL); /* add self to conf */ session = sipe_conf_create(sipe_private, NULL, focus_uri); session->is_call = audio; } struct conf_accept_ctx { gchar *focus_uri; struct sipmsg *msg; struct sipe_user_ask_ctx *ask_ctx; SipeUserAskCb accept_cb; SipeUserAskCb decline_cb; gpointer user_data; }; static void conf_accept_ctx_free(struct conf_accept_ctx *ctx) { g_return_if_fail(ctx != NULL); sipmsg_free(ctx->msg); g_free(ctx->focus_uri); g_free(ctx); } static void conf_accept_cb(struct sipe_core_private *sipe_private, struct conf_accept_ctx *ctx) { accept_incoming_invite_conf(sipe_private, ctx->focus_uri, TRUE, ctx->msg); } static void conf_decline_cb(struct sipe_core_private *sipe_private, struct conf_accept_ctx *ctx) { sip_transport_response(sipe_private, ctx->msg, 603, "Decline", NULL); } void sipe_conf_cancel_unaccepted(struct sipe_core_private *sipe_private, struct sipmsg *msg) { const gchar *callid1 = msg ? sipmsg_find_call_id_header(msg) : NULL; GSList *it = sipe_private->sessions_to_accept; while (it) { struct conf_accept_ctx *ctx = it->data; const gchar *callid2 = NULL; if (msg && ctx->msg) callid2 = sipmsg_find_call_id_header(ctx->msg); if (sipe_strequal(callid1, callid2)) { GSList *tmp; if (ctx->msg) sip_transport_response(sipe_private, ctx->msg, 487, "Request Terminated", NULL); if (msg) sip_transport_response(sipe_private, msg, 200, "OK", NULL); sipe_user_close_ask(ctx->ask_ctx); conf_accept_ctx_free(ctx); tmp = it; it = it->next; sipe_private->sessions_to_accept = g_slist_delete_link(sipe_private->sessions_to_accept, tmp); if (callid1) break; } else it = it->next; } } static void accept_invitation_cb(struct sipe_core_private *sipe_private, gpointer data) { struct conf_accept_ctx *ctx = data; sipe_private->sessions_to_accept = g_slist_remove(sipe_private->sessions_to_accept, ctx); if (ctx->accept_cb) { ctx->accept_cb(sipe_private, ctx); } conf_accept_ctx_free(ctx); } static void decline_invitation_cb(struct sipe_core_private *sipe_private, gpointer data) { struct conf_accept_ctx *ctx = data; sipe_private->sessions_to_accept = g_slist_remove(sipe_private->sessions_to_accept, ctx); if (ctx->decline_cb) { ctx->decline_cb(sipe_private, ctx); } conf_accept_ctx_free(ctx); } static void ask_accept_invitation(struct sipe_core_private *sipe_private, const gchar *focus_uri, const gchar *question, struct sipmsg *msg, SipeUserAskCb accept_cb, SipeUserAskCb decline_cb, gpointer user_data) { gchar **parts; gchar *alias; gchar *question_str; struct conf_accept_ctx *ctx; parts = g_strsplit(focus_uri, ";", 2); alias = sipe_buddy_get_alias(sipe_private, parts[0]); question_str = g_strdup_printf("%s %s", alias ? alias : parts[0], question); g_free(alias); g_strfreev(parts); ctx = g_new0(struct conf_accept_ctx, 1); sipe_private->sessions_to_accept = g_slist_append(sipe_private->sessions_to_accept, ctx); ctx->focus_uri = g_strdup(focus_uri); ctx->msg = msg ? sipmsg_copy(msg) : NULL; ctx->accept_cb = accept_cb; ctx->decline_cb = decline_cb; ctx->user_data = user_data; ctx->ask_ctx = sipe_user_ask(sipe_private, question_str, _("Accept"), accept_invitation_cb, _("Decline"), decline_invitation_cb, ctx); g_free(question_str); } static void ask_accept_voice_conference(struct sipe_core_private *sipe_private, const gchar *focus_uri, struct sipmsg *msg, SipeUserAskCb accept_cb, SipeUserAskCb decline_cb) { gchar *question; const gchar *novv_note; #ifdef HAVE_VV novv_note = ""; #else novv_note = _("\n\nAs this client was not compiled with voice call " "support, if you accept, you will be able to contact " "the other participants only via IM session."); #endif question = g_strdup_printf(_("wants to invite you " "to a conference call%s"), novv_note); ask_accept_invitation(sipe_private, focus_uri, question, msg, accept_cb, decline_cb, NULL); g_free(question); } void process_incoming_invite_conf(struct sipe_core_private *sipe_private, struct sipmsg *msg) { sipe_xml *xn_conferencing = sipe_xml_parse(msg->body, msg->bodylen); const sipe_xml *xn_focus_uri = sipe_xml_child(xn_conferencing, "focus-uri"); const sipe_xml *xn_audio = sipe_xml_child(xn_conferencing, "audio"); gchar *focus_uri = sipe_xml_data(xn_focus_uri); gboolean audio = sipe_strequal(sipe_xml_attribute(xn_audio, "available"), "true"); sipe_xml_free(xn_conferencing); SIPE_DEBUG_INFO("We have received invitation to Conference. Focus URI=%s", focus_uri); if (audio) { sip_transport_response(sipe_private, msg, 180, "Ringing", NULL); ask_accept_voice_conference(sipe_private, focus_uri, msg, (SipeUserAskCb) conf_accept_cb, (SipeUserAskCb) conf_decline_cb); } else { accept_incoming_invite_conf(sipe_private, focus_uri, FALSE, msg); } g_free(focus_uri); } #ifdef HAVE_VV static void process_conference_av_endpoint(const sipe_xml *endpoint, const gchar *user_uri, const gchar *self_uri, struct sip_session *session) { const sipe_xml *media; if (sipe_strequal(user_uri, self_uri)) { const gchar *new_entity = sipe_xml_attribute(endpoint, "entity"); if (!sipe_strequal(session->audio_video_entity, new_entity)) { g_free(session->audio_video_entity); session->audio_video_entity = g_strdup(new_entity); } } media = sipe_xml_child(endpoint, "media"); for (; media; media = sipe_xml_twin(media)) { gchar *type = sipe_xml_data(sipe_xml_child(media, "type")); if (sipe_strequal(type, "audio") && sipe_strequal(user_uri, self_uri)) { session->audio_media_id = sipe_xml_int_attribute(media, "id", 0); } else if (sipe_strequal(type, "video") && session->video_media_source_id == 0) { const sipe_xml *child = sipe_xml_child(media, "media-source-id"); const gchar *data = sipe_xml_data(child); if (data) { session->video_media_source_id = atoi(data); } } g_free(type); } } static void call_accept_cb(struct sipe_core_private *sipe_private, struct conf_accept_ctx *ctx) { struct sip_session *session; session = sipe_session_find_conference(sipe_private, ctx->focus_uri); if (session) { sipe_core_media_connect_conference(SIPE_CORE_PUBLIC, session->chat_session, FALSE); } } #ifdef HAVE_APPSHARE sipe_appshare_role sipe_core_conf_get_appshare_role(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session) { if (chat_session) { gchar *mcu_uri; struct sipe_media_call *call; mcu_uri = sipe_conf_build_uri(chat_session->id, "applicationsharing"); call = sipe_media_call_find(SIPE_CORE_PRIVATE, mcu_uri); g_free(mcu_uri); if (call) { return sipe_appshare_get_role(call); } } return SIPE_APPSHARE_ROLE_NONE; } static gboolean process_conference_appshare_endpoint(const sipe_xml *endpoint) { gboolean presentation_added = FALSE; const sipe_xml *media; for (media = sipe_xml_child(endpoint, "media"); media && !presentation_added; media = sipe_xml_twin(media)) { gchar *type; gchar *media_state; gchar *status; type = sipe_xml_data(sipe_xml_child(media, "type")); media_state = sipe_xml_data(sipe_xml_child(media, "media-state")); status = sipe_xml_data(sipe_xml_child(media, "status")); if (sipe_strequal(type, "applicationsharing") && sipe_strequal(media_state, "connected") && sipe_strequal(status, "sendonly")) { presentation_added = TRUE; } g_free(type); g_free(media_state); g_free(status); } return(presentation_added); } #endif // HAVE_APPSHARE #endif // HAVE_VV void sipe_process_conference(struct sipe_core_private *sipe_private, struct sipmsg *msg) { sipe_xml *xn_conference_info; const sipe_xml *node; const sipe_xml *xn_subject; const gchar *focus_uri; struct sip_session *session; gboolean just_joined = FALSE; #ifdef HAVE_VV gboolean audio_was_added = FALSE; #ifdef HAVE_APPSHARE gboolean presentation_was_added = FALSE; #endif #endif // HAVE_VV if (msg->response != 0 && msg->response != 200) return; if (msg->bodylen == 0 || msg->body == NULL || !sipe_strequal(sipmsg_find_event_header(msg), "conference")) return; xn_conference_info = sipe_xml_parse(msg->body, msg->bodylen); if (!xn_conference_info) return; focus_uri = sipe_xml_attribute(xn_conference_info, "entity"); session = sipe_session_find_conference(sipe_private, focus_uri); if (!session) { SIPE_DEBUG_INFO("sipe_process_conference: unable to find conf session with focus=%s", focus_uri); return; } if (!session->chat_session->backend) { gchar *self = sip_uri_self(sipe_private); /* create chat */ session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC, session->chat_session, session->chat_session->title, self); just_joined = TRUE; /* @TODO ask for full state (re-subscribe) if it was a partial one - * this is to obtain full list of conference participants. */ g_free(self); } /* subject */ if ((xn_subject = sipe_xml_child(xn_conference_info, "conference-description/subject"))) { g_free(session->subject); session->subject = sipe_xml_data(xn_subject); sipe_backend_chat_topic(session->chat_session->backend, session->subject); SIPE_DEBUG_INFO("sipe_process_conference: subject=%s", session->subject ? session->subject : ""); } /* IM MCU URI */ if (!session->im_mcu_uri) { for (node = sipe_xml_child(xn_conference_info, "conference-description/conf-uris/entry"); node; node = sipe_xml_twin(node)) { gchar *purpose = sipe_xml_data(sipe_xml_child(node, "purpose")); if (sipe_strequal("chat", purpose)) { g_free(purpose); session->im_mcu_uri = sipe_xml_data(sipe_xml_child(node, "uri")); SIPE_DEBUG_INFO("sipe_process_conference: im_mcu_uri=%s", session->im_mcu_uri); break; } g_free(purpose); } } /* organizer */ if (!session->chat_session->organizer) { node = sipe_xml_child(xn_conference_info, "conference-description/organizer/display-name"); if (node) { session->chat_session->organizer = sipe_xml_data(node); } } /* join URL */ if (!session->chat_session->join_url) { node = sipe_xml_child(xn_conference_info, "conference-description/join-url"); if (node) { session->chat_session->join_url = sipe_xml_data(node); } } /* dial-in conference id */ if (!session->chat_session->dial_in_conf_id) { node = sipe_xml_child(xn_conference_info, "conference-description/pstn-access/id"); if (node) { session->chat_session->dial_in_conf_id = sipe_xml_data(node); } } /* users */ for (node = sipe_xml_child(xn_conference_info, "users/user"); node; node = sipe_xml_twin(node)) { const gchar *user_uri = sipe_xml_attribute(node, "entity"); const gchar *state = sipe_xml_attribute(node, "state"); gchar *role = sipe_xml_data(sipe_xml_child(node, "roles/entry")); gboolean is_operator = sipe_strequal(role, "presenter"); gboolean is_in_im_mcu = FALSE; gchar *self = sip_uri_self(sipe_private); if (sipe_strequal("deleted", state)) { if (sipe_backend_chat_find(session->chat_session->backend, user_uri)) { sipe_backend_chat_remove(session->chat_session->backend, user_uri); } } else { /* endpoints */ const sipe_xml *endpoint; for (endpoint = sipe_xml_child(node, "endpoint"); endpoint; endpoint = sipe_xml_twin(endpoint)) { const gchar *session_type; gchar *status = sipe_xml_data(sipe_xml_child(endpoint, "status")); gboolean connected = sipe_strequal("connected", status); g_free(status); if (!connected) continue; session_type = sipe_xml_attribute(endpoint, "session-type"); if (sipe_strequal("chat", session_type)) { is_in_im_mcu = TRUE; if (!sipe_backend_chat_find(session->chat_session->backend, user_uri)) { sipe_backend_chat_add(session->chat_session->backend, user_uri, !just_joined && g_ascii_strcasecmp(user_uri, self)); } if (is_operator) { sipe_backend_chat_operator(session->chat_session->backend, user_uri); } } else if (sipe_strequal("audio-video", session_type)) { #ifdef HAVE_VV if (!session->is_call) audio_was_added = TRUE; process_conference_av_endpoint(endpoint, user_uri, self, session); #endif } else if (sipe_strequal("applicationsharing", session_type)) { #ifdef HAVE_APPSHARE if (sipe_core_conf_get_appshare_role(SIPE_CORE_PUBLIC, session->chat_session) == SIPE_APPSHARE_ROLE_NONE && !sipe_strequal(user_uri, self)) { presentation_was_added = process_conference_appshare_endpoint(endpoint); } #endif } } if (!is_in_im_mcu) { if (sipe_backend_chat_find(session->chat_session->backend, user_uri)) { sipe_backend_chat_remove(session->chat_session->backend, user_uri); } } } g_free(role); g_free(self); } #ifdef HAVE_VV if (audio_was_added) { session->is_call = TRUE; ask_accept_voice_conference(sipe_private, focus_uri, NULL, (SipeUserAskCb) call_accept_cb, NULL); } #ifdef HAVE_APPSHARE if (presentation_was_added) { sipe_core_appshare_connect_conference(SIPE_CORE_PUBLIC, session->chat_session, TRUE); } #endif #endif // HAVE_VV /* entity-view, locked */ for (node = sipe_xml_child(xn_conference_info, "conference-view/entity-view"); node; node = sipe_xml_twin(node)) { const sipe_xml *xn_type = sipe_xml_child(node, "entity-state/media/entry/type"); gchar *tmp = NULL; if (xn_type && sipe_strequal("chat", (tmp = sipe_xml_data(xn_type)))) { const sipe_xml *xn_locked = sipe_xml_child(node, "entity-state/locked"); if (xn_locked) { gchar *locked = sipe_xml_data(xn_locked); gboolean prev_locked = session->locked; session->locked = sipe_strequal(locked, "true"); if (prev_locked && !session->locked) { sipe_user_present_info(sipe_private, session, _("This conference is no longer locked. Additional participants can now join.")); } if (!prev_locked && session->locked) { sipe_user_present_info(sipe_private, session, _("This conference is locked. Nobody else can join the conference while it is locked.")); } SIPE_DEBUG_INFO("sipe_process_conference: session->locked=%s", session->locked ? "TRUE" : "FALSE"); g_free(locked); } } g_free(tmp); } sipe_xml_free(xn_conference_info); if (session->im_mcu_uri) { struct sip_dialog *dialog = sipe_dialog_find(session, session->im_mcu_uri); if (!dialog) { dialog = sipe_dialog_add(session); dialog->callid = g_strdup(session->callid); dialog->with = g_strdup(session->im_mcu_uri); /* send INVITE to IM MCU */ sipe_im_invite(sipe_private, session, dialog->with, NULL, NULL, NULL, FALSE); } } sipe_process_pending_invite_queue(sipe_private, session); } void sipe_conf_immcu_closed(struct sipe_core_private *sipe_private, struct sip_session *session) { sipe_user_present_info(sipe_private, session, _("You have been disconnected from this conference.")); sipe_backend_chat_close(session->chat_session->backend); } void conf_session_close(struct sipe_core_private *sipe_private, struct sip_session *session) { if (session) { /* unsubscribe from focus */ sipe_subscribe_conference(sipe_private, session->chat_session->id, TRUE); if (session->focus_dialog) { /* send BYE to focus */ sip_transport_bye(sipe_private, session->focus_dialog); } } } void sipe_process_imdn(struct sipe_core_private *sipe_private, struct sipmsg *msg) { gchar *with = sipmsg_parse_from_address(msg); const gchar *callid = sipmsg_find_call_id_header(msg); static struct sip_session *session; sipe_xml *xn_imdn; const sipe_xml *node; gchar *message_id; gchar *message; session = sipe_session_find_chat_or_im(sipe_private, callid, with); if (!session) { SIPE_DEBUG_INFO("sipe_process_imdn: unable to find conf session with callid=%s", callid); g_free(with); return; } xn_imdn = sipe_xml_parse(msg->body, msg->bodylen); message_id = sipe_xml_data(sipe_xml_child(xn_imdn, "message-id")); message = g_hash_table_lookup(session->conf_unconfirmed_messages, message_id); /* recipient */ for (node = sipe_xml_child(xn_imdn, "recipient"); node; node = sipe_xml_twin(node)) { gchar *tmp = parse_from(sipe_xml_attribute(node, "uri")); gchar *uri = parse_from(tmp); gchar *status = sipe_xml_data(sipe_xml_child(node, "status")); guint error = status ? g_ascii_strtoull(status, NULL, 10) : 0; /* default to error if missing or conversion failed */ if ((error == 0) || (error >= 300)) sipe_user_present_message_undelivered(sipe_private, session, error, -1, uri, message); g_free(status); g_free(tmp); g_free(uri); } sipe_xml_free(xn_imdn); g_hash_table_remove(session->conf_unconfirmed_messages, message_id); SIPE_DEBUG_INFO("sipe_process_imdn: removed message %s from conf_unconfirmed_messages(count=%d)", message_id, g_hash_table_size(session->conf_unconfirmed_messages)); g_free(message_id); g_free(with); } void sipe_core_conf_make_leader(struct sipe_core_public *sipe_public, gpointer parameter, const gchar *buddy_name) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_chat_session *chat_session = parameter; struct sip_session *session; SIPE_DEBUG_INFO("sipe_core_conf_make_leader: chat_title=%s", chat_session->title); session = sipe_session_find_chat(sipe_private, chat_session); sipe_conf_modify_user_role(sipe_private, session, buddy_name); } void sipe_core_conf_remove_from(struct sipe_core_public *sipe_public, gpointer parameter, const gchar *buddy_name) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_chat_session *chat_session = parameter; struct sip_session *session; SIPE_DEBUG_INFO("sipe_core_conf_remove_from: chat_title=%s", chat_session->title); session = sipe_session_find_chat(sipe_private, chat_session); sipe_conf_delete_user(sipe_private, session, buddy_name); } gchar * sipe_conf_build_uri(const gchar *focus_uri, const gchar *session_type) { gchar **parts = g_strsplit(focus_uri, ":focus:", 2); gchar *result = NULL; if (g_strv_length(parts) == 2) { result = g_strconcat(parts[0], ":", session_type, ":", parts[1], NULL); } g_strfreev(parts); return result; } static gchar * access_numbers_info(struct sipe_core_public *sipe_public) { GString *result = g_string_new(""); GList *keys = g_hash_table_get_keys(SIPE_CORE_PRIVATE->access_numbers); keys = g_list_sort(keys, (GCompareFunc)g_strcmp0); for (; keys; keys = g_list_delete_link(keys, keys)) { gchar *value; value = g_hash_table_lookup(SIPE_CORE_PRIVATE->access_numbers, keys->data); g_string_append(result, keys->data); g_string_append(result, "    "); g_string_append(result, value); g_string_append(result, "
"); } return g_string_free(result, FALSE); } gchar * sipe_core_conf_entry_info(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session) { gchar *access_info = access_numbers_info(sipe_public); gchar *result = g_strdup_printf( "%s
" "%s: %s
" "%s: %s
" "
" "%s:
" "%s
" "
" "%s: %s
" "
" "%s
" "%s", _("Dial-in info"), _("Number"), SIPE_CORE_PRIVATE->default_access_number ? SIPE_CORE_PRIVATE->default_access_number : "", _("Conference ID"), chat_session->dial_in_conf_id ? chat_session->dial_in_conf_id : "", _("Meeting link"), chat_session->join_url ? chat_session->join_url : "", _("Organizer"), chat_session->organizer ? chat_session->organizer : "", _("Alternative dial-in numbers"), access_info); g_free(access_info); return result; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-conf.h ================================================ /** * @file sipe-conf.h * * pidgin-sipe * * Copyright (C) 2009-10 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipmsg; struct sip_session; struct sipe_core_private; /** * Obtains conferencing capabilities enabled on the server. * * @param sipe_private SIPE core data */ void sipe_conf_get_capabilities(struct sipe_core_private *sipe_private); /** * Checks whether given Multipoint Control Unit type is supported by the server. */ gboolean sipe_conf_supports_mcu_type(struct sipe_core_private *sipe_private, const gchar *type); /** * Creates conference. */ void sipe_conf_add(struct sipe_core_private *sipe_private, const gchar* who); /** * Processes incoming INVITE with * Content-Type: application/ms-conf-invite+xml * i.e. invitation to join conference. * * Server 2007+ functionality. */ void process_incoming_invite_conf(struct sipe_core_private *sipe_private, struct sipmsg *msg); /** * Create new session with Focus URI * * @param chat_session non-NULL if we rejoin a conference * @param focus_uri non-NULL if we create a new conference * * @return new SIP session */ struct sip_session * sipe_conf_create(struct sipe_core_private *sipe_private, struct sipe_chat_session *chat_session, const gchar *focus_uri); /** * Process of conference state * Content-Type: application/conference-info+xml */ void sipe_process_conference(struct sipe_core_private *sipe_private, struct sipmsg * msg); /** * Invites counterparty to join conference. */ void sipe_invite_conf(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar* who); /** * Modify User Role. * Sends request to Focus. * INFO method is a carrier of application/cccp+xml */ void sipe_conf_modify_user_role(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar* who); /** * Ejects user from conference. * Sends request to Focus. * INFO method is a carrier of application/cccp+xml */ void sipe_conf_delete_user(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar* who); /** * Notifies conference call participants of our microphone mute state. */ void sipe_conf_announce_audio_mute_state(struct sipe_core_private *sipe_private, struct sip_session *session, gboolean is_muted); /** * Invokes when we are ejected from conference * for example or conference has been timed out. */ void sipe_conf_immcu_closed(struct sipe_core_private *sipe_private, struct sip_session *session); /** * Removes a session waiting to be accepted or declined by the user. * * @param sipe_private SIPE core data * @param msg SIP CANCEL message. If NULL is passed, all sessions not accepted * will be canceled */ void sipe_conf_cancel_unaccepted(struct sipe_core_private *sipe_private, struct sipmsg *msg); /** * Creates URI for given session type within a conference specified by its * focus URI. * * @param focus_uri conference focus URI * @param session_type type of session for which to build the URI, * e.g. "audio-video" */ gchar * sipe_conf_build_uri(const gchar *focus_uri, const gchar *session_type); /** * Invokes when we leave conversation. * Usually by closing chat wingow. */ void conf_session_close(struct sipe_core_private *sipe_private, struct sip_session *session); /** * Invoked to process message delivery notification * in conference. */ void sipe_process_imdn(struct sipe_core_private *sipe_private, struct sipmsg *msg); ================================================ FILE: src/core/sipe-core-private.h ================================================ /** * @file sipe-core-private.h * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sip_address_data; struct sip_csta; struct sip_service_data; struct sip_transport; struct sipe_buddies; struct sipe_calendar; struct sipe_certificate; struct sipe_ews_autodiscover; struct sipe_groupchat; struct sipe_groups; struct sipe_http; struct sipe_http_request; struct sipe_lync_autodiscover; struct sipe_media_call_private; struct sipe_svc; struct sipe_ucs; struct sipe_webticket; /** * Private part of the Sipe data structure * * This part contains the information only needed by the core */ struct sipe_core_private { /** * The public part is the first item, i.e. a pointer to the * public part can also be used as a pointer to the private part. */ struct sipe_core_public public; /* sip-transport.c private data */ struct sip_transport *transport; GSList *lync_autodiscover_servers; /* Lync autodiscover */ const struct sip_service_data *service_data; /* autodiscovery SRV records */ const struct sip_address_data *address_data; /* autodiscovery A records */ gchar *user_agent; guint transport_type; guint authentication_type; /* Account information */ gchar *username; gchar *authuser; /* NULL when SSO is enabled */ gchar *password; /* NULL when SSO is enabled */ gchar *email; gchar *email_authuser; /* NULL -> use default authentication */ gchar *email_password; /* SIPE protocol information */ gchar *contact; gchar *register_callid; gchar *focus_factory_uri; GSList *sessions; GSList *sessions_to_accept; /* from REGISTER response: server events * we're allowed to subscribe to */ GSList *allowed_events; /* Presence */ gchar *status; gchar *note; time_t note_since; gboolean status_set_by_user; /* [MS-SIP] deltaNum counters */ guint deltanum_contacts; guint deltanum_acl; /* setACE (OCS2005 only) */ /* [MS-PRES] */ GSList *containers; GSList *our_publication_keys; GHashTable *our_publications; GHashTable *user_state_publications; /* Buddies */ struct sipe_groups *groups; struct sipe_buddies *buddies; /* Calendar and related stuff */ struct sipe_calendar *calendar; /* EWS autodiscover */ struct sipe_ews_autodiscover *ews_autodiscover; /* Lync autodiscover */ struct sipe_lync_autodiscover *lync_autodiscover; /* * 2005 Custom XML piece * * Possibly set by other point of presence or just other client at * earlier time. It should be preserved/modified, not overwritten. * This implies subscription to self-contact. Information kept: * * - User note * - OOF flag * - User status */ gchar *ocs2005_user_states; /* Scheduling system */ GSList *timeouts; /* Active subscriptions */ GHashTable *subscriptions; /* Voice call */ GHashTable *media_calls; gchar *test_call_bot_uri; gchar *uc_line_uri; /** * Provides the necessary information on where we can obtain * credentials for the A/V Edge server service. */ gchar *mras_uri; gchar *media_relay_username; gchar *media_relay_password; GSList *media_relays; SipeEncryptionPolicy server_av_encryption_policy; /* Group chat */ struct sipe_groupchat *groupchat; gchar *persistentChatPool_uri; /* buddy menu memory allocation */ GSList *blist_menu_containers; /* For RCC - Remote Call Control */ struct sip_csta *csta; struct sipe_dns_query *dns_query; /* HTTP service */ struct sipe_http *http; /* TLS-DSK: Certificates & Web services */ struct sipe_certificate *certificate; struct sipe_webticket *webticket; struct sipe_svc *svc; /* Unified Contact Store */ struct sipe_ucs *ucs; /* [MS-DLX] server URI */ gchar *dlx_uri; /* Addressbook server URI */ gchar *addressbook_uri; /* [MS-CONFPRO] CCCP request ID counter */ guint cccp_request_id; guint ms_filetransfer_request_id; GSList *conf_mcu_types; /* Dial-in conferencing phone numbers for different regions */ GHashTable *access_numbers; const gchar *default_access_number; /* Port ranges to use for media connections. Zero means any port. */ guint min_media_port; guint max_media_port; guint min_audio_port; guint max_audio_port; guint min_video_port; guint max_video_port; guint min_appsharing_port; guint max_appsharing_port; guint min_filetransfer_port; guint max_filetransfer_port; }; /** * Flags - stored in sipe_core_public.flags but names not exported */ /* server is OCS2007+ */ #define SIPE_CORE_PRIVATE_FLAG_OCS2007 0x80000000 /* we are connected from outside the enterprise network boundary * via Edge Server */ #define SIPE_CORE_PRIVATE_FLAG_REMOTE_USER 0x40000000 /* multiple points of presence detected */ #define SIPE_CORE_PRIVATE_FLAG_MPOP 0x20000000 /* if there is support for batched subscription*/ #define SIPE_CORE_PRIVATE_FLAG_BATCHED_SUPPORT 0x10000000 /* if note is out-of-office note */ #define SIPE_CORE_PRIVATE_FLAG_OOF_NOTE 0x08000000 /* whether we published our initial state or not */ #define SIPE_CORE_PRIVATE_FLAG_INITIAL_PUBLISH 0x04000000 /* whether basic access level is set or not */ #define SIPE_CORE_PRIVATE_FLAG_ACCESS_LEVEL_SET 0x02000000 /* whether subscribed to buddies presence or not */ #define SIPE_CORE_PRIVATE_FLAG_SUBSCRIBED_BUDDIES 0x01000000 /* user enabled Single-Sign On */ #define SIPE_CORE_PRIVATE_FLAG_SSO 0x00800000 /* server is Lync 2013+ */ #define SIPE_CORE_PRIVATE_FLAG_LYNC2013 0x00400000 /* server is Skype for Business (RTC/6.0 +) */ #define SIPE_CORE_PRIVATE_FLAG_SFB 0x00200000 #define SIPE_CORE_PUBLIC_FLAG_IS(flag) \ ((sipe_private->public.flags & SIPE_CORE_FLAG_ ## flag) == SIPE_CORE_FLAG_ ## flag) #define SIPE_CORE_PUBLIC_FLAG_SET(flag) \ (sipe_private->public.flags |= SIPE_CORE_FLAG_ ## flag) #define SIPE_CORE_PUBLIC_FLAG_UNSET(flag) \ (sipe_private->public.flags &= ~SIPE_CORE_FLAG_ ## flag) #define SIPE_CORE_PRIVATE_FLAG_IS(flag) \ ((sipe_private->public.flags & SIPE_CORE_PRIVATE_FLAG_ ## flag) == SIPE_CORE_PRIVATE_FLAG_ ## flag) #define SIPE_CORE_PRIVATE_FLAG_SET(flag) \ (sipe_private->public.flags |= SIPE_CORE_PRIVATE_FLAG_ ## flag) #define SIPE_CORE_PRIVATE_FLAG_UNSET(flag) \ (sipe_private->public.flags &= ~SIPE_CORE_PRIVATE_FLAG_ ## flag) /* Convenience macros */ #define SIPE_CORE_PRIVATE ((struct sipe_core_private *)sipe_public) #define SIPE_CORE_PUBLIC ((struct sipe_core_public *)sipe_private) /** * sipe-core internal functions */ void sipe_core_backend_initialized(struct sipe_core_private *sipe_private, guint authentication); void sipe_core_connection_cleanup(struct sipe_core_private *sipe_private); void sipe_core_email_authentication(struct sipe_core_private *sipe_private, struct sipe_http_request *request); const gchar *sipe_core_user_agent(struct sipe_core_private *sipe_private); /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-core.c ================================================ /** * @file sipe-core.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Some notes on the history of this project/code/copyrights: * * - the project is called SIPE, but originally the code was only written * for the libpurple framework, i.e. Pidgin. Hence the package name is * "pidgin-sipe". * * - in the beginning almost all of the code was located in a module * called "sipe.c". During the effort to remove the libpurple * dependencies from the SIPE core, thousands of lines of code got * shifted out of sipe.c, mostly to newly created modules and sipe.c * ceased to exist. * * - it would have been tedious to track down the original author or * copyright and preserve them for each line of code that was moved. * Therefore the new modules started with a fresh copyright notice * (like the one above). * * - the original copyright notices from sipe.c have been moved to this * file (see below) and *MUST* be preserved! * * - if necessary the author of a line of code in question can still be * reconstructed from the git repository information. * See also "man git-blame" * * - if you think your copyright should be restored for a piece of code, * then please contact the SIPE project to fix the source files ASAP. * *------------------- Copyright notices from "sipe.c" --------------- * Copyright (C) 2010-11 SIPE Project * Copyright (C) 2009-10 pier11 * Copyright (C) 2008 Novell, Inc. * Copyright (C) 2007-09 Anibal Avelar * Copyright (C) 2005 Thomas Butter * * *** * Thanks to Google's Summer of Code Program and the helpful mentors * *** *------------------- Copyright notices from "sipe.c" --------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sipe-common.h" #include "sip-csta.h" #include "sip-sec.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-cal.h" #include "sipe-certificate.h" #include "sipe-chat.h" #include "sipe-conf.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-crypt.h" #include "sipe-ews-autodiscover.h" #include "sipe-group.h" #include "sipe-groupchat.h" #include "sipe-http.h" #include "sipe-lync-autodiscover.h" #include "sipe-media.h" #include "sipe-mime.h" #include "sipe-nls.h" #include "sipe-ocs2007.h" #include "sipe-schedule.h" #include "sipe-session.h" #include "sipe-status.h" #include "sipe-subscriptions.h" #include "sipe-svc.h" #include "sipe-ucs.h" #include "sipe-utils.h" #include "sipe-webticket.h" #if !GLIB_CHECK_VERSION(2,18,0) #error glib-2.0 >= 2.18.0 is required to build SIPE #endif #ifdef PACKAGE_GIT_COMMIT #define SIPE_CORE_VERSION PACKAGE_VERSION " (git commit " PACKAGE_GIT_COMMIT " / " #else #define SIPE_CORE_VERSION PACKAGE_VERSION " (" #endif /* translate config.h options to strings */ static const gchar * const sipe_core_build_options[] = { /* Authentication */ #ifdef HAVE_SSPI "SSPI", #elif defined(HAVE_GSSAPI_ONLY) "GSSAPI", #ifndef HAVE_GSSAPI_PASSWORD_SUPPORT "SSO only", #endif #else #ifdef HAVE_GSSAPI_GSSAPI_H "Kerberos 5 & NTLM", #else "NTLM", #endif #endif /* D-Bus */ #ifdef HAVE_DBUS "D-Bus", #endif /* Media */ #ifdef HAVE_VV "Voice & Video", #ifdef HAVE_SRTP "SRTP", #endif #ifdef HAVE_XDATA "Lync FT", #endif #ifdef HAVE_APPSHARE #ifdef HAVE_APPSHARE_SERVER "Application Sharing - full", #else "Application Sharing - client only", #endif #endif #endif /* Development */ #ifdef HAVE_VALGRIND "Valgrind", #endif NULL }; /* locale_dir is unused if ENABLE_NLS is not defined */ void sipe_core_init(SIPE_UNUSED_PARAMETER const char *locale_dir) { srand(time(NULL)); sip_sec_init(); #ifdef ENABLE_NLS SIPE_DEBUG_INFO("bindtextdomain = %s", bindtextdomain(PACKAGE_NAME, locale_dir)); SIPE_DEBUG_INFO("bind_textdomain_codeset = %s", bind_textdomain_codeset(PACKAGE_NAME, "UTF-8")); textdomain(PACKAGE_NAME); #endif /* Initialization for crypto backend (production mode) */ sipe_crypto_init(TRUE); sipe_mime_init(); sipe_status_init(); } void sipe_core_destroy(void) { sipe_chat_destroy(); sipe_status_shutdown(); sipe_mime_shutdown(); sipe_crypto_shutdown(); sip_sec_destroy(); } gchar *sipe_core_about(void) { gchar *options = g_strjoinv(" / ", (gchar **) sipe_core_build_options); gchar *about = g_strdup_printf( /* * Non-translatable parts, like markup, are hard-coded * into the format string. This requires more translatable * texts but it makes the translations less error prone. */ "SIPE " SIPE_CORE_VERSION "%s)
" "
" /* 1 */ "%s:
" " - Skype for Business
" " - Microsoft Office 365
" " - Microsoft Business Productivity Online Suite (BPOS)
" " - Microsoft Lync Server
" " - Microsoft Office Communications Server 2007 R2
" " - Microsoft Office Communications Server 2007
" " - Microsoft Live Communications Server 2005
" " - Microsoft Live Communications Server 2003
" "
" /* 2 */ "%s:
" PACKAGE_URL "
" /* 3,4 */ "%s: %s
" /* 5,6 */ "%s: %s
" /* 7 */ "%s: Transifex.com
" /* 8 */ "%s: GPL-2.0-or-later
" "
" /* 9 (REMOVED) */ /* 10,11 */ "%sTransifex.com%s.
" "
" /* 12 */ "%s:
" " - Stefan Becker
" " - Jakub Adam
" " - Jochen De Smet (retired, Miranda port)
" " - Michael Lamb (retired, Adium port)
" " - Anibal Avelar (retired)
" " - Gabriel Burt (retired)
" " - pier11 (retired)
" " - Tomáš Hrabčík (retired)
" "
" /* 13 */ "%s
" , options, /* The next 13 texts make up the SIPE about note text */ /* About note, part 1/13: introduction */ _("A third-party plugin implementing extended version of SIP/SIMPLE used by various products"), /* About note, part 2/13: home page URL (label) */ _("Home Page"), /* About note, part 3/13: support forum URL (label) */ _("Support"), /* About note, part 4/13: support forum name (hyperlink text) */ _("Help Forum"), /* About note, part 5/13: bug tracker URL (label) */ _("Report Problems"), /* About note, part 6/13: bug tracker URL (hyperlink text) */ _("Bug Tracker"), /* About note, part 7/13: translation service URL (label) */ _("Translations"), /* About note, part 8/13: license type (label) */ _("License"), /* About note, part 9/13: (REMOVED) */ /* About note, part 10/13: translation request, text before Transifex.com URL */ /* append a space if text is not empty */ _("Please help us to translate SIPE to your native language here at "), /* About note, part 11/13: translation request, text after Transifex.com URL */ /* start with a space if text is not empty */ _(" using convenient web interface"), /* About note, part 12/13: author list (header) */ _("Authors"), /* About note, part 13/13: Localization credit */ /* PLEASE NOTE: do *NOT* simply translate the english original */ /* but write something similar to the following sentence: */ /* "Localization for (): " */ _("Original texts in English (en): SIPE developers") ); g_free(options); return(about); } struct sipe_core_public *sipe_core_allocate(const gchar *signin_name, gboolean sso, const gchar *login_account, const gchar *password, const gchar *email, const gchar *email_url, const gchar **errmsg) { struct sipe_core_private *sipe_private; gchar **user_domain; gchar *options = g_strjoinv(" / ", (gchar **) sipe_core_build_options); SIPE_LOG_INFO("sipe_core_allocate: SIPE version " SIPE_CORE_VERSION "%s)", options); g_free(options); SIPE_DEBUG_INFO("sipe_core_allocate: signin_name '%s'", signin_name); /* ensure that sign-in name doesn't contain invalid characters */ if (strpbrk(signin_name, "\t\v\r\n") != NULL) { *errmsg = _("SIP Exchange user name contains invalid characters"); return NULL; } /* ensure that sign-in name format is name@domain */ if (!strchr(signin_name, '@') || g_str_has_prefix(signin_name, "@") || g_str_has_suffix(signin_name, "@")) { *errmsg = _("User name should be a valid SIP URI\nExample: user@company.com"); return NULL; } /* ensure that Password is valid when SSO is not selected */ if (!sso && is_empty(password)) { *errmsg = _("Password is required when Single Sign-On is not enabled"); return NULL; } /* ensure that email format is name@domain (if provided) */ if (!is_empty(email) && (!strchr(email, '@') || g_str_has_prefix(email, "@") || g_str_has_suffix(email, "@"))) { *errmsg = _("Email address should be valid if provided\nExample: user@company.com"); return NULL; } /* ensure that user name doesn't contain spaces */ user_domain = g_strsplit(signin_name, "@", 2); SIPE_DEBUG_INFO("sipe_core_allocate: user '%s' domain '%s'", user_domain[0], user_domain[1]); if (strchr(user_domain[0], ' ') != NULL) { g_strfreev(user_domain); *errmsg = _("SIP Exchange user name contains whitespace"); return NULL; } /* ensure that email_url is in proper format if enabled (if provided). * Example (Exchange): https://server.company.com/EWS/Exchange.asmx * Example (Domino) : https://[domino_server]/[mail_database_name].nsf */ if (!is_empty(email_url)) { char *tmp = g_ascii_strdown(email_url, -1); if (!g_str_has_prefix(tmp, "https://")) { g_free(tmp); g_strfreev(user_domain); *errmsg = _("Email services URL should be valid if provided\n" "Example: https://exchange.corp.com/EWS/Exchange.asmx\n" "Example: https://domino.corp.com/maildatabase.nsf"); return NULL; } g_free(tmp); } /* re-use sign-in name if login is empty */ if (is_empty(login_account)) login_account = signin_name; sipe_private = g_new0(struct sipe_core_private, 1); SIPE_CORE_PRIVATE_FLAG_UNSET(SUBSCRIBED_BUDDIES); SIPE_CORE_PRIVATE_FLAG_UNSET(INITIAL_PUBLISH); SIPE_CORE_PRIVATE_FLAG_UNSET(SSO); if (sso) SIPE_CORE_PRIVATE_FLAG_SET(SSO); sipe_private->username = g_strdup(signin_name); sipe_private->email = is_empty(email) ? g_strdup(signin_name) : g_strdup(email); sipe_private->authuser = sso ? NULL : g_strdup(login_account); sipe_private->password = sso ? NULL : g_strdup(password); sipe_private->public.sip_name = g_strdup(user_domain[0]); sipe_private->public.sip_domain = g_strdup(user_domain[1]); g_strfreev(user_domain); sipe_group_init(sipe_private); sipe_buddy_init(sipe_private); sipe_private->our_publications = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_destroy); sipe_subscriptions_init(sipe_private); sipe_lync_autodiscover_init(sipe_private); sipe_ews_autodiscover_init(sipe_private); sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_UNSET); sipe_private->media_calls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); sipe_private->access_numbers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); return((struct sipe_core_public *)sipe_private); } void sipe_core_backend_initialized(struct sipe_core_private *sipe_private, guint authentication) { const gchar *value; sipe_private->authentication_type = authentication; /* user specified email login? */ value = sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_EMAIL_LOGIN); if (!is_empty(value)) { sipe_private->email_authuser = g_strdup(value); sipe_private->email_password = g_strdup(sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_EMAIL_PASSWORD)); } } void sipe_core_connection_cleanup(struct sipe_core_private *sipe_private) { sipe_http_free(sipe_private); sip_transport_drop(sipe_private); sipe_schedule_cancel_all(sipe_private); if (sipe_private->allowed_events) sipe_utils_slist_free_full(sipe_private->allowed_events, g_free); sipe_ocs2007_free(sipe_private); sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC); if (sipe_private->contact) g_free(sipe_private->contact); sipe_private->contact = NULL; if (sipe_private->register_callid) g_free(sipe_private->register_callid); sipe_private->register_callid = NULL; if (sipe_private->focus_factory_uri) g_free(sipe_private->focus_factory_uri); sipe_private->focus_factory_uri = NULL; sipe_groupchat_free(sipe_private); while (sipe_private->lync_autodiscover_servers) sipe_private->lync_autodiscover_servers = sipe_lync_autodiscover_pop(sipe_private->lync_autodiscover_servers); } void sipe_core_deallocate(struct sipe_core_public *sipe_public) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; #ifdef HAVE_VV sipe_media_handle_going_offline(sipe_private); #endif /* leave all conversations */ if (sipe_private->sessions) { GSList *entry; while ((entry = sipe_private->sessions) != NULL) { sipe_session_close(sipe_private, entry->data); } } sipe_conf_cancel_unaccepted(sipe_private, NULL); if (sipe_private->csta) { sip_csta_close(sipe_private); } /* pending service requests must be cancelled first */ sipe_svc_free(sipe_private); sipe_webticket_free(sipe_private); sipe_ucs_free(sipe_private); sipe_lync_autodiscover_free(sipe_private); if (sipe_backend_connection_is_valid(SIPE_CORE_PUBLIC)) { sipe_subscriptions_unsubscribe(sipe_private); sip_transport_deregister(sipe_private); } sipe_core_connection_cleanup(sipe_private); sipe_ews_autodiscover_free(sipe_private); sipe_cal_calendar_free(sipe_private->calendar); sipe_certificate_free(sipe_private); g_free(sipe_private->public.sip_name); g_free(sipe_private->public.sip_domain); g_free(sipe_private->username); g_free(sipe_private->user_agent); g_free(sipe_private->email_password); g_free(sipe_private->email_authuser); g_free(sipe_private->email); g_free(sipe_private->password); g_free(sipe_private->authuser); g_free(sipe_private->status); g_free(sipe_private->note); g_free(sipe_private->ocs2005_user_states); sipe_buddy_free(sipe_private); g_hash_table_destroy(sipe_private->our_publications); g_hash_table_destroy(sipe_private->user_state_publications); g_hash_table_destroy(sipe_private->media_calls); sipe_subscriptions_destroy(sipe_private); sipe_group_free(sipe_private); if (sipe_private->our_publication_keys) sipe_utils_slist_free_full(sipe_private->our_publication_keys, g_free); #ifdef HAVE_VV g_free(sipe_private->test_call_bot_uri); g_free(sipe_private->uc_line_uri); g_free(sipe_private->mras_uri); g_free(sipe_private->media_relay_username); g_free(sipe_private->media_relay_password); sipe_media_relay_list_free(sipe_private->media_relays); #endif g_free(sipe_private->persistentChatPool_uri); g_free(sipe_private->addressbook_uri); g_free(sipe_private->dlx_uri); sipe_utils_slist_free_full(sipe_private->conf_mcu_types, g_free); g_hash_table_destroy(sipe_private->access_numbers); g_free(sipe_private); } void sipe_core_email_authentication(struct sipe_core_private *sipe_private, struct sipe_http_request *request) { if (sipe_private->email_authuser) { sipe_http_request_authentication(request, sipe_private->email_authuser, sipe_private->email_password); } } const gchar *sipe_core_user_agent(struct sipe_core_private *sipe_private) { if (!sipe_private->user_agent) { const gchar *useragent = sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_USER_AGENT); if (is_empty(useragent)) { /*@TODO: better approach to define _user_ OS, it's version and host architecture */ /* ref: lzodefs.h */ #if defined(__linux__) || defined(__linux) || defined(__LINUX__) #define SIPE_TARGET_PLATFORM "linux" #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__) #define SIPE_TARGET_PLATFORM "bsd" #elif defined(__APPLE__) || defined(__MACOS__) #define SIPE_TARGET_PLATFORM "macosx" #elif defined(_AIX) || defined(__AIX__) || defined(__aix__) #define SIPE_TARGET_PLATFORM "aix" #elif defined(__solaris__) || defined(__sun) #define SIPE_TARGET_PLATFORM "sun" #elif defined(_WIN32) #define SIPE_TARGET_PLATFORM "win" #elif defined(__CYGWIN__) #define SIPE_TARGET_PLATFORM "cygwin" #elif defined(__hpux__) #define SIPE_TARGET_PLATFORM "hpux" #elif defined(__sgi__) #define SIPE_TARGET_PLATFORM "irix" #else #define SIPE_TARGET_PLATFORM "unknown" #endif #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64) #define SIPE_TARGET_ARCH "x86_64" #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386) #define SIPE_TARGET_ARCH "i386" #elif defined(__ppc64__) #define SIPE_TARGET_ARCH "ppc64" #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR) #define SIPE_TARGET_ARCH "ppc" #elif defined(__hppa__) || defined(__hppa) #define SIPE_TARGET_ARCH "hppa" #elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000) #define SIPE_TARGET_ARCH "mips" #elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x) #define SIPE_TARGET_ARCH "s390" #elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8) #define SIPE_TARGET_ARCH "sparc" #elif defined(__arm__) #define SIPE_TARGET_ARCH "arm" #else #define SIPE_TARGET_ARCH "other" #endif gchar *backend = sipe_backend_version(); sipe_private->user_agent = g_strdup_printf("%s Sipe/" PACKAGE_VERSION " (" SIPE_TARGET_PLATFORM "-" SIPE_TARGET_ARCH ")", backend); g_free(backend); } else { sipe_private->user_agent = g_strdup(useragent); } } return(sipe_private->user_agent); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-crypt-nss.c ================================================ /** * @file sipe-crypt-nss.c * * pidgin-sipe * * Copyright (C) 2011-2015 SIPE Project * Copyright (C) 2010 pier11 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * Cypher routines implementation based on NSS. * Includes: RC4, DES */ #include "glib.h" #include "nss.h" /* * Work around a compiler error in NSS 3.13.x. Let's hope they fix it for * 3.14.x. See also: https://bugzilla.mozilla.org/show_bug.cgi?id=702090 */ #if (NSS_VMAJOR == 3) && (NSS_VMINOR == 13) #define __GNUC_MINOR __GNUC_MINOR__ #endif #include "pk11pub.h" #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-crypt.h" /* NSS specific initialization/shutdown */ void sipe_crypto_init(SIPE_UNUSED_PARAMETER gboolean production_mode) { if (!NSS_IsInitialized()) { /* * I have a bad feeling about this: according to the NSS * documentation, NSS can only be initialized once. * Unfortunately there seems to be no way to initialize a * "NSS context" that could then be used by the SIPE code * to avoid colliding with other NSS users. * * This seems to work, so it'll have to do for now. * * It might also be required to move this to the backend * so that the backend code can decide when it is OK to * initialize NSS. */ NSS_NoDB_Init("."); SIPE_DEBUG_INFO_NOFORMAT("NSS initialised"); } } void sipe_crypto_shutdown(void) { /* do nothing for NSS. * We don't want accedently switch off NSS possibly used by other plugin - * ssl-nss in Pidgin for example. */ } /* PRIVATE methods */ static PK11Context* sipe_crypt_ctx_create(CK_MECHANISM_TYPE cipherMech, const guchar *key, gsize key_length, const guchar *iv, gsize iv_length) { PK11SlotInfo* slot; SECItem keyItem; SECItem ivItem; PK11SymKey* SymKey; SECItem *SecParam; PK11Context* EncContext; /* For key */ slot = PK11_GetBestSlot(cipherMech, NULL); keyItem.type = siBuffer; keyItem.data = (unsigned char *)key; keyItem.len = key_length; SymKey = PK11_ImportSymKey(slot, cipherMech, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, NULL); /* Parameter for crypto context */ ivItem.type = siBuffer; ivItem.data = (unsigned char *)iv; ivItem.len = iv_length; SecParam = PK11_ParamFromIV(cipherMech, &ivItem); EncContext = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, SymKey, SecParam); PK11_FreeSymKey(SymKey); SECITEM_FreeItem(SecParam, PR_TRUE); PK11_FreeSlot(slot); return EncContext; } static void sipe_crypt_ctx_encrypt(PK11Context* EncContext, const guchar *in, gsize length, guchar *out) { int tmp1_outlen; PK11_CipherOp(EncContext, out, &tmp1_outlen, length, (unsigned char *)in, length); } static void sipe_crypt_ctx_destroy(PK11Context* EncContext) { PK11_DestroyContext(EncContext, PR_TRUE); } static void sipe_crypt(CK_MECHANISM_TYPE cipherMech, const guchar *key, gsize key_length, const guchar *plaintext, gsize plaintext_length, guchar *encrypted_text) { void *EncContext; EncContext = sipe_crypt_ctx_create(cipherMech, key, key_length, NULL, 0); sipe_crypt_ctx_encrypt(EncContext, plaintext, plaintext_length, encrypted_text); sipe_crypt_ctx_destroy(EncContext); } /* PUBLIC methods */ void sipe_crypt_des(const guchar *key, const guchar *plaintext, gsize plaintext_length, guchar *encrypted_text) { sipe_crypt(CKM_DES_ECB, key, 8, plaintext, plaintext_length, encrypted_text); } void sipe_crypt_rc4(const guchar *key, gsize key_length, const guchar *plaintext, gsize plaintext_length, guchar *encrypted_text) { sipe_crypt(CKM_RC4, key, key_length, plaintext, plaintext_length, encrypted_text); } gboolean sipe_crypt_rsa_encrypt(gpointer public, gsize modulus_length, const guchar *plaintext, guchar *encrypted_text) { SECStatus result = PK11_PubEncryptRaw(public, encrypted_text, (guchar *) plaintext, modulus_length, NULL); return(result == SECSuccess); } gboolean sipe_crypt_rsa_decrypt(gpointer private, gsize modulus_length, const guchar *encrypted_text, guchar *plaintext) { unsigned int length; SECStatus result = PK11_PubDecryptRaw(private, (guchar *) encrypted_text, &length, modulus_length, plaintext, modulus_length); return((result == SECSuccess) && (length == modulus_length)); } guchar *sipe_crypt_rsa_sign(gpointer private, const guchar *digest, gsize digest_length, gsize *signature_length) { SECItem digItem; SECItem sigItem; SECStatus length; length = PK11_SignatureLen(private); if (length < 0) return(NULL); /* digest to sign (= encrypt) with private key */ digItem.data = (guchar *) digest; digItem.len = digest_length; /* signature */ sigItem.data = g_malloc(length); sigItem.len = length; length = PK11_Sign(private, &sigItem, &digItem); if (length != SECSuccess) { g_free(sigItem.data); return(NULL); } *signature_length = sigItem.len; return(sigItem.data); } gboolean sipe_crypt_verify_rsa(gpointer public, const guchar *digest, gsize digest_length, const guchar *signature, gsize signature_length) { SECItem digItem; SECItem sigItem; /* digest to verify against */ digItem.data = (guchar *) digest; digItem.len = digest_length; /* signature to decrypt with public key -> digest to compare */ sigItem.data = (guchar *) signature; sigItem.len = signature_length; return(PK11_Verify(public, &sigItem, &digItem, NULL) == SECSuccess); } /* Stream RC4 cipher for file transfer */ gpointer sipe_crypt_ft_start(const guchar *key) { return sipe_crypt_ctx_create(CKM_RC4, key, 16, NULL, 0); } void sipe_crypt_ft_stream(gpointer context, const guchar *in, gsize length, guchar *out) { sipe_crypt_ctx_encrypt(context, in, length, out); } void sipe_crypt_ft_destroy(gpointer context) { sipe_crypt_ctx_destroy(context); } /* * Stream RC4 cipher for TLS * * basically the same as for FT, but with variable key length */ gpointer sipe_crypt_tls_start(const guchar *key, gsize key_length) { return sipe_crypt_ctx_create(CKM_RC4, key, key_length, NULL, 0); } void sipe_crypt_tls_stream(gpointer context, const guchar *in, gsize length, guchar *out) { sipe_crypt_ctx_encrypt(context, in, length, out); } void sipe_crypt_tls_destroy(gpointer context) { sipe_crypt_ctx_destroy(context); } /* Block AES-CBC cipher for TLS */ void sipe_crypt_tls_block(const guchar *key, gsize key_length, const guchar *iv, gsize iv_length, const guchar *in, gsize length, guchar *out) { PK11Context* context = sipe_crypt_ctx_create(CKM_AES_CBC, key, key_length, iv, iv_length); if (context) { sipe_crypt_ctx_encrypt(context, in, length, out); sipe_crypt_ctx_destroy(context); } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-crypt-openssl.c ================================================ /** * @file sipe-crypt-openssl.c * * pidgin-sipe * * Copyright (C) 2013-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * Cipher routines implementation based on OpenSSL. */ #include #include #include "glib.h" #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-crypt.h" /* OpenSSL specific initialization/shutdown */ void sipe_crypto_init(SIPE_UNUSED_PARAMETER gboolean production_mode) { /* nothing to do here */ } void sipe_crypto_shutdown(void) { /* nothing to do here */ } static void openssl_oneshot_crypt(const EVP_CIPHER *type, const guchar *key, gsize key_length, const guchar *plaintext, gsize plaintext_length, guchar *encrypted_text) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); int encrypted_length = 0; /* initialize context */ EVP_EncryptInit_ex(ctx, type, NULL, key, NULL); /* set encryption parameters */ if (key_length) EVP_CIPHER_CTX_set_key_length(ctx, key_length); EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL); /* encrypt */ EVP_EncryptUpdate(ctx, encrypted_text, &encrypted_length, plaintext, plaintext_length); encrypted_text += encrypted_length; EVP_EncryptFinal_ex(ctx, encrypted_text, &encrypted_length); /* cleanup */ EVP_CIPHER_CTX_free(ctx); } /* DES CBC with 56-bit key */ void sipe_crypt_des(const guchar *key, const guchar *plaintext, gsize plaintext_length, guchar *encrypted_text) { openssl_oneshot_crypt(EVP_des_cbc(), key, 0 /* fixed length */, plaintext, plaintext_length, encrypted_text); } /* RC4 with variable length key */ void sipe_crypt_rc4(const guchar *key, gsize key_length, const guchar *plaintext, gsize plaintext_length, guchar *encrypted_text) { openssl_oneshot_crypt(EVP_rc4(), key, key_length, plaintext, plaintext_length, encrypted_text); } gboolean sipe_crypt_rsa_encrypt(gpointer public, gsize modulus_length, const guchar *plaintext, guchar *encrypted_text) { return(RSA_public_encrypt(modulus_length, plaintext, encrypted_text, public, RSA_NO_PADDING) != -1); } gboolean sipe_crypt_rsa_decrypt(gpointer private, gsize modulus_length, const guchar *encrypted_text, guchar *plaintext) { return(RSA_private_decrypt(modulus_length, encrypted_text, plaintext, private, RSA_NO_PADDING) != -1); } guchar *sipe_crypt_rsa_sign(gpointer private, const guchar *digest, gsize digest_length, gsize *signature_length) { guchar *signature = g_malloc(RSA_size(private)); unsigned int length; if (!RSA_sign(NID_md5_sha1, digest, digest_length, signature, &length, private)) { g_free(signature); return(NULL); } *signature_length = length; return(signature); } gboolean sipe_crypt_verify_rsa(gpointer public, const guchar *digest, gsize digest_length, const guchar *signature, gsize signature_length) { return(RSA_verify(NID_md5_sha1, digest, digest_length, /* older OpenSSL version don't have "const" here */ (guchar *) signature, signature_length, public)); } static gpointer openssl_EVP_init(const EVP_CIPHER *type, const guchar *key, gsize key_length, const guchar *iv) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); /* initialize context */ EVP_EncryptInit_ex(ctx, type, NULL, key, iv); /* set encryption parameters */ EVP_CIPHER_CTX_set_key_length(ctx, key_length); EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv); return(ctx); } /* Stream RC4 cipher for file transfer with fixed-length 128-bit key */ gpointer sipe_crypt_ft_start(const guchar *key) { return(openssl_EVP_init(EVP_rc4(), key, 16, NULL)); } void sipe_crypt_ft_stream(gpointer context, const guchar *in, gsize length, guchar *out) { int tmp; EVP_EncryptUpdate(context, out, &tmp, in, length); } void sipe_crypt_ft_destroy(gpointer context) { EVP_CIPHER_CTX_free(context); } /* Stream RC4 cipher for TLS with variable key length */ gpointer sipe_crypt_tls_start(const guchar *key, gsize key_length) { return(openssl_EVP_init(EVP_rc4(), key, key_length, NULL)); } void sipe_crypt_tls_stream(gpointer context, const guchar *in, gsize length, guchar *out) { int tmp; EVP_EncryptUpdate(context, out, &tmp, in, length); } void sipe_crypt_tls_destroy(gpointer context) { EVP_CIPHER_CTX_free(context); } /* Block AES-CBC cipher for TLS */ void sipe_crypt_tls_block(const guchar *key, gsize key_length, const guchar *iv, /* OpenSSL assumes that iv is of correct size */ SIPE_UNUSED_PARAMETER gsize iv_length, const guchar *in, gsize length, guchar *out) { const EVP_CIPHER *type = NULL; switch (key_length) { case 128 / 8: type = EVP_aes_128_cbc(); break; /* * TLS does not use AES-192 * case 192 / 8: type = EVP_aes_192_cbc(); break; */ case 256 / 8: type = EVP_aes_256_cbc(); break; default: SIPE_DEBUG_ERROR("sipe_crypt_tls_block: unsupported key length %" G_GSIZE_FORMAT " bytes for AES CBC", key_length); break; } if (type) { EVP_CIPHER_CTX *context = openssl_EVP_init(type, key, key_length, iv); if (context) { int tmp; EVP_EncryptUpdate(context, out, &tmp, in, length); EVP_CIPHER_CTX_free(context); } } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-crypt.h ================================================ /** * @file sipe-crypt.h * * pidgin-sipe * * Copyright (C) 2010-2015 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * Crypto backend specific initialization/shutdown * * TRUE - production mode, i.e. called from sipe-core.c * FALSE - test mode */ void sipe_crypto_init(gboolean production_mode); void sipe_crypto_shutdown(void); void sipe_crypt_des(const guchar *key, const guchar *plaintext, gsize plaintext_length, guchar *encrypted_text); void sipe_crypt_rc4(const guchar *key, gsize key_length, const guchar *plaintext, gsize plaintext_length, guchar *encrypted_text); /* plaintext & encrypted_text must point to modulus_length long spaces */ gboolean sipe_crypt_rsa_encrypt(gpointer public, gsize modulus_length, const guchar *plaintext, guchar *encrypted_text); gboolean sipe_crypt_rsa_decrypt(gpointer private, gsize modulus_length, const guchar *encrypted_text, guchar *plaintext); /* must be g_free'd() */ guchar *sipe_crypt_rsa_sign(gpointer private, const guchar *digest, gsize digest_length, gsize *signature_length); gboolean sipe_crypt_verify_rsa(gpointer public, const guchar *digest, gsize digest_length, const guchar *signature, gsize signature_length); /* Stream RC4 cipher for file transfer */ gpointer sipe_crypt_ft_start(const guchar *key); void sipe_crypt_ft_stream(gpointer context, const guchar *in, gsize length, guchar *out); void sipe_crypt_ft_destroy(gpointer context); /* Stream RC4 cipher for TLS */ gpointer sipe_crypt_tls_start(const guchar *key, gsize key_length); void sipe_crypt_tls_stream(gpointer context, const guchar *in, gsize length, guchar *out); void sipe_crypt_tls_destroy(gpointer context); /* Block AES-CBC cipher for TLS */ void sipe_crypt_tls_block(const guchar *key, gsize key_length, const guchar *iv, gsize iv_length, const guchar *in, gsize length, guchar *out); ================================================ FILE: src/core/sipe-dialog.c ================================================ /** * @file sipe-dialog.c * * pidgin-sipe * * Copyright (C) 2009-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "sipe-core.h" #include "sipe-common.h" #include "sipe-ft.h" #include "sipmsg.h" #include "sipe-backend.h" #include "sipe-dialog.h" #include "sipe-session.h" #include "sipe-utils.h" void sipe_dialog_free(struct sip_dialog *dialog) { GSList *entry; void *data; if (!dialog) return; g_free(dialog->with); g_free(dialog->endpoint_GUID); entry = dialog->routes; while (entry) { data = entry->data; entry = g_slist_remove(entry, data); g_free(data); } entry = dialog->supported; while (entry) { data = entry->data; entry = g_slist_remove(entry, data); g_free(data); } while (dialog->filetransfers) { struct sipe_file_transfer *ft = dialog->filetransfers->data; sipe_ft_free(ft); } g_free(dialog->callid); g_free(dialog->ourtag); g_free(dialog->theirtag); g_free(dialog->theirepid); g_free(dialog->request); g_free(dialog); } struct sip_dialog *sipe_dialog_add(struct sip_session *session) { struct sip_dialog *dialog = g_new0(struct sip_dialog, 1); session->dialogs = g_slist_append(session->dialogs, dialog); return(dialog); } static struct sip_dialog * sipe_dialog_find_3(struct sip_session *session, struct sip_dialog *dialog_in) { if (session && dialog_in) { SIPE_DIALOG_FOREACH { if ( dialog_in->callid && dialog_in->ourtag && dialog_in->theirtag && dialog->callid && dialog->ourtag && dialog->theirtag && sipe_strcase_equal(dialog_in->callid, dialog->callid) && sipe_strcase_equal(dialog_in->ourtag, dialog->ourtag) && sipe_strcase_equal(dialog_in->theirtag, dialog->theirtag)) { SIPE_DEBUG_INFO("sipe_dialog_find_3 who='%s'", dialog->with ? dialog->with : ""); return dialog; } } SIPE_DIALOG_FOREACH_END; } return NULL; } struct sip_dialog *sipe_dialog_find(struct sip_session *session, const gchar *who) { if (session && who) { SIPE_DIALOG_FOREACH { if (dialog->with && sipe_strcase_equal(who, dialog->with)) { SIPE_DEBUG_INFO("sipe_dialog_find who='%s'", who); return dialog; } } SIPE_DIALOG_FOREACH_END; } return NULL; } void sipe_dialog_remove(struct sip_session *session, const gchar *who) { struct sip_dialog *dialog = sipe_dialog_find(session, who); if (dialog) { SIPE_DEBUG_INFO("sipe_dialog_remove who='%s' with='%s'", who, dialog->with ? dialog->with : ""); session->dialogs = g_slist_remove(session->dialogs, dialog); sipe_dialog_free(dialog); } } void sipe_dialog_remove_3(struct sip_session *session, struct sip_dialog *dialog_in) { struct sip_dialog *dialog = sipe_dialog_find_3(session, dialog_in); if (dialog) { SIPE_DEBUG_INFO("sipe_dialog_remove_3 with='%s'", dialog->with ? dialog->with : ""); session->dialogs = g_slist_remove(session->dialogs, dialog); sipe_dialog_free(dialog); } } void sipe_dialog_remove_all(struct sip_session *session) { GSList *entry = session->dialogs; while (entry) { struct sip_dialog *dialog = entry->data; entry = g_slist_remove(entry, dialog); sipe_dialog_free(dialog); } } static void sipe_dialog_parse_routes(struct sip_dialog *dialog, const struct sipmsg *msg, gboolean outgoing) { GSList *hdr = msg->headers; gchar *contact = sipmsg_parse_contact_address(msg); /* Remove old routes */ while (dialog->routes) { void *data = dialog->routes->data; dialog->routes = g_slist_remove(dialog->routes, data); g_free(data); } g_free(dialog->request); dialog->request = NULL; while (hdr) { struct sipnameval *elem = hdr->data; if (sipe_strcase_equal(elem->name, "Record-Route")) { gchar **parts = g_strsplit(elem->value, ",", 0); gchar **part = parts; while (*part) { SIPE_DEBUG_INFO("sipe_dialog_parse_routes: route %s", *part); dialog->routes = g_slist_append(dialog->routes, g_strdup(*part)); part++; } g_strfreev(parts); } hdr = g_slist_next(hdr); } if (outgoing) { dialog->routes = g_slist_reverse(dialog->routes); } if (contact) { dialog->request = contact; } /* logic for strict router only - RFC3261 - 12.2.1.1 */ /* @TODO: proper check for presence of 'lr' PARAMETER in URI */ if (dialog->routes && !strstr(dialog->routes->data, ";lr")) { gchar *route = dialog->routes->data; dialog->request = sipmsg_find_part_of_header(route, "<", ">", NULL); SIPE_DEBUG_INFO("sipe_dialog_parse_routes: strict route, contact %s", dialog->request); dialog->routes = g_slist_remove(dialog->routes, route); g_free(route); if (contact) { dialog->routes = g_slist_append(dialog->routes, g_strdup_printf("<%s>", contact)); g_free(contact); } } } static void sipe_get_supported_header(const struct sipmsg *msg, struct sip_dialog *dialog, SIPE_UNUSED_PARAMETER gboolean outgoing) { GSList *hdr = msg->headers; struct sipnameval *elem; while(hdr) { elem = hdr->data; if (sipe_strcase_equal(elem->name, "Supported") && !g_slist_find_custom(dialog->supported, elem->value, (GCompareFunc)g_ascii_strcasecmp)) { dialog->supported = g_slist_append(dialog->supported, g_strdup(elem->value)); } hdr = g_slist_next(hdr); } } static gchar *find_tag(const gchar *hdr) { gchar * tag = sipmsg_find_part_of_header (hdr, "tag=", ";", NULL); if (!tag) { // In case it's at the end and there's no trailing ; tag = sipmsg_find_part_of_header (hdr, "tag=", NULL, NULL); } return tag; } void sipe_dialog_parse(struct sip_dialog *dialog, const struct sipmsg *msg, gboolean outgoing) { const gchar *us = outgoing ? "From" : "To"; const gchar *them = outgoing ? "To" : "From"; const gchar *session_expires_header; g_free(dialog->ourtag); g_free(dialog->theirtag); dialog->ourtag = find_tag(sipmsg_find_header(msg, us)); dialog->theirtag = find_tag(sipmsg_find_header(msg, them)); if (!dialog->theirepid) { dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, them), "epid=", ";", NULL); if (!dialog->theirepid) { dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, them), "epid=", NULL, NULL); } } // Catch a tag on the end of the To Header and get rid of it. if (dialog->theirepid && strstr(dialog->theirepid, "tag=")) { dialog->theirepid = strtok(dialog->theirepid, ";"); } if ((session_expires_header = sipmsg_find_header(msg, "Session-Expires"))) { dialog->expires = atoi(session_expires_header); } sipe_dialog_parse_routes(dialog, msg, outgoing); sipe_get_supported_header(msg, dialog, outgoing); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-dialog.h ================================================ /** * @file sipe-dialog.h * * pidgin-sipe * * Copyright (C) 2009-11 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Interface dependencies: * * */ /* Forward declarations */ struct sipe_delayed_invite; struct sipmsg; /* Helper macros to iterate over dialog list in a SIP session */ #define SIPE_DIALOG_FOREACH { \ GSList *entry = session->dialogs; \ while (entry) { \ struct sip_dialog *dialog = entry->data; \ entry = entry->next; #define SIPE_DIALOG_FOREACH_END }} /* dialog is the new term for call-leg */ struct sip_dialog { gchar *with; /* URI */ gchar *endpoint_GUID; /** * >0 - pro * <0 - contra * 0 - didn't participate */ int election_vote; gchar *ourtag; gchar *theirtag; gchar *theirepid; gchar *callid; GSList *routes; gchar *request; GSList *supported; /* counterparty capabilities */ GSList *filetransfers; int cseq; /** corresponds to Session-Expires SIP header value */ int expires; gboolean is_established; struct transaction *outgoing_invite; struct sipe_delayed_invite *delayed_invite; }; /* Forward declaration */ struct sip_session; /** * Free dialog structure * * @param dialog (in) Dialog to be freed. May be NULL. */ void sipe_dialog_free(struct sip_dialog *dialog); /** * Add a new, empty dialog to a session * * @param session (in) * * @return dialog the new dialog structure */ struct sip_dialog *sipe_dialog_add(struct sip_session *session); /** * Find a dialog in a session * * @param session (in) may be NULL * @param who (in) dialog identifier. May be NULL * * @return dialog the requested dialog or NULL */ struct sip_dialog *sipe_dialog_find(struct sip_session *session, const gchar *who); /** * Remove a dialog from a session * * @param session (in) may be NULL * @param who (in) dialog identifier. May be NULL */ void sipe_dialog_remove(struct sip_session *session, const gchar *who); /** * Remove a dialog from a session * * @param session (in) may be NULL * @param dialog (in) dialog identifier. Should contain Call-ID, to-tag and from-tag * to unambiguously identify dialog. May be NULL */ void sipe_dialog_remove_3(struct sip_session *session, struct sip_dialog *dialog_in); /** * Remove all dialogs from a session * * @param session (in) */ void sipe_dialog_remove_all(struct sip_session *session); /** * Does a session have any dialogs? * * @param session (in) */ #define sipe_dialog_any(session) (session->dialogs != NULL) /** * Return first dialog of a session * * @param session (in) */ #define sipe_dialog_first(session) ((struct sip_dialog *)session->dialogs->data) /** * Fill dialog structure from SIP message * * @param dialog (in,out) dialog to fill * @param msg (in) mesage * @param outgoing (in) outgoing or incoming message */ void sipe_dialog_parse(struct sip_dialog *dialog, const struct sipmsg *msg, gboolean outgoing); ================================================ FILE: src/core/sipe-digest-nss.c ================================================ /** * @file sipe-digest-nss.c * * pidgin-sipe * * Copyright (C) 2011-2016 SIPE Project * Copyright (C) 2010 pier11 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * Digest routines implementation based on NSS. * Includes: SHA1, MD5, HMAC_SHA_1, HMAC_MD5 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "glib.h" #include "nss.h" /* * Work around a compiler error in NSS 3.13.x. Let's hope they fix it for * 3.14.x. See also: https://bugzilla.mozilla.org/show_bug.cgi?id=702090 */ #if (NSS_VMAJOR == 3) && (NSS_VMINOR == 13) #define __GNUC_MINOR __GNUC_MINOR__ #endif #include "pk11pub.h" #include "sipe-digest.h" #if !(defined(HAVE_GSSAPI_ONLY) || defined(HAVE_SSPI)) #include "md4.h" #endif /* PRIVATE methods */ static PK11Context *sipe_digest_ctx_create(const SECOidTag algorithm) { PK11Context *context = PK11_CreateDigestContext(algorithm); PK11_DigestBegin(context); return(context); } static PK11Context* sipe_digest_hmac_ctx_create(CK_MECHANISM_TYPE hmacMech, const guchar *key, gsize key_length) { PK11SlotInfo* slot; SECItem keyItem; SECItem noParams; PK11SymKey* SymKey; PK11Context* DigestContext; /* For key */ slot = PK11_GetBestSlot(hmacMech, NULL); keyItem.type = siBuffer; keyItem.data = (unsigned char *)key; keyItem.len = key_length; SymKey = PK11_ImportSymKey(slot, hmacMech, PK11_OriginUnwrap, CKA_SIGN, &keyItem, NULL); /* Parameter for crypto context */ noParams.type = siBuffer; noParams.data = NULL; noParams.len = 0; DigestContext = PK11_CreateContextBySymKey(hmacMech, CKA_SIGN, SymKey, &noParams); PK11_DigestBegin(DigestContext); PK11_FreeSymKey(SymKey); PK11_FreeSlot(slot); return DigestContext; } static void sipe_digest_ctx_append(PK11Context* DigestContext, const guchar *data, gsize data_length) { PK11_DigestOp(DigestContext, data, data_length); } static void sipe_digest_ctx_digest(PK11Context* DigestContext, guchar *digest, gsize digest_length) { unsigned int len; PK11_DigestFinal(DigestContext, digest, &len, digest_length); } static void sipe_digest_ctx_destroy(PK11Context* DigestContext) { PK11_DestroyContext(DigestContext, PR_TRUE); } static void sipe_digest(const SECOidTag algorithm, const guchar *data, gsize data_length, guchar *digest, gsize digest_length) { void *DigestContext; DigestContext = sipe_digest_ctx_create(algorithm); sipe_digest_ctx_append(DigestContext, data, data_length); sipe_digest_ctx_digest(DigestContext, digest, digest_length); sipe_digest_ctx_destroy(DigestContext); } static void sipe_digest_hmac(CK_MECHANISM_TYPE hmacMech, const guchar *key, gsize key_length, const guchar *data, gsize data_length, guchar *digest, gsize digest_length) { void *DigestContext; DigestContext = sipe_digest_hmac_ctx_create(hmacMech, key, key_length); sipe_digest_ctx_append(DigestContext, data, data_length); sipe_digest_ctx_digest(DigestContext, digest, digest_length); sipe_digest_ctx_destroy(DigestContext); } /* PUBLIC methods */ #if !(defined(HAVE_GSSAPI_ONLY) || defined(HAVE_SSPI)) /* One-shot MD4 digest - only used by internal NTLMv2 implementation */ void sipe_digest_md4(const guchar *data, gsize length, guchar *digest) { /* NSS does not provide MD4 - use Mozilla Firefox implementation */ md4sum(data, length, digest); } #endif void sipe_digest_md5(const guchar *data, gsize length, guchar *digest) { sipe_digest(SEC_OID_MD5, data, length, digest, SIPE_DIGEST_MD5_LENGTH); } void sipe_digest_sha1(const guchar *data, gsize length, guchar *digest) { sipe_digest(SEC_OID_SHA1, data, length, digest, SIPE_DIGEST_SHA1_LENGTH); } void sipe_digest_hmac_md5(const guchar *key, gsize key_length, const guchar *data, gsize data_length, guchar *digest) { sipe_digest_hmac(CKM_MD5_HMAC, key, key_length, data, data_length, digest, SIPE_DIGEST_HMAC_MD5_LENGTH); } void sipe_digest_hmac_sha1(const guchar *key, gsize key_length, const guchar *data, gsize data_length, guchar *digest) { sipe_digest_hmac(CKM_SHA_1_HMAC, key, key_length, data, data_length, digest, SIPE_DIGEST_HMAC_SHA1_LENGTH); } /* Stream HMAC(SHA1) digest for file transfer */ gpointer sipe_digest_ft_start(const guchar *sha1_digest) { /* used only the first 16 bytes of the 20 byte SHA1 digest */ return sipe_digest_hmac_ctx_create(CKM_SHA_1_HMAC, sha1_digest, 16); } void sipe_digest_ft_update(gpointer context, const guchar *data, gsize length) { sipe_digest_ctx_append(context, data, length); } void sipe_digest_ft_end(gpointer context, guchar *digest) { sipe_digest_ctx_digest(context, digest, SIPE_DIGEST_FILETRANSFER_LENGTH); } void sipe_digest_ft_destroy(gpointer context) { sipe_digest_ctx_destroy(context); } /* Stream digests, e.g. for TLS */ gpointer sipe_digest_md5_start(void) { return sipe_digest_ctx_create(SEC_OID_MD5); } void sipe_digest_md5_update(gpointer context, const guchar *data, gsize length) { sipe_digest_ctx_append(context, data, length); } void sipe_digest_md5_end(gpointer context, guchar *digest) { unsigned int saved_length; /* save context to ensure this function can be called multiple times */ guchar *saved = PK11_SaveContextAlloc(context, NULL, 0, &saved_length); sipe_digest_ctx_digest(context, digest, SIPE_DIGEST_MD5_LENGTH); PK11_RestoreContext(context, saved, saved_length); PORT_Free(saved); } void sipe_digest_md5_destroy(gpointer context) { sipe_digest_ctx_destroy(context); } gpointer sipe_digest_sha1_start(void) { return sipe_digest_ctx_create(SEC_OID_SHA1); } void sipe_digest_sha1_update(gpointer context, const guchar *data, gsize length) { sipe_digest_ctx_append(context, data, length); } void sipe_digest_sha1_end(gpointer context, guchar *digest) { unsigned int saved_length; /* save context to ensure this function can be called multiple times */ guchar *saved = PK11_SaveContextAlloc(context, NULL, 0, &saved_length); sipe_digest_ctx_digest(context, digest, SIPE_DIGEST_SHA1_LENGTH); PK11_RestoreContext(context, saved, saved_length); PORT_Free(saved); } void sipe_digest_sha1_destroy(gpointer context) { sipe_digest_ctx_destroy(context); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-digest-openssl.c ================================================ /** * @file sipe-digest-openssl.c * * pidgin-sipe * * Copyright (C) 2013-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * Digest routines implementation based on OpenSSL */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #if !(defined(HAVE_GSSAPI_ONLY) || defined(HAVE_SSPI)) #include #endif #include #include #include #include "glib.h" #include "sipe-digest.h" #if !(defined(HAVE_GSSAPI_ONLY) || defined(HAVE_SSPI)) /* One-shot MD4 digest - only used by internal NTLMv2 implementation */ void sipe_digest_md4(const guchar *data, gsize length, guchar *digest) { MD4(data, length, digest); } #endif /* One-shot MD5/SHA-1 digests */ void sipe_digest_md5(const guchar *data, gsize length, guchar *digest) { MD5(data, length, digest); } void sipe_digest_sha1(const guchar *data, gsize length, guchar *digest) { SHA1(data, length, digest); } /* One-shot HMAC(MD5/SHA-1) digests */ void sipe_digest_hmac_md5(const guchar *key, gsize key_length, const guchar *data, gsize data_length, guchar *digest) { HMAC(EVP_md5(), key, key_length, data, data_length, digest, NULL); } void sipe_digest_hmac_sha1(const guchar *key, gsize key_length, const guchar *data, gsize data_length, guchar *digest) { HMAC(EVP_sha1(), key, key_length, data, data_length, digest, NULL); } /* Stream HMAC(SHA1) digest for file transfer */ gpointer sipe_digest_ft_start(const guchar *sha1_digest) { #if OPENSSL_VERSION_NUMBER < 0x10100000L HMAC_CTX *ctx = g_malloc(sizeof(HMAC_CTX)); HMAC_CTX_init(ctx); #else /* OpenSSL 1.1.0 or newer */ HMAC_CTX *ctx = HMAC_CTX_new(); #endif /* used are only the first 16 bytes of the 20 byte SHA1 digest */ HMAC_Init_ex(ctx, sha1_digest, 16, EVP_sha1(), NULL); return(ctx); } void sipe_digest_ft_update(gpointer context, const guchar *data, gsize length) { HMAC_Update(context, data, length); } void sipe_digest_ft_end(gpointer context, guchar *digest) { HMAC_Final(context, digest, NULL); } void sipe_digest_ft_destroy(gpointer context) { #if OPENSSL_VERSION_NUMBER < 0x10100000L HMAC_CTX_cleanup(context); g_free(context); #else /* OpenSSL 1.1.0 or newer */ HMAC_CTX_free(context); #endif } /* Stream digests, e.g. for TLS */ gpointer sipe_digest_md5_start(void) { MD5_CTX *ctx = g_malloc(sizeof(MD5_CTX)); MD5_Init(ctx); return(ctx); } void sipe_digest_md5_update(gpointer context, const guchar *data, gsize length) { MD5_Update(context, data, length); } void sipe_digest_md5_end(gpointer context, guchar *digest) { /* save context to ensure this function can be called multiple times */ MD5_CTX *orig_ctx = context; MD5_CTX saved_ctx = *orig_ctx; MD5_Final(digest, orig_ctx); *orig_ctx = saved_ctx; } void sipe_digest_md5_destroy(gpointer context) { g_free(context); } gpointer sipe_digest_sha1_start(void) { SHA_CTX *ctx = g_malloc(sizeof(SHA_CTX)); SHA1_Init(ctx); return(ctx); } void sipe_digest_sha1_update(gpointer context, const guchar *data, gsize length) { SHA1_Update(context, data, length); } void sipe_digest_sha1_end(gpointer context, guchar *digest) { /* save context to ensure this function can be called multiple times */ SHA_CTX *orig_ctx = context; SHA_CTX saved_ctx = *orig_ctx; SHA1_Final(digest, orig_ctx); *orig_ctx = saved_ctx; } void sipe_digest_sha1_destroy(gpointer context) { g_free(context); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-digest.h ================================================ /** * @file sipe-digest.h * * pidgin-sipe * * Copyright (C) 2010-2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Plain digests */ /* NOTE: can only be used by internal NTLMv2 implementation */ #define SIPE_DIGEST_MD4_LENGTH 16 void sipe_digest_md4(const guchar *data, gsize length, guchar *digest); #define SIPE_DIGEST_MD5_LENGTH 16 void sipe_digest_md5(const guchar *data, gsize length, guchar *digest); #define SIPE_DIGEST_SHA1_LENGTH 20 void sipe_digest_sha1(const guchar *data, gsize length, guchar *digest); /* HMAC digests */ #define SIPE_DIGEST_HMAC_MD5_LENGTH SIPE_DIGEST_MD5_LENGTH void sipe_digest_hmac_md5(const guchar *key, gsize key_length, const guchar *data, gsize data_length, guchar *digest); #define SIPE_DIGEST_HMAC_SHA1_LENGTH SIPE_DIGEST_SHA1_LENGTH void sipe_digest_hmac_sha1(const guchar *key, gsize key_length, const guchar *data, gsize data_length, guchar *digest); /* Stream HMAC(SHA1) digest for file transfer */ #define SIPE_DIGEST_FILETRANSFER_LENGTH SIPE_DIGEST_SHA1_LENGTH gpointer sipe_digest_ft_start(const guchar *sha1_digest); void sipe_digest_ft_update(gpointer context, const guchar *data, gsize length); void sipe_digest_ft_end(gpointer context, guchar *digest); void sipe_digest_ft_destroy(gpointer context); /* Stream digests, e.g. for TLS */ gpointer sipe_digest_md5_start(void); void sipe_digest_md5_update(gpointer context, const guchar *data, gsize length); void sipe_digest_md5_end(gpointer context, guchar *digest); void sipe_digest_md5_destroy(gpointer context); gpointer sipe_digest_sha1_start(void); void sipe_digest_sha1_update(gpointer context, const guchar *data, gsize length); void sipe_digest_sha1_end(gpointer context, guchar *digest); void sipe_digest_sha1_destroy(gpointer context); ================================================ FILE: src/core/sipe-domino.c ================================================ /** * @file sipe-domino.c * * pidgin-sipe * * Copyright (C) 2010-2017 SIPE Project * Copyright (C) 2010 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** For communication with Lotus Domino groupware server. Server requirements: Domino 5.0.2 and above with Web Access. 1) Tries to read user's notes.ini for mail database name. Windows registry keys for notes.ini location: HKEY_CURRENT_USER\Software\Lotus\Notes\6.0\NotesIniPath 2) Authenticates to server (HTTPS POST, plaintext login/password over SSL) https://[domino_server]/[databasename].nsf/?Login Content-Type=application/x-www-form-urlencoded Username=[email]&Password=[password] (params are url-encoded) Saves auth cookie. Set-Cookie=DomAuthSessId=17D0428F7B9D57D4D0B064AE42FD21F9; path=/ 3) Queries Calendar data (HTTPS GET, result is XML) https://[domino_server]/[databasename].nsf/[viewname]?ReadViewEntries https://[domino_server]/[databasename].nsf/($Calendar)?ReadViewEntries&KeyType=time&StartKey=20090805T000000Z&UntilKey=20090806T000000Z&Count=-1&TZType=UTC Uses auth cookie. Cookie=DomAuthSessId=17D0428F7B9D57D4D0B064AE42FD21F9 It is able to retrieve our Calendar information (Meetings schedule, subject and location) from Lotus Domino for subsequent publishing. Ref. for more implementation details: https://sourceforge.net/tracker/?func=detail&aid=2945346&group_id=194563&atid=949934 Similar functionality for iCalendar/CalDAV/Google would be great to implement too. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include /* for registry read */ #ifdef _WIN32 #include "sipe-win32dep.h" #endif #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-cal.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-domino.h" #include "sipe-http.h" #include "sipe-nls.h" #include "sipe-utils.h" #include "sipe-xml.h" /** * POST request for Login to Domino server * @param email (%s) Should be URL-encoded. Ex.: alice@cosmo.local * @param password (%s) Should be URL-encoded. */ #define SIPE_DOMINO_LOGIN_REQUEST \ "Username=%s&Password=%s" /** * GET request to Domino server * to obtain our Calendar information. * @param start_time (%s) Ex.: 20090805T000000Z * @param end_time (%s) Ex.: 20090806T000000Z */ #define SIPE_DOMINO_CALENDAR_REQUEST \ "/($Calendar)?ReadViewEntries&KeyType=time&StartKey=%s&UntilKey=%s&Count=-1&TZType=UTC" /* 20100423T103000,00Z 158 20100423T103000,00Z - 20100423T120000,00Z G. S. ..I. L. T. Hall Location: Auditorium - W. House Chair: S. S. */ #define VIEWENTITY_START0_TIME "$134" #define VIEWENTITY_START_TIME "$144" #define VIEWENTITY_END_TIME "$146" #define VIEWENTITY_TEXT_LIST "$147" static int sipe_domino_get_slot_no(time_t fb_start, time_t in) { return (in - fb_start) / SIPE_FREE_BUSY_GRANULARITY_SEC; } static char * sipe_domino_get_free_busy(time_t fb_start, GSList *cal_events) { GSList *entry = cal_events; char *res; if (!cal_events) return NULL; res = g_strnfill(SIPE_FREE_BUSY_PERIOD_SEC / SIPE_FREE_BUSY_GRANULARITY_SEC, SIPE_CAL_FREE + '0'); while (entry) { struct sipe_cal_event *cal_event = entry->data; int start = sipe_domino_get_slot_no(fb_start, cal_event->start_time); int end = sipe_domino_get_slot_no(fb_start, (cal_event->end_time - 1)); int i; for (i = start; i <= end; i++) { res[i] = SIPE_CAL_BUSY + '0'; } entry = entry->next; } SIPE_DEBUG_INFO("sipe_domino_get_free_busy: res=\n%s", res); return res; } static void sipe_domino_process_calendar_response(struct sipe_core_private *sipe_private, guint status, GSList *headers, const gchar *body, gpointer data) { struct sipe_calendar *cal = data; const gchar *content_type = sipe_utils_nameval_find(headers, "Content-Type"); SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_process_calendar_response: cb started."); cal->request = NULL; if (content_type && !g_str_has_prefix(content_type, "text/xml")) { cal->is_domino_disabled = TRUE; SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_process_calendar_response: not XML, disabling."); return; } if ((status == SIPE_HTTP_STATUS_OK) && body) { const sipe_xml *node, *node2, *node3; sipe_xml *xml; SIPE_DEBUG_INFO("sipe_domino_process_calendar_response: SUCCESS, ret=%d", status); xml = sipe_xml_parse(body, strlen(body)); sipe_cal_events_free(cal->cal_events); cal->cal_events = NULL; /* viewentry */ for (node = sipe_xml_child(xml, "viewentry"); node; node = sipe_xml_twin(node)) { struct sipe_cal_event *cal_event = g_new0(struct sipe_cal_event, 1); cal->cal_events = g_slist_append(cal->cal_events, cal_event); cal_event->cal_status = SIPE_CAL_BUSY; cal_event->is_meeting = TRUE; /* SIPE_DEBUG_INFO("viewentry unid=%s", sipe_xml_attribute(node, "unid")); */ /* entrydata */ for (node2 = sipe_xml_child(node, "entrydata"); node2; node2 = sipe_xml_twin(node2)) { const char *name = sipe_xml_attribute(node2, "name"); SIPE_DEBUG_INFO("\tentrydata name=%s", name); if (sipe_strequal(name, VIEWENTITY_START0_TIME) || sipe_strequal(name, VIEWENTITY_START_TIME) || sipe_strequal(name, VIEWENTITY_END_TIME)) { char *tmp = sipe_xml_data(sipe_xml_child(node2, "datetime")); time_t time_val = sipe_utils_str_to_time(tmp); if (sipe_strequal(name, VIEWENTITY_START_TIME)) { cal_event->start_time = time_val; } else if (sipe_strequal(name, VIEWENTITY_END_TIME)) { cal_event->end_time = time_val; } SIPE_DEBUG_INFO("\t\tdatetime=%s", sipe_utils_time_to_debug_str(gmtime(&time_val))); g_free(tmp); } else if (sipe_strequal(name, VIEWENTITY_TEXT_LIST)) { int i = 0; /* test */ for (node3 = sipe_xml_child(node2, "textlist/text"); node3; node3 = sipe_xml_twin(node3)) { char *tmp = sipe_xml_data(node3); if (!tmp) continue; SIPE_DEBUG_INFO("\t\ttext=%s", tmp); if (i == 0) { cal_event->subject = g_strdup(tmp); SIPE_DEBUG_INFO("\t\t*Subj.=%s", tmp); } else { /* plain English, don't localize! */ if (!g_ascii_strncasecmp(tmp, "Location:", 9)) { if (strlen(tmp) > 9) { cal_event->location = g_strdup(g_strstrip(tmp+9)); SIPE_DEBUG_INFO("\t\t*Loc.=%s", cal_event->location); } /* Translators: (!) should be as in localized Lotus Notes to be able to extract meeting location */ } else if (g_str_has_prefix(tmp, _("Location:"))) { guint len = strlen(_("Location:")); if (strlen(tmp) > len) { cal_event->location = g_strdup(g_strstrip(tmp+len)); SIPE_DEBUG_INFO("\t\t*Loc.=%s", cal_event->location); } } } i++; g_free(tmp); } } } } sipe_xml_free(xml); /* creates FreeBusy from cal->cal_events */ g_free(cal->free_busy); cal->free_busy = sipe_domino_get_free_busy(cal->fb_start, cal->cal_events); /* update SIP server */ cal->is_updated = TRUE; sipe_cal_presence_publish(sipe_private, TRUE); } else if (!headers) { SIPE_DEBUG_INFO("sipe_domino_process_calendar_response: rather FAILURE, ret=%d", status); } sipe_http_session_close(cal->session); cal->session = NULL; } /* Domino doesn't like '-' and ':' in ISO timestamps */ static gchar * sipe_domino_time_to_str(time_t timestamp) { char *res, *tmp; res = sipe_utils_time_to_str(timestamp); res = sipe_utils_str_replace((tmp = res), "-", ""); g_free(tmp); res = sipe_utils_str_replace((tmp = res), ":", ""); g_free(tmp); return res; } static void sipe_domino_send_http_request(struct sipe_calendar *cal) { if (cal->request) { sipe_core_email_authentication(cal->sipe_private, cal->request); sipe_http_request_session(cal->request, cal->session); sipe_http_request_ready(cal->request); } } static void sipe_domino_do_calendar_request(struct sipe_calendar *cal) { if (cal->domino_url) { char *url_req; char *url; time_t end; time_t now = time(NULL); char *start_str; char *end_str; struct tm *now_tm; SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_do_calendar_request: going Calendar req."); now_tm = gmtime(&now); /* start -1 day, 00:00:00 */ now_tm->tm_sec = 0; now_tm->tm_min = 0; now_tm->tm_hour = 0; cal->fb_start = sipe_mktime_tz(now_tm, "UTC"); cal->fb_start -= 24*60*60; /* end = start + 4 days - 1 sec */ end = cal->fb_start + SIPE_FREE_BUSY_PERIOD_SEC - 1; start_str = sipe_domino_time_to_str(cal->fb_start); end_str = sipe_domino_time_to_str(end); url_req = g_strdup_printf(SIPE_DOMINO_CALENDAR_REQUEST, start_str, end_str); g_free(start_str); g_free(end_str); url = g_strconcat(cal->domino_url, url_req, NULL); g_free(url_req); cal->request = sipe_http_request_get(cal->sipe_private, url, NULL, sipe_domino_process_calendar_response, cal); g_free(url); sipe_domino_send_http_request(cal); } } static void sipe_domino_process_login_response(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, guint status, GSList *headers, /* temporary? */ SIPE_UNUSED_PARAMETER const gchar *body, gpointer data) { struct sipe_calendar *cal = data; SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_process_login_response: cb started."); cal->request = NULL; if ((status >= SIPE_HTTP_STATUS_OK) && (status < SIPE_HTTP_STATUS_CLIENT_ERROR)) { SIPE_DEBUG_INFO("sipe_domino_process_login_response: rather SUCCESS, ret=%d", status); /* next query */ sipe_domino_do_calendar_request(cal); } else if (!headers || (status >= SIPE_HTTP_STATUS_CLIENT_ERROR)) { SIPE_DEBUG_INFO("sipe_domino_process_login_response: rather FAILURE, ret=%d", status); /* stop here */ /* cal->is_domino_disabled = TRUE; */ } } static gchar *sipe_domino_uri_escape(const gchar *string) { if (!string) return(NULL); if (!g_utf8_validate(string, -1, NULL)) return(NULL); return(g_uri_escape_string(string, NULL, FALSE)); } static void sipe_domino_do_login_request(struct sipe_calendar *cal) { if (cal->domino_url) { struct sipe_core_private *sipe_private = cal->sipe_private; char *body; const char *content_type = "application/x-www-form-urlencoded"; char *login_url = g_strconcat(cal->domino_url, "/?Login", NULL); char *user; gchar *password = sipe_private->email_password ? sipe_private->email_password : sipe_private->password; SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_do_login_request: going Login req."); if (!password) return; /* @TODO replace purple_url_encode() with non-purple equiv. */ user = sipe_domino_uri_escape(cal->email); password = sipe_domino_uri_escape(password); body = g_strdup_printf(SIPE_DOMINO_LOGIN_REQUEST, user, password); g_free(user); g_free(password); cal->request = sipe_http_request_post(sipe_private, login_url, NULL, body, content_type, sipe_domino_process_login_response, cal); g_free(login_url); g_free(body); sipe_domino_send_http_request(cal); } } /* in notes.ni MailFile=mail5\mhe111bm.nsf MailServer=CN=MSGM2222/OU=srv/O=xxcom Output values should be freed if requested. */ static void sipe_domino_read_notes_ini(const char *filename_with_path, char **mail_server, char **mail_file) { char rbuf[256]; FILE *fp = fopen(filename_with_path, "r+"); if (fp) { while (fgets(rbuf, sizeof (rbuf), fp)) { char *prop = "MailFile="; guint prop_len = strlen(prop); /* SIPE_DEBUG_INFO("\t%s (%"G_GSIZE_FORMAT")", rbuf, strlen(rbuf)); */ if (mail_file && !g_ascii_strncasecmp(rbuf, prop, prop_len) && (strlen(rbuf) > prop_len)) { *mail_file = g_strdup(g_strstrip((rbuf+prop_len))); } prop = "MailServer="; prop_len = strlen(prop); if (mail_server && !g_ascii_strncasecmp(rbuf, prop, prop_len) && (strlen(rbuf) > prop_len)) { *mail_server = g_strdup(g_strstrip((rbuf+prop_len))); } } fclose(fp); } else { SIPE_DEBUG_ERROR("sipe_domino_read_notes_ini(): could not open `%s': %s", filename_with_path, g_strerror (errno)); } } /** @param protocol Ex.: https @param mail_server Ex.: CN=MSGM2222/OU=srv/O=xxcom @param mail_file Ex.: mail5\mhe111bm.nsf @return Ex.: https://msgm2222/mail5/mhe111bm.nsf */ static char * sipe_domino_compose_url(const char *protocol, const char *mail_server, const char *mail_file) { const char *ptr; char *tmp, *tmp2, *tmp3; g_return_val_if_fail(protocol, NULL); g_return_val_if_fail(mail_server, NULL); g_return_val_if_fail(mail_file, NULL); /* mail_server: exptacting just common name */ if ((ptr = strstr(mail_server, "/"))) { tmp = g_strndup(mail_server, (ptr-mail_server)); } else { tmp = g_strdup(mail_server); } if ((!g_ascii_strncasecmp(tmp, "CN=", 3))) { tmp2 = g_strdup(tmp+3); } else { tmp2 = g_strdup(tmp); } g_free(tmp); tmp = g_ascii_strdown(tmp2, -1); g_free(tmp2); /* mail_file */ tmp3 = sipe_utils_str_replace(mail_file, "\\", "/"); tmp2 = g_strconcat(protocol, "://", tmp, "/", tmp3, NULL); g_free(tmp); g_free(tmp3); return tmp2; } void sipe_domino_update_calendar(struct sipe_core_private *sipe_private) { struct sipe_calendar* cal; SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_update_calendar: started."); sipe_cal_calendar_init(sipe_private); /* check if URL is valid if provided */ cal = sipe_private->calendar; if (cal && !is_empty(cal->domino_url)) { char *tmp = g_ascii_strdown(cal->domino_url, -1); if (!g_str_has_suffix(tmp, ".nsf")) { /* not valid Domino mail services URL */ cal->is_domino_disabled = TRUE; SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_update_calendar: invalid Domino URI supplied, disabling."); } g_free(tmp); } /* Autodiscovery. * Searches location of notes.ini in Registry, reads it, extracts mail server and mail file, * composes HTTPS URL to Domino web, basing on that */ if (cal && is_empty(cal->domino_url)) { char *path = NULL; #ifdef _WIN32 /* fine for Notes 8.5 too */ path = wpurple_read_reg_expand_string(HKEY_CURRENT_USER, "Software\\Lotus\\Notes\\8.0", "NotesIniPath"); if (is_empty(path)) { g_free(path); path = wpurple_read_reg_expand_string(HKEY_CURRENT_USER, "Software\\Lotus\\Notes\\7.0", "NotesIniPath"); if (is_empty(path)) { g_free(path); path = wpurple_read_reg_expand_string(HKEY_CURRENT_USER, "Software\\Lotus\\Notes\\6.0", "NotesIniPath"); if (is_empty(path)) { g_free(path); path = wpurple_read_reg_expand_string(HKEY_CURRENT_USER, "Software\\Lotus\\Notes\\5.0", "NotesIniPath"); } } } SIPE_DEBUG_INFO("sipe_domino_update_calendar: notes.ini path:\n%s", path ? path : ""); #else /* How to know location of notes.ini on *NIX ? */ #endif /* get server url */ if (path) { char *mail_server = NULL; char *mail_file = NULL; sipe_domino_read_notes_ini(path, &mail_server, &mail_file); g_free(path); SIPE_DEBUG_INFO("sipe_domino_update_calendar: mail_server=%s", mail_server ? mail_server : ""); SIPE_DEBUG_INFO("sipe_domino_update_calendar: mail_file=%s", mail_file ? mail_file : ""); g_free(cal->domino_url); cal->domino_url = sipe_domino_compose_url("https", mail_server, mail_file); g_free(mail_server); g_free(mail_file); SIPE_DEBUG_INFO("sipe_domino_update_calendar: cal->domino_url=%s", cal->domino_url ? cal->domino_url : ""); } else { /* No domino_url, no path discovered, disabling */ cal->is_domino_disabled = TRUE; SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_update_calendar: Domino URI hasn't been discovered, neither provided, disabling."); } } if (cal) { if (cal->is_domino_disabled) { SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_update_calendar: disabled, exiting."); return; } /* re-create session */ sipe_http_session_close(cal->session); cal->session = sipe_http_session_start(); sipe_domino_do_login_request(cal); } SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_update_calendar: finished."); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-domino.h ================================================ /** * @file sipe-domino.h * * pidgin-sipe * * Copyright (C) 2010 pier11 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_core_private; /** * Connects to Lotus Domino Web Access server, * pulls out our Calendar information * and publishes it to Office Communications server. * * Advised schedule: 30 minutes. */ void sipe_domino_update_calendar(struct sipe_core_private *sipe_private); ================================================ FILE: src/core/sipe-ews-autodiscover.c ================================================ /** * @file sipe-ews-autodiscover.c * * pidgin-sipe * * Copyright (C) 2013-2015 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Specification references: * * - POX: plain old XML autodiscover * - [MS-OXDSCLI]: http://msdn.microsoft.com/en-us/library/cc463896.aspx * - Autdiscover for Exchange: * http://msdn.microsoft.com/en-us/library/office/jj900169.aspx * - POX autodiscover: http://msdn.microsoft.com/en-us/library/office/aa581522.aspx * - POX redirect: http://msdn.microsoft.com/en-us/library/office/dn467392.aspx */ #include #include #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-ews-autodiscover.h" #include "sipe-http.h" #include "sipe-utils.h" #include "sipe-xml.h" struct sipe_ews_autodiscover_cb { sipe_ews_autodiscover_callback *cb; gpointer cb_data; }; struct autodiscover_method { const gchar *template; gboolean redirect; }; struct sipe_ews_autodiscover { struct sipe_ews_autodiscover_data *data; struct sipe_http_request *request; GSList *callbacks; gchar *email; const struct autodiscover_method *method; gboolean retry; gboolean completed; }; static void sipe_ews_autodiscover_complete(struct sipe_core_private *sipe_private, struct sipe_ews_autodiscover_data *ews_data) { struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover; GSList *entry = sea->callbacks; while (entry) { struct sipe_ews_autodiscover_cb *sea_cb = entry->data; sea_cb->cb(sipe_private, ews_data, sea_cb->cb_data); g_free(sea_cb); entry = entry->next; } g_slist_free(sea->callbacks); sea->callbacks = NULL; sea->completed = TRUE; } static void sipe_ews_autodiscover_request(struct sipe_core_private *sipe_private, gboolean next_method); static gboolean sipe_ews_autodiscover_url(struct sipe_core_private *sipe_private, const gchar *url); static void sipe_ews_autodiscover_parse(struct sipe_core_private *sipe_private, const gchar *body) { struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover; struct sipe_ews_autodiscover_data *ews_data = sea->data = g_new0(struct sipe_ews_autodiscover_data, 1); sipe_xml *xml = sipe_xml_parse(body, strlen(body)); const sipe_xml *account = sipe_xml_child(xml, "Response/Account"); gboolean complete = TRUE; /* valid POX autodiscover response? */ if (account) { const sipe_xml *node; /* POX autodiscover settings? */ if ((node = sipe_xml_child(account, "Protocol")) != NULL) { /* Autodiscover/Response/User/LegacyDN (requires trimming) */ gchar *tmp = sipe_xml_data(sipe_xml_child(xml, "Response/User/LegacyDN")); if (tmp) ews_data->legacy_dn = g_strstrip(tmp); /* extract settings */ for (; node; node = sipe_xml_twin(node)) { gchar *type = sipe_xml_data(sipe_xml_child(node, "Type")); /* Exchange or Office 365 */ if (sipe_strequal("EXCH", type) || sipe_strequal("EXPR", type)) { #define _URL(name, field) \ if (!ews_data->field) { \ ews_data->field = sipe_xml_data(sipe_xml_child(node, #name)); \ SIPE_DEBUG_INFO("sipe_ews_autodiscover_parse: " #field " = '%s'", \ ews_data->field ? ews_data->field : ""); \ } /* use first entry */ _URL(ASUrl, as_url); _URL(EwsUrl, ews_url); _URL(OABUrl, oab_url); _URL(OOFUrl, oof_url); #undef _URL } g_free(type); } /* POX autodiscover redirect to new email address? */ } else if ((node = sipe_xml_child(account, "RedirectAddr")) != NULL) { gchar *addr = sipe_xml_data(node); /* * Sanity checks for new email address: * - must contain a "@" character * - must be different from current address */ if (addr && strchr(addr, '@') && !sipe_strequal(sea->email, addr)) { g_free(sea->email); sea->email = addr; addr = NULL; /* sea takes ownership */ SIPE_DEBUG_INFO("sipe_ews_autodiscover_parse: restarting with email address '%s'", sea->email); /* restart process with new email address */ sea->method = NULL; complete = FALSE; sipe_ews_autodiscover_request(sipe_private, TRUE); } g_free(addr); /* POX autodiscover redirect to new URL? */ } else if ((node = sipe_xml_child(account, "RedirectUrl")) != NULL) { gchar *url = sipe_xml_data(node); if (!is_empty(url)) { SIPE_DEBUG_INFO("sipe_ews_autodiscover_parse: redirected to URL '%s'", url); complete = !sipe_ews_autodiscover_url(sipe_private, url); } g_free(url); /* ignore all other POX autodiscover responses */ } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_ews_autodiscover_parse: unknown response detected"); } } sipe_xml_free(xml); if (complete) sipe_ews_autodiscover_complete(sipe_private, ews_data); } static void sipe_ews_autodiscover_response(struct sipe_core_private *sipe_private, guint status, GSList *headers, const gchar *body, gpointer data) { struct sipe_ews_autodiscover *sea = data; const gchar *type = sipe_utils_nameval_find(headers, "Content-Type"); sea->request = NULL; switch (status) { case SIPE_HTTP_STATUS_OK: /* only accept XML responses */ if (body && g_str_has_prefix(type, "text/xml")) sipe_ews_autodiscover_parse(sipe_private, body); else sipe_ews_autodiscover_request(sipe_private, TRUE); break; case SIPE_HTTP_STATUS_CLIENT_FORBIDDEN: /* * Authentication succeeded but we still weren't allowed to * view the page. At least at our work place this error is * temporary, i.e. the next access with the exact same * authentication succeeds. * * Let's try again, but only once... */ sipe_ews_autodiscover_request(sipe_private, !sea->retry); break; case SIPE_HTTP_STATUS_ABORTED: /* we are not allowed to generate new requests */ break; default: sipe_ews_autodiscover_request(sipe_private, TRUE); break; } } static gboolean sipe_ews_autodiscover_url(struct sipe_core_private *sipe_private, const gchar *url) { struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover; gchar *body = g_strdup_printf("" " " " %s" " http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a" " " "", sea->email); SIPE_DEBUG_INFO("sipe_ews_autodiscover_url: trying '%s'", url); sea->request = sipe_http_request_post(sipe_private, url, "Accept: text/xml\r\n", body, "text/xml", sipe_ews_autodiscover_response, sea); g_free(body); if (sea->request) { sipe_core_email_authentication(sipe_private, sea->request); sipe_http_request_allow_redirect(sea->request); sipe_http_request_ready(sea->request); return(TRUE); } return(FALSE); } static void sipe_ews_autodiscover_redirect_response(struct sipe_core_private *sipe_private, guint status, GSList *headers, SIPE_UNUSED_PARAMETER const gchar *body, gpointer data) { struct sipe_ews_autodiscover *sea = data; gboolean failed = (status != (guint) SIPE_HTTP_STATUS_ABORTED); sea->request = NULL; /* Start attempt with URL from redirect (3xx) response */ if ((status >= SIPE_HTTP_STATUS_REDIRECTION) && (status < SIPE_HTTP_STATUS_CLIENT_ERROR)) { const gchar *location = sipe_utils_nameval_find_instance(headers, "Location", 0); if (location) failed = !sipe_ews_autodiscover_url(sipe_private, location); } if (failed) sipe_ews_autodiscover_request(sipe_private, TRUE); } static gboolean sipe_ews_autodiscover_redirect(struct sipe_core_private *sipe_private, const gchar *url) { struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover; SIPE_DEBUG_INFO("sipe_ews_autodiscover_redirect: trying '%s'", url); sea->request = sipe_http_request_get(sipe_private, url, NULL, sipe_ews_autodiscover_redirect_response, sea); if (sea->request) { sipe_http_request_ready(sea->request); return(TRUE); } return(FALSE); } static void sipe_ews_autodiscover_request(struct sipe_core_private *sipe_private, gboolean next_method) { struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover; static const struct autodiscover_method methods[] = { { "https://Autodiscover.%s/Autodiscover/Autodiscover.xml", FALSE }, { "http://Autodiscover.%s/Autodiscover/Autodiscover.xml", TRUE }, { "http://Autodiscover.%s/Autodiscover/Autodiscover.xml", FALSE }, { "https://%s/Autodiscover/Autodiscover.xml", FALSE }, { NULL, FALSE }, }; sea->retry = next_method; if (sea->method) { if (next_method) sea->method++; } else sea->method = methods; if (sea->method->template) { gchar *url = g_strdup_printf(sea->method->template, strstr(sea->email, "@") + 1); if (!(sea->method->redirect ? sipe_ews_autodiscover_redirect(sipe_private, url) : sipe_ews_autodiscover_url(sipe_private, url))) sipe_ews_autodiscover_request(sipe_private, TRUE); g_free(url); } else { SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_autodiscover_request: no more methods to try!"); sipe_ews_autodiscover_complete(sipe_private, NULL); } } void sipe_ews_autodiscover_start(struct sipe_core_private *sipe_private, sipe_ews_autodiscover_callback *callback, gpointer callback_data) { struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover; if (sea->completed) { (*callback)(sipe_private, sea->data, callback_data); } else { struct sipe_ews_autodiscover_cb *sea_cb = g_new(struct sipe_ews_autodiscover_cb, 1); sea_cb->cb = callback; sea_cb->cb_data = callback_data; sea->callbacks = g_slist_prepend(sea->callbacks, sea_cb); if (!sea->method) sipe_ews_autodiscover_request(sipe_private, TRUE); } } void sipe_ews_autodiscover_init(struct sipe_core_private *sipe_private) { struct sipe_ews_autodiscover *sea = g_new0(struct sipe_ews_autodiscover, 1); sea->email = g_strdup(sipe_private->email); sipe_private->ews_autodiscover = sea; } void sipe_ews_autodiscover_free(struct sipe_core_private *sipe_private) { struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover; struct sipe_ews_autodiscover_data *ews_data = sea->data; sipe_ews_autodiscover_complete(sipe_private, NULL); if (ews_data) { g_free((gchar *)ews_data->as_url); g_free((gchar *)ews_data->ews_url); g_free((gchar *)ews_data->legacy_dn); g_free((gchar *)ews_data->oab_url); g_free((gchar *)ews_data->oof_url); g_free(ews_data); } g_free(sea->email); g_free(sea); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-ews-autodiscover.h ================================================ /** * @file sipe-ews-autodiscover.h * * pidgin-sipe * * Copyright (C) 2013 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_core_private; /* EWS data determined by autodiscover */ struct sipe_ews_autodiscover_data { const gchar *as_url; const gchar *ews_url; const gchar *legacy_dn; const gchar *oab_url; const gchar *oof_url; }; /** * * EWS autodiscover callback * * @param sipe_private SIPE core private data * @param ews_data EWS autodiscover data (NULL when failed/aborted) * @param callback_data callback data */ typedef void (sipe_ews_autodiscover_callback)(struct sipe_core_private *sipe_private, const struct sipe_ews_autodiscover_data *ews_data, gpointer callback_data); /** * Trigger EWS autodiscover * * @param sipe_private SIPE core private data * @param callback callback function * @param callback_data callback data */ void sipe_ews_autodiscover_start(struct sipe_core_private *sipe_private, sipe_ews_autodiscover_callback *callback, gpointer callback_data); /** * Initialize EWS autodiscover data * * @param sipe_private SIPE core private data */ void sipe_ews_autodiscover_init(struct sipe_core_private *sipe_private); /** * Free EWS autodiscover data * * @param sipe_private SIPE core private data */ void sipe_ews_autodiscover_free(struct sipe_core_private *sipe_private); ================================================ FILE: src/core/sipe-ews.c ================================================ /** * @file sipe-ews.c * * pidgin-sipe * * Copyright (C) 2010-2015 SIPE Project * Copyright (C) 2010, 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** For communication with Exchange 2007/2010 Web Server/Web Services: 1) Autodiscover (HTTPS POST request). With redirect support. XML content. 1.1) DNS SRV record _autodiscover._tcp. may also be resolved. 2) Availability Web service (SOAP = HTTPS POST + XML) call. 3) Out of Office (OOF) Web Service (SOAP = HTTPS POST + XML) call. 4) Web server authentication required - NTLM and/or Negotiate (Kerberos). Note: ews - EWS stands for Exchange Web Services. It will be able to retrieve our Calendar information (FreeBusy, WorkingHours, Meetings Subject and Location, Is_Meeting) as well as our Out of Office (OOF) note from Exchange Web Services for subsequent publishing. Ref. for more implementation details: http://sourceforge.net/projects/sipe/forums/forum/688535/topic/3403462 Similar functionality for Lotus Notes/Domino, iCalendar/CalDAV/Google would be great to implement too. */ #include #include #include #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-cal.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-ews.h" #include "sipe-ews-autodiscover.h" #include "sipe-http.h" #include "sipe-utils.h" #include "sipe-xml.h" /** * GetUserOofSettingsRequest SOAP request to Exchange Web Services * to obtain our Out-of-office (OOF) information. * @param email (%s) Ex.: alice@cosmo.local */ #define SIPE_EWS_USER_OOF_SETTINGS_REQUEST \ ""\ ""\ ""\ ""\ ""\ "
%s
"\ "
"\ "
"\ "
"\ "
" /** * GetUserAvailabilityRequest SOAP request to Exchange Web Services * to obtain our Availability (FreeBusy, WorkingHours, Meetings) information. * @param email (%s) Ex.: alice@cosmo.local * @param start_time (%s) Ex.: 2009-12-06T00:00:00 * @param end_time (%s) Ex.: 2009-12-09T23:59:59 */ #define SIPE_EWS_USER_AVAILABILITY_REQUEST \ ""\ ""\ ""\ ""\ ""\ "0"\ ""\ "0"\ ""\ "0"\ "0"\ "Sunday"\ ""\ ""\ "0"\ ""\ "0"\ "0"\ "Sunday"\ ""\ ""\ ""\ ""\ ""\ "%s"\ ""\ "Required"\ "false"\ ""\ ""\ ""\ ""\ "%s"\ "%s"\ ""\ "15"\ "DetailedMerged"\ ""\ ""\ ""\ "" #define SIPE_EWS_STATE_IDLE 0 #define SIPE_EWS_STATE_AUTODISCOVER_TRIGGERED 1 #define SIPE_EWS_STATE_AVAILABILITY_SUCCESS 2 #define SIPE_EWS_STATE_AVAILABILITY_FAILURE -2 #define SIPE_EWS_STATE_OOF_SUCCESS 3 #define SIPE_EWS_STATE_OOF_FAILURE -3 char * sipe_ews_get_oof_note(struct sipe_calendar *cal) { time_t now = time(NULL); if (!cal || !cal->oof_state) return NULL; if (sipe_strequal(cal->oof_state, "Enabled") || (sipe_strequal(cal->oof_state, "Scheduled") && now >= cal->oof_start && now <= cal->oof_end)) { return cal->oof_note; } else { return NULL; } } static void sipe_ews_run_state_machine(struct sipe_calendar *cal); static void sipe_ews_process_avail_response(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, guint status, SIPE_UNUSED_PARAMETER GSList *headers, const gchar *body, gpointer data) { struct sipe_calendar *cal = data; SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_process_avail_response: cb started."); cal->request = NULL; if ((status == SIPE_HTTP_STATUS_OK) && body) { const sipe_xml *node; const sipe_xml *resp; /** ref: [MS-OXWAVLS] */ sipe_xml *xml = sipe_xml_parse(body, strlen(body)); /* Envelope/Body/GetUserAvailabilityResponse/FreeBusyResponseArray/FreeBusyResponse/ResponseMessage@ResponseClass="Success" Envelope/Body/GetUserAvailabilityResponse/FreeBusyResponseArray/FreeBusyResponse/FreeBusyView/MergedFreeBusy Envelope/Body/GetUserAvailabilityResponse/FreeBusyResponseArray/FreeBusyResponse/FreeBusyView/CalendarEventArray/CalendarEvent Envelope/Body/GetUserAvailabilityResponse/FreeBusyResponseArray/FreeBusyResponse/FreeBusyView/WorkingHours */ resp = sipe_xml_child(xml, "Body/GetUserAvailabilityResponse/FreeBusyResponseArray/FreeBusyResponse"); if (!resp) return; /* rather soap:Fault */ if (!sipe_strequal(sipe_xml_attribute(sipe_xml_child(resp, "ResponseMessage"), "ResponseClass"), "Success")) { return; /* Error response */ } /* MergedFreeBusy */ g_free(cal->free_busy); cal->free_busy = sipe_xml_data(sipe_xml_child(resp, "FreeBusyView/MergedFreeBusy")); /* WorkingHours */ node = sipe_xml_child(resp, "FreeBusyView/WorkingHours"); g_free(cal->working_hours_xml_str); cal->working_hours_xml_str = sipe_xml_stringify(node); SIPE_DEBUG_INFO("sipe_ews_process_avail_response: cal->working_hours_xml_str:\n%s", cal->working_hours_xml_str ? cal->working_hours_xml_str : ""); sipe_cal_events_free(cal->cal_events); cal->cal_events = NULL; /* CalendarEvents */ for (node = sipe_xml_child(resp, "FreeBusyView/CalendarEventArray/CalendarEvent"); node; node = sipe_xml_twin(node)) { char *tmp; /* 2009-12-07T13:30:00 2009-12-07T14:30:00 Busy 0000000... Lunch Cafe false true false true false */ struct sipe_cal_event *cal_event = g_new0(struct sipe_cal_event, 1); cal->cal_events = g_slist_append(cal->cal_events, cal_event); tmp = sipe_xml_data(sipe_xml_child(node, "StartTime")); cal_event->start_time = sipe_utils_str_to_time(tmp); g_free(tmp); tmp = sipe_xml_data(sipe_xml_child(node, "EndTime")); cal_event->end_time = sipe_utils_str_to_time(tmp); g_free(tmp); tmp = sipe_xml_data(sipe_xml_child(node, "BusyType")); if (sipe_strequal("Free", tmp)) { cal_event->cal_status = SIPE_CAL_FREE; } else if (sipe_strequal("Tentative", tmp)) { cal_event->cal_status = SIPE_CAL_TENTATIVE; } else if (sipe_strequal("Busy", tmp)) { cal_event->cal_status = SIPE_CAL_BUSY; } else if (sipe_strequal("OOF", tmp)) { cal_event->cal_status = SIPE_CAL_OOF; } else { cal_event->cal_status = SIPE_CAL_NO_DATA; } g_free(tmp); cal_event->subject = sipe_xml_data(sipe_xml_child(node, "CalendarEventDetails/Subject")); cal_event->location = sipe_xml_data(sipe_xml_child(node, "CalendarEventDetails/Location")); tmp = sipe_xml_data(sipe_xml_child(node, "CalendarEventDetails/IsMeeting")); cal_event->is_meeting = tmp ? sipe_strequal(tmp, "true") : TRUE; g_free(tmp); } sipe_xml_free(xml); cal->state = SIPE_EWS_STATE_AVAILABILITY_SUCCESS; sipe_ews_run_state_machine(cal); } else { cal->state = SIPE_EWS_STATE_AVAILABILITY_FAILURE; sipe_ews_run_state_machine(cal); } } static void sipe_ews_process_oof_response(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, guint status, SIPE_UNUSED_PARAMETER GSList *headers, const gchar *body, gpointer data) { struct sipe_calendar *cal = data; SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_process_oof_response: cb started."); cal->request = NULL; if ((status == SIPE_HTTP_STATUS_OK) && body) { char *old_note; const sipe_xml *resp; const sipe_xml *xn_duration; /** ref: [MS-OXWOOF] */ sipe_xml *xml = sipe_xml_parse(body, strlen(body)); /* Envelope/Body/GetUserOofSettingsResponse/ResponseMessage@ResponseClass="Success" * Envelope/Body/GetUserOofSettingsResponse/OofSettings/OofState=Enabled * Envelope/Body/GetUserOofSettingsResponse/OofSettings/Duration/StartTime * Envelope/Body/GetUserOofSettingsResponse/OofSettings/Duration/EndTime * Envelope/Body/GetUserOofSettingsResponse/OofSettings/InternalReply/Message */ resp = sipe_xml_child(xml, "Body/GetUserOofSettingsResponse"); if (!resp) return; /* rather soap:Fault */ if (!sipe_strequal(sipe_xml_attribute(sipe_xml_child(resp, "ResponseMessage"), "ResponseClass"), "Success")) { return; /* Error response */ } g_free(cal->oof_state); cal->oof_state = sipe_xml_data(sipe_xml_child(resp, "OofSettings/OofState")); old_note = cal->oof_note; cal->oof_note = NULL; if (!sipe_strequal(cal->oof_state, "Disabled")) { char *tmp = sipe_xml_data( sipe_xml_child(resp, "OofSettings/InternalReply/Message")); char *html; /* UTF-8 encoded BOM (0xEF 0xBB 0xBF) as a signature to mark the beginning of a UTF-8 file */ if (g_str_has_prefix(tmp, "\xEF\xBB\xBF")) { html = g_strdup(tmp+3); } else { html = g_strdup(tmp); } g_free(tmp); tmp = g_strstrip(sipe_backend_markup_strip_html(html)); g_free(html); cal->oof_note = g_markup_escape_text(tmp, -1); g_free(tmp); } if (sipe_strequal(cal->oof_state, "Scheduled") && (xn_duration = sipe_xml_child(resp, "OofSettings/Duration"))) { char *tmp = sipe_xml_data(sipe_xml_child(xn_duration, "StartTime")); cal->oof_start = sipe_utils_str_to_time(tmp); g_free(tmp); tmp = sipe_xml_data(sipe_xml_child(xn_duration, "EndTime")); cal->oof_end = sipe_utils_str_to_time(tmp); g_free(tmp); } if (!sipe_strequal(old_note, cal->oof_note)) { /* oof note changed */ cal->updated = time(NULL); cal->published = FALSE; } g_free(old_note); sipe_xml_free(xml); cal->state = SIPE_EWS_STATE_OOF_SUCCESS; sipe_ews_run_state_machine(cal); } else { cal->state = SIPE_EWS_STATE_OOF_FAILURE; sipe_ews_run_state_machine(cal); } } static void sipe_ews_send_http_request(struct sipe_calendar *cal) { if (cal->request) { sipe_core_email_authentication(cal->sipe_private, cal->request); sipe_http_request_allow_redirect(cal->request); sipe_http_request_ready(cal->request); } } static void sipe_ews_do_avail_request(struct sipe_calendar *cal) { if (cal->as_url) { char *body; time_t end; time_t now = time(NULL); char *start_str; char *end_str; struct tm *now_tm; SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_do_avail_request: going Availability req."); now_tm = gmtime(&now); /* start -1 day, 00:00:00 */ now_tm->tm_sec = 0; now_tm->tm_min = 0; now_tm->tm_hour = 0; cal->fb_start = sipe_mktime_tz(now_tm, "UTC"); cal->fb_start -= 24*60*60; /* end = start + 4 days - 1 sec */ end = cal->fb_start + SIPE_FREE_BUSY_PERIOD_SEC - 1; start_str = sipe_utils_time_to_str(cal->fb_start); end_str = sipe_utils_time_to_str(end); body = g_strdup_printf(SIPE_EWS_USER_AVAILABILITY_REQUEST, cal->email, start_str, end_str); cal->request = sipe_http_request_post(cal->sipe_private, cal->as_url, NULL, body, "text/xml; charset=UTF-8", sipe_ews_process_avail_response, cal); g_free(body); g_free(start_str); g_free(end_str); sipe_ews_send_http_request(cal); } } static void sipe_ews_do_oof_request(struct sipe_calendar *cal) { if (cal->oof_url) { char *body; SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_do_oof_request: going OOF req."); body = g_strdup_printf(SIPE_EWS_USER_OOF_SETTINGS_REQUEST, cal->email); cal->request = sipe_http_request_post(cal->sipe_private, cal->as_url, NULL, body, "text/xml; charset=UTF-8", sipe_ews_process_oof_response, cal); g_free(body); sipe_ews_send_http_request(cal); } } static void sipe_ews_run_state_machine(struct sipe_calendar *cal) { switch (cal->state) { case SIPE_EWS_STATE_AVAILABILITY_FAILURE: case SIPE_EWS_STATE_OOF_FAILURE: cal->is_ews_disabled = TRUE; break; case SIPE_EWS_STATE_IDLE: sipe_ews_do_avail_request(cal); break; case SIPE_EWS_STATE_AUTODISCOVER_TRIGGERED: /* do nothing */ break; case SIPE_EWS_STATE_AVAILABILITY_SUCCESS: sipe_ews_do_oof_request(cal); break; case SIPE_EWS_STATE_OOF_SUCCESS: { struct sipe_core_private *sipe_private = cal->sipe_private; cal->state = SIPE_EWS_STATE_IDLE; cal->is_updated = TRUE; sipe_cal_presence_publish(sipe_private, TRUE); } break; } } static void sipe_calendar_ews_autodiscover_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, const struct sipe_ews_autodiscover_data *ews_data, gpointer callback_data) { struct sipe_calendar *cal = callback_data; if (ews_data) { cal->as_url = g_strdup(ews_data->as_url); cal->legacy_dn = g_strdup(ews_data->legacy_dn); cal->oab_url = g_strdup(ews_data->oab_url); cal->oof_url = g_strdup(ews_data->oof_url); cal->state = SIPE_EWS_STATE_IDLE; sipe_ews_run_state_machine(cal); } else { SIPE_DEBUG_INFO_NOFORMAT("sipe_calendar_ews_autodiscover_cb: EWS disabled"); cal->is_ews_disabled = TRUE; } } void sipe_ews_update_calendar(struct sipe_core_private *sipe_private) { //char *autodisc_srv = g_strdup_printf("_autodiscover._tcp.%s", maildomain); struct sipe_calendar *cal; SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_update_calendar: started."); sipe_cal_calendar_init(sipe_private); cal = sipe_private->calendar; if (cal->is_ews_disabled) { SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_update_calendar: disabled, exiting."); } else if (!cal->as_url && (cal->state != SIPE_EWS_STATE_AUTODISCOVER_TRIGGERED)) { cal->state = SIPE_EWS_STATE_AUTODISCOVER_TRIGGERED; sipe_ews_autodiscover_start(sipe_private, sipe_calendar_ews_autodiscover_cb, cal); } else { sipe_ews_run_state_machine(cal); SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_update_calendar: finished."); } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-ews.h ================================================ /** * @file sipe-ews.h * * pidgin-sipe * * Copyright (C) 2010 SIPE Project * Copyright (C) 2009 pier11 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_calendar; struct sipe_core_private; /** * Connects to Exchange 2007/2010 Server's Web Services, * pulls out our Availability and Out-of-Office (OOF) information * and publishes it to Communications server. * * Advised schedule: 30 minutes. */ void sipe_ews_update_calendar(struct sipe_core_private *sipe_private); /** * Returns OOF note if enabled in the moment * otherwise NULL. */ char * sipe_ews_get_oof_note(struct sipe_calendar *cal); ================================================ FILE: src/core/sipe-ft-lync.c ================================================ /** * @file sipe-ft-lync.c * * pidgin-sipe * * Copyright (C) 2014-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-ft-lync.h" #include "sipe-media.h" #include "sipe-mime.h" #include "sipe-nls.h" #include "sipe-utils.h" #include "sipe-xml.h" #include "sipmsg.h" struct sipe_file_transfer_lync { struct sipe_file_transfer public; gchar *sdp; gchar *file_name; gchar *id; gsize file_size; guint request_id; guint bytes_left_in_chunk; guint8 buffer[2048]; guint buffer_len; guint buffer_read_pos; int backend_pipe[2]; int backend_pipe_write_source_id; struct sipe_core_private *sipe_private; struct sipe_media_call *call; void (*call_reject_parent_cb)(struct sipe_media_call *call, gboolean local); }; #define SIPE_FILE_TRANSFER ((struct sipe_file_transfer *) ft_private) #define SIPE_FILE_TRANSFER_PRIVATE ((struct sipe_file_transfer_lync *) ft) typedef enum { SIPE_XDATA_DATA_CHUNK = 0x00, SIPE_XDATA_START_OF_STREAM = 0x01, SIPE_XDATA_END_OF_STREAM = 0x02 } SipeXDataMessages; #define XDATA_HEADER_SIZE sizeof (guint8) + sizeof (guint16) static void sipe_file_transfer_lync_free(struct sipe_file_transfer_lync *ft_private) { int our_pipe_end; our_pipe_end = sipe_backend_ft_is_incoming(SIPE_FILE_TRANSFER) ? 1 : 0; if (ft_private->backend_pipe[our_pipe_end] != 0) { // Backend is responsible for closing the pipe's other end. close(ft_private->backend_pipe[our_pipe_end]); } g_free(ft_private->file_name); g_free(ft_private->sdp); g_free(ft_private->id); if (ft_private->backend_pipe_write_source_id) { g_source_remove(ft_private->backend_pipe_write_source_id); } g_free(ft_private); } static void send_ms_filetransfer_msg(char *body, struct sipe_file_transfer_lync *ft_private, TransCallback callback) { sip_transport_info(sipe_media_get_sipe_core_private(ft_private->call), "Content-Type: application/ms-filetransfer+xml\r\n", body, sipe_media_get_sip_dialog(ft_private->call), callback); g_free(body); } static void send_ms_filetransfer_response(struct sipe_file_transfer_lync *ft_private, const gchar *code, const gchar *reason, TransCallback callback) { static const gchar *RESPONSE_STR = ""; send_ms_filetransfer_msg(g_strdup_printf(RESPONSE_STR, ft_private->request_id, code, reason ? "reason=\"" : "", reason ? reason : "", reason ? "\"" : ""), ft_private, callback); } static void mime_mixed_cb(gpointer user_data, const GSList *fields, const gchar *body, gsize length) { struct sipe_file_transfer_lync *ft_private = user_data; const gchar *ctype = sipe_utils_nameval_find(fields, "Content-Type"); /* Lync 2010 file transfer */ if (g_str_has_prefix(ctype, "application/ms-filetransfer+xml")) { sipe_xml *xml = sipe_xml_parse(body, length); if (xml) { const sipe_xml *node; ft_private->request_id = sipe_xml_int_attribute(xml, "requestId", ft_private->request_id); node = sipe_xml_child(xml, "publishFile/fileInfo/name"); if (node) { g_free(ft_private->file_name); ft_private->file_name = sipe_xml_data(node); } node = sipe_xml_child(xml, "publishFile/fileInfo/id"); if (node) { g_free(ft_private->id); ft_private->id = sipe_xml_data(node); } node = sipe_xml_child(xml, "publishFile/fileInfo/size"); if (node) { gchar *size_str = sipe_xml_data(node); if (size_str) { ft_private->file_size = atoi(size_str); g_free(size_str); } } sipe_xml_free(xml); } } else if (g_str_has_prefix(ctype, "application/sdp")) { g_free(ft_private->sdp); ft_private->sdp = g_strndup(body, length); } } static void candidate_pairs_established_cb(struct sipe_media_stream *stream) { struct sipe_file_transfer_lync *ft_private; static const gchar *DOWNLOAD_FILE_REQUEST = "" "" "" "%s" "%s" "" "" ""; g_return_if_fail(sipe_strequal(stream->id, "data")); ft_private = sipe_media_stream_get_data(stream); send_ms_filetransfer_response(ft_private, "success", NULL, NULL); send_ms_filetransfer_msg(g_strdup_printf(DOWNLOAD_FILE_REQUEST, ++ft_private->request_id, ft_private->id, ft_private->file_name), ft_private, NULL); } static gboolean create_pipe(int pipefd[2]) { #ifdef _WIN32 #error "Pipes not implemented for Windows" /* Those interested in porting the code may use Pidgin's wpurple_input_pipe() in * win32dep.c as an inspiration. */ #else if (pipe(pipefd) != 0) { return FALSE; } /* @TODO: ignoring potential error return - how to handle? */ (void) fcntl(pipefd[0], F_SETFL, fcntl(pipefd[0], F_GETFL) | O_NONBLOCK); (void) fcntl(pipefd[1], F_SETFL, fcntl(pipefd[1], F_GETFL) | O_NONBLOCK); return TRUE; #endif } static void xdata_start_of_stream_cb(struct sipe_media_stream *stream, guint8 *buffer, gsize len) { struct sipe_file_transfer_lync *ft_private = sipe_media_stream_get_data(stream); struct sipe_backend_fd *fd; buffer[len] = 0; SIPE_DEBUG_INFO("Received new stream for requestId : %s", buffer); if (!create_pipe(ft_private->backend_pipe)) { SIPE_DEBUG_ERROR_NOFORMAT("Couldn't create backend pipe"); sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER); return; } fd = sipe_backend_fd_from_int(ft_private->backend_pipe[0]); sipe_backend_ft_start(SIPE_FILE_TRANSFER, fd, NULL, 0); sipe_backend_fd_free(fd); } static void xdata_end_of_stream_cb(SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, guint8 *buffer, gsize len) { buffer[len] = 0; SIPE_DEBUG_INFO("Received end of stream for requestId : %s", buffer); } static void xdata_got_header_cb(struct sipe_media_stream *stream, guint8 *buffer, SIPE_UNUSED_PARAMETER gsize len) { struct sipe_file_transfer_lync *ft_private = sipe_media_stream_get_data(stream); guint8 type = buffer[0]; guint16 size = (buffer[1] << 8) + buffer[2]; /* stored as big-endian */ switch (type) { case SIPE_XDATA_START_OF_STREAM: sipe_media_stream_read_async(stream, ft_private->buffer, size, xdata_start_of_stream_cb); break; case SIPE_XDATA_DATA_CHUNK: SIPE_DEBUG_INFO("Received new data chunk of size %d", size); ft_private->bytes_left_in_chunk = size; break; /* We'll read the data when read_cb is called again. */ case SIPE_XDATA_END_OF_STREAM: sipe_media_stream_read_async(stream, ft_private->buffer, size, xdata_end_of_stream_cb); break; } } static void read_cb(struct sipe_media_stream *stream) { struct sipe_file_transfer_lync *ft_private = sipe_media_stream_get_data(stream); if (ft_private->buffer_read_pos < ft_private->buffer_len) { /* Have data in buffer, write them to the backend. */ gpointer buffer; size_t len; ssize_t written; buffer = ft_private->buffer + ft_private->buffer_read_pos; len = ft_private->buffer_len - ft_private->buffer_read_pos; written = write(ft_private->backend_pipe[1], buffer, len); if (written > 0) { ft_private->buffer_read_pos += written; } else if (written < 0 && errno != EAGAIN) { SIPE_DEBUG_ERROR_NOFORMAT("Error while writing into " "backend pipe"); sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER); return; } } else if (ft_private->bytes_left_in_chunk != 0) { /* Have data from the sender, replenish our buffer with it. */ ft_private->buffer_len = MIN(ft_private->bytes_left_in_chunk, sizeof (ft_private->buffer)); ft_private->buffer_len = sipe_backend_media_stream_read(stream, ft_private->buffer, ft_private->buffer_len); ft_private->bytes_left_in_chunk -= ft_private->buffer_len; ft_private->buffer_read_pos = 0; SIPE_DEBUG_INFO("Read %d bytes. %d left in this chunk.", ft_private->buffer_len, ft_private->bytes_left_in_chunk); } else { /* No data available. This is either stream start, beginning of * chunk, or stream end. */ sipe_media_stream_read_async(stream, ft_private->buffer, XDATA_HEADER_SIZE, xdata_got_header_cb); } } static void ft_lync_incoming_init(struct sipe_file_transfer *ft, SIPE_UNUSED_PARAMETER const gchar *filename, SIPE_UNUSED_PARAMETER gsize size, SIPE_UNUSED_PARAMETER const gchar *who) { struct sipe_media_call *call = SIPE_FILE_TRANSFER_PRIVATE->call; if (call) { sipe_backend_media_accept(call->backend_private, TRUE); } } static void ft_lync_request_denied(struct sipe_file_transfer *ft) { struct sipe_file_transfer_lync *ft_private = SIPE_FILE_TRANSFER_PRIVATE; struct sipe_media_call *call; g_return_if_fail(ft_private); call = ft_private->call; if (call && call->backend_private) { sipe_backend_media_reject(call->backend_private, TRUE); } } static struct sipe_file_transfer_lync * ft_private_from_call(struct sipe_media_call *call) { struct sipe_media_stream *stream = sipe_core_media_get_stream_by_id(call, "data"); g_return_val_if_fail(stream, NULL); return sipe_media_stream_get_data(stream); } static void send_transfer_progress(struct sipe_file_transfer_lync *ft_private) { static const gchar *FILETRANSFER_PROGRESS = "" "" "%d" "" "0" "%d" "" "" ""; send_ms_filetransfer_msg(g_strdup_printf(FILETRANSFER_PROGRESS, rand(), ft_private->request_id, ft_private->file_size - 1), ft_private, NULL); } static gboolean ft_lync_end(struct sipe_file_transfer *ft) { send_transfer_progress(SIPE_FILE_TRANSFER_PRIVATE); return TRUE; } static void call_reject_cb(struct sipe_media_call *call, gboolean local) { struct sipe_file_transfer_lync *ft_private = ft_private_from_call(call); g_return_if_fail(ft_private); if (ft_private->call_reject_parent_cb) { ft_private->call_reject_parent_cb(call, local); } if (!local) { sipe_backend_ft_cancel_remote(&ft_private->public); } } static void ft_lync_incoming_cancelled(struct sipe_file_transfer *ft) { static const gchar *FILETRANSFER_CANCEL_REQUEST = "" "" "%d" "" "%s" "%s" "" "" ""; struct sipe_file_transfer_lync *ft_private = SIPE_FILE_TRANSFER_PRIVATE; struct sipe_media_stream *stream; send_ms_filetransfer_msg(g_strdup_printf(FILETRANSFER_CANCEL_REQUEST, ft_private->request_id + 1, ft_private->request_id, ft_private->id, ft_private->file_name), ft_private, NULL); stream = sipe_core_media_get_stream_by_id(ft_private->call, "data"); if (stream) { stream->read_cb = NULL; } sipe_backend_media_hangup(ft_private->call->backend_private, FALSE); } void process_incoming_invite_ft_lync(struct sipe_core_private *sipe_private, struct sipmsg *msg) { struct sipe_file_transfer_lync *ft_private; struct sipe_media_stream *stream; ft_private = g_new0(struct sipe_file_transfer_lync, 1); sipe_mime_parts_foreach(sipmsg_find_content_type_header(msg), msg->body, mime_mixed_cb, ft_private); if (!ft_private->file_name || !ft_private->file_size || !ft_private->sdp) { sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL); sipe_file_transfer_lync_free(ft_private); return; } /* Use the selected SDP part of multipart SIP message to initialize * media session. */ ft_private->call = process_incoming_invite_call(sipe_private, msg, ft_private->sdp); g_free(ft_private->sdp); ft_private->sdp = NULL; if (!ft_private->call) { sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL); sipe_file_transfer_lync_free(ft_private); return; } ft_private->public.ft_init = ft_lync_incoming_init; ft_private->public.ft_request_denied = ft_lync_request_denied; ft_private->public.ft_cancelled = ft_lync_incoming_cancelled; ft_private->public.ft_end = ft_lync_end; ft_private->call_reject_parent_cb = ft_private->call->call_reject_cb; ft_private->call->call_reject_cb = call_reject_cb; stream = sipe_core_media_get_stream_by_id(ft_private->call, "data"); if (stream) { stream->candidate_pairs_established_cb = candidate_pairs_established_cb; stream->read_cb = read_cb; sipe_media_stream_add_extra_attribute(stream, "recvonly", NULL); sipe_media_stream_set_data(stream, ft_private, (GDestroyNotify)sipe_file_transfer_lync_free); sipe_backend_ft_incoming(SIPE_CORE_PUBLIC, SIPE_FILE_TRANSFER, ft_private->call->with, ft_private->file_name, ft_private->file_size); } else { sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL); sipe_file_transfer_lync_free(ft_private); return; } } static void process_response_incoming(struct sipe_file_transfer_lync *ft_private, sipe_xml *xml) { const gchar *attr; guint request_id = sipe_xml_int_attribute(xml, "requestId", 0); if (request_id != ft_private->request_id) { return; } attr = sipe_xml_attribute(xml, "code"); if (sipe_strequal(attr, "failure")) { const gchar *reason = sipe_xml_attribute(xml, "reason"); if (sipe_strequal(reason, "requestCancelled")) { sipe_backend_ft_cancel_remote(SIPE_FILE_TRANSFER); } } } static void write_chunk(struct sipe_media_stream *stream, guint8 type, guint16 len, const gchar *buffer) { guint16 len_be = GUINT16_TO_BE(len); sipe_media_stream_write(stream, &type, sizeof (guint8)); sipe_media_stream_write(stream, (guint8 *)&len_be, sizeof (guint16)); sipe_media_stream_write(stream, (guint8 *)buffer, len); } static gboolean send_file_chunk(SIPE_UNUSED_PARAMETER GIOChannel *source, SIPE_UNUSED_PARAMETER GIOCondition condition, gpointer data) { struct sipe_file_transfer_lync *ft_private = data; struct sipe_media_call *call = ft_private->call; struct sipe_media_stream *stream; gssize bytes_read; stream = sipe_core_media_get_stream_by_id(call, "data"); if (!stream) { SIPE_DEBUG_ERROR_NOFORMAT("Couldn't find data stream"); sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER); ft_private->backend_pipe_write_source_id = 0; return FALSE; /* G_SOURCE_REMOVE */ } if (!sipe_media_stream_is_writable(stream)) { return TRUE; /* G_SOURCE_CONTINUE */ } bytes_read = read(ft_private->backend_pipe[0], ft_private->buffer, sizeof (ft_private->buffer)); if (bytes_read > 0) { write_chunk(stream, SIPE_XDATA_DATA_CHUNK, bytes_read, (const gchar *)ft_private->buffer); } else if (bytes_read == 0) { /* EOF, write end of stream */ gchar *request_id_str; request_id_str = g_strdup_printf("%u", ft_private->request_id); write_chunk(stream, SIPE_XDATA_END_OF_STREAM, strlen(request_id_str), request_id_str); g_free(request_id_str); return FALSE; /* G_SOURCE_REMOVE */ } return TRUE; /* G_SOURCE_CONTINUE */ } static void start_writing(struct sipe_file_transfer_lync *ft_private) { struct sipe_media_stream *stream; gchar *request_id_str; struct sipe_backend_fd *fd; GIOChannel *channel; stream = sipe_core_media_get_stream_by_id(ft_private->call, "data"); if (!stream) { return; } if (!create_pipe(ft_private->backend_pipe)) { SIPE_DEBUG_ERROR_NOFORMAT("Couldn't create backend pipe"); sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER); return; } request_id_str = g_strdup_printf("%u", ft_private->request_id); write_chunk(stream, SIPE_XDATA_START_OF_STREAM, strlen(request_id_str), request_id_str); g_free(request_id_str); channel = g_io_channel_unix_new(ft_private->backend_pipe[0]); ft_private->backend_pipe_write_source_id = g_io_add_watch(channel, G_IO_IN | G_IO_HUP, send_file_chunk, ft_private); g_io_channel_unref(channel); fd = sipe_backend_fd_from_int(ft_private->backend_pipe[1]); sipe_backend_ft_start(SIPE_FILE_TRANSFER, fd, NULL, 0); sipe_backend_fd_free(fd); } static void process_request(struct sipe_file_transfer_lync *ft_private, sipe_xml *xml) { static const gchar *DOWNLOAD_PENDING_RESPONSE = ""; if (sipe_xml_child(xml, "downloadFile")) { ft_private->request_id = atoi(sipe_xml_attribute(xml, "requestId")); send_ms_filetransfer_msg(g_strdup_printf(DOWNLOAD_PENDING_RESPONSE, ft_private->request_id), ft_private, NULL); start_writing(ft_private); } } static void process_notify(struct sipe_file_transfer_lync *ft_private, sipe_xml *xml) { static const gchar *DOWNLOAD_SUCCESS_RESPONSE = ""; const sipe_xml *progress_node = sipe_xml_child(xml, "fileTransferProgress"); if (progress_node) { gchar *to_str = sipe_xml_data(sipe_xml_child(progress_node, "bytesReceived/to")); if (atoi(to_str) == (int)(ft_private->file_size - 1)) { send_ms_filetransfer_msg(g_strdup_printf(DOWNLOAD_SUCCESS_RESPONSE, ft_private->request_id), ft_private, NULL); sipe_backend_media_hangup(ft_private->call->backend_private, TRUE); } g_free(to_str); } } void process_incoming_info_ft_lync(struct sipe_core_private *sipe_private, struct sipmsg *msg) { struct sipe_media_call *call; struct sipe_file_transfer_lync *ft_private; sipe_xml *xml; call = g_hash_table_lookup(sipe_private->media_calls, sipmsg_find_call_id_header(msg)); if (!call) { return; } ft_private = ft_private_from_call(call); if (!ft_private) { return; } xml = sipe_xml_parse(msg->body, msg->bodylen); if (!xml) { return; } sip_transport_response(sipe_private, msg, 200, "OK", NULL); if (sipe_backend_ft_is_incoming(SIPE_FILE_TRANSFER)) { if (sipe_strequal(sipe_xml_name(xml), "response")) { process_response_incoming(ft_private, xml); } } else { if (sipe_strequal(sipe_xml_name(xml), "request")) { process_request(ft_private, xml); } else if (sipe_strequal(sipe_xml_name(xml), "notify")) { process_notify(ft_private, xml); } } sipe_xml_free(xml); } static void append_publish_file_invite(struct sipe_media_call *call, struct sipe_file_transfer_lync *ft_private) { static const gchar *PUBLISH_FILE_REQUEST = "Content-Type: application/ms-filetransfer+xml\r\n" "Content-Transfer-Encoding: 7bit\r\n" "Content-Disposition: render; handling=optional\r\n" "\r\n" "" "" "" "{6244F934-2EB1-443F-8E2C-48BA64AF463D}" "%s" "%u" "" "" "\r\n"; gchar *body; ft_private->request_id = ++ft_private->sipe_private->ms_filetransfer_request_id; body = g_strdup_printf(PUBLISH_FILE_REQUEST, ft_private->request_id, ft_private->file_name, ft_private->file_size); sipe_media_add_extra_invite_section(call, "multipart/mixed", body); } static void ft_lync_outgoing_init(struct sipe_file_transfer *ft, const gchar *filename, gsize size, SIPE_UNUSED_PARAMETER const gchar *who) { struct sipe_core_private *sipe_private = SIPE_FILE_TRANSFER_PRIVATE->sipe_private; struct sipe_file_transfer_lync *ft_private = SIPE_FILE_TRANSFER_PRIVATE; struct sipe_media_call *call; struct sipe_media_stream *stream; ft_private->file_name = g_strdup(filename); ft_private->file_size = size; call = sipe_media_call_new(sipe_private, who, NULL, SIPE_ICE_RFC_5245, SIPE_MEDIA_CALL_NO_UI); ft_private->call = call; ft_private->call_reject_parent_cb = call->call_reject_cb; call->call_reject_cb = call_reject_cb; stream = sipe_media_stream_add(call, "data", SIPE_MEDIA_APPLICATION, SIPE_ICE_RFC_5245, TRUE, 0); if (!stream) { sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Error occurred"), _("Error creating data stream")); sipe_backend_media_hangup(call->backend_private, FALSE); sipe_backend_ft_cancel_local(ft); return; } sipe_media_stream_add_extra_attribute(stream, "sendonly", NULL); sipe_media_stream_add_extra_attribute(stream, "mid", "1"); sipe_media_stream_set_data(stream, ft, (GDestroyNotify)sipe_file_transfer_lync_free); append_publish_file_invite(call, ft_private); } struct sipe_file_transfer * sipe_file_transfer_lync_new_outgoing(struct sipe_core_private *sipe_private) { struct sipe_file_transfer_lync *ft_private; ft_private = g_new0(struct sipe_file_transfer_lync, 1); ft_private->sipe_private = sipe_private; ft_private->public.ft_init = ft_lync_outgoing_init; return SIPE_FILE_TRANSFER; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-ft-lync.h ================================================ /** * @file sipe-ft-lync.h * * pidgin-sipe * * Copyright (C) 2014-2015 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_core_private; struct sipmsg; struct sipe_file_transfer * sipe_file_transfer_lync_new_outgoing(struct sipe_core_private *sipe_private); void process_incoming_invite_ft_lync(struct sipe_core_private *sipe_private, struct sipmsg *msg); void process_incoming_info_ft_lync(struct sipe_core_private *sipe_private, struct sipmsg *msg); ================================================ FILE: src/core/sipe-ft-tftp.c ================================================ /** * @file sipe-ft-tftp.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * Copyright (C) 2010 Jakub Adam * Copyright (C) 2010 Tomáš Hrabčík * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-crypt.h" #include "sipe-dialog.h" #include "sipe-digest.h" #include "sipe-ft.h" #include "sipe-ft-tftp.h" #include "sipe-nls.h" #include "sipe-utils.h" #define BUFFER_SIZE 50 #define SIPE_FT_CHUNK_HEADER_LENGTH 3 static gboolean write_exact(struct sipe_file_transfer_private *ft_private, const guchar *data, gsize size) { gssize bytes_written = sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, data, size); if ((bytes_written < 0) || ((gsize) bytes_written != size)) return FALSE; return TRUE; } static gboolean read_exact(struct sipe_file_transfer_private *ft_private, guchar *data, gsize size) { const gulong READ_TIMEOUT = 10000000; gulong time_spent = 0; while (size) { gssize bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC, data, size); if (bytes_read == 0) { g_usleep(100000); time_spent += 100000; } else if (bytes_read < 0 || time_spent > READ_TIMEOUT) { return FALSE; } else { size -= bytes_read; data += bytes_read; time_spent = 0; } } return TRUE; } static gboolean read_line(struct sipe_file_transfer_private *ft_private, guchar *data, gsize size) { gsize pos = 0; if (size < 2) return FALSE; memset(data, 0, size--); do { if (!read_exact(ft_private, data + pos, 1)) return FALSE; } while ((data[pos] != '\n') && (++pos < size)); /* Buffer too short? */ if ((pos == size) && (data[pos - 1] != '\n')) { return FALSE; } return TRUE; } static void raise_ft_socket_read_error_and_cancel(struct sipe_file_transfer_private *ft_private) { sipe_ft_raise_error_and_cancel(ft_private, _("Socket read failed")); } static void raise_ft_socket_write_error_and_cancel(struct sipe_file_transfer_private *ft_private) { sipe_ft_raise_error_and_cancel(ft_private, _("Socket write failed")); } static gpointer sipe_cipher_context_init(const guchar *enc_key) { /* * Decryption of file from SIPE file transfer * * Decryption: * 1.) SHA1-Key = SHA1sum (Encryption-Key); Do SHA1 digest from Encryption-Key, return 20 bytes SHA1-Key. * 2.) Decrypt-Data = RC4 (Encrypt-Data, substr(SHA1-Key, 0, 15)); Decryption of encrypted data, used 16 bytes SHA1-Key; */ guchar k2[SIPE_DIGEST_SHA1_LENGTH]; /* 1.) SHA1 sum */ sipe_digest_sha1(enc_key, SIPE_FT_KEY_LENGTH, k2); /* 2.) RC4 decryption */ return sipe_crypt_ft_start(k2); } static gpointer sipe_hmac_context_init(const guchar *hash_key) { /* * Count MAC digest * * HMAC digest: * 1.) SHA1-Key = SHA1sum (Hash-Key); Do SHA1 digest from Hash-Key, return 20 bytes SHA1-Key. * 2.) MAC = HMAC_SHA1 (Decrypt-Data, substr(HMAC-Key,0,15)); Digest of decrypted file and SHA1-Key (used again only 16 bytes) */ guchar k2[SIPE_DIGEST_SHA1_LENGTH]; /* 1.) SHA1 sum */ sipe_digest_sha1(hash_key, SIPE_FT_KEY_LENGTH, k2); /* 2.) HMAC (initialization only) */ return sipe_digest_ft_start(k2); } static gchar * sipe_hmac_finalize(gpointer hmac_context) { guchar hmac_digest[SIPE_DIGEST_FILETRANSFER_LENGTH]; /* MAC = Digest of decrypted file and SHA1-Key (used again only 16 bytes) */ sipe_digest_ft_end(hmac_context, hmac_digest); return g_base64_encode(hmac_digest, sizeof (hmac_digest)); } void sipe_ft_tftp_start_receiving(struct sipe_file_transfer *ft, gsize total_size) { static const guchar VER[] = "VER MSN_SECURE_FTP\r\n"; static const guchar TFR[] = "TFR\r\n"; const gsize FILE_SIZE_OFFSET = 4; struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; guchar buf[BUFFER_SIZE]; gchar *request; gsize file_size; if (!write_exact(ft_private, VER, sizeof(VER) - 1)) { raise_ft_socket_read_error_and_cancel(ft_private); return; } if (!read_line(ft_private, buf, BUFFER_SIZE)) { raise_ft_socket_read_error_and_cancel(ft_private); return; } request = g_strdup_printf("USR %s %u\r\n", ft_private->sipe_private->username, ft_private->auth_cookie); if (!write_exact(ft_private, (guchar *)request, strlen(request))) { raise_ft_socket_write_error_and_cancel(ft_private); g_free(request); return; } g_free(request); if (!read_line(ft_private, buf, BUFFER_SIZE)) { raise_ft_socket_read_error_and_cancel(ft_private); return; } file_size = g_ascii_strtoull((gchar *) buf + FILE_SIZE_OFFSET, NULL, 10); if (file_size != total_size) { sipe_ft_raise_error_and_cancel(ft_private, _("File size is different from the advertised value.")); return; } if (sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, TFR, sizeof(TFR) - 1) != (sizeof(TFR) - 1)) { raise_ft_socket_write_error_and_cancel(ft_private); return; } ft_private->bytes_remaining_chunk = 0; ft_private->cipher_context = sipe_cipher_context_init(ft_private->encryption_key); ft_private->hmac_context = sipe_hmac_context_init(ft_private->hash_key); } gboolean sipe_ft_tftp_stop_receiving(struct sipe_file_transfer *ft) { static const guchar BYE[] = "BYE 16777989\r\n"; const gsize MAC_OFFSET = 4; struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; gchar buffer[BUFFER_SIZE]; gsize mac_len; gchar *mac; gchar *mac1; if (sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, BYE, sizeof(BYE) - 1) != (sizeof(BYE) - 1)) { raise_ft_socket_write_error_and_cancel(ft_private); return FALSE; } if (!read_line(ft_private, (guchar *) buffer, BUFFER_SIZE)) { raise_ft_socket_read_error_and_cancel(ft_private); return FALSE; } mac_len = strlen(buffer); if (mac_len < (MAC_OFFSET)) { sipe_ft_raise_error_and_cancel(ft_private, _("Received MAC is corrupted")); return FALSE; } /* Check MAC */ mac = g_strndup(buffer + MAC_OFFSET, mac_len - MAC_OFFSET); mac1 = sipe_hmac_finalize(ft_private->hmac_context); if (!sipe_strequal(mac, mac1)) { g_free(mac1); g_free(mac); sipe_ft_raise_error_and_cancel(ft_private, _("Received file is corrupted")); return(FALSE); } g_free(mac1); g_free(mac); sipe_ft_free(ft); return(TRUE); } void sipe_ft_tftp_start_sending(struct sipe_file_transfer *ft, gsize total_size) { static const guchar VER[] = "VER MSN_SECURE_FTP\r\n"; struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; guchar buf[BUFFER_SIZE]; gchar **parts; unsigned auth_cookie_received; gboolean users_match; if (!read_line(ft_private, buf, BUFFER_SIZE)) { raise_ft_socket_read_error_and_cancel(ft_private); return; } if (!sipe_strequal((gchar *)buf, (gchar *)VER)) { sipe_ft_raise_error_and_cancel(ft_private, _("File transfer initialization failed.")); SIPE_DEBUG_INFO("File transfer VER string incorrect, received: %s expected: %s", buf, VER); return; } if (!write_exact(ft_private, VER, sizeof(VER) - 1)) { raise_ft_socket_write_error_and_cancel(ft_private); return; } if (!read_line(ft_private, buf, BUFFER_SIZE)) { raise_ft_socket_read_error_and_cancel(ft_private); return; } parts = g_strsplit((gchar *)buf, " ", 3); auth_cookie_received = g_ascii_strtoull(parts[2], NULL, 10); /* dialog->with has 'sip:' prefix, skip these four characters */ users_match = sipe_strcase_equal(parts[1], (ft_private->dialog->with + 4)); g_strfreev(parts); SIPE_DEBUG_INFO("File transfer authentication: %s Expected: USR %s %u", buf, ft_private->dialog->with + 4, ft_private->auth_cookie); if (!users_match || (ft_private->auth_cookie != auth_cookie_received)) { sipe_ft_raise_error_and_cancel(ft_private, _("File transfer authentication failed.")); return; } g_sprintf((gchar *)buf, "FIL %" G_GSIZE_FORMAT "\r\n", total_size); if (!write_exact(ft_private, buf, strlen((gchar *)buf))) { raise_ft_socket_write_error_and_cancel(ft_private); return; } /* TFR */ if (!read_line(ft_private ,buf, BUFFER_SIZE)) { raise_ft_socket_read_error_and_cancel(ft_private); return; } ft_private->bytes_remaining_chunk = 0; ft_private->cipher_context = sipe_cipher_context_init(ft_private->encryption_key); ft_private->hmac_context = sipe_hmac_context_init(ft_private->hash_key); } gboolean sipe_ft_tftp_stop_sending(struct sipe_file_transfer *ft) { struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; guchar buffer[BUFFER_SIZE]; gchar *mac; gsize mac_len; /* BYE */ if (!read_line(ft_private, buffer, BUFFER_SIZE)) { raise_ft_socket_read_error_and_cancel(ft_private); return FALSE; } mac = sipe_hmac_finalize(ft_private->hmac_context); g_sprintf((gchar *)buffer, "MAC %s \r\n", mac); g_free(mac); mac_len = strlen((gchar *)buffer); /* There must be this zero byte between mac and \r\n */ buffer[mac_len - 3] = 0; if (!write_exact(ft_private, buffer, mac_len)) { raise_ft_socket_write_error_and_cancel(ft_private); return FALSE; } sipe_ft_free(ft); return TRUE; } static void raise_ft_error(struct sipe_file_transfer_private *ft_private, const gchar *errmsg) { gchar *tmp = g_strdup_printf("%s: %s", errmsg, sipe_backend_ft_get_error(SIPE_FILE_TRANSFER_PUBLIC)); sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, tmp); g_free(tmp); } gssize sipe_ft_tftp_read(struct sipe_file_transfer *ft, guchar **buffer, gsize bytes_remaining, gsize bytes_available) { struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; gsize bytes_to_read; gssize bytes_read; if (ft_private->bytes_remaining_chunk == 0) { guchar hdr_buf[SIPE_FT_CHUNK_HEADER_LENGTH] = { 0, 0, 0 }; /* read chunk header */ if (!read_exact(ft_private, hdr_buf, sizeof(hdr_buf))) { raise_ft_error(ft_private, _("Socket read failed")); return -1; } /* chunk header format: * * 0: 00 unknown (always zero?) * 1: LL chunk size in bytes (low byte) * 2: HH chunk size in bytes (high byte) * * Convert size from little endian to host order */ ft_private->bytes_remaining_chunk = hdr_buf[1] + (hdr_buf[2] << 8); } bytes_to_read = MIN(bytes_remaining, bytes_available); bytes_to_read = MIN(bytes_to_read, ft_private->bytes_remaining_chunk); *buffer = g_malloc(bytes_to_read); if (!*buffer) { sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Out of memory")); SIPE_DEBUG_ERROR("sipe_core_ft_read: can't allocate %" G_GSIZE_FORMAT " bytes for receive buffer", bytes_to_read); return -1; } bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC, *buffer, bytes_to_read); if (bytes_read < 0) { raise_ft_error(ft_private, _("Socket read failed")); g_free(*buffer); *buffer = NULL; return -1; } if (bytes_read > 0) { guchar *decrypted = g_malloc(bytes_read); if (!decrypted) { sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Out of memory")); SIPE_DEBUG_ERROR("sipe_core_ft_read: can't allocate %" G_GSIZE_FORMAT " bytes for decryption buffer", (gsize)bytes_read); g_free(*buffer); *buffer = NULL; return -1; } sipe_crypt_ft_stream(ft_private->cipher_context, *buffer, bytes_read, decrypted); g_free(*buffer); *buffer = decrypted; sipe_digest_ft_update(ft_private->hmac_context, decrypted, bytes_read); ft_private->bytes_remaining_chunk -= bytes_read; } return(bytes_read); } gssize sipe_ft_tftp_write(struct sipe_file_transfer *ft, const guchar *buffer, gsize size) { struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; gssize bytes_written; /* When sending data via server with ForeFront installed, block bigger than * this default causes ending of transmission. Hard limit block to this value * when libpurple sends us more data. */ const gsize DEFAULT_BLOCK_SIZE = 2045; if (size > DEFAULT_BLOCK_SIZE) size = DEFAULT_BLOCK_SIZE; if (ft_private->bytes_remaining_chunk == 0) { gssize bytes_read; guchar local_buf[16 + 1]; /* space for string terminator */ guchar hdr_buf[SIPE_FT_CHUNK_HEADER_LENGTH]; /* Check if receiver did not cancel the transfer before it is finished */ bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC, local_buf, sizeof(local_buf) - 1); local_buf[sizeof(local_buf) - 1] = '\0'; if (bytes_read < 0) { sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Socket read failed")); return -1; } else if ((bytes_read > 0) && (g_str_has_prefix((gchar *)local_buf, "CCL\r\n") || g_str_has_prefix((gchar *)local_buf, "BYE 2164261682\r\n"))) { return -1; } if (ft_private->outbuf_size < size) { g_free(ft_private->encrypted_outbuf); ft_private->outbuf_size = size; ft_private->encrypted_outbuf = g_malloc(ft_private->outbuf_size); if (!ft_private->encrypted_outbuf) { sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Out of memory")); SIPE_DEBUG_ERROR("sipe_core_ft_write: can't allocate %" G_GSIZE_FORMAT " bytes for send buffer", ft_private->outbuf_size); return -1; } } ft_private->bytes_remaining_chunk = size; ft_private->outbuf_ptr = ft_private->encrypted_outbuf; sipe_crypt_ft_stream(ft_private->cipher_context, buffer, size, ft_private->encrypted_outbuf); sipe_digest_ft_update(ft_private->hmac_context, buffer, size); /* chunk header format: * * 0: 00 unknown (always zero?) * 1: LL chunk size in bytes (low byte) * 2: HH chunk size in bytes (high byte) * * Convert size from host order to little endian */ hdr_buf[0] = 0; hdr_buf[1] = (ft_private->bytes_remaining_chunk & 0x00FF); hdr_buf[2] = (ft_private->bytes_remaining_chunk & 0xFF00) >> 8; /* write chunk header */ if (sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, hdr_buf, sizeof(hdr_buf)) != sizeof(hdr_buf)) { sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Socket write failed")); return -1; } } bytes_written = sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, ft_private->outbuf_ptr, ft_private->bytes_remaining_chunk); if (bytes_written < 0) { raise_ft_error(ft_private, _("Socket write failed")); } else if (bytes_written > 0) { ft_private->bytes_remaining_chunk -= bytes_written; ft_private->outbuf_ptr += bytes_written; } return bytes_written; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-ft-tftp.h ================================================ /** * @file sipe-ft-tftp.h * * pidgin-sipe * * Copyright (C) 2014-2015 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ void sipe_ft_tftp_start_receiving(struct sipe_file_transfer *ft, gsize total_size); gboolean sipe_ft_tftp_stop_receiving(struct sipe_file_transfer *ft); gssize sipe_ft_tftp_read(struct sipe_file_transfer *ft, guchar **buffer, gsize bytes_remaining, gsize bytes_available); void sipe_ft_tftp_start_sending(struct sipe_file_transfer *ft, gsize total_size); gboolean sipe_ft_tftp_stop_sending(struct sipe_file_transfer *ft); gssize sipe_ft_tftp_write(struct sipe_file_transfer *ft, const guchar *buffer, gsize size); ================================================ FILE: src/core/sipe-ft.c ================================================ /** * @file sipe-ft.c * * pidgin-sipe * * Copyright (C) 2010-2016 SIPE Project * Copyright (C) 2010 Jakub Adam * Copyright (C) 2010 Tomáš Hrabčík * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sipmsg.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-crypt.h" #include "sipe-dialog.h" #include "sipe-digest.h" #include "sipe-ft.h" #include "sipe-ft-lync.h" #include "sipe-ft-tftp.h" #include "sipe-im.h" #include "sipe-nls.h" #include "sipe-session.h" #include "sipe-utils.h" /* * DO NOT CHANGE THE FOLLOWING CONSTANTS!!! * * It seems that Microsoft Office Communicator client will accept * file transfer invitations *only* within this port range! * * If a firewall is active on your system you need to open these ports if * you want to *send* files to other users. Receiving files uses an outgoing * connection and should therefore automatically penetrate your firewall. */ #define SIPE_FT_TCP_PORT_MIN 6891 #define SIPE_FT_TCP_PORT_MAX 6901 static void ft_outgoing_init(struct sipe_file_transfer *ft, const gchar *filename, gsize size, const gchar *who); void sipe_ft_raise_error_and_cancel(struct sipe_file_transfer_private *ft_private, const gchar *errmsg) { sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, errmsg); sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER_PUBLIC); } static void generate_key(guchar *buffer, gsize size) { gsize i = 0; while (i < size) buffer[i++] = rand(); } static struct sipe_file_transfer * sipe_file_transfer_new_outgoing(struct sipe_core_private *sipe_private) { struct sipe_file_transfer_private *ft_private; ft_private = g_new0(struct sipe_file_transfer_private, 1); ft_private->sipe_private = sipe_private; ft_private->public.ft_init = ft_outgoing_init; ft_private->public.ft_start = sipe_ft_tftp_start_sending; ft_private->public.ft_write = sipe_ft_tftp_write; ft_private->public.ft_cancelled = sipe_ft_free; ft_private->public.ft_end = sipe_ft_tftp_stop_sending; ft_private->invitation_cookie = g_strdup_printf("%u", rand() % 1000000000); return SIPE_FILE_TRANSFER_PUBLIC; } struct sipe_file_transfer * sipe_core_ft_create_outgoing(struct sipe_core_public *sipe_public, const gchar *who, const gchar *file) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_file_transfer *ft; #ifdef HAVE_XDATA if (SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013)) { ft = sipe_file_transfer_lync_new_outgoing(sipe_private); } else #endif { ft = sipe_file_transfer_new_outgoing(sipe_private); } if (!ft) { SIPE_DEBUG_ERROR_NOFORMAT("Couldn't initialize core file " "transfer structure"); return NULL; } sipe_backend_ft_outgoing(sipe_public, ft, who, file); return ft; } void sipe_ft_free(struct sipe_file_transfer *ft) { struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; struct sip_dialog *dialog = ft_private->dialog; if (dialog) dialog->filetransfers = g_slist_remove(dialog->filetransfers, ft_private); if (ft->backend_private) sipe_backend_ft_deallocate(ft); if (ft_private->listendata) sipe_backend_network_listen_cancel(ft_private->listendata); if (ft_private->cipher_context) sipe_crypt_ft_destroy(ft_private->cipher_context); if (ft_private->hmac_context) sipe_digest_ft_destroy(ft_private->hmac_context); g_free(ft_private->invitation_cookie); g_free(ft_private->encrypted_outbuf); g_free(ft_private); } static void sipe_ft_request(struct sipe_file_transfer_private *ft_private, const gchar *body) { struct sip_dialog *dialog = ft_private->dialog; sip_transport_request(ft_private->sipe_private, "MESSAGE", dialog->with, dialog->with, "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n", body, dialog, NULL); } static void ft_request_denied(struct sipe_file_transfer *ft) { struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; gchar *body = g_strdup_printf("Invitation-Command: CANCEL\r\n" "Invitation-Cookie: %s\r\n" "Cancel-Code: REJECT\r\n", ft_private->invitation_cookie); sipe_ft_request(ft_private, body); g_free(body); sipe_ft_free(ft); } static void send_ft_accept(struct sipe_file_transfer_private *ft_private, gboolean send_enc_key, gboolean send_connect_data, gboolean sender_connect) { GString *body = g_string_new(""); g_string_append_printf(body, "Invitation-Command: ACCEPT\r\n" "Request-Data: IP-Address:\r\n" "Invitation-Cookie: %s\r\n", ft_private->invitation_cookie); if (send_enc_key) { gchar *b64_encryption_key; gchar *b64_hash_key; b64_encryption_key = g_base64_encode(ft_private->encryption_key, SIPE_FT_KEY_LENGTH); b64_hash_key = g_base64_encode(ft_private->hash_key, SIPE_FT_KEY_LENGTH); g_string_append_printf(body, "Encryption-Key: %s\r\n" "Hash-Key: %s\r\n", b64_encryption_key, b64_hash_key); g_free(b64_hash_key); g_free(b64_encryption_key); } if (send_connect_data) { struct sipe_core_private *sipe_private = ft_private->sipe_private; g_string_append_printf(body, "IP-Address: %s\r\n" "Port: %d\r\n" "PortX: 11178\r\n" "AuthCookie: %u\r\n", sip_transport_ip_address(sipe_private), ft_private->port, ft_private->auth_cookie); } if (sender_connect) { g_string_append(body, "Sender-Connect: TRUE\r\n"); } sipe_ft_request(ft_private, body->str); g_string_free(body, TRUE); } static void listen_socket_created_cb(unsigned short port, gpointer data) { struct sipe_file_transfer *ft = data; SIPE_FILE_TRANSFER_PRIVATE->port = port; SIPE_FILE_TRANSFER_PRIVATE->auth_cookie = rand() % 1000000000; if (sipe_backend_ft_is_incoming(ft)) send_ft_accept(SIPE_FILE_TRANSFER_PRIVATE, TRUE, TRUE, TRUE); else send_ft_accept(SIPE_FILE_TRANSFER_PRIVATE, FALSE, TRUE, FALSE); } static void client_connected_cb(struct sipe_backend_fd *fd, gpointer data) { struct sipe_file_transfer *ft = data; SIPE_FILE_TRANSFER_PRIVATE->listendata = NULL; if (!sipe_backend_fd_is_valid(fd)) { sipe_backend_ft_error(ft, _("Socket read failed")); sipe_backend_ft_cancel_local(ft); } else { sipe_backend_ft_start(ft, fd, NULL, 0); } sipe_backend_fd_free(fd); } static void ft_incoming_init(struct sipe_file_transfer *ft, SIPE_UNUSED_PARAMETER const gchar *filename, SIPE_UNUSED_PARAMETER gsize size, SIPE_UNUSED_PARAMETER const gchar *who) { struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; if (ft_private->peer_using_nat) { ft_private->listendata = sipe_backend_network_listen_range(SIPE_FT_TCP_PORT_MIN, SIPE_FT_TCP_PORT_MAX, listen_socket_created_cb, client_connected_cb, ft); } else { send_ft_accept(ft_private, TRUE, FALSE, FALSE); } } static void ft_outgoing_init(struct sipe_file_transfer *ft, const gchar *filename, gsize size, const gchar *who) { struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; struct sipe_core_private *sipe_private = ft_private->sipe_private; struct sip_dialog *dialog; const gchar *ip = sip_transport_ip_address(sipe_private); gchar *body = g_strdup_printf("Application-Name: File Transfer\r\n" "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n" "Invitation-Command: INVITE\r\n" "Invitation-Cookie: %s\r\n" "Application-File: %s\r\n" "Application-FileSize: %" G_GSIZE_FORMAT "\r\n" "%s" "Encryption: R\r\n", // TODO: non encrypted file transfer support ft_private->invitation_cookie, filename, size, sipe_utils_ip_is_private(ip) ? "Connectivity: N\r\n" : ""); struct sip_session *session = sipe_session_find_or_add_im(sipe_private, who); // Queue the message sipe_session_enqueue_message(session, body, "text/x-msmsgsinvite"); dialog = sipe_dialog_find(session, who); if (dialog && !dialog->outgoing_invite) { sipe_im_process_queue(sipe_private, session); } else if (!dialog || !dialog->outgoing_invite) { // Need to send the INVITE to get the outgoing dialog setup sipe_im_invite(sipe_private, session, who, body, "text/x-msmsgsinvite", NULL, FALSE); dialog = sipe_dialog_find(session, who); } dialog->filetransfers = g_slist_append(dialog->filetransfers, ft_private); ft_private->dialog = dialog; g_free(body); } void sipe_ft_incoming_transfer(struct sipe_core_private *sipe_private, struct sip_dialog *dialog, const GSList *body) { struct sipe_file_transfer_private *ft_private; gsize file_size; ft_private = g_new0(struct sipe_file_transfer_private, 1); ft_private->sipe_private = sipe_private; ft_private->public.ft_init = ft_incoming_init; ft_private->public.ft_start = sipe_ft_tftp_start_receiving; ft_private->public.ft_read = sipe_ft_tftp_read; ft_private->public.ft_cancelled = sipe_ft_free; ft_private->public.ft_end = sipe_ft_tftp_stop_receiving; ft_private->public.ft_request_denied = ft_request_denied; generate_key(ft_private->encryption_key, SIPE_FT_KEY_LENGTH); generate_key(ft_private->hash_key, SIPE_FT_KEY_LENGTH); ft_private->invitation_cookie = g_strdup(sipe_utils_nameval_find(body, "Invitation-Cookie")); ft_private->peer_using_nat = sipe_strequal(sipe_utils_nameval_find(body, "Connectivity"), "N"); ft_private->dialog = dialog; file_size = g_ascii_strtoull(sipe_utils_nameval_find(body, "Application-FileSize"), NULL, 10); sipe_backend_ft_incoming(SIPE_CORE_PUBLIC, SIPE_FILE_TRANSFER_PUBLIC, dialog->with, sipe_utils_nameval_find(body, "Application-File"), file_size); if (ft_private->public.backend_private != NULL) { ft_private->dialog->filetransfers = g_slist_append(ft_private->dialog->filetransfers, ft_private); } else { sipe_ft_free(SIPE_FILE_TRANSFER_PUBLIC); } } static struct sipe_file_transfer_private * sipe_find_ft(const struct sip_dialog *dialog, const gchar *inv_cookie) { GSList *ftlist = dialog->filetransfers; for (; ftlist != NULL; ftlist = ftlist->next) { struct sipe_file_transfer_private *ft_private = ftlist->data; if (sipe_strequal(ft_private->invitation_cookie, inv_cookie)) return ft_private; } return NULL; } void sipe_ft_incoming_accept(struct sip_dialog *dialog, const GSList *body) { const gchar *inv_cookie = sipe_utils_nameval_find(body, "Invitation-Cookie"); struct sipe_file_transfer_private *ft_private = sipe_find_ft(dialog, inv_cookie); if (ft_private) { const gchar *ip = sipe_utils_nameval_find(body, "IP-Address"); const gchar *port_str = sipe_utils_nameval_find(body, "Port"); const gchar *auth_cookie = sipe_utils_nameval_find(body, "AuthCookie"); const gchar *enc_key_b64 = sipe_utils_nameval_find(body, "Encryption-Key"); const gchar *hash_key_b64 = sipe_utils_nameval_find(body, "Hash-Key"); if (auth_cookie) ft_private->auth_cookie = g_ascii_strtoull(auth_cookie, NULL, 10); if (enc_key_b64) { gsize ret_len; guchar *enc_key = g_base64_decode(enc_key_b64, &ret_len); if (ret_len == SIPE_FT_KEY_LENGTH) { memcpy(ft_private->encryption_key, enc_key, SIPE_FT_KEY_LENGTH); } else { sipe_ft_raise_error_and_cancel(ft_private, _("Received encryption key has wrong size.")); g_free(enc_key); return; } g_free(enc_key); } if (hash_key_b64) { gsize ret_len; guchar *hash_key = g_base64_decode(hash_key_b64, &ret_len); if (ret_len == SIPE_FT_KEY_LENGTH) { memcpy(ft_private->hash_key, hash_key, SIPE_FT_KEY_LENGTH); } else { sipe_ft_raise_error_and_cancel(ft_private, _("Received hash key has wrong size.")); g_free(hash_key); return; } g_free(hash_key); } if (ip && port_str) { sipe_backend_ft_start(SIPE_FILE_TRANSFER_PUBLIC, NULL, ip, g_ascii_strtoull(port_str, NULL, 10)); } else { ft_private->listendata = sipe_backend_network_listen_range(SIPE_FT_TCP_PORT_MIN, SIPE_FT_TCP_PORT_MAX, listen_socket_created_cb, client_connected_cb, ft_private); if (!ft_private->listendata) sipe_ft_raise_error_and_cancel(ft_private, _("Could not create listen socket")); } } } void sipe_ft_incoming_cancel(struct sip_dialog *dialog, const GSList *body) { const gchar *inv_cookie = sipe_utils_nameval_find(body, "Invitation-Cookie"); struct sipe_file_transfer_private *ft_private = sipe_find_ft(dialog, inv_cookie); if (ft_private) sipe_backend_ft_cancel_remote(SIPE_FILE_TRANSFER_PUBLIC); } GSList *sipe_ft_parse_msg_body(const gchar *body) { GSList *list = NULL; gchar **lines = g_strsplit(body, "\r\n", 0); if (sipe_utils_parse_lines(&list, lines, ":") == FALSE) { sipe_utils_nameval_free(list); list = NULL; } g_strfreev(lines); return list; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-ft.h ================================================ /** * @file sipe-ft.h * * pidgin-sipe * * Copyright (C) 2010 SIPE Project * Copyright (C) 2010 Jakub Adam * Copyright (C) 2010 Tomáš Hrabčík * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_core_private; #define SIPE_FT_KEY_LENGTH 24 /** * File transport (private part) */ struct sipe_file_transfer_private { struct sipe_file_transfer public; struct sipe_core_private *sipe_private; gboolean peer_using_nat; unsigned short port; guchar encryption_key[SIPE_FT_KEY_LENGTH]; guchar hash_key[SIPE_FT_KEY_LENGTH]; unsigned auth_cookie; gchar *invitation_cookie; struct sip_dialog *dialog; gpointer cipher_context; gpointer hmac_context; gsize bytes_remaining_chunk; guchar *encrypted_outbuf; guchar *outbuf_ptr; gsize outbuf_size; struct sipe_backend_listendata *listendata; }; #define SIPE_FILE_TRANSFER_PUBLIC ((struct sipe_file_transfer *) ft_private) #define SIPE_FILE_TRANSFER_PRIVATE ((struct sipe_file_transfer_private *) ft) /** * Called when remote peer wants to send a file. * * Function initializes libpurple filetransfer API structure and calls * purple_xfer_request(). * * @param sipe_private Sipe core private data * @param dialog SIP dialog used for the file transfer * @param body parsed SIP message body as name-value pairs */ void sipe_ft_incoming_transfer(struct sipe_core_private *sipe_private, struct sip_dialog *dialog, const GSList *body); /** * Handles incoming filetransfer message with ACCEPT invitation command. * * This message is sent during the negotiation phase when parameters of the * transfer like IP address or TCP port are going to be set up. * * @param dialog SIP dialog used for the file transfer * @param body parsed SIP message body as name-value pairs */ void sipe_ft_incoming_accept(struct sip_dialog *dialog, const GSList *body); /** * Called when remote peer cancels ongoing file transfer. * * Function dispatches the request to libpurple * * @param dialog SIP dialog used for the file transfer * @param body parsed SIP message body as name-value pairs */ void sipe_ft_incoming_cancel(struct sip_dialog *dialog, const GSList *body); /** * Parses file transfer message body and creates a list with name-value pairs * * @param body file transfer SIP message body * * @return GSList of name-value pairs parsed from message body, NULL if body has * incorrect format */ GSList *sipe_ft_parse_msg_body(const gchar *body); void sipe_ft_raise_error_and_cancel(struct sipe_file_transfer_private *ft_private, const gchar *errmsg); /** * Deallocates a sipe_file_transfer structure. * * @param ft [in] a sipe_file_transfer structure. */ void sipe_ft_free(struct sipe_file_transfer *ft); ================================================ FILE: src/core/sipe-generic-tests.c ================================================ /** * @file sipe-generic-tests.c * * pidgin-sipe * * Copyright (C) 2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-crypt.h" #include "sipe-utils.h" #include "sip-transport.h" /* * Stubs */ gboolean sipe_backend_debug_enabled(void) { return(TRUE); } void sipe_backend_debug_literal(sipe_debug_level level, const gchar *msg) { printf("DEBUG(%d): %s\n", level, msg); } void sipe_backend_debug(sipe_debug_level level, const gchar *format, ...) { va_list ap; gchar *newformat = g_strdup_printf("DEBUG(%d): %s\n", level, format); va_start(ap, format); vprintf(newformat, ap); va_end(ap); g_free(newformat); } const gchar *sip_transport_epid(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private) { return(NULL); } /* needed when linking against NSS */ void md4sum(const uint8_t *data, uint32_t length, uint8_t *digest); void md4sum(SIPE_UNUSED_PARAMETER const uint8_t *data, SIPE_UNUSED_PARAMETER uint32_t length, SIPE_UNUSED_PARAMETER uint8_t *digest) { } /* * Tester code */ static guint succeeded = 0; static guint failed = 0; static void assert_equal_str(const char *expected, const gchar *got) { if (sipe_strequal(expected, got)) { succeeded++; } else { printf("FAILED: %s\n %s\n", got, expected); failed++; } } static void assert_equal_uint(gsize expected, gsize got) { if (expected == got) { succeeded++; } else { printf("FAILED: %" G_GSIZE_FORMAT "\n %" G_GSIZE_FORMAT "\n", got, expected); failed++; } } static void tests_sipe_utils_time(void) { gchar *result_str; time_t result_time; #define UNIX_EPOCH_IN_ISO8601_UTC "1970-01-01T00:00:00Z" result_str = sipe_utils_time_to_str(0); assert_equal_str(result_str, UNIX_EPOCH_IN_ISO8601_UTC); g_free(result_str); result_time = sipe_utils_str_to_time(NULL); assert_equal_uint(result_time, 0); result_time = sipe_utils_str_to_time(UNIX_EPOCH_IN_ISO8601_UTC); assert_equal_uint(result_time, 0); /* handle missing "Z" */ result_time = sipe_utils_str_to_time("1970-01-01T00:00:01"); assert_equal_uint(result_time, 1); result_time = sipe_utils_str_to_time("1970-01-01T00:00:20Z"); assert_equal_uint(result_time, 20); result_time = sipe_utils_str_to_time("1970-01-01T00:03:00"); assert_equal_uint(result_time, 3 * 60); result_time = sipe_utils_str_to_time("1970-01-01T00:40:00Z"); assert_equal_uint(result_time, 40 * 60); result_time = sipe_utils_str_to_time("1970-01-01T05:00:00"); assert_equal_uint(result_time, 5 * 60 * 60); result_time = sipe_utils_str_to_time("1970-01-01T23:00:00Z"); assert_equal_uint(result_time, 23 * 60 * 60); /* 6th day after epoch */ result_time = sipe_utils_str_to_time("1970-01-07T00:00:00"); assert_equal_uint(result_time, 6 * 24 * 60 * 60); /* 17th day after epoch */ result_time = sipe_utils_str_to_time("1970-01-18T00:00:00Z"); assert_equal_uint(result_time, 17 * 24 * 60 * 60); result_time = sipe_utils_str_to_time("1970-02-01T00:00:00"); assert_equal_uint(result_time, 31 * 24 * 60 * 60); result_time = sipe_utils_str_to_time("1970-12-01T00:00:00Z"); /* 365 - 31 days */ assert_equal_uint(result_time, 334 * 24 * 60 * 60); result_time = sipe_utils_str_to_time("1971-01-01T00:00:00"); assert_equal_uint(result_time, 365 * 24 * 60 * 60); } static void generic_tests(void) { tests_sipe_utils_time(); } int main(SIPE_UNUSED_PARAMETER int argc, SIPE_UNUSED_PARAMETER char *argv[]) { /* Initialization for crypto backend (test mode) */ sipe_crypto_init(FALSE); generic_tests(); printf("Result: %d PASSED %d FAILED\n", succeeded, failed); return(failed); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-group.c ================================================ /** * @file sipe-group.c * * pidgin-sipe * * Copyright (C) 2011-2013 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "sipmsg.h" #include "sip-soap.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-group.h" #include "sipe-nls.h" #include "sipe-ucs.h" #include "sipe-utils.h" #include "sipe-xml.h" struct sipe_groups { GSList *list; }; struct group_user_context { gchar *group_name; gchar *user_name; }; static void sipe_group_context_destroy(gpointer data) { struct group_user_context *ctx = data; g_free(ctx->group_name); g_free(ctx->user_name); g_free(ctx); } static gboolean process_add_group_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans) { if (msg->response == 200) { struct sipe_group *group; struct group_user_context *ctx = trans->payload->data; sipe_xml *xml; const sipe_xml *node; char *group_id; xml = sipe_xml_parse(msg->body, msg->bodylen); if (!xml) { return FALSE; } node = sipe_xml_child(xml, "Body/addGroup/groupID"); if (!node) { sipe_xml_free(xml); return FALSE; } group_id = sipe_xml_data(node); if (!group_id) { sipe_xml_free(xml); return FALSE; } group = sipe_group_add(sipe_private, ctx->group_name, NULL, NULL, g_ascii_strtoull(group_id, NULL, 10)); g_free(group_id); if (group) { struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private, ctx->user_name); if (buddy) { sipe_buddy_insert_group(buddy, group); sipe_group_update_buddy(sipe_private, buddy); } } sipe_xml_free(xml); return TRUE; } return FALSE; } struct sipe_group* sipe_group_find_by_id(struct sipe_core_private *sipe_private, guint id) { struct sipe_group *group; GSList *entry; if (!sipe_private) return NULL; entry = sipe_private->groups->list; while (entry) { group = entry->data; if (group->id == id) { return group; } entry = entry->next; } return NULL; } struct sipe_group* sipe_group_find_by_name(struct sipe_core_private *sipe_private, const gchar * name) { struct sipe_group *group; GSList *entry; if (!sipe_private || !name) return NULL; entry = sipe_private->groups->list; while (entry) { group = entry->data; if (sipe_strequal(group->name, name)) { return group; } entry = entry->next; } return NULL; } void sipe_group_create(struct sipe_core_private *sipe_private, struct sipe_ucs_transaction *trans, const gchar *name, const gchar *who) { /* "trans" is always set for UCS code paths, otherwise NULL */ if (trans) { sipe_ucs_group_create(sipe_private, trans, name, who); } else { struct transaction_payload *payload = g_new0(struct transaction_payload, 1); struct group_user_context *ctx = g_new0(struct group_user_context, 1); const gchar *soap_name = sipe_strequal(name, _("Other Contacts")) ? "~" : name; gchar *request; ctx->group_name = g_strdup(name); ctx->user_name = g_strdup(who); payload->destroy = sipe_group_context_destroy; payload->data = ctx; /* soap_name can contain restricted characters */ request = g_markup_printf_escaped("%s" "", soap_name); sip_soap_request_cb(sipe_private, "addGroup", request, process_add_group_response, payload); g_free(request); } } gboolean sipe_group_rename(struct sipe_core_private *sipe_private, struct sipe_group *group, const gchar *name) { gboolean renamed = sipe_backend_buddy_group_rename(SIPE_CORE_PUBLIC, group->name, name); if (renamed) { g_free(group->name); group->name = g_strdup(name); } return(renamed); } struct sipe_group *sipe_group_add(struct sipe_core_private *sipe_private, const gchar *name, const gchar *exchange_key, const gchar *change_key, guint id) { struct sipe_group *group = NULL; if (!is_empty(name)) { group = sipe_group_find_by_name(sipe_private, name); if (!group && sipe_backend_buddy_group_add(SIPE_CORE_PUBLIC, name)) { group = g_new0(struct sipe_group, 1); group->name = g_strdup(name); group->id = id; if (exchange_key) group->exchange_key = g_strdup(exchange_key); if (change_key) group->change_key = g_strdup(change_key); sipe_private->groups->list = g_slist_append(sipe_private->groups->list, group); SIPE_DEBUG_INFO("sipe_group_add: created backend group '%s' with id %d", group->name, group->id); } else { SIPE_DEBUG_INFO("sipe_group_add: backend group '%s' already exists", name ? name : ""); if (group) group->is_obsolete = FALSE; } } return(group); } static void group_free(struct sipe_core_private *sipe_private, struct sipe_group *group) { sipe_private->groups->list = g_slist_remove(sipe_private->groups->list, group); g_free(group->name); g_free(group->exchange_key); g_free(group->change_key); g_free(group); } void sipe_group_remove(struct sipe_core_private *sipe_private, struct sipe_group *group) { if (group) { SIPE_DEBUG_INFO("sipe_group_remove: %s (id %d)", group->name, group->id); sipe_backend_buddy_group_remove(SIPE_CORE_PUBLIC, group->name); group_free(sipe_private, group); } } void sipe_core_group_rename(struct sipe_core_public *sipe_public, const gchar *old_name, const gchar *new_name) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_group *s_group = sipe_group_find_by_name(sipe_private, old_name); if (s_group) { SIPE_DEBUG_INFO("sipe_core_group_rename: from '%s' to '%s'", old_name, new_name); if (sipe_ucs_is_migrated(sipe_private)) { sipe_ucs_group_rename(sipe_private, s_group, new_name); } else { /* new_name can contain restricted characters */ gchar *request = g_markup_printf_escaped("%d" "%s" "", s_group->id, new_name); sip_soap_request(sipe_private, "modifyGroup", request); g_free(request); } g_free(s_group->name); s_group->name = g_strdup(new_name); } else { SIPE_DEBUG_INFO("sipe_core_group_rename: cannot find group '%s'", old_name); } } void sipe_core_group_remove(struct sipe_core_public *sipe_public, const gchar *name) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_group *s_group = sipe_group_find_by_name(sipe_private, name); if (s_group) { /* ignore backend events while deleting obsoleted groups */ if (!s_group->is_obsolete) { SIPE_DEBUG_INFO("sipe_core_group_remove: delete '%s'", name); if (sipe_ucs_is_migrated(sipe_private)) { sipe_ucs_group_remove(sipe_private, s_group); } else { gchar *request = g_strdup_printf("%d", s_group->id); sip_soap_request(sipe_private, "deleteGroup", request); g_free(request); } group_free(sipe_private, s_group); } } else { SIPE_DEBUG_INFO("sipe_core_group_remove: cannot find group '%s'", name); } } /** * Sends buddy update to server * * NOTE: must not be called when contact list has been migrated to UCS */ static void send_buddy_update(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy, const gchar *alias) { gchar *groups = sipe_buddy_groups_string(buddy); if (groups) { gchar *request; SIPE_DEBUG_INFO("Saving buddy %s with alias '%s' and groups '%s'", buddy->name, alias, groups); /* alias can contain restricted characters */ request = g_markup_printf_escaped("%s" "%s" "true" "%s" "", alias ? alias : "", groups, buddy->name); g_free(groups); sip_soap_request(sipe_private, "setContact", request); g_free(request); } } /** * indicates that buddy information on the server needs updating * * NOTE: must not be called when contact list has been migrated to UCS */ void sipe_group_update_buddy(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy) { if (buddy) { sipe_backend_buddy backend_buddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, buddy->name, NULL); if (backend_buddy) { gchar *alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, backend_buddy); send_buddy_update(sipe_private, buddy, alias); g_free(alias); } } } /** * @param alias new alias (may be @c NULL) */ void sipe_core_group_set_alias(struct sipe_core_public *sipe_public, const gchar *who, const gchar *alias) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; /* UCS does not support setting of display name/alias */ if (sipe_ucs_is_migrated(sipe_private)) SIPE_DEBUG_INFO("sipe_core_group_set_alias: not supported for UCS (uri '%s' alias '%s')", who, alias ? alias : ""); else { struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private, who); if (buddy) send_buddy_update(sipe_private, buddy, alias); } } void sipe_group_update_start(struct sipe_core_private *sipe_private) { GSList *entry = sipe_private->groups->list; while (entry) { ((struct sipe_group *) entry->data)->is_obsolete = TRUE; entry = entry->next; } } void sipe_group_update_finish(struct sipe_core_private *sipe_private) { GSList *entry = sipe_private->groups->list; while (entry) { struct sipe_group *group = entry->data; /* next group entry */ entry = entry->next; if (group->is_obsolete) sipe_group_remove(sipe_private, group); } } struct sipe_group *sipe_group_first(struct sipe_core_private *sipe_private) { return(sipe_private->groups->list ? sipe_private->groups->list->data : NULL); } guint sipe_group_count(struct sipe_core_private *sipe_private) { return(g_slist_length(sipe_private->groups->list)); } void sipe_group_init(struct sipe_core_private *sipe_private) { sipe_private->groups = g_new0(struct sipe_groups, 1); } void sipe_group_free(struct sipe_core_private *sipe_private) { GSList *entry; while ((entry = sipe_private->groups->list) != NULL) group_free(sipe_private, entry->data); g_free(sipe_private->groups); sipe_private->groups = NULL; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-group.h ================================================ /** * @file sipe-group.h * * pidgin-sipe * * Copyright (C) 2011-2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_buddy; struct sipe_core_private; struct sipe_ucs_transaction; struct sipe_group { gchar *name; gchar *exchange_key; gchar *change_key; guint id; gboolean is_obsolete; }; struct sipe_group *sipe_group_find_by_id(struct sipe_core_private *sipe_private, guint id); struct sipe_group *sipe_group_find_by_name(struct sipe_core_private *sipe_private, const gchar * name); /** * Request creation of group @c name on the server and add buddy @c who to it * * @param sipe_private SIPE core data * @param trans UCS transaction (@c NULL when UCS is not in use) * @param name name of group * @param who SIP URI of buddy (may be @c NULL, i.e. empty group) */ void sipe_group_create(struct sipe_core_private *sipe_private, struct sipe_ucs_transaction *trans, const gchar *name, const gchar *who); gboolean sipe_group_rename(struct sipe_core_private *sipe_private, struct sipe_group *group, const gchar *name); /** * Creates @c sipe_group structure for a new group and adds it into the group * list of given account. If buddy is already in the list, its existing * structure is returned. * * @param sipe_private SIPE core data * @param name name of group (may be @c NULL) * @param exchange_key Exchange key (may be @c NULL) * @param change_key Change key (may be @c NULL) * @param id numeric ID of group * * @return @c sipe_group structure or @c NULL if group creation failed */ struct sipe_group *sipe_group_add(struct sipe_core_private *sipe_private, const gchar *name, const gchar *exchange_key, const gchar *change_key, guint id); /* remove group from core & backend */ void sipe_group_remove(struct sipe_core_private *sipe_private, struct sipe_group *group); /* update alias/group list for a buddy on the server */ void sipe_group_update_buddy(struct sipe_core_private *sipe_private, struct sipe_buddy *buddy); /** * Prepare group list for an update * * @param sipe_private SIPE core data */ void sipe_group_update_start(struct sipe_core_private *sipe_private); /** * Finish group list update. This will remove obsolete groups. * * NOTE: this must be call after sipe_buddy_update_finish(), i.e. it * assumes that the group is no longer associated with any buddy. * * @param sipe_private SIPE core data */ void sipe_group_update_finish(struct sipe_core_private *sipe_private); /** * Return first group * * @param sipe_private SIPE core data * * @return sipe_group structure or @c NULL if there are no groups */ struct sipe_group *sipe_group_first(struct sipe_core_private *sipe_private); /** * Number of groups * * @param sipe_private SIPE core data */ guint sipe_group_count(struct sipe_core_private *sipe_private); /** * Initialize group data * * @param sipe_private SIPE core data */ void sipe_group_init(struct sipe_core_private *sipe_private); /** * Free group data * * @param sipe_private SIPE core data */ void sipe_group_free(struct sipe_core_private *sipe_private); ================================================ FILE: src/core/sipe-groupchat.c ================================================ /** * @file sipe-groupchat.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * This module implements the OCS2007R2 Group Chat functionality * * Documentation references: * * Microsoft TechNet: Key Protocols and Windows Services Used by Group Chat * * Microsoft TechNet: Group Chat Call Flows * * Microsoft Office Communications Server 2007 R2 Technical Reference Guide * * Microsoft DevNet: [MS-XCCOSIP] Extensible Chat Control Over SIP * * RFC 4028: Session Timers in the Session Initiation Protocol (SIP) * * * * @TODO: * * -.cmd:getserverinfo * * rpl:getservinfo * * * is there any information in there we would need/use? * * - cmd:getpref/rpl:getpref/cmd:setpref/rpl:setpref * probably useless, as libpurple stores configuration locally * * can store base64 encoded "free text" in key/value fashion * * * * * * * * * * * * use this to sync chats in buddy list on multiple clients? * * - cmd:getinv * * rpl:getinv * ??? * * according to documentation should provide list of outstanding invites. * [no log file examples] * should we automatically join those channels or ask user to join/add? * * - chatserver_command_message() * needs to support multiple nodes? * [no log file examples] * * - create/delete chat rooms * [no log file examples] * are these related to this functionality? * * * * * * * SUCCESS_OK * * * * - file transfer (uses HTTPS PUT/GET via a filestore server) * [no log file examples] * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sipe-common.h" #include "sipmsg.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-chat.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-dialog.h" #include "sipe-groupchat.h" #include "sipe-im.h" #include "sipe-nls.h" #include "sipe-schedule.h" #include "sipe-session.h" #include "sipe-utils.h" #include "sipe-xml.h" #define GROUPCHAT_RETRY_TIMEOUT 5*60 /* seconds */ /** * aib node - magic numbers? * * Example: * * * * "value" corresponds to the "id" attribute in uib nodes. * * @TODO: Confirm "guessed" meaning of the magic numbers: * 3984 = normal users * 12276 = channel operators */ #define GROUPCHAT_AIB_KEY_USER "3984" #define GROUPCHAT_AIB_KEY_CHANOP "12276" struct sipe_groupchat { struct sip_session *session; gchar *domain; GSList *join_queue; GHashTable *uri_to_chat_session; GHashTable *msgs; guint envid; guint expires; gboolean connected; }; struct sipe_groupchat_msg { GHashTable *container; struct sipe_chat_session *session; gchar *content; gchar *xccos; guint envid; }; /* GDestroyNotify */ static void sipe_groupchat_msg_free(gpointer data) { struct sipe_groupchat_msg *msg = data; g_free(msg->content); g_free(msg->xccos); g_free(msg); } /* GDestroyNotify */ static void sipe_groupchat_msg_remove(gpointer data) { struct sipe_groupchat_msg *msg = data; g_hash_table_remove(msg->container, &msg->envid); } static void sipe_groupchat_allocate(struct sipe_core_private *sipe_private) { struct sipe_groupchat *groupchat = g_new0(struct sipe_groupchat, 1); groupchat->uri_to_chat_session = g_hash_table_new(g_str_hash, g_str_equal); groupchat->msgs = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, sipe_groupchat_msg_free); groupchat->envid = rand(); groupchat->connected = FALSE; sipe_private->groupchat = groupchat; } static void sipe_groupchat_free_join_queue(struct sipe_groupchat *groupchat) { sipe_utils_slist_free_full(groupchat->join_queue, g_free); groupchat->join_queue = NULL; } void sipe_groupchat_free(struct sipe_core_private *sipe_private) { struct sipe_groupchat *groupchat = sipe_private->groupchat; if (groupchat) { sipe_groupchat_free_join_queue(groupchat); g_hash_table_destroy(groupchat->msgs); g_hash_table_destroy(groupchat->uri_to_chat_session); g_free(groupchat->domain); g_free(groupchat); sipe_private->groupchat = NULL; } } static struct sipe_groupchat_msg *generate_xccos_message(struct sipe_groupchat *groupchat, const gchar *content) { struct sipe_groupchat_msg *msg = g_new0(struct sipe_groupchat_msg, 1); msg->container = groupchat->msgs; msg->envid = groupchat->envid++; msg->xccos = g_strdup_printf("" "%s" "", msg->envid, content); g_hash_table_insert(groupchat->msgs, &msg->envid, msg); return(msg); } /** * Create short-lived dialog with ocschat@ (or user specified value) * This initiates the Group Chat feature */ void sipe_groupchat_init(struct sipe_core_private *sipe_private) { const gchar *setting = sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_GROUPCHAT_USER); const gchar *persistent = sipe_private->persistentChatPool_uri; gboolean user_set = !is_empty(setting); gboolean provisioned = !is_empty(persistent); gchar **parts = g_strsplit(user_set ? setting : provisioned ? persistent : sipe_private->username, "@", 2); gboolean domain_found = !is_empty(parts[1]); const gchar *user = "ocschat"; const gchar *domain = parts[domain_found ? 1 : 0]; gchar *chat_uri; struct sip_session *session; struct sipe_groupchat *groupchat; /* User specified or provisioned URI is valid 'user@company.com' */ if ((user_set || provisioned) && domain_found && !is_empty(parts[0])) user = parts[0]; SIPE_DEBUG_INFO("sipe_groupchat_init: username '%s' setting '%s' persistent '%s' split '%s'/'%s' GC user %s@%s", sipe_private->username, setting ? setting : "(null)", persistent ? persistent : "(null)", parts[0], parts[1] ? parts[1] : "(null)", user, domain); if (!sipe_private->groupchat) sipe_groupchat_allocate(sipe_private); groupchat = sipe_private->groupchat; chat_uri = g_strdup_printf("sip:%s@%s", user, domain); session = sipe_session_find_or_add_im(sipe_private, chat_uri); session->is_groupchat = TRUE; sipe_im_invite(sipe_private, session, chat_uri, NULL, NULL, NULL, FALSE); g_free(groupchat->domain); groupchat->domain = g_strdup(domain); g_free(chat_uri); g_strfreev(parts); } /* sipe_schedule_action */ static void groupchat_init_retry_cb(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER gpointer data) { sipe_groupchat_init(sipe_private); } static void groupchat_init_retry(struct sipe_core_private *sipe_private) { struct sipe_groupchat *groupchat = sipe_private->groupchat; SIPE_DEBUG_INFO_NOFORMAT("groupchat_init_retry: trying again later..."); groupchat->session = NULL; groupchat->connected = FALSE; sipe_schedule_seconds(sipe_private, "<+groupchat-retry>", NULL, GROUPCHAT_RETRY_TIMEOUT, groupchat_init_retry_cb, NULL); } void sipe_groupchat_invite_failed(struct sipe_core_private *sipe_private, struct sip_session *session) { struct sipe_groupchat *groupchat = sipe_private->groupchat; const gchar *setting = sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_GROUPCHAT_USER); gboolean retry = FALSE; if (groupchat->session) { /* response to group chat server invite */ SIPE_DEBUG_ERROR_NOFORMAT("can't connect to group chat server!"); /* group chat server exists, but communication failed */ retry = TRUE; } else { /* response to initial invite */ SIPE_DEBUG_INFO_NOFORMAT("no group chat server found."); } sipe_session_close(sipe_private, session); if (!is_empty(setting)) { gchar *msg = g_strdup_printf(_("Group Chat Proxy setting is incorrect:\n\n\t%s\n\nPlease update your Account."), setting); sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Couldn't find Group Chat server!"), msg); g_free(msg); /* user specified group chat settings: we should retry */ retry = TRUE; } if (retry) { groupchat_init_retry(sipe_private); } else { SIPE_DEBUG_INFO_NOFORMAT("disabling group chat feature."); } } static gchar *generate_chanid_node(const gchar *uri, guint key) { /* ma-chan:/// */ gchar **parts = g_strsplit(uri, "/", 4); gchar *chanid = NULL; if (parts[2] && parts[3]) { chanid = g_strdup_printf("", key, parts[2], parts[3]); } else { SIPE_DEBUG_ERROR("generate_chanid_node: mal-formed URI '%s'", uri); } g_strfreev(parts); return chanid; } /* TransCallback */ static void groupchat_update_cb(struct sipe_core_private *sipe_private, gpointer data); static gboolean groupchat_expired_session_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { struct sipe_groupchat *groupchat = sipe_private->groupchat; /* 481 Call Leg Does Not Exist -> server dropped session */ if (msg->response == 481) { struct sip_session *session = groupchat->session; struct sip_dialog *dialog = sipe_dialog_find(session, session->with); if (dialog) { /* close dialog from our side */ sip_transport_bye(sipe_private, dialog); sipe_dialog_remove(session, session->with); /* dialog is no longer valid */ } /* re-initialize groupchat session */ groupchat->session = NULL; groupchat->connected = FALSE; sipe_groupchat_init(sipe_private); } else { sipe_schedule_seconds(sipe_private, "<+groupchat-expires>", NULL, groupchat->expires, groupchat_update_cb, NULL); } return(TRUE); } /* sipe_schedule_action */ static void groupchat_update_cb(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER gpointer data) { struct sipe_groupchat *groupchat = sipe_private->groupchat; if (groupchat->session) { struct sip_dialog *dialog = sipe_dialog_find(groupchat->session, groupchat->session->with); if (dialog) sip_transport_update(sipe_private, dialog, groupchat_expired_session_response); } } static struct sipe_groupchat_msg *chatserver_command(struct sipe_core_private *sipe_private, const gchar *cmd); void sipe_groupchat_invite_response(struct sipe_core_private *sipe_private, struct sip_dialog *dialog, struct sipmsg *response) { struct sipe_groupchat *groupchat = sipe_private->groupchat; SIPE_DEBUG_INFO_NOFORMAT("sipe_groupchat_invite_response"); if (!groupchat->session) { /* response to initial invite */ struct sipe_groupchat_msg *msg = generate_xccos_message(groupchat, ""); const gchar *session_expires = sipmsg_find_header(response, "Session-Expires"); sip_transport_info(sipe_private, "Content-Type: text/plain\r\n", msg->xccos, dialog, NULL); sipe_groupchat_msg_remove(msg); if (session_expires) { groupchat->expires = strtoul(session_expires, NULL, 10); if (groupchat->expires) { SIPE_DEBUG_INFO("sipe_groupchat_invite_response: session expires in %d seconds", groupchat->expires); if (groupchat->expires > 10) groupchat->expires -= 10; sipe_schedule_seconds(sipe_private, "<+groupchat-expires>", NULL, groupchat->expires, groupchat_update_cb, NULL); } } } else { /* response to group chat server invite */ gchar *invcmd; SIPE_DEBUG_INFO_NOFORMAT("connection to group chat server established."); groupchat->connected = TRUE; /* Any queued joins? */ if (groupchat->join_queue) { GString *cmd = g_string_new("" ""); GSList *entry; guint i = 0; /* We used g_slist_prepend() to create the list */ groupchat->join_queue = entry = g_slist_reverse(groupchat->join_queue); while (entry) { gchar *chanid = generate_chanid_node(entry->data, i++); g_string_append(cmd, chanid); g_free(chanid); entry = entry->next; } sipe_groupchat_free_join_queue(groupchat); g_string_append(cmd, ""); chatserver_command(sipe_private, cmd->str); g_string_free(cmd, TRUE); } /* Request outstanding invites from server */ invcmd = g_strdup_printf("" "" "" "" "", groupchat->domain); chatserver_command(sipe_private, invcmd); g_free(invcmd); } } static void chatserver_command_error_notify(struct sipe_core_private *sipe_private, struct sipe_chat_session *chat_session, const gchar *content) { gchar *label = g_strdup_printf(_("This message was not delivered to chat room '%s'"), chat_session->title); gchar *errmsg = g_strdup_printf("%s:\n%s", label, content); g_free(label); sipe_backend_notify_message_error(SIPE_CORE_PUBLIC, chat_session->backend, NULL, errmsg); g_free(errmsg); } /* TransCallback */ static gboolean chatserver_command_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans) { if (msg->response != 200) { struct sipe_groupchat_msg *gmsg = trans->payload->data; struct sipe_chat_session *chat_session = gmsg->session; SIPE_DEBUG_INFO("chatserver_command_response: failure %d", msg->response); if (chat_session) chatserver_command_error_notify(sipe_private, chat_session, gmsg->content); groupchat_expired_session_response(sipe_private, msg, trans); } return TRUE; } static struct sipe_groupchat_msg *chatserver_command(struct sipe_core_private *sipe_private, const gchar *cmd) { struct sipe_groupchat *groupchat = sipe_private->groupchat; struct sipe_groupchat_msg *msg = NULL; if (groupchat->session) { struct sip_dialog *dialog = sipe_dialog_find(groupchat->session, groupchat->session->with); if (dialog) { struct transaction *trans; msg = generate_xccos_message(groupchat, cmd); trans = sip_transport_info(sipe_private, "Content-Type: text/plain\r\n", msg->xccos, dialog, chatserver_command_response); if (trans) { struct transaction_payload *payload = g_new0(struct transaction_payload, 1); payload->destroy = sipe_groupchat_msg_remove; payload->data = msg; trans->payload = payload; } else { /* SIP transport is no longer valid - give up */ sipe_groupchat_msg_remove(msg); msg = NULL; } } } return(msg); } static void chatserver_response_uri(struct sipe_core_private *sipe_private, struct sip_session *session, SIPE_UNUSED_PARAMETER guint result, SIPE_UNUSED_PARAMETER const gchar *message, const sipe_xml *xml) { const sipe_xml *uib = sipe_xml_child(xml, "uib"); const gchar *uri = sipe_xml_attribute(uib, "uri"); /* drop connection to ocschat@ again */ sipe_session_close(sipe_private, session); if (uri) { struct sipe_groupchat *groupchat = sipe_private->groupchat; SIPE_DEBUG_INFO("chatserver_response_uri: '%s'", uri); groupchat->session = session = sipe_session_find_or_add_im(sipe_private, uri); session->is_groupchat = TRUE; sipe_im_invite(sipe_private, session, uri, NULL, NULL, NULL, FALSE); } else { SIPE_DEBUG_WARNING_NOFORMAT("chatserver_response_uri: no server URI found!"); groupchat_init_retry(sipe_private); } } static void chatserver_response_channel_search(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER struct sip_session *session, guint result, const gchar *message, const sipe_xml *xml) { struct sipe_core_public *sipe_public = SIPE_CORE_PUBLIC; if (result != 200) { sipe_backend_notify_error(sipe_public, _("Error retrieving room list"), message); } else { const sipe_xml *chanib; for (chanib = sipe_xml_child(xml, "chanib"); chanib; chanib = sipe_xml_twin(chanib)) { const gchar *name = sipe_xml_attribute(chanib, "name"); const gchar *desc = sipe_xml_attribute(chanib, "description"); const gchar *uri = sipe_xml_attribute(chanib, "uri"); const sipe_xml *node; guint user_count = 0; guint32 flags = 0; /* information */ for (node = sipe_xml_child(chanib, "info"); node; node = sipe_xml_twin(node)) { const gchar *id = sipe_xml_attribute(node, "id"); gchar *data; if (!id) continue; data = sipe_xml_data(node); if (data) { if (sipe_strcase_equal(id, "urn:parlano:ma:info:ucnt")) { user_count = g_ascii_strtoll(data, NULL, 10); } else if (sipe_strcase_equal(id, "urn:parlano:ma:info:visibilty")) { if (sipe_strcase_equal(data, "private")) { flags |= SIPE_GROUPCHAT_ROOM_PRIVATE; } } g_free(data); } } /* properties */ for (node = sipe_xml_child(chanib, "prop"); node; node = sipe_xml_twin(node)) { const gchar *id = sipe_xml_attribute(node, "id"); gchar *data; if (!id) continue; data = sipe_xml_data(node); if (data) { gboolean value = sipe_strcase_equal(data, "true"); g_free(data); if (value) { guint32 add = 0; if (sipe_strcase_equal(id, "urn:parlano:ma:prop:filepost")) { add = SIPE_GROUPCHAT_ROOM_FILEPOST; } else if (sipe_strcase_equal(id, "urn:parlano:ma:prop:invite")) { add = SIPE_GROUPCHAT_ROOM_INVITE; } else if (sipe_strcase_equal(id, "urn:parlano:ma:prop:logged")) { add = SIPE_GROUPCHAT_ROOM_LOGGED; } flags |= add; } } } SIPE_DEBUG_INFO("group chat channel '%s': '%s' (%s) with %u users, flags 0x%x", name, desc, uri, user_count, flags); sipe_backend_groupchat_room_add(sipe_public, uri, name, desc, user_count, flags); } } sipe_backend_groupchat_room_terminate(sipe_public); } static gboolean is_chanop(const sipe_xml *aib) { return sipe_strequal(sipe_xml_attribute(aib, "key"), GROUPCHAT_AIB_KEY_CHANOP); } static void add_user(struct sipe_chat_session *chat_session, const gchar *uri, gboolean new, gboolean chanop) { SIPE_DEBUG_INFO("add_user: %s%s%s to room %s (%s)", new ? "new " : "", chanop ? "chanop " : "", uri, chat_session->title, chat_session->id); sipe_backend_chat_add(chat_session->backend, uri, new); if (chanop) sipe_backend_chat_operator(chat_session->backend, uri); } static void chatserver_response_join(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER struct sip_session *session, guint result, const gchar *message, const sipe_xml *xml) { if (result != 200) { sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Error joining chat room"), message); } else { struct sipe_groupchat *groupchat = sipe_private->groupchat; const sipe_xml *node; GHashTable *user_ids = g_hash_table_new(g_str_hash, g_str_equal); /* Extract user IDs & URIs and generate ID -> URI map */ for (node = sipe_xml_child(xml, "uib"); node; node = sipe_xml_twin(node)) { const gchar *id = sipe_xml_attribute(node, "id"); const gchar *uri = sipe_xml_attribute(node, "uri"); if (id && uri) g_hash_table_insert(user_ids, (gpointer) id, (gpointer) uri); } /* Process channel data */ for (node = sipe_xml_child(xml, "chanib"); node; node = sipe_xml_twin(node)) { const gchar *uri = sipe_xml_attribute(node, "uri"); if (uri) { struct sipe_chat_session *chat_session = g_hash_table_lookup(groupchat->uri_to_chat_session, uri); gboolean new = (chat_session == NULL); const gchar *attr = sipe_xml_attribute(node, "name"); gchar *self = sip_uri_self(sipe_private); const sipe_xml *aib; if (new) { chat_session = sipe_chat_create_session(SIPE_CHAT_TYPE_GROUPCHAT, sipe_xml_attribute(node, "uri"), attr ? attr : ""); g_hash_table_insert(groupchat->uri_to_chat_session, chat_session->id, chat_session); SIPE_DEBUG_INFO("joined room '%s' (%s)", chat_session->title, chat_session->id); chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC, chat_session, chat_session->title, self); } else { SIPE_DEBUG_INFO("rejoining room '%s' (%s)", chat_session->title, chat_session->id); sipe_backend_chat_rejoin(SIPE_CORE_PUBLIC, chat_session->backend, self, chat_session->title); } g_free(self); attr = sipe_xml_attribute(node, "topic"); if (attr) { sipe_backend_chat_topic(chat_session->backend, attr); } /* Process user map for channel */ for (aib = sipe_xml_child(node, "aib"); aib; aib = sipe_xml_twin(aib)) { const gchar *value = sipe_xml_attribute(aib, "value"); gboolean chanop = is_chanop(aib); gchar **ids = g_strsplit(value, ",", 0); if (ids) { gchar **uid = ids; while (*uid) { const gchar *uri = g_hash_table_lookup(user_ids, *uid); if (uri) add_user(chat_session, uri, FALSE, chanop); uid++; } g_strfreev(ids); } } /* Request last 25 entries from channel history */ self = g_strdup_printf("" "" "" "" "" "", chat_session->id); chatserver_command(sipe_private, self); g_free(self); } } g_hash_table_destroy(user_ids); } } static void chatserver_grpchat_message(struct sipe_core_private *sipe_private, const sipe_xml *grpchat); static void chatserver_response_history(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER struct sip_session *session, SIPE_UNUSED_PARAMETER guint result, SIPE_UNUSED_PARAMETER const gchar *message, const sipe_xml *xml) { const sipe_xml *grpchat; for (grpchat = sipe_xml_child(xml, "chanib/msg"); grpchat; grpchat = sipe_xml_twin(grpchat)) if (sipe_strequal(sipe_xml_attribute(grpchat, "id"), "grpchat")) chatserver_grpchat_message(sipe_private, grpchat); } static void chatserver_response_part(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER struct sip_session *session, guint result, const gchar *message, const sipe_xml *xml) { if (result != 200) { SIPE_DEBUG_WARNING("chatserver_response_part: failed with %d: %s. Dropping room", result, message); } else { struct sipe_groupchat *groupchat = sipe_private->groupchat; const gchar *uri = sipe_xml_attribute(sipe_xml_child(xml, "chanib"), "uri"); struct sipe_chat_session *chat_session; if (uri && (chat_session = g_hash_table_lookup(groupchat->uri_to_chat_session, uri))) { SIPE_DEBUG_INFO("leaving room '%s' (%s)", chat_session->title, chat_session->id); g_hash_table_remove(groupchat->uri_to_chat_session, uri); sipe_chat_remove_session(chat_session); } else { SIPE_DEBUG_WARNING("chatserver_response_part: unknown chat room uri '%s'", uri ? uri : ""); } } } static void chatserver_notice_join(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER struct sip_session *session, SIPE_UNUSED_PARAMETER guint result, SIPE_UNUSED_PARAMETER const gchar *message, const sipe_xml *xml) { struct sipe_groupchat *groupchat = sipe_private->groupchat; const sipe_xml *uib; for (uib = sipe_xml_child(xml, "uib"); uib; uib = sipe_xml_twin(uib)) { const gchar *uri = sipe_xml_attribute(uib, "uri"); if (uri) { const sipe_xml *aib; for (aib = sipe_xml_child(uib, "aib"); aib; aib = sipe_xml_twin(aib)) { const gchar *domain = sipe_xml_attribute(aib, "domain"); const gchar *path = sipe_xml_attribute(aib, "value"); if (domain && path) { gchar *room_uri = g_strdup_printf("ma-chan://%s/%s", domain, path); struct sipe_chat_session *chat_session = g_hash_table_lookup(groupchat->uri_to_chat_session, room_uri); if (chat_session) add_user(chat_session, uri, TRUE, is_chanop(aib)); g_free(room_uri); } } } } } static void chatserver_notice_part(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER struct sip_session *session, SIPE_UNUSED_PARAMETER guint result, SIPE_UNUSED_PARAMETER const gchar *message, const sipe_xml *xml) { struct sipe_groupchat *groupchat = sipe_private->groupchat; const sipe_xml *chanib; for (chanib = sipe_xml_child(xml, "chanib"); chanib; chanib = sipe_xml_twin(chanib)) { const gchar *room_uri = sipe_xml_attribute(chanib, "uri"); if (room_uri) { struct sipe_chat_session *chat_session = g_hash_table_lookup(groupchat->uri_to_chat_session, room_uri); if (chat_session) { const sipe_xml *uib; for (uib = sipe_xml_child(chanib, "uib"); uib; uib = sipe_xml_twin(uib)) { const gchar *uri = sipe_xml_attribute(uib, "uri"); if (uri) { SIPE_DEBUG_INFO("remove_user: %s from room %s (%s)", uri, chat_session->title, chat_session->id); sipe_backend_chat_remove(chat_session->backend, uri); } } } } } } static const struct response { const gchar *key; void (* const handler)(struct sipe_core_private *, struct sip_session *, guint result, const gchar *, const sipe_xml *xml); } response_table[] = { { "rpl:requri", chatserver_response_uri }, { "rpl:chansrch", chatserver_response_channel_search }, { "rpl:join", chatserver_response_join }, { "rpl:bjoin", chatserver_response_join }, { "rpl:bccontext", chatserver_response_history }, { "rpl:part", chatserver_response_part }, { "ntc:join", chatserver_notice_join }, { "ntc:bjoin", chatserver_notice_join }, { "ntc:part", chatserver_notice_part }, { NULL, NULL } }; /* Handles rpl:XXX & ntc:YYY */ static void chatserver_response(struct sipe_core_private *sipe_private, const sipe_xml *reply, struct sip_session *session) { do { const sipe_xml *resp, *data; const gchar *id; gchar *message; guint result = 500; const struct response *r; id = sipe_xml_attribute(reply, "id"); if (!id) { SIPE_DEBUG_INFO_NOFORMAT("chatserver_response: no reply ID found!"); continue; } resp = sipe_xml_child(reply, "resp"); if (resp) { result = sipe_xml_int_attribute(resp, "code", 500); message = sipe_xml_data(resp); } else { message = g_strdup(""); } data = sipe_xml_child(reply, "data"); SIPE_DEBUG_INFO("chatserver_response: '%s' result (%d) %s", id, result, message ? message : ""); for (r = response_table; r->key; r++) { if (sipe_strcase_equal(id, r->key)) { (*r->handler)(sipe_private, session, result, message, data); /* session can be invalid now */ session = NULL; break; } } if (!r->key) { SIPE_DEBUG_INFO_NOFORMAT("chatserver_response: ignoring unknown response"); } g_free(message); } while ((reply = sipe_xml_twin(reply)) != NULL); } static void chatserver_grpchat_message(struct sipe_core_private *sipe_private, const sipe_xml *grpchat) { struct sipe_groupchat *groupchat = sipe_private->groupchat; const gchar *uri = sipe_xml_attribute(grpchat, "chanUri"); const gchar *from = sipe_xml_attribute(grpchat, "author"); time_t when = sipe_utils_str_to_time(sipe_xml_attribute(grpchat, "ts")); gchar *text = sipe_xml_data(sipe_xml_child(grpchat, "chat")); struct sipe_chat_session *chat_session; gchar *escaped; if (!uri || !from) { SIPE_DEBUG_INFO("chatserver_grpchat_message: message '%s' received without chat room URI or author!", text ? text : ""); g_free(text); return; } chat_session = g_hash_table_lookup(groupchat->uri_to_chat_session, uri); if (!chat_session) { SIPE_DEBUG_INFO("chatserver_grpchat_message: message '%s' from '%s' received from unknown chat room '%s'!", text ? text : "", from, uri); g_free(text); return; } /* libxml2 decodes all entities, but the backend expects HTML */ escaped = g_markup_escape_text(text, -1); g_free(text); sipe_backend_chat_message(SIPE_CORE_PUBLIC, chat_session->backend, from, when, escaped); g_free(escaped); } void process_incoming_info_groupchat(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct sip_session *session) { sipe_xml *xml = sipe_xml_parse(msg->body, msg->bodylen); const sipe_xml *node; const gchar *callid; struct sip_dialog *dialog; callid = sipmsg_find_call_id_header(msg); dialog = sipe_dialog_find(session, session->with); if (sipe_strequal(callid, dialog->callid)) { sip_transport_response(sipe_private, msg, 200, "OK", NULL); if (((node = sipe_xml_child(xml, "rpl")) != NULL) || ((node = sipe_xml_child(xml, "ntc")) != NULL)) { chatserver_response(sipe_private, node, session); } else if ((node = sipe_xml_child(xml, "grpchat")) != NULL) { chatserver_grpchat_message(sipe_private, node); } else { SIPE_DEBUG_INFO_NOFORMAT("process_incoming_info_groupchat: ignoring unknown response"); } } else { /* * Our last session got disconnected without proper shutdown, * e.g. by Pidgin crashing or network connection loss. When * we reconnect to the group chat the server will send INFO * messages to the current *AND* the obsolete Call-ID, until * the obsolete session expires. * * Ignore these INFO messages to avoid, e.g. duplicate texts, * and respond with an error so that the server knows that we * consider this dialog to be terminated. */ SIPE_DEBUG_INFO("process_incoming_info_groupchat: ignoring unsolicited INFO message to obsolete Call-ID: %s", callid); sip_transport_response(sipe_private, msg, 487, "Request Terminated", NULL); } sipe_xml_free(xml); } void sipe_groupchat_send(struct sipe_core_private *sipe_private, struct sipe_chat_session *chat_session, const gchar *what) { struct sipe_groupchat *groupchat = sipe_private->groupchat; gchar *cmd, *self, *timestamp, *tmp; gchar **lines, **strvp; struct sipe_groupchat_msg *msg; if (!groupchat || !chat_session) return; SIPE_DEBUG_INFO("sipe_groupchat_send: '%s' to %s", what, chat_session->id); self = sip_uri_self(sipe_private); timestamp = sipe_utils_time_to_str(time(NULL)); /** * 'what' is already XML-escaped, e.g. * * " -> " * > -> > * < -> < * & -> & * * Group Chat only accepts plain text, not full HTML. So we have to * strip all HTML tags and XML escape the text. * * Line breaks are encoded as
and therefore need to be replaced * before stripping. In order to prevent HTML stripping to strip line * endings, we need to split the text into lines on
. */ lines = g_strsplit(what, "
", 0); for (strvp = lines; *strvp; strvp++) { /* replace array entry with HTML stripped & XML escaped version */ gchar *stripped = sipe_backend_markup_strip_html(*strvp); gchar *escaped = g_markup_escape_text(stripped, -1); g_free(stripped); g_free(*strvp); *strvp = escaped; } tmp = g_strjoinv("\r\n", lines); g_strfreev(lines); cmd = g_strdup_printf("" "%s" "", chat_session->id, self, timestamp, tmp); g_free(tmp); g_free(timestamp); g_free(self); msg = chatserver_command(sipe_private, cmd); g_free(cmd); if (msg) { msg->session = chat_session; msg->content = g_strdup(what); } else { chatserver_command_error_notify(sipe_private, chat_session, what); } } void sipe_groupchat_leave(struct sipe_core_private *sipe_private, struct sipe_chat_session *chat_session) { struct sipe_groupchat *groupchat = sipe_private->groupchat; gchar *cmd; if (!groupchat || !chat_session) return; SIPE_DEBUG_INFO("sipe_groupchat_leave: %s", chat_session->id); cmd = g_strdup_printf("" "" "" "" "", chat_session->id); chatserver_command(sipe_private, cmd); g_free(cmd); } gboolean sipe_core_groupchat_query_rooms(struct sipe_core_public *sipe_public) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_groupchat *groupchat = sipe_private->groupchat; if (!groupchat || !groupchat->connected) return FALSE; chatserver_command(sipe_private, "" "" "" "" ""); return TRUE; } void sipe_core_groupchat_join(struct sipe_core_public *sipe_public, const gchar *uri) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_groupchat *groupchat = sipe_private->groupchat; if (!g_str_has_prefix(uri, "ma-chan://")) return; if (!groupchat) { /* This happens when a user has set auto-join on a channel */ sipe_groupchat_allocate(sipe_private); groupchat = sipe_private->groupchat; } if (groupchat->connected) { struct sipe_chat_session *chat_session = g_hash_table_lookup(groupchat->uri_to_chat_session, uri); /* Already joined? */ if (chat_session) { /* Yes, update backend session */ SIPE_DEBUG_INFO("sipe_core_groupchat_join: show '%s' (%s)", chat_session->title, chat_session->id); sipe_backend_chat_show(chat_session->backend); } else { /* No, send command out directly */ gchar *chanid = generate_chanid_node(uri, 0); if (chanid) { gchar *cmd = g_strdup_printf("" "%s" "", chanid); SIPE_DEBUG_INFO("sipe_core_groupchat_join: join %s", uri); chatserver_command(sipe_private, cmd); g_free(cmd); g_free(chanid); } } } else { /* Add it to the queue but avoid duplicates */ if (!g_slist_find_custom(groupchat->join_queue, uri, (GCompareFunc)g_strcmp0)) { SIPE_DEBUG_INFO_NOFORMAT("sipe_core_groupchat_join: URI queued"); groupchat->join_queue = g_slist_prepend(groupchat->join_queue, g_strdup(uri)); } } } void sipe_groupchat_rejoin(struct sipe_core_private *sipe_private, struct sipe_chat_session *chat_session) { struct sipe_groupchat *groupchat = sipe_private->groupchat; if (!groupchat) { /* First rejoined channel after reconnect will trigger this */ sipe_groupchat_allocate(sipe_private); groupchat = sipe_private->groupchat; } /* Remember "old" session, so that we don't recreate it at join */ g_hash_table_insert(groupchat->uri_to_chat_session, chat_session->id, chat_session); sipe_core_groupchat_join(SIPE_CORE_PUBLIC, chat_session->id); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-groupchat.h ================================================ /** * @file sipe-groupchat.h * * pidgin-sipe * * Copyright (C) 2010-2013 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipmsg; struct sip_dialog; struct sip_session; struct sipe_chat_session; struct sipe_core_private; void sipe_groupchat_free(struct sipe_core_private *sipe_private); void sipe_groupchat_init(struct sipe_core_private *sipe_private); void sipe_groupchat_invite_failed(struct sipe_core_private *sipe_private, struct sip_session *session); void sipe_groupchat_invite_response(struct sipe_core_private *sipe_private, struct sip_dialog *dialog, struct sipmsg *response); void process_incoming_info_groupchat(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct sip_session *session); void sipe_groupchat_send(struct sipe_core_private *sipe_private, struct sipe_chat_session *chat_session, const gchar *what); void sipe_groupchat_leave(struct sipe_core_private *sipe_private, struct sipe_chat_session *chat_session); void sipe_groupchat_rejoin(struct sipe_core_private *sipe_private, struct sipe_chat_session *chat_session); ================================================ FILE: src/core/sipe-http-request.c ================================================ /** * @file sipe-http-request.c * * pidgin-sipe * * Copyright (C) 2013-2018 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * SIPE HTTP request layer implementation * * - request handling: creation, parameters, deletion, cancelling * - session handling: creation, closing * - client authorization handling * - connection request queue handling * - compile HTTP header contents and hand-off to transport layer * - process HTTP response and hand-off to user callback */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "sipe-common.h" #include "sipmsg.h" #include "sip-sec.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-http.h" #define _SIPE_HTTP_PRIVATE_IF_REQUEST #include "sipe-http-request.h" #define _SIPE_HTTP_PRIVATE_IF_TRANSPORT #include "sipe-http-transport.h" struct sipe_http_session { GHashTable *cookie_jar; }; struct sipe_http_request { struct sipe_http_connection_public *connection; struct sipe_http_session *session; gchar *path; gchar *headers; gchar *body; /* NULL for GET */ gchar *content_type; /* NULL if body == NULL */ gchar *authorization; const gchar *user; /* not copied */ const gchar *password; /* not copied */ sipe_http_response_callback *cb; gpointer cb_data; guint32 flags; }; #define SIPE_HTTP_REQUEST_FLAG_FIRST 0x00000001 #define SIPE_HTTP_REQUEST_FLAG_REDIRECT 0x00000002 #define SIPE_HTTP_REQUEST_FLAG_AUTHDATA 0x00000004 #define SIPE_HTTP_REQUEST_FLAG_HANDSHAKE 0x00000008 static void sipe_http_request_free(struct sipe_core_private *sipe_private, struct sipe_http_request *req, guint status) { if (req->cb) /* Callback: aborted/failed/cancelled */ (*req->cb)(sipe_private, status, NULL, NULL, req->cb_data); g_free(req->path); g_free(req->headers); g_free(req->body); g_free(req->content_type); g_free(req->authorization); g_free(req); } static void add_cookie_cb(SIPE_UNUSED_PARAMETER const gchar *key, const gchar *cookie, GString *string) { g_string_append_printf(string, "Cookie: %s\r\n", cookie); } static void sipe_http_request_send(struct sipe_http_connection_public *conn_public) { struct sipe_http_request *req = conn_public->pending_requests->data; gchar *header; gchar *content = NULL; gchar *cookie = NULL; if (req->body) content = g_strdup_printf("Content-Length: %" G_GSIZE_FORMAT "\r\n" "Content-Type: %s\r\n", strlen(req->body), req->content_type); if (req->session && g_hash_table_size(req->session->cookie_jar)) { GString *cookies = g_string_new(""); g_hash_table_foreach(req->session->cookie_jar, (GHFunc) add_cookie_cb, cookies); cookie = g_string_free(cookies, FALSE); } header = g_strdup_printf("%s /%s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: %s\r\n" "%s%s%s%s", content ? "POST" : "GET", req->path, conn_public->host, sipe_core_user_agent(conn_public->sipe_private), conn_public->cached_authorization ? conn_public->cached_authorization : req->authorization ? req->authorization : "", req->headers ? req->headers : "", cookie ? cookie : "", content ? content : ""); g_free(cookie); g_free(content); /* only use authorization once */ g_free(req->authorization); req->authorization = NULL; sipe_http_transport_send(conn_public, header, req->body); g_free(header); } gboolean sipe_http_request_pending(struct sipe_http_connection_public *conn_public) { return(conn_public->pending_requests != NULL); } void sipe_http_request_next(struct sipe_http_connection_public *conn_public) { sipe_http_request_send(conn_public); } static void sipe_http_request_enqueue(struct sipe_core_private *sipe_private, struct sipe_http_request *req, const struct sipe_http_parsed_uri *parsed_uri) { struct sipe_http_connection_public *conn_public; req->path = g_strdup(parsed_uri->path); req->connection = conn_public = sipe_http_transport_new(sipe_private, parsed_uri->host, parsed_uri->port, parsed_uri->tls); if (!sipe_http_request_pending(conn_public)) req->flags |= SIPE_HTTP_REQUEST_FLAG_FIRST; conn_public->pending_requests = g_slist_append(conn_public->pending_requests, req); } static void sipe_http_request_drop_context(struct sipe_http_connection_public *conn_public) { g_free(conn_public->cached_authorization); conn_public->cached_authorization = NULL; sip_sec_destroy_context(conn_public->context); conn_public->context = NULL; } static void sipe_http_request_finalize_negotiate(struct sipe_http_request *req, struct sipmsg *msg) { #if defined(HAVE_GSSAPI_GSSAPI_H) || defined(HAVE_SSPI) /* * Negotiate can send a final package in the successful response. * We need to forward this to the context or otherwise it will * never reach the ready state. */ struct sipe_http_connection_public *conn_public = req->connection; if (sip_sec_context_type(conn_public->context) == SIPE_AUTHENTICATION_TYPE_NEGOTIATE) { const gchar *header = sipmsg_find_auth_header(msg, "Negotiate"); if (header) { gchar **parts = g_strsplit(header, " ", 0); gchar *spn = g_strdup_printf("HTTP/%s", conn_public->host); gchar *token; SIPE_DEBUG_INFO("sipe_http_request_finalize_negotiate: init context target '%s' token '%s'", spn, parts[1] ? parts[1] : ""); if (sip_sec_init_context_step(conn_public->context, spn, parts[1], &token, NULL)) { g_free(token); } else { SIPE_DEBUG_INFO_NOFORMAT("sipe_http_request_finalize_negotiate: security context init step failed, throwing away context"); sipe_http_request_drop_context(conn_public); } g_free(spn); g_strfreev(parts); } } #else (void) req; /* keep compiler happy */ (void) msg; /* keep compiler happy */ #endif } /* TRUE indicates failure */ static gboolean sipe_http_request_response_redirection(struct sipe_core_private *sipe_private, struct sipe_http_request *req, struct sipmsg *msg) { const gchar *location = sipmsg_find_header(msg, "Location"); gboolean failed = TRUE; sipe_http_request_finalize_negotiate(req, msg); if (location) { struct sipe_http_parsed_uri *parsed_uri = sipe_http_parse_uri(location); if (parsed_uri) { /* remove request from old connection */ struct sipe_http_connection_public *conn_public = req->connection; conn_public->pending_requests = g_slist_remove(conn_public->pending_requests, req); /* free old request data */ g_free(req->path); req->flags &= ~( SIPE_HTTP_REQUEST_FLAG_FIRST | SIPE_HTTP_REQUEST_FLAG_HANDSHAKE ); /* resubmit request on other connection */ sipe_http_request_enqueue(sipe_private, req, parsed_uri); failed = FALSE; sipe_http_parsed_uri_free(parsed_uri); } else SIPE_DEBUG_INFO("sipe_http_request_response_redirection: invalid redirection to '%s'", location); } else SIPE_DEBUG_INFO_NOFORMAT("sipe_http_request_response_redirection: no URL found?!?"); return(failed); } /* TRUE indicates failure */ static gboolean sipe_http_request_response_unauthorized(struct sipe_core_private *sipe_private, struct sipe_http_request *req, struct sipmsg *msg) { struct sipe_http_connection_public *conn_public = req->connection; const gchar *header = NULL; guint type; gboolean failed = TRUE; /* * There are some buggy HTTP servers out there that add superfluous * WWW-Authenticate: headers during the authentication handshake. * Look only for the header of the active security context. */ if (conn_public->context) { const gchar *name = sip_sec_context_name(conn_public->context); header = sipmsg_find_auth_header(msg, name); type = sip_sec_context_type(conn_public->context); if (!header) { SIPE_DEBUG_INFO("sipe_http_request_response_unauthorized: expected authentication scheme %s not found", name); return(failed); } if (conn_public->cached_authorization) { /* * The "Basic" scheme doesn't have any state. * * If we enter here then we have already tried "Basic" * authentication once for this request and it was * rejected by the server. As all future requests will * also be rejected, we need to abort here in order to * prevent an endless request/401/request/... loop. */ SIPE_DEBUG_INFO("sipe_http_request_response_unauthorized: Basic authentication has failed for host '%s', please check user name and password!", conn_public->host); return(failed); } } else { #if defined(HAVE_GSSAPI_GSSAPI_H) || defined(HAVE_SSPI) #define DEBUG_STRING ", NTLM and Negotiate" /* Use "Negotiate" unless the user requested "NTLM" */ if (sipe_private->authentication_type != SIPE_AUTHENTICATION_TYPE_NTLM) header = sipmsg_find_auth_header(msg, "Negotiate"); if (header) { type = SIPE_AUTHENTICATION_TYPE_NEGOTIATE; } else #else #define DEBUG_STRING " and NTLM" (void) sipe_private; /* keep compiler happy */ #endif { header = sipmsg_find_auth_header(msg, "NTLM"); type = SIPE_AUTHENTICATION_TYPE_NTLM; } /* only fall back to "Basic" after everything else fails */ if (!header) { header = sipmsg_find_auth_header(msg, "Basic"); type = SIPE_AUTHENTICATION_TYPE_BASIC; } } if (header) { if (!conn_public->context) { gboolean valid = req->flags & SIPE_HTTP_REQUEST_FLAG_AUTHDATA; conn_public->context = sip_sec_create_context(type, !valid, /* Single Sign-On flag */ TRUE, /* connection-based for HTTP */ valid ? req->user : NULL, valid ? req->password : NULL); } if (conn_public->context) { gchar **parts = g_strsplit(header, " ", 0); gchar *spn = g_strdup_printf("HTTP/%s", conn_public->host); gchar *token_out; const gchar *token_in = parts[1]; SIPE_DEBUG_INFO("sipe_http_request_response_unauthorized: init context target '%s' token '%s'", spn, token_in ? token_in : ""); /* * If we receive a NULL token during the handshake * then the authentication scheme has failed. */ if ((req->flags & SIPE_HTTP_REQUEST_FLAG_HANDSHAKE) && !token_in) { SIPE_DEBUG_INFO_NOFORMAT("sipe_http_request_response_unauthorized: authentication failed, throwing away context"); sipe_http_request_drop_context(conn_public); } else if (sip_sec_init_context_step(conn_public->context, spn, token_in, &token_out, NULL)) { /* handshake has started */ req->flags |= SIPE_HTTP_REQUEST_FLAG_HANDSHAKE; /* generate authorization header */ req->authorization = g_strdup_printf("Authorization: %s %s\r\n", sip_sec_context_name(conn_public->context), token_out ? token_out : ""); g_free(token_out); /* * authorization never changes for Basic * authentication scheme, so we can keep it. */ if (type == SIPE_AUTHENTICATION_TYPE_BASIC) { g_free(conn_public->cached_authorization); conn_public->cached_authorization = g_strdup(req->authorization); } /* * Keep the request in the queue. As it is at * the head it will be pulled automatically * by the transport layer after returning. */ failed = FALSE; } else { SIPE_DEBUG_INFO_NOFORMAT("sipe_http_request_response_unauthorized: security context init step failed, throwing away context"); sipe_http_request_drop_context(conn_public); } g_free(spn); g_strfreev(parts); } else SIPE_DEBUG_INFO_NOFORMAT("sipe_http_request_response_unauthorized: security context creation failed"); } else SIPE_DEBUG_INFO_NOFORMAT("sipe_http_request_response_unauthorized: only Basic" DEBUG_STRING " authentication schemes are supported"); return(failed); } static void sipe_http_request_response_callback(struct sipe_core_private *sipe_private, struct sipe_http_request *req, struct sipmsg *msg) { sipe_http_request_finalize_negotiate(req, msg); /* Set-Cookie: RMID=732423sdfs73242; expires=Fri, 31-Dec-2010 23:59:59 GMT; path=/; domain=.example.net */ if (req->session) { guint instance = 0; const gchar *hdr; /* extract all cookies from header */ while ((hdr = sipmsg_find_header_instance(msg, "Set-Cookie", instance++)) != NULL) { gchar **parts, **current; const gchar *part; gchar *new = NULL; current = parts = g_strsplit(hdr, ";", 0); while ((part = *current++) != NULL) { /* strip these parts from cookie */ if (!(strstr(part, "path=") || strstr(part, "domain=") || strstr(part, "expires=") || strstr(part, "secure"))) { gchar *tmp = new; new = new ? g_strconcat(new, ";", part, NULL) : g_strdup(part); g_free(tmp); } } if (new) { g_hash_table_insert(req->session->cookie_jar, g_strdup(*parts), new); SIPE_DEBUG_INFO("sipe_http_request_response_callback: cookie: %s", new); } g_strfreev(parts); } } /* Callback: success */ (*req->cb)(sipe_private, msg->response, msg->headers, msg->body, req->cb_data); /* remove completed request */ sipe_http_request_cancel(req); } void sipe_http_request_response(struct sipe_http_connection_public *conn_public, struct sipmsg *msg) { struct sipe_core_private *sipe_private = conn_public->sipe_private; struct sipe_http_request *req = conn_public->pending_requests->data; gboolean failed; if ((req->flags & SIPE_HTTP_REQUEST_FLAG_REDIRECT) && (msg->response >= SIPE_HTTP_STATUS_REDIRECTION) && (msg->response < SIPE_HTTP_STATUS_CLIENT_ERROR)) { failed = sipe_http_request_response_redirection(sipe_private, req, msg); } else if (msg->response == SIPE_HTTP_STATUS_CLIENT_UNAUTHORIZED) { failed = sipe_http_request_response_unauthorized(sipe_private, req, msg); } else { /* On some errors throw away the security context */ if (((msg->response == SIPE_HTTP_STATUS_CLIENT_FORBIDDEN) || (msg->response == SIPE_HTTP_STATUS_CLIENT_PROXY_AUTH) || (msg->response >= SIPE_HTTP_STATUS_SERVER_ERROR)) && conn_public->context) { SIPE_DEBUG_INFO("sipe_http_request_response: response was %d, throwing away security context", msg->response); sipe_http_request_drop_context(conn_public); } /* All other cases are passed on to the user */ sipe_http_request_response_callback(sipe_private, req, msg); /* req is no longer valid */ failed = FALSE; } if (failed) { /* Callback: request failed */ (*req->cb)(sipe_private, SIPE_HTTP_STATUS_FAILED, msg->headers, NULL, req->cb_data); /* remove failed request */ sipe_http_request_cancel(req); } } void sipe_http_request_shutdown(struct sipe_http_connection_public *conn_public, gboolean abort) { if (conn_public->pending_requests) { GSList *entry = conn_public->pending_requests; guint status = abort ? SIPE_HTTP_STATUS_ABORTED : SIPE_HTTP_STATUS_FAILED; gboolean warn = conn_public->connected && !abort; while (entry) { struct sipe_http_request *req = entry->data; if (warn) { SIPE_DEBUG_ERROR("sipe_http_request_shutdown: pending request at shutdown: could indicate missing _ready() call on request. Debugging information:\n" "Host: %s\n" "Port: %d\n" "Path: %s\n" "Method: %s\n", conn_public->host, conn_public->port, req->path, req->body ? "POST" : "GET"); } sipe_http_request_free(conn_public->sipe_private, req, status); entry = entry->next; } g_slist_free(conn_public->pending_requests); conn_public->pending_requests = NULL; } if (conn_public->context) { g_free(conn_public->cached_authorization); conn_public->cached_authorization = NULL; sip_sec_destroy_context(conn_public->context); conn_public->context = NULL; } } struct sipe_http_request *sipe_http_request_new(struct sipe_core_private *sipe_private, const struct sipe_http_parsed_uri *parsed_uri, const gchar *headers, const gchar *body, const gchar *content_type, sipe_http_response_callback *callback, gpointer callback_data) { struct sipe_http_request *req; if (!parsed_uri) return(NULL); if (sipe_http_shutting_down(sipe_private)) { SIPE_DEBUG_ERROR("sipe_http_request_new: new HTTP request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n" "Host: %s\n" "Port: %d\n" "Path: %s\n" "Headers: %s\n" "Body: %s\n", parsed_uri->host, parsed_uri->port, parsed_uri->path, headers ? headers : "", body ? body : ""); return(NULL); } req = g_new0(struct sipe_http_request, 1); req->flags = 0; req->cb = callback; req->cb_data = callback_data; if (headers) req->headers = g_strdup(headers); if (body) { req->body = g_strdup(body); req->content_type = g_strdup(content_type); } /* default authentication */ if (!SIPE_CORE_PRIVATE_FLAG_IS(SSO)) sipe_http_request_authentication(req, sipe_private->authuser, sipe_private->password); sipe_http_request_enqueue(sipe_private, req, parsed_uri); return(req); } void sipe_http_request_ready(struct sipe_http_request *request) { struct sipe_http_connection_public *conn_public = request->connection; /* pass first request on already opened connection through directly */ if ((request->flags & SIPE_HTTP_REQUEST_FLAG_FIRST) && conn_public->connected) sipe_http_request_send(conn_public); } struct sipe_http_session *sipe_http_session_start(void) { struct sipe_http_session *session = g_new0(struct sipe_http_session, 1); session->cookie_jar = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); return(session); } void sipe_http_session_close(struct sipe_http_session *session) { if (session) { g_hash_table_destroy(session->cookie_jar); g_free(session); } } void sipe_http_request_cancel(struct sipe_http_request *request) { struct sipe_http_connection_public *conn_public = request->connection; conn_public->pending_requests = g_slist_remove(conn_public->pending_requests, request); /* cancelled by requester, don't use callback */ request->cb = NULL; sipe_http_request_free(conn_public->sipe_private, request, SIPE_HTTP_STATUS_CANCELLED); } void sipe_http_request_session(struct sipe_http_request *request, struct sipe_http_session *session) { request->session = session; } void sipe_http_request_allow_redirect(struct sipe_http_request *request) { request->flags |= SIPE_HTTP_REQUEST_FLAG_REDIRECT; } void sipe_http_request_authentication(struct sipe_http_request *request, const gchar *user, const gchar *password) { request->flags |= SIPE_HTTP_REQUEST_FLAG_AUTHDATA; request->user = user; request->password = password; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-http-request.h ================================================ /** * @file sipe-http-request.h * * pidgin-sipe * * Copyright (C) 2013 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Private interface between HTTP Public <-> Request <-> Transport layers */ #ifndef _SIPE_HTTP_PRIVATE_IF_REQUEST #error "you are not allowed to include sipe-http-request.h!" #endif /* * Interface dependencies: * * */ /* Forward declarations */ struct sipmsg; struct sipe_core_private; struct sipe_http_connection_public; struct sipe_http_request; struct sipe_http_parsed_uri { gchar *host; gchar *path; guint port; gboolean tls; }; /** * Parse URI * * @param uri text to parse * * @return pointer to parsed URI. Must be freed with @c sipe_http_parsed_uri_free() */ struct sipe_http_parsed_uri *sipe_http_parse_uri(const gchar *uri); /** * Free parsed URI data structure * * @param pointer to parsed URI (may be @c NULL) */ void sipe_http_parsed_uri_free(struct sipe_http_parsed_uri *parsed_uri); /** * Is there pending request for HTTP connection? * * @param conn_public HTTP connection public data */ gboolean sipe_http_request_pending(struct sipe_http_connection_public *conn_public); /** * HTTP connection is ready for next request * * @param conn_public HTTP connection public data */ void sipe_http_request_next(struct sipe_http_connection_public *conn_public); /** * HTTP response received * * @param conn_public HTTP connection public data * @param msg parsed message */ void sipe_http_request_response(struct sipe_http_connection_public *conn_public, struct sipmsg *msg); /** * HTTP connection shutdown * * @param conn_public HTTP connection public data * @param abort @c TRUE if HTTP stack is shutting down */ void sipe_http_request_shutdown(struct sipe_http_connection_public *conn_public, gboolean abort); /** * Create new HTTP request (internal raw version) * * @param sipe_private SIPE core private data * @param parsed_uri pointer to parsed URI * @param headers additional headers to add (may be @c NULL) * @param body body (may be @c NULL) * @param content_type MIME type for body (may be @c NULL if body is @c NULL) * @param callback callback function * @param callback_data callback data * * @return pointer to opaque HTTP request data structure */ struct sipe_http_request *sipe_http_request_new(struct sipe_core_private *sipe_private, const struct sipe_http_parsed_uri *parsed_uri, const gchar *headers, const gchar *body, const gchar *content_type, sipe_http_response_callback *callback, gpointer callback_data); ================================================ FILE: src/core/sipe-http-transport.c ================================================ /** * @file sipe-http-transport.c * * pidgin-sipe * * Copyright (C) 2013-2019 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * SIPE HTTP transport layer implementation * * - connection handling: opening, closing, timeout * - interface to backend: sending & receiving of raw messages * - request queue pulling */ #include #include #include #include "sipmsg.h" #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-http.h" #include "sipe-schedule.h" #include "sipe-utils.h" #define _SIPE_HTTP_PRIVATE_IF_REQUEST #include "sipe-http-request.h" #define _SIPE_HTTP_PRIVATE_IF_TRANSPORT #include "sipe-http-transport.h" #define SIPE_HTTP_CONNECTION ((struct sipe_http_connection *) connection->user_data) #define SIPE_HTTP_CONNECTION_PRIVATE ((struct sipe_http_connection *) conn_public) #define SIPE_HTTP_CONNECTION_PUBLIC ((struct sipe_http_connection_public *) conn) #define SIPE_HTTP_TIMEOUT_ACTION "<+http-timeout>" #define SIPE_HTTP_DEFAULT_TIMEOUT 60 /* in seconds */ struct sipe_http_connection { struct sipe_http_connection_public public; struct sipe_transport_connection *connection; gchar *host_port; time_t timeout; /* in seconds from epoch */ gboolean use_tls; }; struct sipe_http { GHashTable *connections; GQueue *timeouts; time_t next_timeout; /* in seconds from epoch, 0 if timer isn't running */ gboolean shutting_down; }; static gint timeout_compare(gconstpointer a, gconstpointer b, SIPE_UNUSED_PARAMETER gpointer user_data) { return(((struct sipe_http_connection *) a)->timeout - ((struct sipe_http_connection *) b)->timeout); } static void sipe_http_transport_update_timeout_queue(struct sipe_http_connection *conn, gboolean remove); static void sipe_http_transport_free(gpointer data) { struct sipe_http_connection *conn = data; SIPE_DEBUG_INFO("sipe_http_transport_free: destroying connection '%s'(%p)", conn->host_port, conn->connection); if (conn->connection) sipe_backend_transport_disconnect(conn->connection); conn->connection = NULL; sipe_http_transport_update_timeout_queue(conn, TRUE); sipe_http_request_shutdown(SIPE_HTTP_CONNECTION_PUBLIC, conn->public.sipe_private->http->shutting_down); g_free(conn->public.host); g_free(conn->host_port); g_free(conn); } static void sipe_http_transport_drop(struct sipe_http *http, struct sipe_http_connection *conn, const gchar *message) { SIPE_LOG_INFO("sipe_http_transport_drop: '%s'(%p): %s", conn->host_port, conn->connection, message ? message : "REASON UNKNOWN"); #if GLIB_CHECK_VERSION(2,30,0) /* this triggers sipe_http_transport_free() */ g_hash_table_remove(http->connections, conn->host_port); #else /* GLIB < 2.30 calls destroy notifiers *before* removing the entry */ /* which can cause a race condition with sipe_http_transport_new() */ g_hash_table_steal(http->connections, conn->host_port); sipe_http_transport_free(conn); #endif /* conn is no longer valid */ } static void start_timer(struct sipe_core_private *sipe_private, time_t current_time); static void sipe_http_transport_timeout(struct sipe_core_private *sipe_private, gpointer data) { struct sipe_http *http = sipe_private->http; struct sipe_http_connection *conn = data; time_t current_time = time(NULL); /* timer has expired */ http->next_timeout = 0; while (1) { sipe_http_transport_drop(http, conn, "timeout"); /* conn is no longer valid */ /* is there another active connection? */ conn = g_queue_peek_head(http->timeouts); if (!conn) break; /* restart timer for next connection */ if (conn->timeout > current_time) { start_timer(sipe_private, current_time); break; } /* next connection timed-out too, loop around */ } } static void start_timer(struct sipe_core_private *sipe_private, time_t current_time) { struct sipe_http *http = sipe_private->http; struct sipe_http_connection *conn = g_queue_peek_head(http->timeouts); http->next_timeout = conn->timeout; sipe_schedule_seconds(sipe_private, SIPE_HTTP_TIMEOUT_ACTION, conn, http->next_timeout - current_time, sipe_http_transport_timeout, NULL); } static void sipe_http_transport_update_timeout_queue(struct sipe_http_connection *conn, gboolean remove) { struct sipe_core_private *sipe_private = conn->public.sipe_private; struct sipe_http *http = sipe_private->http; GQueue *timeouts = http->timeouts; time_t current_time = time(NULL); /* is this connection at head of queue? */ gboolean update = (conn == g_queue_peek_head(timeouts)); /* update timeout queue */ if (remove) { g_queue_remove(timeouts, conn); } else { conn->timeout = current_time + SIPE_HTTP_DEFAULT_TIMEOUT; g_queue_sort(timeouts, timeout_compare, NULL); } /* update timer if necessary */ if (update) { sipe_schedule_cancel(sipe_private, SIPE_HTTP_TIMEOUT_ACTION); if (g_queue_is_empty(timeouts)) { http->next_timeout = 0; } else { start_timer(sipe_private, current_time); } } } gboolean sipe_http_shutting_down(struct sipe_core_private *sipe_private) { struct sipe_http *http = sipe_private->http; /* We need to return FALSE in case HTTP stack isn't initialized yet */ if (!http) return(FALSE); return(http->shutting_down); } void sipe_http_free(struct sipe_core_private *sipe_private) { struct sipe_http *http = sipe_private->http; if (!http) return; /* HTTP stack is shutting down: reject all new requests */ http->shutting_down = TRUE; sipe_schedule_cancel(sipe_private, SIPE_HTTP_TIMEOUT_ACTION); g_hash_table_destroy(http->connections); g_queue_free(http->timeouts); g_free(http); sipe_private->http = NULL; } static void sipe_http_init(struct sipe_core_private *sipe_private) { struct sipe_http *http; if (sipe_private->http) return; sipe_private->http = http = g_new0(struct sipe_http, 1); http->connections = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, sipe_http_transport_free); http->timeouts = g_queue_new(); } static void sipe_http_transport_connected(struct sipe_transport_connection *connection) { struct sipe_http_connection *conn = SIPE_HTTP_CONNECTION; struct sipe_core_private *sipe_private = conn->public.sipe_private; struct sipe_http *http = sipe_private->http; time_t current_time = time(NULL); SIPE_LOG_INFO("sipe_http_transport_connected: '%s'(%p)", conn->host_port, connection); conn->public.connected = TRUE; /* add active connection to timeout queue */ conn->timeout = current_time + SIPE_HTTP_DEFAULT_TIMEOUT; g_queue_insert_sorted(http->timeouts, conn, timeout_compare, NULL); /* start timeout timer if necessary */ if (http->next_timeout == 0) start_timer(sipe_private, current_time); sipe_http_request_next(SIPE_HTTP_CONNECTION_PUBLIC); } static void sipe_http_transport_input(struct sipe_transport_connection *connection) { struct sipe_http_connection *conn = SIPE_HTTP_CONNECTION; char *current = connection->buffer; /* according to the RFC remove CRLF at the beginning */ while (*current == '\r' || *current == '\n') { current++; } if (current != connection->buffer) sipe_utils_shrink_buffer(connection, current); if (conn->connection && (current = strstr(connection->buffer, "\r\n\r\n")) != NULL) { struct sipmsg *msg; gboolean drop = FALSE; gboolean next; current += 2; current[0] = '\0'; msg = sipmsg_parse_header(connection->buffer); if (!msg) { /* restore header for next try */ current[0] = '\r'; return; } /* HTTP/1.1 Transfer-Encoding: chunked */ if (msg->bodylen == SIPMSG_BODYLEN_CHUNKED) { gchar *start = current + 2; GSList *chunks = NULL; gboolean incomplete = TRUE; msg->bodylen = 0; while (strlen(start) > 0) { gchar *tmp; guint length = g_ascii_strtoll(start, &tmp, 16); guint remainder; struct _chunk { guint length; const gchar *start; } *chunk; /* Illegal number */ if ((length == 0) && (start == tmp)) break; msg->bodylen += length; /* Chunk header not finished yet */ tmp = strstr(tmp, "\r\n"); if (tmp == NULL) break; /* Chunk not finished yet */ tmp += 2; remainder = connection->buffer_used - (tmp - connection->buffer); if (remainder < length + 2) break; /* Next chunk */ start = tmp + length + 2; /* Body completed */ if (length == 0) { gchar *dummy = g_malloc(msg->bodylen + 1); gchar *p = dummy; GSList *entry = chunks; while (entry) { chunk = entry->data; memcpy(p, chunk->start, chunk->length); p += chunk->length; entry = entry->next; } p[0] = '\0'; msg->body = dummy; sipe_utils_message_debug(connection, "HTTP", connection->buffer, msg->body, FALSE); current = start; sipe_utils_shrink_buffer(connection, current); incomplete = FALSE; break; } /* Append completed chunk */ chunk = g_new0(struct _chunk, 1); chunk->length = length; chunk->start = tmp; chunks = g_slist_append(chunks, chunk); } if (chunks) sipe_utils_slist_free_full(chunks, g_free); if (incomplete) { /* restore header for next try */ sipmsg_free(msg); current[0] = '\r'; return; } } else { guint remainder = connection->buffer_used - (current + 2 - connection->buffer); if (remainder >= (guint) msg->bodylen) { char *dummy = g_malloc(msg->bodylen + 1); current += 2; memcpy(dummy, current, msg->bodylen); dummy[msg->bodylen] = '\0'; msg->body = dummy; current += msg->bodylen; sipe_utils_message_debug(connection, "HTTP", connection->buffer, msg->body, FALSE); sipe_utils_shrink_buffer(connection, current); } else { SIPE_DEBUG_INFO("sipe_http_transport_input: body too short (%d < %d, strlen %" G_GSIZE_FORMAT ") - ignoring message", remainder, msg->bodylen, strlen(connection->buffer)); /* restore header for next try */ sipmsg_free(msg); current[0] = '\r'; return; } } if (msg->response == SIPMSG_RESPONSE_FATAL_ERROR) { /* fatal header parse error */ msg->response = SIPE_HTTP_STATUS_SERVER_ERROR; drop = TRUE; } else if (sipe_strcase_equal(sipmsg_find_header(msg, "Connection"), "close")) { SIPE_DEBUG_INFO("sipe_http_transport_input: server requested close '%s'", conn->host_port); drop = TRUE; } sipe_http_request_response(SIPE_HTTP_CONNECTION_PUBLIC, msg); next = sipe_http_request_pending(SIPE_HTTP_CONNECTION_PUBLIC); if (drop) { /* drop backend connection */ sipe_backend_transport_disconnect(conn->connection); conn->connection = NULL; conn->public.connected = FALSE; /* if we have pending requests we need to trigger re-connect */ if (next) sipe_http_transport_new(conn->public.sipe_private, conn->public.host, conn->public.port, conn->use_tls); } else if (next) { /* trigger sending of next pending request */ sipe_http_request_next(SIPE_HTTP_CONNECTION_PUBLIC); } sipmsg_free(msg); } } static void sipe_http_transport_error(struct sipe_transport_connection *connection, const gchar *msg) { struct sipe_http_connection *conn = SIPE_HTTP_CONNECTION; sipe_http_transport_drop(conn->public.sipe_private->http, conn, msg); /* conn is no longer valid */ } struct sipe_http_connection_public *sipe_http_transport_new(struct sipe_core_private *sipe_private, const gchar *host_in, const guint32 port, gboolean use_tls) { struct sipe_http *http; struct sipe_http_connection *conn = NULL; /* host name matching should be case insensitive */ gchar *host = g_ascii_strdown(host_in, -1); gchar *host_port = g_strdup_printf("%s:%" G_GUINT32_FORMAT, host, port); sipe_http_init(sipe_private); http = sipe_private->http; if (http->shutting_down) { SIPE_DEBUG_ERROR("sipe_http_transport_new: new connection requested during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n" "Host/Port: %s", host_port); } else { conn = g_hash_table_lookup(http->connections, host_port); if (conn) { /* re-establishing connection */ if (!conn->connection) { SIPE_DEBUG_INFO("sipe_http_transport_new: re-establishing %s", host_port); /* will be re-inserted after connect */ sipe_http_transport_update_timeout_queue(conn, TRUE); } } else { /* new connection */ SIPE_DEBUG_INFO("sipe_http_transport_new: new %s", host_port); conn = g_new0(struct sipe_http_connection, 1); conn->public.sipe_private = sipe_private; conn->public.host = g_strdup(host); conn->public.port = port; conn->host_port = host_port; conn->use_tls = use_tls; g_hash_table_insert(http->connections, host_port, conn); host_port = NULL; /* conn_private takes ownership of the key */ } if (!conn->connection) { sipe_connect_setup setup = { use_tls ? SIPE_TRANSPORT_TLS : SIPE_TRANSPORT_TCP, host, port, conn, sipe_http_transport_connected, sipe_http_transport_input, sipe_http_transport_error }; conn->public.connected = FALSE; conn->connection = sipe_backend_transport_connect(SIPE_CORE_PUBLIC, &setup); } } g_free(host_port); g_free(host); return(SIPE_HTTP_CONNECTION_PUBLIC); } void sipe_http_transport_send(struct sipe_http_connection_public *conn_public, const gchar *header, const gchar *body) { struct sipe_http_connection *conn = SIPE_HTTP_CONNECTION_PRIVATE; GString *message = g_string_new(header); g_string_append_printf(message, "\r\n%s", body ? body : ""); sipe_utils_message_debug(conn->connection, "HTTP", message->str, NULL, TRUE); sipe_backend_transport_message(conn->connection, message->str); g_string_free(message, TRUE); sipe_http_transport_update_timeout_queue(conn, FALSE); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-http-transport.h ================================================ /** * @file sipe-http-transport.h * * pidgin-sipe * * Copyright (C) 2013 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Private interface between HTTP Request <-> Transport layers */ #ifndef _SIPE_HTTP_PRIVATE_IF_TRANSPORT #error "you are not allowed to include sipe-http-transport.h!" #endif /* * Interface dependencies: * * */ /* Forward declarations */ struct sipe_core_private; struct sip_sec_context; struct sipe_http_connection_public { struct sipe_core_private *sipe_private; GSList *pending_requests; /* handled by sipe-http-request.c */ struct sip_sec_context *context; /* handled by sipe-http-request.c */ gchar *cached_authorization; /* handled by sipe-http-request.c */ gchar *host; guint32 port; gboolean connected; }; /** * Check if we're shutting down the HTTP stack * * @param sipe_private SIPE core private data * * @return return @c TRUE if HTTP stack is shutting down */ gboolean sipe_http_shutting_down(struct sipe_core_private *sipe_private); /** * Initiate HTTP connection * * If a connection to this host/port already exists it will be reused. * * @param sipe_private SIPE core private data * @param host name of the host to connect to * @param port port number to connect to * @param use_tls use TLS if @c TRUE, otherwise TCP * * @return HTTP connection public data */ struct sipe_http_connection_public *sipe_http_transport_new(struct sipe_core_private *sipe_private, const gchar *host, guint32 port, gboolean use_tls); /** * Send HTTP request * * @param conn_public HTTP connection public data * @param header HTTP header * @param body HTTP body (may be @c NULL) */ void sipe_http_transport_send(struct sipe_http_connection_public *conn_public, const gchar *header, const gchar *body); ================================================ FILE: src/core/sipe-http.c ================================================ /** * @file sipe-http.c * * pidgin-sipe * * Copyright (C) 2013 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * SIPE HTTP API implementation * * - convenience functions for public API: GET & POST requests * - URL parsing * - all other public API functions are implemented by lower layers */ #include #include "sipe-backend.h" #include "sipe-http.h" #define _SIPE_HTTP_PRIVATE_IF_REQUEST #include "sipe-http-request.h" void sipe_http_parsed_uri_free(struct sipe_http_parsed_uri *parsed_uri) { if (parsed_uri) { g_free(parsed_uri->host); g_free(parsed_uri->path); g_free(parsed_uri); } } struct sipe_http_parsed_uri *sipe_http_parse_uri(const gchar *uri) { struct sipe_http_parsed_uri *parsed_uri = NULL; guint offset = 0; gboolean tls = FALSE; // SIPE_DEBUG_INFO("sipe_http_parse_uri: '%s'", uri); if (g_str_has_prefix(uri, "https://")) { offset = 8; tls = TRUE; } else if (g_str_has_prefix(uri, "http://")) { offset = 7; } if (offset) { gchar **hostport_path = g_strsplit(uri + offset, "/", 2); if (hostport_path && hostport_path[0] && hostport_path[1]) { gchar **host_port = g_strsplit(hostport_path[0], ":", 2); // SIPE_DEBUG_INFO("sipe_http_parse_uri: hostport '%s' path '%s'", hostport_path[0], hostport_path[1]); /* ":port" is optional */ if (host_port && host_port[0]) { parsed_uri = g_new0(struct sipe_http_parsed_uri, 1); parsed_uri->host = g_strdup(host_port[0]); parsed_uri->path = g_strdup(hostport_path[1]); parsed_uri->tls = tls; if (host_port[1]) parsed_uri->port = g_ascii_strtoull(host_port[1], NULL, 10); if (parsed_uri->port == 0) { if (tls) /* default port for https */ parsed_uri->port = 443; else /* default port for http */ parsed_uri->port = 80; } SIPE_DEBUG_INFO("sipe_http_parse_uri: host '%s' port %d path '%s'", parsed_uri->host, parsed_uri->port, parsed_uri->path); } g_strfreev(host_port); } g_strfreev(hostport_path); } if (!parsed_uri) SIPE_DEBUG_ERROR("sipe_http_parse_uri: FAILED '%s'", uri); return(parsed_uri); } struct sipe_http_request *sipe_http_request_get(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *headers, sipe_http_response_callback *callback, gpointer callback_data) { struct sipe_http_request *req; struct sipe_http_parsed_uri *parsed_uri = sipe_http_parse_uri(uri); req = sipe_http_request_new(sipe_private, parsed_uri, headers, NULL, NULL, callback, callback_data); sipe_http_parsed_uri_free(parsed_uri); return(req); } struct sipe_http_request *sipe_http_request_post(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *headers, const gchar *body, const gchar *content_type, sipe_http_response_callback *callback, gpointer callback_data) { struct sipe_http_request *req; struct sipe_http_parsed_uri *parsed_uri = sipe_http_parse_uri(uri); req = sipe_http_request_new(sipe_private, parsed_uri, headers, body, content_type, callback, callback_data); sipe_http_parsed_uri_free(parsed_uri); return(req); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-http.h ================================================ /** * @file sipe-http.h * * pidgin-sipe * * Copyright (C) 2013-2015 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Public interface to HTTP request service */ /* * Interface dependencies: * * */ /* Forward declarations */ struct sipe_core_private; struct sipe_http_request; struct sipe_http_session; /** * HTTP response callback * * @param sipe_private SIPE core private data * @param status status code * @param headers response headers (@c NULL if request aborted) * @param body response body (@c NULL if request aborted) * @param callback_data callback data */ typedef void (sipe_http_response_callback)(struct sipe_core_private *sipe_private, guint status, GSList *headers, const gchar *body, gpointer callback_data); /* HTTP response status codes */ #define SIPE_HTTP_STATUS_FAILED 0 /* internal use */ #define SIPE_HTTP_STATUS_OK 200 #define SIPE_HTTP_STATUS_REDIRECTION 300 /* - 399 */ #define SIPE_HTTP_STATUS_CLIENT_ERROR 400 /* - 499 */ #define SIPE_HTTP_STATUS_CLIENT_UNAUTHORIZED 401 #define SIPE_HTTP_STATUS_CLIENT_FORBIDDEN 403 #define SIPE_HTTP_STATUS_CLIENT_PROXY_AUTH 407 #define SIPE_HTTP_STATUS_SERVER_ERROR 500 /* - 599 */ #define SIPE_HTTP_STATUS_CANCELLED -2 /* internal use */ #define SIPE_HTTP_STATUS_ABORTED -1 /* internal use */ /** * Free HTTP data * * @param sipe_private SIPE core private data */ void sipe_http_free(struct sipe_core_private *sipe_private); /** * Start HTTP session * * @return pointer to opaque HTTP session data structure */ struct sipe_http_session *sipe_http_session_start(void); /** * Close HTTP session * * @param session pointer to opaque HTTP session data structure */ void sipe_http_session_close(struct sipe_http_session *session); /** * Create HTTP GET request * * @param sipe_private SIPE core private data * @param uri URI * @param headers additional headers (may be @c NULL) * @param callback callback function * @param callback_data callback data * * @return pointer to opaque HTTP request data structure (@c NULL if failed) */ struct sipe_http_request *sipe_http_request_get(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *headers, sipe_http_response_callback *callback, gpointer callback_data); /** * Create HTTP POST request * * @param sipe_private SIPE core private data * @param uri URI * @param headers additional headers (may be @c NULL) * @param body body contents * @param content_type body content type * @param callback callback function * @param callback_data callback data * * @return pointer to opaque HTTP request data structure (@c NULL if failed) */ struct sipe_http_request *sipe_http_request_post(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *headers, const gchar *body, const gchar *content_type, sipe_http_response_callback *callback, gpointer callback_data); /** * HTTP request is ready to be sent * * @param request pointer to opaque HTTP request data structure */ void sipe_http_request_ready(struct sipe_http_request *request); /** * Cancel pending HTTP request * * @param request pointer to opaque HTTP request data structure */ void sipe_http_request_cancel(struct sipe_http_request *request); /** * Assign request to HTTP session * * @param request pointer to opaque HTTP request data structure * @param session pointer to opaque HTTP session data structure */ void sipe_http_request_session(struct sipe_http_request *request, struct sipe_http_session *session); /** * Allow redirection of HTTP request * * @param request pointer to opaque HTTP request data structure */ void sipe_http_request_allow_redirect(struct sipe_http_request *request); /** * Provide authentication information for HTTP request * * @param request pointer to opaque HTTP request data structure * @param user user name (MUST stay valid for duration of request!) * @param password Password (MUST stay valid for duration of request!) */ void sipe_http_request_authentication(struct sipe_http_request *request, const gchar *user, const gchar *password); ================================================ FILE: src/core/sipe-im.c ================================================ /** * @file sipe-im.c * * pidgin-sipe * * Copyright (C) 2011-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sipe-common.h" #include "sipmsg.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-chat.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-dialog.h" #include "sipe-ft.h" #include "sipe-groupchat.h" #include "sipe-im.h" #include "sipe-incoming.h" #include "sipe-nls.h" #include "sipe-session.h" #include "sipe-user.h" #include "sipe-utils.h" #include "sipe-xml.h" /* * Hash key template for unconfirmed messages * * Call-ID Recipient URI (or empty) * | | * | SIP method | CSeq * | | | | */ #define UNCONFIRMED_KEY_TEMPLATE(method, cseq) "<%s><" method "><%s><" cseq /* key must be g_free()'d */ static gchar *get_unconfirmed_message_key(const gchar *callid, unsigned int cseq, const gchar *with) { return(g_strdup_printf(UNCONFIRMED_KEY_TEMPLATE("%s", "%d>"), callid, with ? "MESSAGE" : "INVITE", with ? with : "", cseq)); } static void insert_unconfirmed_message(struct sip_session *session, struct sip_dialog *dialog, const gchar *with, const gchar *body, const gchar *content_type) { gchar *key = get_unconfirmed_message_key(dialog->callid, dialog->cseq + 1, with); struct queued_message *message = g_new0(struct queued_message, 1); message->body = g_strdup(body); if (content_type != NULL) message->content_type = g_strdup(content_type); message->cseq = dialog->cseq + 1; g_hash_table_insert(session->unconfirmed_messages, key, message); SIPE_DEBUG_INFO("insert_unconfirmed_message: added %s to list (count=%d)", key, g_hash_table_size(session->unconfirmed_messages)); } static gboolean remove_unconfirmed_message(struct sip_session *session, const gchar *key) { gboolean found = g_hash_table_remove(session->unconfirmed_messages, key); if (found) { SIPE_DEBUG_INFO("remove_unconfirmed_message: removed %s from list (count=%d)", key, g_hash_table_size(session->unconfirmed_messages)); } else { SIPE_DEBUG_INFO("remove_unconfirmed_message: key %s not found", key); } return(found); } static void sipe_refer_notify(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *who, int status, const gchar *desc) { gchar *hdr; gchar *body; struct sip_dialog *dialog = sipe_dialog_find(session, who); hdr = g_strdup_printf( "Event: refer\r\n" "Subscription-State: %s\r\n" "Content-Type: message/sipfrag\r\n", status >= 200 ? "terminated" : "active"); body = g_strdup_printf( "SIP/2.0 %d %s\r\n", status, desc); sip_transport_request(sipe_private, "NOTIFY", who, who, hdr, body, dialog, NULL); g_free(hdr); g_free(body); } static gboolean process_invite_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans) { gchar *with = sipmsg_parse_to_address(msg); struct sip_session *session; struct sip_dialog *dialog; gchar *key; struct queued_message *message; struct sipmsg *request_msg = trans->msg; const gchar *callid = sipmsg_find_call_id_header(msg); gchar *referred_by; session = sipe_session_find_chat_or_im(sipe_private, callid, with); if (!session) { SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: unable to find IM session"); g_free(with); return FALSE; } dialog = sipe_dialog_find(session, with); if (!dialog) { SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: session outgoing dialog is NULL"); g_free(with); return FALSE; } sipe_dialog_parse(dialog, msg, TRUE); key = get_unconfirmed_message_key(dialog->callid, sipmsg_parse_cseq(msg), NULL); message = g_hash_table_lookup(session->unconfirmed_messages, key); if (msg->response != 200) { gchar *alias = sipe_buddy_get_alias(sipe_private, with); int warning = sipmsg_parse_warning(msg, NULL); SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: INVITE response not 200"); /* cancel file transfer as rejected by server */ if (msg->response == 606 && /* Not acceptable all. */ warning == 309 && /* Message contents not allowed by policy */ message && g_str_has_prefix(message->content_type, "text/x-msmsgsinvite")) { GSList *parsed_body = sipe_ft_parse_msg_body(message->body); sipe_ft_incoming_cancel(dialog, parsed_body); sipe_utils_nameval_free(parsed_body); } if (message) { /* generate error for each unprocessed message */ GSList *entry = session->outgoing_message_queue; while (entry) { struct queued_message *queued = entry->data; sipe_user_present_message_undelivered(sipe_private, session, msg->response, warning, alias ? alias : with, queued->body); entry = sipe_session_dequeue_message(session); } } else { /* generate one error and remove all unprocessed messages */ gchar *tmp_msg = g_strdup_printf(_("Failed to invite %s"), alias ? alias : with); sipe_user_present_error(sipe_private, session, tmp_msg); g_free(tmp_msg); while (sipe_session_dequeue_message(session)); } g_free(alias); remove_unconfirmed_message(session, key); /* message is no longer valid */ g_free(key); sipe_dialog_remove(session, with); g_free(with); if (session->is_groupchat) { sipe_groupchat_invite_failed(sipe_private, session); /* session is no longer valid */ } return FALSE; } dialog->cseq = 0; sip_transport_ack(sipe_private, dialog); dialog->outgoing_invite = NULL; dialog->is_established = TRUE; referred_by = sipmsg_parse_address_from_header(request_msg, "Referred-By"); if (referred_by) { sipe_refer_notify(sipe_private, session, referred_by, 200, "OK"); g_free(referred_by); } /* add user to chat if it is a multiparty session */ if (session->chat_session && (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY)) { sipe_backend_chat_add(session->chat_session->backend, with, TRUE); } if (session->is_groupchat) { sipe_groupchat_invite_response(sipe_private, dialog, msg); } if(g_slist_find_custom(dialog->supported, "ms-text-format", (GCompareFunc)g_ascii_strcasecmp)) { SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: remote system accepted message in INVITE"); sipe_session_dequeue_message(session); } sipe_im_process_queue(sipe_private, session); remove_unconfirmed_message(session, key); g_free(key); g_free(with); return TRUE; } /* EndPoints: "alice alisson" , ;epid=ebca82d94d, */ static gchar *get_end_points(struct sipe_core_private *sipe_private, struct sip_session *session) { gchar *res; if (session == NULL) { return NULL; } res = g_strdup_printf("", sipe_private->username); SIPE_DIALOG_FOREACH { gchar *tmp = res; res = g_strdup_printf("%s, <%s>", res, dialog->with); g_free(tmp); if (dialog->theirepid) { tmp = res; res = g_strdup_printf("%s;epid=%s", res, dialog->theirepid); g_free(tmp); } } SIPE_DIALOG_FOREACH_END; return res; } void sipe_im_invite(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *who, const gchar *msg_body, const gchar *content_type, const gchar *referred_by, const gboolean is_triggered) { gchar *hdr; gchar *to; gchar *contact; gchar *body; gchar *self; char *ms_text_format = NULL; char *ms_conversation_id = NULL; gchar *roster_manager; gchar *end_points; gchar *referred_by_str; gboolean is_multiparty = session->chat_session && (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY); struct sip_dialog *dialog = sipe_dialog_find(session, who); if (dialog && dialog->is_established) { SIPE_DEBUG_INFO("session with %s already has a dialog open", who); return; } if (!dialog) { dialog = sipe_dialog_add(session); dialog->callid = session->callid ? g_strdup(session->callid) : gencallid(); dialog->with = g_strdup(who); } if (!(dialog->ourtag)) { dialog->ourtag = gentag(); } to = sip_uri(who); if (msg_body) { char *msgtext = NULL; char *base64_msg; const gchar *msgr = ""; gchar *tmp = NULL; if (!g_str_has_prefix(content_type, "text/x-msmsgsinvite")) { char *msgformat = NULL; gchar *msgr_value; sipe_parse_html(msg_body, &msgformat, &msgtext); SIPE_DEBUG_INFO("sipe_invite: msgformat=%s", msgformat); msgr_value = sipmsg_get_msgr_string(msgformat); g_free(msgformat); if (msgr_value) { msgr = tmp = g_strdup_printf(";msgr=%s", msgr_value); g_free(msgr_value); } /* When Sipe reconnects after a crash, we are not able * to send messages to contacts with which we had open * conversations when the crash occured. Server sends * error response with reason="This client has an IM * session with the same conversation ID" * * Setting random Ms-Conversation-ID prevents this problem * so we can continue the conversation. */ ms_conversation_id = g_strdup_printf("Ms-Conversation-ID: %u\r\n", rand() % 1000000000); } else { msgtext = g_strdup(msg_body); } base64_msg = g_base64_encode((guchar*) msgtext, strlen(msgtext)); ms_text_format = g_strdup_printf("ms-text-format: %s; charset=UTF-8%s;ms-body=%s\r\n", content_type ? content_type : "text/plain", msgr, base64_msg); g_free(msgtext); g_free(tmp); g_free(base64_msg); insert_unconfirmed_message(session, dialog, NULL, msg_body, content_type); } contact = get_contact(sipe_private); end_points = get_end_points(sipe_private, session); self = sip_uri_self(sipe_private); roster_manager = g_strdup_printf( "Roster-Manager: %s\r\n" "EndPoints: %s\r\n", self, end_points); referred_by_str = referred_by ? g_strdup_printf( "Referred-By: %s\r\n", referred_by) : g_strdup(""); hdr = g_strdup_printf( "Supported: ms-sender\r\n" "%s" "%s" "%s" "%s" "Contact: %s\r\n%s" "%s" "Content-Type: application/sdp\r\n", is_multiparty && sipe_strcase_equal(session->chat_session->id, self) ? roster_manager : "", referred_by_str, is_triggered ? "TriggeredInvite: TRUE\r\n" : "", is_triggered || is_multiparty ? "Require: com.microsoft.rtc-multiparty\r\n" : "", contact, ms_text_format ? ms_text_format : "", ms_conversation_id ? ms_conversation_id : ""); g_free(ms_text_format); g_free(ms_conversation_id); g_free(self); body = g_strdup_printf( "v=0\r\n" "o=- 0 0 IN %s %s\r\n" "s=session\r\n" "c=IN %s %s\r\n" "t=0 0\r\n" "m=%s %d sip null\r\n" "a=accept-types:" SDP_ACCEPT_TYPES "\r\n", sip_transport_sdp_address_marker(sipe_private), sip_transport_ip_address(sipe_private), sip_transport_sdp_address_marker(sipe_private), sip_transport_ip_address(sipe_private), SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message", sip_transport_port(sipe_private)); dialog->outgoing_invite = sip_transport_request(sipe_private, "INVITE", to, to, hdr, body, dialog, process_invite_response); g_free(to); g_free(roster_manager); g_free(end_points); g_free(referred_by_str); g_free(body); g_free(hdr); g_free(contact); } static gboolean process_message_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { gboolean ret = TRUE; gchar *with = sipmsg_parse_to_address(msg); const gchar *callid = sipmsg_find_call_id_header(msg); struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, callid, with); struct sip_dialog *dialog; gchar *key; struct queued_message *message; if (!session) { SIPE_DEBUG_INFO_NOFORMAT("process_message_response: unable to find IM session"); g_free(with); return FALSE; } dialog = sipe_dialog_find(session, with); if (!dialog) { SIPE_DEBUG_INFO_NOFORMAT("process_message_response: session outgoing dialog is NULL"); g_free(with); return FALSE; } key = get_unconfirmed_message_key(sipmsg_find_call_id_header(msg), sipmsg_parse_cseq(msg), with); message = g_hash_table_lookup(session->unconfirmed_messages, key); if (msg->response >= 400) { int warning = sipmsg_parse_warning(msg, NULL); SIPE_DEBUG_INFO_NOFORMAT("process_message_response: MESSAGE response >= 400"); /* cancel file transfer as rejected by server */ if (msg->response == 606 && /* Not acceptable all. */ warning == 309 && /* Message contents not allowed by policy */ message && g_str_has_prefix(message->content_type, "text/x-msmsgsinvite")) { GSList *parsed_body = sipe_ft_parse_msg_body(msg->body); sipe_ft_incoming_cancel(dialog, parsed_body); sipe_utils_nameval_free(parsed_body); } /* drop dangling IM sessions: assume that BYE from remote never reached us */ if (msg->response == 408 || /* Request timeout */ msg->response == 480 || /* Temporarily Unavailable */ msg->response == 481) { /* Call/Transaction Does Not Exist */ sipe_im_cancel_dangling(sipe_private, session, dialog, with, sipe_im_cancel_unconfirmed); /* dialog is no longer valid */ } else { gchar *alias = sipe_buddy_get_alias(sipe_private, with); sipe_user_present_message_undelivered(sipe_private, session, msg->response, warning, alias ? alias : with, message ? message->body : NULL); remove_unconfirmed_message(session, key); /* message is no longer valid */ g_free(alias); } ret = FALSE; } else { const gchar *message_id = sipmsg_find_header(msg, "Message-Id"); if (message_id) { g_hash_table_insert(session->conf_unconfirmed_messages, g_strdup(message_id), g_strdup(message->body)); SIPE_DEBUG_INFO("process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)", message_id, g_hash_table_size(session->conf_unconfirmed_messages)); } remove_unconfirmed_message(session, key); } g_free(key); g_free(with); if (ret) sipe_im_process_queue(sipe_private, session); return ret; } #ifndef ENABLE_OCS2005_MESSAGE_HACK /* * Hack to circumvent problems reported in the bug report * * #3267073 - False "could not be delivered" errors * * The logs provided by the reporters indicate that OCS2005 clients DO NOT * acknowledge our SIP MESSAGEs. Therefore the message timeout is triggered * and messages are reported to the user as not delivered. * * Either this is a bug in the OCS2005 client or we do something wrong in our * SIP MESSAGEs. This hack removes the message timeout and is provided for * users who need to communicate with a still existing OCS2005 user base. * * Do not enable it by default! */ static gboolean process_message_timeout(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { gchar *with = sipmsg_parse_to_address(msg); const gchar *callid = sipmsg_find_call_id_header(msg); struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, callid, with); gchar *key; gboolean found; if (!session) { SIPE_DEBUG_INFO_NOFORMAT("process_message_timeout: unable to find IM session"); g_free(with); return TRUE; } /* Remove timed-out message from unconfirmed list */ key = get_unconfirmed_message_key(sipmsg_find_call_id_header(msg), sipmsg_parse_cseq(msg), with); found = remove_unconfirmed_message(session, key); g_free(key); if (found) { gchar *alias = sipe_buddy_get_alias(sipe_private, with); sipe_user_present_message_undelivered(sipe_private, session, -1, -1, alias ? alias : with, msg->body); g_free(alias); } g_free(with); return TRUE; } #endif static void sipe_im_send_message(struct sipe_core_private *sipe_private, struct sip_dialog *dialog, const gchar *msg_body, const gchar *content_type) { gchar *hdr; gchar *tmp; char *msgtext = NULL; const gchar *msgr = ""; gchar *tmp2 = NULL; if (content_type == NULL) content_type = "text/plain"; if (!g_str_has_prefix(content_type, "text/x-msmsgsinvite")) { char *msgformat = NULL; gchar *msgr_value; sipe_parse_html(msg_body, &msgformat, &msgtext); SIPE_DEBUG_INFO("sipe_send_message: msgformat=%s", msgformat); msgr_value = sipmsg_get_msgr_string(msgformat); g_free(msgformat); if (msgr_value) { msgr = tmp2 = g_strdup_printf(";msgr=%s", msgr_value); g_free(msgr_value); } } else { msgtext = g_strdup(msg_body); } tmp = get_contact(sipe_private); //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n"); //hdr = g_strdup("Content-Type: text/rtf\r\n"); //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n"); hdr = g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp, content_type, msgr); g_free(tmp); g_free(tmp2); #ifdef ENABLE_OCS2005_MESSAGE_HACK sip_transport_request( #else sip_transport_request_timeout( #endif sipe_private, "MESSAGE", dialog->with, dialog->with, hdr, msgtext, dialog, process_message_response #ifndef ENABLE_OCS2005_MESSAGE_HACK , 60, process_message_timeout #endif ); g_free(msgtext); g_free(hdr); } void sipe_im_process_queue(struct sipe_core_private *sipe_private, struct sip_session *session) { GSList *entry2 = session->outgoing_message_queue; while (entry2) { struct queued_message *msg = entry2->data; /* for multiparty chat or conference */ if (session->chat_session) { gchar *who = sip_uri_self(sipe_private); sipe_backend_chat_message(SIPE_CORE_PUBLIC, session->chat_session->backend, who, 0, msg->body); g_free(who); } SIPE_DIALOG_FOREACH { if (dialog->outgoing_invite) continue; /* do not send messages as INVITE is not responded. */ insert_unconfirmed_message(session, dialog, dialog->with, msg->body, msg->content_type); sipe_im_send_message(sipe_private, dialog, msg->body, msg->content_type); } SIPE_DIALOG_FOREACH_END; entry2 = sipe_session_dequeue_message(session); } } struct unconfirmed_callback_data { const gchar *prefix; GSList *list; }; struct unconfirmed_message { const gchar *key; const struct queued_message *msg; }; static gint compare_cseq(gconstpointer a, gconstpointer b) { return(((struct unconfirmed_message *) a)->msg->cseq - ((struct unconfirmed_message *) b)->msg->cseq); } static void unconfirmed_message_callback(gpointer key, gpointer value, gpointer user_data) { const gchar *message_key = key; struct unconfirmed_callback_data *data = user_data; SIPE_DEBUG_INFO("unconfirmed_message_callback: key %s", message_key); /* Put messages with the same prefix on a list sorted by CSeq */ if (g_str_has_prefix(message_key, data->prefix)) { struct unconfirmed_message *msg = g_malloc(sizeof(struct unconfirmed_message)); msg->key = message_key; msg->msg = value; data->list = g_slist_insert_sorted(data->list, msg, compare_cseq); } } static void foreach_unconfirmed_message(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *callid, const gchar *with, unconfirmed_callback callback, const gchar *callback_data) { gchar *prefix = g_strdup_printf(UNCONFIRMED_KEY_TEMPLATE("MESSAGE", ""), callid, with); struct unconfirmed_callback_data data = { prefix, NULL }; SIPE_DEBUG_INFO("foreach_unconfirmed_message: prefix %s", prefix); /* Generate list of matching unconfirmed messages */ g_hash_table_foreach(session->unconfirmed_messages, unconfirmed_message_callback, &data); g_free(prefix); /* Process list unconfirmed messages */ if (data.list) { GSList *entry; while ((entry = data.list) != NULL) { struct unconfirmed_message *unconfirmed = entry->data; data.list = g_slist_remove(data.list, unconfirmed); SIPE_DEBUG_INFO("foreach_unconfirmed_message: %s", unconfirmed->key); (*callback)(sipe_private, session, unconfirmed->msg->body, callback_data); g_hash_table_remove(session->unconfirmed_messages, unconfirmed->key); g_free(unconfirmed); } } } static void cancel_callback(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *body, const gchar *with) { sipe_user_present_message_undelivered(sipe_private, session, -1, -1, with, body); } void sipe_im_cancel_unconfirmed(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *callid, const gchar *with) { gchar *alias = sipe_buddy_get_alias(sipe_private, with); SIPE_DEBUG_INFO("sipe_im_cancel_unconfirmed: with %s callid '%s'", with, callid); foreach_unconfirmed_message(sipe_private, session, callid, with, cancel_callback, alias ? alias : with); g_free(alias); } static void reenqueue_callback(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *body, SIPE_UNUSED_PARAMETER const gchar *with) { sipe_session_enqueue_message(session, body, NULL); } void sipe_im_reenqueue_unconfirmed(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *callid, const gchar *with) { /* Remember original list, start with an empty list */ GSList *first = session->outgoing_message_queue; session->outgoing_message_queue = NULL; SIPE_DEBUG_INFO("sipe_im_reenqueue_unconfirmed: with %s callid '%s'", with, callid); /* Enqueue unconfirmed messages */ foreach_unconfirmed_message(sipe_private, session, callid, with, reenqueue_callback, NULL); /* Append or restore original list */ if (session->outgoing_message_queue) { GSList *last = g_slist_last(session->outgoing_message_queue); last->next = first; } else { session->outgoing_message_queue = first; } } void sipe_core_im_send(struct sipe_core_public *sipe_public, const gchar *who, const gchar *what) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sip_session *session; struct sip_dialog *dialog; gchar *uri = sip_uri(who); SIPE_DEBUG_INFO("sipe_core_im_send: '%s'", what); session = sipe_session_find_or_add_im(sipe_private, uri); dialog = sipe_dialog_find(session, uri); /* Queue the message */ sipe_session_enqueue_message(session, what, NULL); if (dialog && !dialog->outgoing_invite) { if (dialog->delayed_invite) sipe_incoming_cancel_delayed_invite(sipe_private, dialog); sipe_im_process_queue(sipe_private, session); } else if (!dialog || !dialog->outgoing_invite) { /* Need to send the INVITE to get the outgoing dialog setup */ sipe_im_invite(sipe_private, session, uri, what, NULL, NULL, FALSE); } g_free(uri); } void sipe_core_im_close(struct sipe_core_public *sipe_public, const gchar *who) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; SIPE_DEBUG_INFO("sipe_core_im_close: conversation with %s closed", who); sipe_session_close(sipe_private, sipe_session_find_im(sipe_private, who)); } void sipe_im_cancel_dangling(struct sipe_core_private *sipe_private, struct sip_session *session, struct sip_dialog *dialog, const gchar *with, unconfirmed_callback callback) { SIPE_DEBUG_INFO_NOFORMAT("sipe_im_cancel_dangling: assuming dangling IM session, dropping it."); sip_transport_bye(sipe_private, dialog); (*callback)(sipe_private, session, dialog->callid, with); /* We might not get a valid reply to our BYE, so make sure the dialog is removed for sure. */ sipe_dialog_remove(session, with); /* dialog is no longer valid */ } void sipe_im_topic(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *topic) { g_free(session->subject); session->subject = g_strdup(topic); sipe_backend_im_topic(SIPE_CORE_PUBLIC, session->with, topic); } void process_incoming_info_conversation(struct sipe_core_private *sipe_private, struct sipmsg *msg) { sipe_xml *xml = sipe_xml_parse(msg->body, msg->bodylen); const gchar *from = NULL; gchar *subject = NULL; if (!xml) return; if (sipe_strequal(sipe_xml_name(xml), "ConversationInfo")) { const sipe_xml *node = sipe_xml_child(xml, "From"); if (node) from = sipe_xml_attribute(node, "uri"); node = sipe_xml_child(xml, "Subject"); if (node) subject = sipe_xml_data(node); } if (from && subject) { struct sip_session *session; session = sipe_session_find_im(sipe_private, from); if (session) sipe_im_topic(sipe_private, session, subject); } g_free(subject); sipe_xml_free(xml); sip_transport_response(sipe_private, msg, 200, "OK", NULL); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-im.h ================================================ /** * @file sipe-im.h * * pidgin-sipe * * Copyright (C) 2011 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sip_dialog; struct sip_session; struct sipe_core_private; #ifdef HAVE_GMIME /* pls. don't add multipart/related - it's not used in IM modality */ #define SDP_ACCEPT_TYPES "text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml application/ms-imdn+xml text/x-msmsgsinvite" #else /* this is a rediculous hack as Pidgin's MIME implementastion doesn't support (or have bug) in multipart/alternative */ /* OCS/OC won't use multipart/related so we don't advertase it */ #define SDP_ACCEPT_TYPES "text/plain text/html image/gif application/im-iscomposing+xml application/ms-imdn+xml text/x-msmsgsinvite" #endif /** * Send invitation and initial message to IM session * * @param sipe_private SIPE core private data * @param session session for the IM conversation(s) * @param who URI of the invitee * @param msg_body message body or NULL * @param content_type message body MIME type * @param referred_by value for Referred-By or NULL * @param is_triggered triggered invite or not */ void sipe_im_invite(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *who, const gchar *msg_body, const gchar *content_type, const gchar *referred_by, const gboolean is_triggered); /** * Process queue IM messages * * @param sipe_private SIPE core private data * @param session session for the IM conversation(s) */ void sipe_im_process_queue(struct sipe_core_private *sipe_private, struct sip_session *session); /** * Cancel unconfirmed IM messages * * @param sipe_private SIPE core private data * @param session session for the IM conversation(s) * @param callid Call ID of the conversation * @param with URI of the remote party */ void sipe_im_cancel_unconfirmed(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *callid, const gchar *with); /** * Re-enqueue unconfirmed IM messages * * @param sipe_private SIPE core private data * @param session session for the IM conversation(s) * @param callid Call ID of the conversation * @param with URI of the remote party */ void sipe_im_reenqueue_unconfirmed(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *callid, const gchar *with); typedef void (*unconfirmed_callback)(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *callid, const gchar *with); /** * Close dangling IM session * * @param sipe_private (in) SIPE core data. * @param session (in) pointer to session * @param dialog (in) pointer to dialog * @param with (in) URI of dialog partner * @param callback (in) callback for unconfirmed message */ void sipe_im_cancel_dangling(struct sipe_core_private *sipe_private, struct sip_session *session, struct sip_dialog *dialog, const gchar *with, unconfirmed_callback callback); /** * Sets a topic for IM conversation * * @param sipe_private (in) SIPE core data * @param session (in) pointer to session * @param topic (in) string describing conversation topic */ void sipe_im_topic(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *topic); /** * Processes INFO message with application/xml+conversationinfo content type * * @param sipe_private (in) SIPE core data * @param msg (in) SIP INFO message */ void process_incoming_info_conversation(struct sipe_core_private *sipe_private, struct sipmsg *msg); ================================================ FILE: src/core/sipe-incoming.c ================================================ /** * @file sipe-incoming.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sipmsg.h" #include "sip-csta.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-chat.h" #include "sipe-conf.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-appshare.h" #include "sipe-dialog.h" #include "sipe-ft.h" #include "sipe-ft-lync.h" #include "sipe-groupchat.h" #include "sipe-im.h" #include "sipe-incoming.h" #include "sipe-media.h" #include "sipe-mime.h" #include "sipe-nls.h" #include "sipe-schedule.h" #include "sipe-session.h" #include "sipe-user.h" #include "sipe-utils.h" #include "sipe-xml.h" void process_incoming_bye(struct sipe_core_private *sipe_private, struct sipmsg *msg) { const gchar *callid = sipmsg_find_call_id_header(msg); gchar *from = sipmsg_parse_from_address(msg); struct sip_session *session; struct sip_dialog *dialog; #ifdef HAVE_VV struct sipe_media_call_private *call_private = g_hash_table_lookup(sipe_private->media_calls, callid); if (is_media_session_msg(call_private, msg)) { // BYE ends a media call sipe_media_hangup(call_private); } #endif /* collect dialog identification * we need callid, ourtag and theirtag to unambiguously identify dialog */ /* take data before 'msg' will be modified by sip_transport_response */ dialog = g_new0(struct sip_dialog, 1); dialog->callid = g_strdup(callid); dialog->cseq = sipmsg_parse_cseq(msg); dialog->with = g_strdup(from); sipe_dialog_parse(dialog, msg, FALSE); sip_transport_response(sipe_private, msg, 200, "OK", NULL); session = sipe_session_find_chat_or_im(sipe_private, callid, from); if (!session) { SIPE_DEBUG_INFO_NOFORMAT("process_incoming_bye: couldn't find session. Ignoring"); sipe_dialog_free(dialog); g_free(from); return; } SIPE_DEBUG_INFO("process_incoming_bye: session found (chat ID %s)", (session->chat_session && session->chat_session->id) ? session->chat_session->id : ""); if (session->chat_session && (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY) && session->chat_session->id && !g_ascii_strcasecmp(from, session->chat_session->id)) sipe_chat_set_roster_manager(session, NULL); sipe_im_cancel_unconfirmed(sipe_private, session, callid, from); /* This what BYE is essentially for - terminating dialog */ sipe_dialog_remove_3(session, dialog); sipe_dialog_free(dialog); if (session->chat_session) { if ((session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) && !g_ascii_strcasecmp(from, session->im_mcu_uri)) { SIPE_DEBUG_INFO("process_incoming_bye: disconnected from conference %s", session->im_mcu_uri); sipe_conf_immcu_closed(sipe_private, session); } else if (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY) { SIPE_DEBUG_INFO_NOFORMAT("process_incoming_bye: disconnected from multiparty chat"); sipe_backend_chat_remove(session->chat_session->backend, from); } } g_free(from); } void process_incoming_cancel(struct sipe_core_private *sipe_private, struct sipmsg *msg) { const gchar *callid = sipmsg_find_call_id_header(msg); #ifdef HAVE_VV struct sipe_media_call_private *call_private = g_hash_table_lookup(sipe_private->media_calls, callid); if (is_media_session_msg(call_private, msg)) { process_incoming_cancel_call(call_private, msg); return; } #endif if (!sipe_session_find_chat_by_callid(sipe_private, callid)) sipe_conf_cancel_unaccepted(sipe_private, msg); } void process_incoming_info(struct sipe_core_private *sipe_private, struct sipmsg *msg) { const gchar *contenttype = sipmsg_find_content_type_header(msg); const gchar *callid = sipmsg_find_call_id_header(msg); gchar *from; struct sip_session *session; SIPE_DEBUG_INFO_NOFORMAT("process_incoming_info"); /* Call Control protocol */ if (g_str_has_prefix(contenttype, "application/csta+xml")) { process_incoming_info_csta(sipe_private, msg); return; } else if (g_str_has_prefix(contenttype, "application/xml+conversationinfo")) { process_incoming_info_conversation(sipe_private, msg); return; } #ifdef HAVE_XDATA else if (g_str_has_prefix(contenttype, "application/ms-filetransfer+xml")) { process_incoming_info_ft_lync(sipe_private, msg); return; } #endif from = sipmsg_parse_from_address(msg); session = sipe_session_find_chat_or_im(sipe_private, callid, from); if (!session) { g_free(from); return; } /* Group Chat uses text/plain */ if (session->is_groupchat) { process_incoming_info_groupchat(sipe_private, msg, session); g_free(from); return; } if (g_str_has_prefix(contenttype, "application/x-ms-mim")) { sipe_xml *xn_action = sipe_xml_parse(msg->body, msg->bodylen); const sipe_xml *xn_request_rm = sipe_xml_child(xn_action, "RequestRM"); const sipe_xml *xn_set_rm = sipe_xml_child(xn_action, "SetRM"); sipmsg_add_header(msg, "Content-Type", "application/x-ms-mim"); if (xn_request_rm) { //const char *rm = sipe_xml_attribute(xn_request_rm, "uri"); int bid = sipe_xml_int_attribute(xn_request_rm, "bid", 0); gchar *body = g_strdup_printf( "\r\n" "" "\r\n", sipe_private->username, session->bid < bid ? "true" : "false"); sip_transport_response(sipe_private, msg, 200, "OK", body); g_free(body); } else if (xn_set_rm) { gchar *body; sipe_chat_set_roster_manager(session, sipe_xml_attribute(xn_set_rm, "uri")); body = g_strdup_printf( "\r\n" "" "\r\n", sipe_private->username); sip_transport_response(sipe_private, msg, 200, "OK", body); g_free(body); } sipe_xml_free(xn_action); } else { /* looks like purple lacks typing notification for chat */ if (!session->chat_session) { sipe_xml *xn_keyboard_activity = sipe_xml_parse(msg->body, msg->bodylen); const char *status = sipe_xml_attribute(sipe_xml_child(xn_keyboard_activity, "status"), "status"); if (sipe_strequal(status, "type")) { sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC, from); } else if (sipe_strequal(status, "idle")) { sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC, from); } sipe_xml_free(xn_keyboard_activity); } sip_transport_response(sipe_private, msg, 200, "OK", NULL); } g_free(from); } static gboolean sipe_process_incoming_x_msmsgsinvite(struct sipe_core_private *sipe_private, struct sip_dialog *dialog, GSList *parsed_body) { gboolean found = FALSE; if (parsed_body) { const gchar *invitation_command = sipe_utils_nameval_find(parsed_body, "Invitation-Command"); if (sipe_strequal(invitation_command, "INVITE")) { sipe_ft_incoming_transfer(sipe_private, dialog, parsed_body); found = TRUE; } else if (sipe_strequal(invitation_command, "CANCEL")) { sipe_ft_incoming_cancel(dialog, parsed_body); found = TRUE; } else if (sipe_strequal(invitation_command, "ACCEPT")) { sipe_ft_incoming_accept(dialog, parsed_body); found = TRUE; } } return found; } #ifdef HAVE_VV static void sipe_invite_mime_cb(gpointer user_data, const GSList *fields, const gchar *body, gsize length) { struct sipmsg *msg = user_data; const gchar *type; gchar *body_lcase; if (g_str_has_prefix(sipmsg_find_content_type_header(msg), "application/sdp")) { /* We have already found suitable alternative and set message's body * and Content-Type accordingly. */ return; } type = sipe_utils_nameval_find(fields, "Content-Type"); if (!body || !g_str_has_prefix(type, "application/sdp")) return; body_lcase = g_ascii_strdown(body, length); if (strstr(body_lcase, " typ host") || strstr(body_lcase, " typ relay") || strstr(body_lcase, " typ srflx") || strstr(body_lcase, " typ prflx")) { /* RFC 5245 SDP body */ sipmsg_remove_header_now(msg, "Content-Type"); sipmsg_add_header_now(msg, "Content-Type", type); /* Replace message body with chosen alternative, so we can continue to * process it as a normal single part message. */ g_free(msg->body); msg->body = g_strndup(body, length); msg->bodylen = length; } g_free(body_lcase); } #endif static void send_invite_response(struct sipe_core_private *sipe_private, struct sipmsg *msg) { gchar *body = g_strdup_printf( "v=0\r\n" "o=- 0 0 IN %s %s\r\n" "s=session\r\n" "c=IN %s %s\r\n" "t=0 0\r\n" "m=%s %d sip sip:%s\r\n" "a=accept-types:" SDP_ACCEPT_TYPES "\r\n", sip_transport_sdp_address_marker(sipe_private), sip_transport_ip_address(sipe_private), sip_transport_sdp_address_marker(sipe_private), sip_transport_ip_address(sipe_private), SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message", sip_transport_port(sipe_private), sipe_private->username); sipmsg_add_header(msg, "Content-Type", "application/sdp"); sip_transport_response(sipe_private, msg, 200, "OK", body); g_free(body); } struct sipe_delayed_invite { gchar *action; struct sipmsg *msg; }; static void delayed_invite_destroy(gpointer data) { struct sipe_delayed_invite *delayed_invite = data; sipmsg_free(delayed_invite->msg); g_free(delayed_invite->action); g_free(delayed_invite); } static void delayed_invite_timeout(struct sipe_core_private *sipe_private, gpointer data) { struct sipe_delayed_invite *delayed_invite = data; send_invite_response(sipe_private, delayed_invite->msg); } static void delayed_invite_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, const gchar *callid) { struct sipe_delayed_invite *delayed_invite = g_new0(struct sipe_delayed_invite, 1); delayed_invite->action = g_strdup_printf("", callid); delayed_invite->msg = sipmsg_copy(msg); sipe_schedule_seconds(sipe_private, delayed_invite->action, delayed_invite, 10, delayed_invite_timeout, delayed_invite_destroy); } void sipe_incoming_cancel_delayed_invite(struct sipe_core_private *sipe_private, struct sip_dialog *dialog) { struct sipe_delayed_invite *delayed_invite = dialog->delayed_invite; dialog->delayed_invite = NULL; send_invite_response(sipe_private, delayed_invite->msg); sipe_schedule_cancel(sipe_private, delayed_invite->action); } void process_incoming_invite(struct sipe_core_private *sipe_private, struct sipmsg *msg) { gboolean is_multiparty = FALSE; gboolean was_multiparty = TRUE; gboolean just_joined = FALSE; gchar *from; const gchar *callid = sipmsg_find_call_id_header(msg); const gchar *roster_manager = sipmsg_find_header(msg, "Roster-Manager"); const gchar *end_points_hdr = sipmsg_find_header(msg, "EndPoints"); const gchar *trig_invite = sipmsg_find_header(msg, "TriggeredInvite"); const gchar *content_type = sipmsg_find_content_type_header(msg); const gchar *subject = sipmsg_find_header(msg, "Subject"); GSList *end_points = NULL; struct sip_session *session; struct sip_dialog *dialog; const gchar *ms_text_format; gboolean dont_delay = FALSE; #ifdef HAVE_VV if (g_str_has_prefix(content_type, "multipart/alternative")) { sipe_mime_parts_foreach(content_type, msg->body, sipe_invite_mime_cb, msg); /* Reload Content-Type to get type of the selected message part */ content_type = sipmsg_find_content_type_header(msg); } #endif if (g_str_has_prefix(content_type, "multipart/mixed")) { if (sipe_mime_parts_contain(content_type, msg->body, "application/ms-filetransfer+xml")) { /* Lync 2010 file transfer */ #ifdef HAVE_XDATA process_incoming_invite_ft_lync(sipe_private, msg); #else sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL); #endif return; } } /* Invitation to join conference */ if (g_str_has_prefix(content_type, "application/ms-conf-invite+xml")) { process_incoming_invite_conf(sipe_private, msg); return; } #ifdef HAVE_VV /* Application sharing */ if (sipe_strcase_equal(content_type, "application/sdp") && msg->body && strstr(msg->body, "m=applicationsharing") && sipe_strequal(sipmsg_find_cseq_header(msg), "1 INVITE")) { #ifdef HAVE_APPSHARE process_incoming_invite_appshare(sipe_private, msg); #else sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL); #endif return; } /* Invitation to audio call or file transfer */ if (msg->body && (strstr(msg->body, "m=audio") || strstr(msg->body, "m=data") || strstr(msg->body, "m=applicationsharing"))) { process_incoming_invite_call(sipe_private, msg, msg->body); return; } #endif /* Only accept text invitations */ if (msg->body && !(strstr(msg->body, "m=message") || strstr(msg->body, "m=x-ms-message"))) { sip_transport_response(sipe_private, msg, 501, "Not implemented", NULL); return; } // TODO There *must* be a better way to clean up the To header to add a tag... sipmsg_update_to_header_tag(msg); if (end_points_hdr) { end_points = sipmsg_parse_endpoints_header(end_points_hdr); if (g_slist_length(end_points) > 2) { is_multiparty = TRUE; } } if (trig_invite && !g_ascii_strcasecmp(trig_invite, "TRUE")) { is_multiparty = TRUE; } /* Multiparty session */ session = sipe_session_find_chat_by_callid(sipe_private, callid); if (is_multiparty) { if (session) { if (session->chat_session) { /* Update roster manager for existing multiparty session */ if (roster_manager) sipe_chat_set_roster_manager(session, roster_manager); } else { gchar *chat_title = sipe_chat_get_name(); /* Convert IM session to multiparty session */ g_free(session->with); session->with = NULL; was_multiparty = FALSE; session->chat_session = sipe_chat_create_session(SIPE_CHAT_TYPE_MULTIPARTY, roster_manager, chat_title); g_free(chat_title); } } else { /* New multiparty session */ session = sipe_session_add_chat(sipe_private, NULL, TRUE, roster_manager); } /* Create chat */ if (!session->chat_session->backend) { gchar *self = sip_uri_self(sipe_private); session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC, session->chat_session, session->chat_session->title, self); g_free(self); } } /* IM session */ from = sipmsg_parse_from_address(msg); if (!session) session = sipe_session_find_or_add_im(sipe_private, from); /* session is now initialized */ g_free(session->callid); session->callid = g_strdup(callid); if (is_multiparty && end_points) { gchar *to = sipmsg_parse_to_address(msg); GSList *entry = end_points; while (entry) { struct sipendpoint *end_point = entry->data; entry = entry->next; if (!g_ascii_strcasecmp(from, end_point->contact) || !g_ascii_strcasecmp(to, end_point->contact)) continue; dialog = sipe_dialog_find(session, end_point->contact); if (dialog) { g_free(dialog->theirepid); dialog->theirepid = end_point->epid; end_point->epid = NULL; } else { dialog = sipe_dialog_add(session); dialog->callid = g_strdup(session->callid); dialog->with = end_point->contact; end_point->contact = NULL; dialog->theirepid = end_point->epid; end_point->epid = NULL; just_joined = TRUE; /* send triggered INVITE */ sipe_im_invite(sipe_private, session, dialog->with, NULL, NULL, NULL, TRUE); } } g_free(to); } if (end_points) { GSList *entry = end_points; while (entry) { struct sipendpoint *end_point = entry->data; entry = entry->next; g_free(end_point->contact); g_free(end_point->epid); g_free(end_point); } g_slist_free(end_points); } dialog = sipe_dialog_find(session, from); if (dialog) { sipe_im_cancel_dangling(sipe_private, session, dialog, from, sipe_im_reenqueue_unconfirmed); /* dialog is no longer valid */ } else { just_joined = TRUE; } dialog = sipe_dialog_add(session); dialog->with = g_strdup(from); dialog->callid = g_strdup(session->callid); dialog->is_established = TRUE; sipe_dialog_parse(dialog, msg, FALSE); if (is_multiparty && !was_multiparty) { /* add current IM counterparty to chat */ sipe_backend_chat_add(session->chat_session->backend, sipe_dialog_first(session)->with, FALSE); } /* add inviting party to chat */ if (just_joined && session->chat_session) { sipe_backend_chat_add(session->chat_session->backend, from, TRUE); } if (!is_multiparty && subject) sipe_im_topic(sipe_private, session, subject); /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */ /* This used only in 2005 official client, not 2007 or Reuters. Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs. Only enabled for 2005 multiparty chats as otherwise the first message got lost completely. */ /* also enabled for 2005 file transfer. Didn't work otherwise. */ ms_text_format = sipmsg_find_header(msg, "ms-text-format"); if (is_multiparty || (ms_text_format && g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite")) ) { if (ms_text_format) { if (g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite")) { dont_delay = TRUE; } else if (g_str_has_prefix(ms_text_format, "text/plain") || g_str_has_prefix(ms_text_format, "text/html") || g_str_has_prefix(ms_text_format, "text/rtf")) { /* please do not optimize logic inside as this code may be re-enabled for other cases */ gchar *html = get_html_message(ms_text_format, NULL); if (html) { if (is_multiparty) { sipe_backend_chat_message(SIPE_CORE_PUBLIC, session->chat_session->backend, from, 0, html); } else { sipe_backend_im_message(SIPE_CORE_PUBLIC, from, html); } g_free(html); sipmsg_add_header(msg, "Supported", "ms-text-format"); /* accepts received message */ dont_delay = TRUE; } } } } g_free(from); sipmsg_add_header(msg, "Supported", "com.microsoft.rtc-multiparty"); if (dont_delay || !SIPE_CORE_PRIVATE_FLAG_IS(MPOP)) { send_invite_response(sipe_private, msg); } else { delayed_invite_response(sipe_private, msg, session->callid); } } void process_incoming_message(struct sipe_core_private *sipe_private, struct sipmsg *msg) { gchar *from; const gchar *contenttype; gboolean found = FALSE; from = sipmsg_parse_from_address(msg); if (!from) return; SIPE_DEBUG_INFO("got message from %s: %s", from, msg->body); contenttype = sipmsg_find_content_type_header(msg); if (g_str_has_prefix(contenttype, "text/plain") || g_str_has_prefix(contenttype, "text/html") || g_str_has_prefix(contenttype, "text/rtf") || g_str_has_prefix(contenttype, "multipart/related") || g_str_has_prefix(contenttype, "multipart/alternative")) { const gchar *callid = sipmsg_find_call_id_header(msg); gchar *html = get_html_message(contenttype, msg->body); struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, callid, from); if (session && session->chat_session) { if (session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) { /* a conference */ gchar *tmp = sipmsg_parse_address_from_header(msg, "Ms-Sender"); gchar *sender = parse_from(tmp); g_free(tmp); sipe_backend_chat_message(SIPE_CORE_PUBLIC, session->chat_session->backend, sender, 0, html); g_free(sender); } else { /* a multiparty chat */ sipe_backend_chat_message(SIPE_CORE_PUBLIC, session->chat_session->backend, from, 0, html); } } else { sipe_backend_im_message(SIPE_CORE_PUBLIC, from, html); } g_free(html); sip_transport_response(sipe_private, msg, 200, "OK", NULL); found = TRUE; } else if (g_str_has_prefix(contenttype, "application/im-iscomposing+xml")) { sipe_xml *isc = sipe_xml_parse(msg->body, msg->bodylen); const sipe_xml *state; gchar *statedata; if (!isc) { SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: can not parse iscomposing"); g_free(from); return; } state = sipe_xml_child(isc, "state"); if (!state) { SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: no state found"); sipe_xml_free(isc); g_free(from); return; } statedata = sipe_xml_data(state); if (statedata) { if (strstr(statedata, "active")) { sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC, from); } else { sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC, from); } g_free(statedata); } sipe_xml_free(isc); sip_transport_response(sipe_private, msg, 200, "OK", NULL); found = TRUE; } else if (g_str_has_prefix(contenttype, "text/x-msmsgsinvite")) { const gchar *callid = sipmsg_find_call_id_header(msg); struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, callid, from); if (session) { struct sip_dialog *dialog = sipe_dialog_find(session, from); GSList *body = sipe_ft_parse_msg_body(msg->body); found = sipe_process_incoming_x_msmsgsinvite(sipe_private, dialog, body); sipe_utils_nameval_free(body); if (found) { sip_transport_response(sipe_private, msg, 200, "OK", NULL); } } else { sip_transport_response(sipe_private, msg, 481, "Call Leg/Transaction Does Not Exist", NULL); found = TRUE; } } if (!found) { const gchar *callid = sipmsg_find_call_id_header(msg); struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, callid, from); if (session) { gchar *errmsg = g_strdup_printf(_("Received a message with unrecognized contents from %s"), from); sipe_user_present_error(sipe_private, session, errmsg); g_free(errmsg); } SIPE_DEBUG_INFO("got unknown mime-type '%s'", contenttype); sip_transport_response(sipe_private, msg, 415, "Unsupported media type", NULL); } g_free(from); } void process_incoming_options(struct sipe_core_private *sipe_private, struct sipmsg *msg) { gchar *body; sipmsg_add_header(msg, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY"); sipmsg_add_header(msg, "Content-Type", "application/sdp"); body = g_strdup_printf( "v=0\r\n" "o=- 0 0 IN IP4 0.0.0.0\r\n" "s=session\r\n" "c=IN IP4 0.0.0.0\r\n" "t=0 0\r\n" "m=%s %d sip sip:%s\r\n" "a=accept-types:" SDP_ACCEPT_TYPES "\r\n", SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message", sip_transport_port(sipe_private), sipe_private->username); sip_transport_response(sipe_private, msg, 200, "OK", body); g_free(body); } void process_incoming_refer(struct sipe_core_private *sipe_private, struct sipmsg *msg) { gchar *self = sip_uri_self(sipe_private); const gchar *callid = sipmsg_find_call_id_header(msg); gchar *from = sipmsg_parse_from_address(msg); gchar *refer_to = sipmsg_parse_address_from_header(msg, "Refer-to"); gchar *referred_by = g_strdup(sipmsg_find_header(msg, "Referred-By")); struct sip_session *session; struct sip_dialog *dialog; session = sipe_session_find_chat_by_callid(sipe_private, callid); dialog = sipe_dialog_find(session, from); if (!session || !dialog || !session->chat_session || (session->chat_session->type != SIPE_CHAT_TYPE_MULTIPARTY) || !session->chat_session->id || !sipe_strcase_equal(session->chat_session->id, self)) { sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL); } else { sip_transport_response(sipe_private, msg, 202, "Accepted", NULL); sipe_im_invite(sipe_private, session, refer_to, NULL, NULL, referred_by, FALSE); } g_free(self); g_free(from); g_free(refer_to); g_free(referred_by); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-incoming.h ================================================ /** * @file sipe-incoming.h * * pidgin-sipe * * Copyright (C) 2010 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_core_private; struct sip_dialog; struct sipmsg; void process_incoming_bye(struct sipe_core_private *sipe_private, struct sipmsg *msg); void process_incoming_cancel(struct sipe_core_private *sipe_private, struct sipmsg *msg); void process_incoming_info(struct sipe_core_private *sipe_private, struct sipmsg *msg); void process_incoming_invite(struct sipe_core_private *sipe_private, struct sipmsg *msg); void process_incoming_message(struct sipe_core_private *sipe_private, struct sipmsg *msg); void process_incoming_options(struct sipe_core_private *sipe_private, struct sipmsg *msg); void process_incoming_refer(struct sipe_core_private *sipe_private, struct sipmsg *msg); void sipe_incoming_cancel_delayed_invite(struct sipe_core_private *sipe_private, struct sip_dialog *dialog); ================================================ FILE: src/core/sipe-lync-autodiscover.c ================================================ /** * @file sipe-lync-autodiscover.c * * pidgin-sipe * * Copyright (C) 2016-2018 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Specification references: * * - [MS-OCDISCWS]: https://msdn.microsoft.com/en-us/library/hh623245.aspx * - Understanding Autodiscover in Lync Server 2013 * https://technet.microsoft.com/en-us/library/jj945654.aspx */ #include #include #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-http.h" #include "sipe-lync-autodiscover.h" #include "sipe-utils.h" #include "sipe-svc.h" #include "sipe-webticket.h" #include "sipe-xml.h" #define LYNC_AUTODISCOVER_ACCEPT_HEADER \ "Accept: application/vnd.microsoft.rtc.autodiscover+xml;v=1\r\n" struct lync_autodiscover_request { sipe_lync_autodiscover_callback *cb; gpointer cb_data; gpointer id; /* != NULL for active request */ struct sipe_http_request *request; struct sipe_svc_session *session; const gchar *protocol; const gchar **method; gchar *uri; gboolean is_pending; }; struct sipe_lync_autodiscover { GSList *pending_requests; }; /* Use "lar" inside the code fragment */ #define FOR_ALL_REQUESTS_WITH_SAME_ID(code) \ { \ GSList *entry = sipe_private->lync_autodiscover->pending_requests; \ while (entry) { \ struct lync_autodiscover_request *lar = entry->data; \ entry = entry->next; \ if (lar->id == id) { \ code; \ } \ } \ } static void sipe_lync_autodiscover_request_free(struct sipe_core_private *sipe_private, struct lync_autodiscover_request *request) { struct sipe_lync_autodiscover *sla = sipe_private->lync_autodiscover; sla->pending_requests = g_slist_remove(sla->pending_requests, request); if (request->request) sipe_http_request_cancel(request->request); if (request->cb) /* Callback: aborted */ (*request->cb)(sipe_private, NULL, request->cb_data); sipe_svc_session_close(request->session); g_free(request->uri); g_free(request); } static void sipe_lync_autodiscover_cb(struct sipe_core_private *sipe_private, guint status, GSList *headers, const gchar *body, gpointer callback_data); static void lync_request(struct sipe_core_private *sipe_private, struct lync_autodiscover_request *request, const gchar *uri, const gchar *headers) { request->request = sipe_http_request_get(sipe_private, uri, headers ? headers : LYNC_AUTODISCOVER_ACCEPT_HEADER, sipe_lync_autodiscover_cb, request); if (request->request) sipe_http_request_ready(request->request); } static GSList *sipe_lync_autodiscover_add(GSList *servers, const sipe_xml *node, const gchar *name) { const sipe_xml *child = sipe_xml_child(node, name); const gchar *fqdn = sipe_xml_attribute(child, "fqdn"); guint port = sipe_xml_int_attribute(child, "port", 0); /* Add new entry to head of list */ if (fqdn && (port != 0)) { struct sipe_lync_autodiscover_data *lync_data = g_new0(struct sipe_lync_autodiscover_data, 1); lync_data->server = g_strdup(fqdn); lync_data->port = port; servers = g_slist_prepend(servers, lync_data); } return(servers); } GSList *sipe_lync_autodiscover_pop(GSList *servers) { if (servers) { struct sipe_lync_autodiscover_data *lync_data = servers->data; servers = g_slist_remove(servers, lync_data); if (lync_data) { g_free((gchar *) lync_data->server); g_free(lync_data); } } return(servers); } static void sipe_lync_autodiscover_queue_request(struct sipe_core_private *sipe_private, struct lync_autodiscover_request *request); static void sipe_lync_autodiscover_parse(struct sipe_core_private *sipe_private, struct lync_autodiscover_request *request, const gchar *body) { sipe_xml *xml = sipe_xml_parse(body, strlen(body)); const sipe_xml *node; gboolean next = TRUE; /* Root/Link: resources exposed by this server */ for (node = sipe_xml_child(xml, "Root/Link"); node; node = sipe_xml_twin(node)) { const gchar *token = sipe_xml_attribute(node, "token"); const gchar *uri = sipe_xml_attribute(node, "href"); if (token && uri) { /* Redirect? */ if (sipe_strcase_equal(token, "Redirect")) { SIPE_DEBUG_INFO("sipe_lync_autodiscover_parse: redirect to %s", uri); lync_request(sipe_private, request, uri, NULL); next = FALSE; break; /* User? */ } else if (sipe_strcase_equal(token, "User")) { SIPE_DEBUG_INFO("sipe_lync_autodiscover_parse: user %s", uri); /* remember URI for authentication failure */ request->uri = g_strdup(uri); lync_request(sipe_private, request, uri, NULL); next = FALSE; break; } else SIPE_DEBUG_INFO("sipe_lync_autodiscover_parse: unknown token %s", token); } } /* User/Link: topology information of the user’s home server */ for (node = sipe_xml_child(xml, "User/Link"); node; node = sipe_xml_twin(node)) { const gchar *token = sipe_xml_attribute(node, "token"); const gchar *uri = sipe_xml_attribute(node, "href"); if (token && uri) { /* Redirect? */ if (sipe_strcase_equal(token, "Redirect")) { SIPE_DEBUG_INFO("sipe_lync_autodiscover_parse: redirect to %s", uri); lync_request(sipe_private, request, uri, NULL); next = FALSE; break; } else SIPE_DEBUG_INFO("sipe_lync_autodiscover_parse: unknown token %s", token); } } /* if nothing else matched */ if (next) { const gchar *access_location = sipe_xml_attribute(xml, "AccessLocation"); /* User: topology information of the user’s home server */ if ((node = sipe_xml_child(xml, "User")) != NULL) { gpointer id = request->id; /* Active request? */ if (id) { GSList *servers; /* List is reversed, i.e. internal will be tried first */ servers = g_slist_prepend(NULL, NULL); if (!access_location || sipe_strcase_equal(access_location, "external")) { servers = sipe_lync_autodiscover_add(servers, node, "SipClientExternalAccess"); } if (!access_location || sipe_strcase_equal(access_location, "internal")) { servers = sipe_lync_autodiscover_add(servers, node, "SipClientInternalAccess"); } /* Callback takes ownership of servers list */ (*request->cb)(sipe_private, servers, request->cb_data); /* We're done with requests for this callback */ FOR_ALL_REQUESTS_WITH_SAME_ID( \ lar->cb = NULL; \ lar->id = NULL \ ); } /* Request completed */ next = FALSE; sipe_lync_autodiscover_request_free(sipe_private, request); /* request is invalid */ } } sipe_xml_free(xml); if (next) sipe_lync_autodiscover_queue_request(sipe_private, request); } static void sipe_lync_autodiscover_webticket(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER const gchar *base_uri, const gchar *auth_uri, const gchar *wsse_security, SIPE_UNUSED_PARAMETER const gchar *failure_msg, gpointer callback_data) { struct lync_autodiscover_request *request = callback_data; gchar *saml; /* Extract SAML Assertion from WSSE Security XML text */ if (wsse_security && ((saml = sipe_xml_extract_raw(wsse_security, "Assertion", TRUE)) != NULL)) { gchar *base64 = g_base64_encode((const guchar *) saml, strlen(saml)); gchar *headers = g_strdup_printf(LYNC_AUTODISCOVER_ACCEPT_HEADER "X-MS-WebTicket: opaque=%s\r\n", base64); g_free(base64); SIPE_DEBUG_INFO("sipe_lync_autodiscover_webticket: got ticket for Auth URI %s", auth_uri); g_free(saml); lync_request(sipe_private, request, auth_uri, headers); g_free(headers); } else sipe_lync_autodiscover_queue_request(sipe_private, request); } static void sipe_lync_autodiscover_cb(struct sipe_core_private *sipe_private, guint status, GSList *headers, const gchar *body, gpointer callback_data) { struct lync_autodiscover_request *request = callback_data; const gchar *type = sipe_utils_nameval_find(headers, "Content-Type"); gchar *uri = request->uri; request->request = NULL; request->uri = NULL; switch (status) { case SIPE_HTTP_STATUS_OK: /* only accept Autodiscover XML responses */ if (body && g_str_has_prefix(type, "application/vnd.microsoft.rtc.autodiscover+xml")) sipe_lync_autodiscover_parse(sipe_private, request, body); else sipe_lync_autodiscover_queue_request(sipe_private, request); break; case SIPE_HTTP_STATUS_FAILED: { if (uri) { /* check for authentication failure */ const gchar *webticket_uri = sipe_utils_nameval_find(headers, "X-MS-WebTicketURL"); if (!(webticket_uri && sipe_webticket_request_with_auth(sipe_private, request->session, webticket_uri, uri, /* Auth URI */ sipe_lync_autodiscover_webticket, request))) sipe_lync_autodiscover_queue_request(sipe_private, request); } else sipe_lync_autodiscover_queue_request(sipe_private, request); } break; case SIPE_HTTP_STATUS_ABORTED: /* we are not allowed to generate new requests */ sipe_lync_autodiscover_request_free(sipe_private, request); break; default: sipe_lync_autodiscover_queue_request(sipe_private, request); break; } g_free(uri); } /* Proceed to next method for request */ static void sipe_lync_autodiscover_request(struct sipe_core_private *sipe_private, struct lync_autodiscover_request *request) { gpointer id = request->id; /* Active request? */ if (id) { static const gchar *methods[] = { "%s://LyncDiscoverInternal.%s/?sipuri=%s", "%s://LyncDiscover.%s/?sipuri=%s", NULL }; request->is_pending = TRUE; if (request->method) request->method++; else request->method = methods; if (*request->method) { gchar *uri = g_strdup_printf(*request->method, request->protocol, SIPE_CORE_PUBLIC->sip_domain, sipe_private->username); SIPE_DEBUG_INFO("sipe_lync_autodiscover_request: trying '%s'", uri); lync_request(sipe_private, request, uri, NULL); g_free(uri); } else { guint count = 0; /* Count entries with the same request ID */ FOR_ALL_REQUESTS_WITH_SAME_ID( \ count++; \ ); if (count == 1) { /* * This is the last pending request for this * ID, i.e. autodiscover has failed. Create * empty server list and return it. */ GSList *servers = g_slist_prepend(NULL, NULL); /* All methods tried, indicate failure to caller */ SIPE_DEBUG_INFO_NOFORMAT("sipe_lync_autodiscover_request: no more methods to try!"); /* Callback takes ownership of servers list */ (*request->cb)(sipe_private, servers, request->cb_data); } /* Request completed */ request->cb = NULL; sipe_lync_autodiscover_request_free(sipe_private, request); /* request is invalid */ } } else { /* Inactive request, callback already NULL */ sipe_lync_autodiscover_request_free(sipe_private, request); /* request is invalid */ } } /* Proceed to next method for all requests */ static void sipe_lync_autodiscover_queue_request(struct sipe_core_private *sipe_private, struct lync_autodiscover_request *request) { gpointer id = request->id; /* This request is ready to proceed to next method */ request->is_pending = FALSE; /* Is any request for the same ID still pending? */ FOR_ALL_REQUESTS_WITH_SAME_ID( \ if (lar->is_pending) \ return \ ); SIPE_DEBUG_INFO_NOFORMAT("sipe_lync_autodiscover_queue_request: proceed in lockstep"); /* No, proceed to next method for all requests */ FOR_ALL_REQUESTS_WITH_SAME_ID( \ sipe_lync_autodiscover_request(sipe_private, \ lar) \ ); } static gpointer sipe_lync_autodiscover_create(struct sipe_core_private *sipe_private, gpointer id, const gchar *protocol, sipe_lync_autodiscover_callback *callback, gpointer callback_data) { struct sipe_lync_autodiscover *sla = sipe_private->lync_autodiscover; struct lync_autodiscover_request *request = g_new0(struct lync_autodiscover_request, 1); /* use address of first request structure as unique ID */ if (id == NULL) id = request; request->protocol = protocol; request->cb = callback; request->cb_data = callback_data; request->id = id; request->session = sipe_svc_session_start(); sla->pending_requests = g_slist_prepend(sla->pending_requests, request); sipe_lync_autodiscover_request(sipe_private, request); return(id); } void sipe_lync_autodiscover_start(struct sipe_core_private *sipe_private, sipe_lync_autodiscover_callback *callback, gpointer callback_data) { gpointer id = NULL; #define CREATE(protocol) \ id = sipe_lync_autodiscover_create(sipe_private, \ id, \ #protocol, \ callback, \ callback_data) CREATE(http); CREATE(https); } void sipe_lync_autodiscover_init(struct sipe_core_private *sipe_private) { struct sipe_lync_autodiscover *sla = g_new0(struct sipe_lync_autodiscover, 1); sipe_private->lync_autodiscover = sla; } void sipe_lync_autodiscover_free(struct sipe_core_private *sipe_private) { struct sipe_lync_autodiscover *sla = sipe_private->lync_autodiscover; while (sla->pending_requests) sipe_lync_autodiscover_request_free(sipe_private, sla->pending_requests->data); g_free(sla); sipe_private->lync_autodiscover = NULL; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-lync-autodiscover.h ================================================ /** * @file sipe-lync-autodiscover.h * * pidgin-sipe * * Copyright (C) 2016 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipe_core_private; /* Lync data determined by autodiscover */ struct sipe_lync_autodiscover_data { const gchar *server; guint port; }; /** * Lync autodiscover callback * * @param sipe_private SIPE core private data * @param servers list with Lync autodiscover data * @param callback_data callback data * * servers will be @c NULL when request got aborted. * last entry in the list will be a @c NULL entry. */ typedef void (sipe_lync_autodiscover_callback)(struct sipe_core_private *sipe_private, GSList *servers, gpointer callback_data); /** * Free first callback data entry on the server list * * @param servers list given to callback (may be @c NULL) * * @return new list header */ GSList *sipe_lync_autodiscover_pop(GSList *servers); /** * Trigger Lync autodiscover * * @param sipe_private SIPE core private data * @param callback callback function * @param callback_data callback data */ void sipe_lync_autodiscover_start(struct sipe_core_private *sipe_private, sipe_lync_autodiscover_callback *callback, gpointer callback_data); /** * Initialize Lync autodiscover data * * @param sipe_private SIPE core private data */ void sipe_lync_autodiscover_init(struct sipe_core_private *sipe_private); /** * Free Lync autodiscover data * * @param sipe_private SIPE core private data */ void sipe_lync_autodiscover_free(struct sipe_core_private *sipe_private); ================================================ FILE: src/core/sipe-media.c ================================================ /** * @file sipe-media.c * * pidgin-sipe * * Copyright (C) 2011-2019 SIPE Project * Copyright (C) 2010 Jakub Adam * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-common.h" #include "sipmsg.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sdpmsg.h" #include "sipe-conf.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-dialog.h" #include "sipe-media.h" #include "sipe-ocs2007.h" #include "sipe-session.h" #include "sipe-utils.h" #include "sipe-nls.h" #include "sipe-schedule.h" #include "sipe-xml.h" /* [MS-SDPEXT] 3.1.5.31.2 says a range size of 100 SHOULD be used for video and * some clients really demand this. */ #define VIDEO_SSRC_COUNT 100 struct sipe_media_call_private { struct sipe_media_call public; /* private part starts here */ struct sipe_core_private *sipe_private; struct sip_session *session; struct sip_session *conference_session; GSList *streams; struct sipmsg *invitation; SipeIceVersion ice_version; gboolean encryption_compatible; gchar *extra_invite_section; gchar *invite_content_type; GSList *ssrc_ranges; struct sdpmsg *smsg; GSList *failed_media; gchar *ringing_key; gchar *timeout_key; }; #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private) #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call) struct sipe_media_stream_private { struct sipe_media_stream public; gchar *timeout_key; guchar *encryption_key; int encryption_key_id; gboolean remote_candidates_and_codecs_set; gboolean established; #ifdef HAVE_XDATA gboolean sdp_negotiation_concluded; gboolean writable; #endif GSList *extra_sdp; GQueue *write_queue; GQueue *async_reads; gssize read_pos; /* User data associated with the stream. */ gpointer data; GDestroyNotify data_free_func; }; #define SIPE_MEDIA_STREAM ((struct sipe_media_stream *) stream_private) #define SIPE_MEDIA_STREAM_PRIVATE ((struct sipe_media_stream_private *) stream) #define SIPE_MEDIA_STREAM_CONNECTION_TIMEOUT_SECONDS 120 #define SIPE_MEDIA_CALL_RINGING_TIMEOUT_SECONDS 60 #define SIPE_MEDIA_CALL_TIMEOUT_SECONDS 120 struct async_read_data { guint8 *buffer; gssize len; sipe_media_stream_read_callback callback; }; static void stream_schedule_cancel_timeout(struct sipe_media_call *call, struct sipe_media_stream_private *stream_private); static void call_schedule_cancel_request_timeout(struct sipe_media_call *call); static void call_schedule_cancel_ringing_timeout(struct sipe_media_call *call); static void sipe_media_codec_list_free(GList *codecs) { for (; codecs; codecs = g_list_delete_link(codecs, codecs)) sipe_backend_codec_free(codecs->data); } static void sipe_media_candidate_list_free(GList *candidates) { for (; candidates; candidates = g_list_delete_link(candidates, candidates)) sipe_backend_candidate_free(candidates->data); } static void sipe_media_stream_free(struct sipe_media_stream_private *stream_private) { struct sipe_media_call_private *call_private; call_private = (struct sipe_media_call_private *)SIPE_MEDIA_STREAM->call; stream_schedule_cancel_timeout(SIPE_MEDIA_CALL, stream_private); sipe_media_stream_set_data(SIPE_MEDIA_STREAM, NULL, NULL); if (call_private) { call_private->streams = g_slist_remove(call_private->streams, stream_private); if (SIPE_MEDIA_STREAM->ssrc_range) { call_private->ssrc_ranges = g_slist_remove(call_private->ssrc_ranges, SIPE_MEDIA_STREAM->ssrc_range); g_free(SIPE_MEDIA_STREAM->ssrc_range); } } if (SIPE_MEDIA_STREAM->backend_private) { sipe_backend_media_stream_free(SIPE_MEDIA_STREAM->backend_private); } g_free(SIPE_MEDIA_STREAM->id); g_free(stream_private->encryption_key); g_queue_free_full(stream_private->write_queue, (GDestroyNotify)g_byte_array_unref); g_queue_free_full(stream_private->async_reads, g_free); sipe_utils_nameval_free(stream_private->extra_sdp); g_free(stream_private); } static gboolean call_private_equals(SIPE_UNUSED_PARAMETER const gchar *callid, struct sipe_media_call_private *call_private1, struct sipe_media_call_private *call_private2) { return call_private1 == call_private2; } static void sipe_media_call_free(struct sipe_media_call_private *call_private) { if (call_private) { g_hash_table_foreach_remove(call_private->sipe_private->media_calls, (GHRFunc) call_private_equals, call_private); call_schedule_cancel_request_timeout(SIPE_MEDIA_CALL); call_schedule_cancel_ringing_timeout(SIPE_MEDIA_CALL); while (call_private->streams) { sipe_media_stream_free(call_private->streams->data); } sipe_backend_media_free(call_private->public.backend_private); if (call_private->session) { sipe_session_remove(call_private->sipe_private, call_private->session); } if (call_private->invitation) sipmsg_free(call_private->invitation); // Frees any referenced extra invite data. sipe_media_add_extra_invite_section(SIPE_MEDIA_CALL, NULL, NULL); sipe_utils_slist_free_full(call_private->ssrc_ranges, g_free); sdpmsg_free(call_private->smsg); sipe_utils_slist_free_full(call_private->failed_media, (GDestroyNotify)sdpmedia_free); g_free(SIPE_MEDIA_CALL->with); g_free(call_private); } } static gint candidate_sort_cb(struct sdpcandidate *c1, struct sdpcandidate *c2) { int cmp = g_strcmp0(c1->foundation, c2->foundation); if (cmp == 0) { cmp = g_strcmp0(c1->username, c2->username); if (cmp == 0) cmp = c1->component - c2->component; } return cmp; } static GSList * backend_candidates_to_sdpcandidate(GList *candidates) { GSList *result = NULL; GList *i; for (i = candidates; i; i = i->next) { struct sipe_backend_candidate *candidate = i->data; struct sdpcandidate *c; gchar *ip = sipe_backend_candidate_get_ip(candidate); gchar *base_ip = sipe_backend_candidate_get_base_ip(candidate); if (is_empty(ip) || strchr(ip, ':') || (base_ip && strchr(base_ip, ':'))) { /* Ignore IPv6 candidates. */ g_free(ip); g_free(base_ip); continue; } c = g_new(struct sdpcandidate, 1); c->foundation = sipe_backend_candidate_get_foundation(candidate); c->component = sipe_backend_candidate_get_component_type(candidate); c->type = sipe_backend_candidate_get_type(candidate); c->protocol = sipe_backend_candidate_get_protocol(candidate); c->ip = ip; c->port = sipe_backend_candidate_get_port(candidate); c->base_ip = base_ip; c->base_port = sipe_backend_candidate_get_base_port(candidate); c->priority = sipe_backend_candidate_get_priority(candidate); c->username = sipe_backend_candidate_get_username(candidate); c->password = sipe_backend_candidate_get_password(candidate); result = g_slist_insert_sorted(result, c, (GCompareFunc)candidate_sort_cb); } return result; } static void get_stream_ip_and_ports(GSList *candidates, gchar **ip, guint *rtp_port, guint *rtcp_port) { guint32 rtp_max_priority = 0; guint32 rtcp_max_priority = 0; *ip = 0; *rtp_port = 0; *rtcp_port = 0; for (; candidates; candidates = candidates->next) { struct sdpcandidate *candidate = candidates->data; if (candidate->component == SIPE_COMPONENT_RTP && candidate->priority > rtp_max_priority) { rtp_max_priority = candidate->priority; *rtp_port = candidate->port; g_free(*ip); *ip = g_strdup(candidate->ip); } else if (candidate->component == SIPE_COMPONENT_RTCP && candidate->priority > rtcp_max_priority) { rtcp_max_priority = candidate->priority; *rtcp_port = candidate->port; } } } static gint sdpcodec_compare(gconstpointer a, gconstpointer b) { return ((const struct sdpcodec *)a)->id - ((const struct sdpcodec *)b)->id; } static GList * remove_wrong_farstream_0_1_tcp_candidates(GList *candidates) { GList *i = candidates; GHashTable *foundation_to_candidate = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); while (i) { GList *next = i->next; struct sipe_backend_candidate *c1 = i->data; if (sipe_backend_candidate_get_protocol(c1) == SIPE_NETWORK_PROTOCOL_UDP) { gchar *foundation = sipe_backend_candidate_get_foundation(c1); struct sipe_backend_candidate *c2 = g_hash_table_lookup(foundation_to_candidate, foundation); if (c2) { g_free(foundation); if (sipe_backend_candidate_get_port(c1) == sipe_backend_candidate_get_port(c2) || (sipe_backend_candidate_get_type(c1) != SIPE_CANDIDATE_TYPE_HOST && sipe_backend_candidate_get_base_port(c1) == sipe_backend_candidate_get_base_port(c2))) { /* * We assume that RTP+RTCP UDP pairs * that share the same port are * actually mistagged TCP candidates. */ candidates = g_list_remove(candidates, c2); candidates = g_list_delete_link(candidates, i); sipe_backend_candidate_free(c1); sipe_backend_candidate_free(c2); } } else /* hash table takes ownership of "foundation" */ g_hash_table_insert(foundation_to_candidate, foundation, c1); } i = next; } g_hash_table_destroy(foundation_to_candidate); return candidates; } static void fill_zero_tcp_act_ports_from_tcp_pass(GSList *candidates, GSList *all_candidates) { GSList *i; GHashTable *ip_to_port = g_hash_table_new(g_str_hash, g_str_equal); for (i = candidates; i; i = i->next) { struct sdpcandidate *c = i->data; GSList *j; if (c->protocol != SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) { continue; } for (j = all_candidates; j; j = j->next) { struct sdpcandidate *passive = j->data; if (passive->protocol != SIPE_NETWORK_PROTOCOL_TCP_PASSIVE || c->type != passive->type) { continue; } if (sipe_strequal(c->ip, passive->ip) && sipe_strequal(c->base_ip, passive->base_ip)) { if (c->port == 0) { c->port = passive->port; } if (c->base_port == 0) { c->base_port = passive->base_port; } break; } } } for (i = all_candidates; i; i = i->next) { struct sdpcandidate *c = i->data; if (c->protocol == SIPE_NETWORK_PROTOCOL_TCP_PASSIVE && c->type == SIPE_CANDIDATE_TYPE_HOST) { g_hash_table_insert(ip_to_port, c->ip, &c->port); } } /* Fill base ports of all TCP relay candidates using what we have * collected from host candidates. */ for (i = candidates; i; i = i->next) { struct sdpcandidate *c = i->data; if (c->type == SIPE_CANDIDATE_TYPE_RELAY && c->base_port == 0) { guint *base_port = (guint*)g_hash_table_lookup(ip_to_port, c->base_ip); if (base_port) { c->base_port = *base_port; } else { SIPE_DEBUG_WARNING("Couldn't determine base port for candidate " "with foundation %s", c->foundation); } } } g_hash_table_destroy(ip_to_port); } static SipeEncryptionPolicy get_encryption_policy(struct sipe_core_private *sipe_private) { SipeEncryptionPolicy result = sipe_backend_media_get_encryption_policy(SIPE_CORE_PUBLIC); if (result == SIPE_ENCRYPTION_POLICY_OBEY_SERVER) { result = sipe_private->server_av_encryption_policy; } return result; } static GList * get_local_codecs(struct sipe_media_call_private *call_private, struct sipe_media_stream_private *stream_private) { struct sipe_core_private *sipe_private = call_private->sipe_private; gboolean is_conference = g_strstr_len(SIPE_MEDIA_CALL->with, strlen(SIPE_MEDIA_CALL->with), "app:conf:audio-video:") != NULL; GList *codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL, SIPE_MEDIA_STREAM); GList *i; for (i = codecs; i; i = i->next) { struct sipe_backend_codec *codec = i->data; char *name = sipe_backend_codec_get_name(codec); if (/* Do not announce Theora. Its optional parameters are too * long, Communicator rejects such SDP message and does not * support the codec anyway. */ sipe_strequal(name,"THEORA") || /* For an unknown reason, MS Lync A/V conferencing server * does not accept SIPE audio encoded with SIREN. Disable * the codec when SIPE is connected to a server version * prior Skype for Business. */ (!SIPE_CORE_PRIVATE_FLAG_IS(SFB) && is_conference && sipe_strequal(name,"SIREN"))) { GList *tmp; sipe_backend_codec_free(codec); tmp = i->next; codecs = g_list_delete_link(codecs, i); i = tmp; } g_free(name); } return codecs; } static struct sdpmedia * media_stream_to_sdpmedia(struct sipe_media_call_private *call_private, struct sipe_media_stream_private *stream_private) { struct sdpmedia *sdpmedia = g_new0(struct sdpmedia, 1); GList *codecs = get_local_codecs(call_private, stream_private); SipeEncryptionPolicy encryption_policy = get_encryption_policy(call_private->sipe_private); guint rtcp_port = 0; SipeMediaType type; GSList *attributes = NULL; GSList *sdpcandidates; GSList *all_sdpcandidates; GList *candidates; GList *i; GSList *j; sdpmedia->name = g_strdup(SIPE_MEDIA_STREAM->id); if (sipe_strequal(sdpmedia->name, "audio")) type = SIPE_MEDIA_AUDIO; else if (sipe_strequal(sdpmedia->name, "video")) type = SIPE_MEDIA_VIDEO; else if (sipe_strequal(sdpmedia->name, "data")) type = SIPE_MEDIA_APPLICATION; else if (sipe_strequal(sdpmedia->name, "applicationsharing")) type = SIPE_MEDIA_APPLICATION; else { // TODO: incompatible media, should not happen here g_free(sdpmedia->name); g_free(sdpmedia); sipe_media_codec_list_free(codecs); return(NULL); } // Process codecs for (i = codecs; i; i = i->next) { struct sipe_backend_codec *codec = i->data; struct sdpcodec *c = g_new0(struct sdpcodec, 1); GList *params; c->id = sipe_backend_codec_get_id(codec); c->name = sipe_backend_codec_get_name(codec); c->clock_rate = sipe_backend_codec_get_clock_rate(codec); c->type = type; params = sipe_backend_codec_get_optional_parameters(codec); for (; params; params = params->next) { struct sipnameval *param = params->data; struct sipnameval *copy = g_new0(struct sipnameval, 1); copy->name = g_strdup(param->name); copy->value = g_strdup(param->value); c->parameters = g_slist_append(c->parameters, copy); } /* Buggy(?) codecs may report non-unique id (a.k.a. payload * type) that must not appear in SDP messages we send. Thus, * let's ignore any codec having the same id as one we already * have in the converted list. */ if (g_slist_find_custom(sdpmedia->codecs, c, sdpcodec_compare)) { sdpcodec_free(c); } else { sdpmedia->codecs = g_slist_append(sdpmedia->codecs, c); } } sipe_media_codec_list_free(codecs); // Process local candidates // If we have established candidate pairs, send them in SDP response. // Otherwise send all available local candidates. candidates = sipe_backend_media_stream_get_active_local_candidates(SIPE_MEDIA_STREAM); sdpcandidates = backend_candidates_to_sdpcandidate(candidates); sipe_media_candidate_list_free(candidates); candidates = sipe_backend_get_local_candidates(SIPE_MEDIA_CALL, SIPE_MEDIA_STREAM); candidates = remove_wrong_farstream_0_1_tcp_candidates(candidates); all_sdpcandidates = backend_candidates_to_sdpcandidate(candidates); sipe_media_candidate_list_free(candidates); if (!sdpcandidates) { sdpcandidates = all_sdpcandidates; } fill_zero_tcp_act_ports_from_tcp_pass(sdpcandidates, all_sdpcandidates); sdpmedia->candidates = sdpcandidates; if (all_sdpcandidates != sdpcandidates) { sipe_utils_slist_free_full(all_sdpcandidates, (GDestroyNotify)sdpcandidate_free); } get_stream_ip_and_ports(sdpmedia->candidates, &sdpmedia->ip, &sdpmedia->port, &rtcp_port); if (sipe_backend_stream_is_held(SIPE_MEDIA_STREAM)) attributes = sipe_utils_nameval_add(attributes, "inactive", ""); if (rtcp_port) { gchar *tmp = g_strdup_printf("%u", rtcp_port); attributes = sipe_utils_nameval_add(attributes, "rtcp", tmp); g_free(tmp); } if (encryption_policy != call_private->sipe_private->server_av_encryption_policy) { const gchar *encryption = NULL; switch (encryption_policy) { case SIPE_ENCRYPTION_POLICY_REJECTED: encryption = "rejected"; break; case SIPE_ENCRYPTION_POLICY_OPTIONAL: encryption = "optional"; break; case SIPE_ENCRYPTION_POLICY_REQUIRED: default: encryption = "required"; break; } attributes = sipe_utils_nameval_add(attributes, "encryption", encryption); } if (SIPE_MEDIA_STREAM->ssrc_range) { gchar *tmp; tmp = g_strdup_printf("%u-%u", SIPE_MEDIA_STREAM->ssrc_range->begin, SIPE_MEDIA_STREAM->ssrc_range->end); attributes = sipe_utils_nameval_add(attributes, "x-ssrc-range", tmp); g_free(tmp); } // Process remote candidates candidates = sipe_backend_media_stream_get_active_remote_candidates(SIPE_MEDIA_STREAM); sdpmedia->remote_candidates = backend_candidates_to_sdpcandidate(candidates); sipe_media_candidate_list_free(candidates); sdpmedia->encryption_active = stream_private->encryption_key && call_private->encryption_compatible && stream_private->remote_candidates_and_codecs_set && encryption_policy != SIPE_ENCRYPTION_POLICY_REJECTED; // Set our key if encryption is enabled. if (stream_private->encryption_key && encryption_policy != SIPE_ENCRYPTION_POLICY_REJECTED) { sdpmedia->encryption_key = g_memdup(stream_private->encryption_key, SIPE_SRTP_KEY_LEN); sdpmedia->encryption_key_id = stream_private->encryption_key_id; } // Append extra attributes assigned to the stream. for (j = stream_private->extra_sdp; j; j = g_slist_next(j)) { struct sipnameval *attr = j->data; attributes = sipe_utils_nameval_add(attributes, attr->name, attr->value); } sdpmedia->attributes = attributes; return sdpmedia; } static struct sdpmsg * sipe_media_to_sdpmsg(struct sipe_media_call_private *call_private) { struct sdpmsg *msg = g_new0(struct sdpmsg, 1); GSList *streams = call_private->streams; for (; streams; streams = streams->next) { struct sdpmedia *media = media_stream_to_sdpmedia(call_private, streams->data); if (media) { msg->media = g_slist_append(msg->media, media); if (msg->ip == NULL) msg->ip = g_strdup(media->ip); } } msg->media = g_slist_concat(msg->media, call_private->failed_media); call_private->failed_media = NULL; msg->ice_version = call_private->ice_version; return msg; } static void sipe_invite_call(struct sipe_media_call_private *call_private, TransCallback tc) { struct sipe_core_private *sipe_private = call_private->sipe_private; gchar *hdr; gchar *contact; gchar *p_preferred_identity = NULL; gchar *body; struct sip_dialog *dialog; struct sdpmsg *msg; dialog = sipe_media_get_sip_dialog(SIPE_MEDIA_CALL); contact = get_contact(sipe_private); if (sipe_private->uc_line_uri) { gchar *self = sip_uri_self(sipe_private); p_preferred_identity = g_strdup_printf( "P-Preferred-Identity: <%s>, <%s>\r\n", self, sipe_private->uc_line_uri); g_free(self); } hdr = g_strdup_printf( "ms-keep-alive: UAC;hop-hop=yes\r\n" "Contact: %s\r\n" "%s" "Content-Type: %s%s\r\n", contact, p_preferred_identity ? p_preferred_identity : "", call_private->invite_content_type ? call_private->invite_content_type : "application/sdp", call_private->invite_content_type ? ";boundary=\"----=_NextPart_000_001E_01CB4397.0B5EB570\"" : ""); g_free(contact); g_free(p_preferred_identity); msg = sipe_media_to_sdpmsg(call_private); body = sdpmsg_to_string(msg); if (call_private->extra_invite_section) { gchar *tmp; tmp = g_strdup_printf( "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n" "%s" "\r\n" "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n" "Content-Type: application/sdp\r\n" "Content-Transfer-Encoding: 7bit\r\n" "Content-Disposition: session; handling=optional\r\n" "\r\n" "%s" "\r\n" "------=_NextPart_000_001E_01CB4397.0B5EB570--\r\n", call_private->extra_invite_section, body); g_free(body); body = tmp; sipe_media_add_extra_invite_section(SIPE_MEDIA_CALL, NULL, NULL); } sdpmsg_free(msg); dialog->outgoing_invite = sip_transport_invite(sipe_private, hdr, body, dialog, tc); g_free(body); g_free(hdr); } static void send_response_with_session_description(struct sipe_media_call_private *call_private, int code, const gchar *text) { struct sdpmsg *msg = sipe_media_to_sdpmsg(call_private); gchar *body = sdpmsg_to_string(msg); sdpmsg_free(msg); sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp"); sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body); g_free(body); } static gboolean process_invite_call_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans); struct sipe_media_stream * sipe_core_media_get_stream_by_id(struct sipe_media_call *call, const gchar *id) { GSList *i; for (i = SIPE_MEDIA_CALL_PRIVATE->streams; i; i = i->next) { struct sipe_media_stream *stream = i->data; if (sipe_strequal(stream->id, id)) return stream; } return NULL; } static gboolean update_call_from_remote_sdp(struct sipe_media_call_private* call_private, struct sdpmedia *media) { struct sipe_media_stream *stream; GList *backend_candidates = NULL; GList *backend_codecs = NULL; GSList *i; gboolean result = TRUE; stream = sipe_core_media_get_stream_by_id(SIPE_MEDIA_CALL, media->name); if (media->port == 0) { if (stream) { sipe_backend_media_stream_end(SIPE_MEDIA_CALL, stream); } return FALSE; } if (!stream) return FALSE; if (sipe_utils_nameval_find(media->attributes, "inactive")) { sipe_backend_stream_hold(SIPE_MEDIA_CALL, stream, FALSE); } else if (sipe_backend_stream_is_held(stream)) { sipe_backend_stream_unhold(SIPE_MEDIA_CALL, stream, FALSE); } if (SIPE_MEDIA_STREAM_PRIVATE->remote_candidates_and_codecs_set) { return TRUE; } for (i = media->codecs; i; i = i->next) { struct sdpcodec *c = i->data; struct sipe_backend_codec *codec; GSList *j; codec = sipe_backend_codec_new(c->id, c->name, c->type, c->clock_rate, c->channels); for (j = c->parameters; j; j = j->next) { struct sipnameval *attr = j->data; sipe_backend_codec_add_optional_parameter(codec, attr->name, attr->value); } backend_codecs = g_list_append(backend_codecs, codec); } if (media->encryption_key && SIPE_MEDIA_STREAM_PRIVATE->encryption_key) { sipe_backend_media_set_encryption_keys(SIPE_MEDIA_CALL, stream, SIPE_MEDIA_STREAM_PRIVATE->encryption_key, media->encryption_key); SIPE_MEDIA_STREAM_PRIVATE->encryption_key_id = media->encryption_key_id; } else { // We now know that the stream won't be encrypted. // Allow unencrypted data to pass srtpdec freely sipe_backend_media_set_require_encryption(SIPE_MEDIA_CALL, stream, FALSE); } result = sipe_backend_set_remote_codecs(SIPE_MEDIA_CALL, stream, backend_codecs); sipe_media_codec_list_free(backend_codecs); if (result == FALSE) { sipe_backend_media_stream_end(SIPE_MEDIA_CALL, stream); return FALSE; } for (i = media->candidates; i; i = i->next) { struct sdpcandidate *c = i->data; struct sipe_backend_candidate *candidate; candidate = sipe_backend_candidate_new(c->foundation, c->component, c->type, c->protocol, c->ip, c->port, c->username, c->password); sipe_backend_candidate_set_priority(candidate, c->priority); backend_candidates = g_list_append(backend_candidates, candidate); } sipe_backend_media_add_remote_candidates(SIPE_MEDIA_CALL, stream, backend_candidates); sipe_media_candidate_list_free(backend_candidates); if (sipe_strequal(stream->id, "video") && call_private->conference_session) { stream->media_source_id = call_private->conference_session->video_media_source_id; } SIPE_MEDIA_STREAM_PRIVATE->remote_candidates_and_codecs_set = TRUE; return TRUE; } static void apply_remote_message(struct sipe_media_call_private* call_private, struct sdpmsg* msg) { GSList *i; sipe_utils_slist_free_full(call_private->failed_media, (GDestroyNotify)sdpmedia_free); call_private->failed_media = NULL; call_private->encryption_compatible = TRUE; for (i = msg->media; i; i = i->next) { struct sdpmedia *media = i->data; const gchar *enc_level = sipe_utils_nameval_find(media->attributes, "encryption"); if (sipe_strequal(enc_level, "rejected") && get_encryption_policy(call_private->sipe_private) == SIPE_ENCRYPTION_POLICY_REQUIRED) { call_private->encryption_compatible = FALSE; } if (!update_call_from_remote_sdp(call_private, media)) { media->port = 0; call_private->failed_media = g_slist_append(call_private->failed_media, media); } } /* We need to keep failed medias until response is sent, remove them * from sdpmsg that is to be freed. */ for (i = call_private->failed_media; i; i = i->next) { msg->media = g_slist_remove(msg->media, i->data); } } static gboolean call_initialized(struct sipe_media_call *call) { GSList *streams = SIPE_MEDIA_CALL_PRIVATE->streams; for (; streams; streams = streams->next) { if (!sipe_backend_stream_initialized(call, streams->data)) { return FALSE; } } return TRUE; } static void stream_connection_timeout_cb(struct sipe_core_private *sipe_private, gpointer data) { struct sipe_media_call_private *call_private = data; sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Couldn't create stream"), _("Connection timed out")); sipe_backend_media_hangup(SIPE_MEDIA_CALL->backend_private, TRUE); } static void stream_schedule_timeout(struct sipe_media_call *call) { GSList *i; for (i = SIPE_MEDIA_CALL_PRIVATE->streams; i; i = i->next) { struct sipe_media_stream_private *stream_private = i->data; if (stream_private->established) continue; stream_private->timeout_key = g_strdup_printf("<%s><%s>", sipe_media_get_sip_dialog(call)->callid, SIPE_MEDIA_STREAM->id); sipe_schedule_seconds(SIPE_MEDIA_CALL_PRIVATE->sipe_private, stream_private->timeout_key, SIPE_MEDIA_CALL_PRIVATE, SIPE_MEDIA_STREAM_CONNECTION_TIMEOUT_SECONDS, stream_connection_timeout_cb, NULL); } } static void stream_schedule_cancel_timeout(struct sipe_media_call *call, struct sipe_media_stream_private *stream_private) { if (stream_private->timeout_key) { sipe_schedule_cancel(SIPE_MEDIA_CALL_PRIVATE->sipe_private, stream_private->timeout_key); g_free(stream_private->timeout_key); } stream_private->timeout_key = NULL; } static void call_request_timeout_cb(struct sipe_core_private *sipe_private, gpointer data) { struct sipe_media_call_private *call_private = data; sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Request timed out"), _("Call could not be answered")); sipe_backend_media_hangup(SIPE_MEDIA_CALL->backend_private, TRUE); } static void call_schedule_request_timeout(struct sipe_media_call *call) { SIPE_MEDIA_CALL_PRIVATE->timeout_key = g_strdup_printf("<%s>", sipe_media_get_sip_dialog(call)->callid); sipe_schedule_seconds(SIPE_MEDIA_CALL_PRIVATE->sipe_private, SIPE_MEDIA_CALL_PRIVATE->timeout_key, SIPE_MEDIA_CALL_PRIVATE, SIPE_MEDIA_CALL_TIMEOUT_SECONDS, call_request_timeout_cb, NULL); } static void call_schedule_cancel_request_timeout(struct sipe_media_call *call) { if (SIPE_MEDIA_CALL_PRIVATE->timeout_key) { sipe_schedule_cancel(SIPE_MEDIA_CALL_PRIVATE->sipe_private, SIPE_MEDIA_CALL_PRIVATE->timeout_key); g_free(SIPE_MEDIA_CALL_PRIVATE->timeout_key); } SIPE_MEDIA_CALL_PRIVATE->timeout_key = NULL; } static void call_ringing_timeout_cb(struct sipe_core_private *sipe_private, gpointer data) { struct sipe_media_call_private *call_private = data; sip_transport_response(sipe_private, call_private->invitation, 408, "Request Timeout", NULL); sipe_backend_media_hangup(SIPE_MEDIA_CALL->backend_private, FALSE); } static void call_schedule_ringing_timeout(struct sipe_media_call *call) { SIPE_MEDIA_CALL_PRIVATE->ringing_key = g_strdup_printf("<%s>", sipe_media_get_sip_dialog(call)->callid); sipe_schedule_seconds(SIPE_MEDIA_CALL_PRIVATE->sipe_private, SIPE_MEDIA_CALL_PRIVATE->ringing_key, SIPE_MEDIA_CALL_PRIVATE, SIPE_MEDIA_CALL_RINGING_TIMEOUT_SECONDS, call_ringing_timeout_cb, NULL); } static void call_schedule_cancel_ringing_timeout(struct sipe_media_call *call) { if (SIPE_MEDIA_CALL_PRIVATE->ringing_key) { sipe_schedule_cancel(SIPE_MEDIA_CALL_PRIVATE->sipe_private, SIPE_MEDIA_CALL_PRIVATE->ringing_key); g_free(SIPE_MEDIA_CALL_PRIVATE->ringing_key); } SIPE_MEDIA_CALL_PRIVATE->ringing_key = NULL; } // Sends an invite response when the call is accepted and local candidates were // prepared, otherwise does nothing. If error response is sent, call_private is // disposed before function returns. static void maybe_send_first_invite_response(struct sipe_media_call_private *call_private) { struct sipe_backend_media *backend_media; backend_media = call_private->public.backend_private; if (!sipe_backend_media_accepted(backend_media) || !call_initialized(&call_private->public)) return; if (!call_private->encryption_compatible) { struct sipe_core_private *sipe_private = call_private->sipe_private; sipmsg_add_header(call_private->invitation, "Warning", "308 lcs.microsoft.com \"Encryption Levels not compatible\""); sip_transport_response(sipe_private, call_private->invitation, 488, "Encryption Levels not compatible", NULL); sipe_backend_media_reject(backend_media, FALSE); sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Unable to establish a call"), _("Encryption settings of peer are incompatible with ours.")); } else { send_response_with_session_description(call_private, 200, "OK"); stream_schedule_timeout(SIPE_MEDIA_CALL); call_schedule_cancel_ringing_timeout(SIPE_MEDIA_CALL); sipmsg_free(call_private->invitation); call_private->invitation = NULL; } } static void stream_initialized_cb(struct sipe_media_call *call, struct sipe_media_stream *stream) { if (call_initialized(call)) { struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE; if (sipe_backend_media_is_initiator(call, stream)) { sipe_invite_call(call_private, process_invite_call_response); } else if (call_private->smsg) { struct sdpmsg *smsg = call_private->smsg; call_private->smsg = NULL; apply_remote_message(call_private, smsg); maybe_send_first_invite_response(call_private); sdpmsg_free(smsg); } } } static void phone_state_publish(struct sipe_core_private *sipe_private) { if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) { sipe_ocs2007_phone_state_publish(sipe_private); } else { // TODO: OCS 2005 support. Is anyone still using it at all? } } void sipe_core_media_stream_end(struct sipe_media_stream *stream) { sipe_media_stream_free(SIPE_MEDIA_STREAM_PRIVATE); } static void media_end_cb(struct sipe_media_call *call) { struct sipe_core_private *sipe_private; g_return_if_fail(call); sipe_private = SIPE_MEDIA_CALL_PRIVATE->sipe_private; sipe_media_call_free(SIPE_MEDIA_CALL_PRIVATE); phone_state_publish(sipe_private); } static void call_accept_cb(struct sipe_media_call *call, gboolean local) { if (local) { maybe_send_first_invite_response(SIPE_MEDIA_CALL_PRIVATE); } phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private); } static void call_reject_cb(struct sipe_media_call *call, gboolean local) { if (local) { struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE; sip_transport_response(call_private->sipe_private, call_private->invitation, 603, "Decline", NULL); if (call_private->session) { sipe_session_remove(call_private->sipe_private, call_private->session); call_private->session = NULL; } } } static void av_call_reject_cb(struct sipe_media_call *call, gboolean local) { if (!local) { struct sipe_core_private *sipe_private; gchar *desc; sipe_private = SIPE_MEDIA_CALL_PRIVATE->sipe_private; desc = g_strdup_printf(_("User %s rejected call"), call->with); sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Call rejected"), desc); g_free(desc); } call_reject_cb(call, local); } static gboolean sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans); static void call_hold_cb(struct sipe_media_call *call, gboolean local, SIPE_UNUSED_PARAMETER gboolean state) { if (local) { sipe_invite_call(SIPE_MEDIA_CALL_PRIVATE, sipe_media_send_ack); } } static void call_hangup_cb(struct sipe_media_call *call, gboolean local) { if (local) { struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE; if (call_private->session) { sipe_session_close(call_private->sipe_private, call_private->session); call_private->session = NULL; } } } static void error_cb(struct sipe_media_call *call, gchar *message) { struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE; struct sipe_core_private *sipe_private = call_private->sipe_private; gboolean initiator = sipe_backend_media_is_initiator(call, NULL); gboolean accepted = sipe_backend_media_accepted(call->backend_private); gchar *title = g_strdup_printf("Call with %s failed", call->with); sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, message); g_free(title); if (!initiator && !accepted && call_private->invitation) { sip_transport_response(sipe_private, call_private->invitation, 488, "Not Acceptable Here", NULL); } } struct sipe_media_call * sipe_media_call_new(struct sipe_core_private *sipe_private, const gchar* with, struct sipmsg *msg, SipeIceVersion ice_version, SipeMediaCallFlags flags) { struct sipe_media_call_private *call_private; struct sip_session *session; struct sip_dialog *dialog; gchar *cname; session = sipe_session_add_call(sipe_private, with); dialog = sipe_dialog_add(session); dialog->with = g_strdup(with); if (msg) { sipmsg_update_to_header_tag(msg); dialog->callid = g_strdup(sipmsg_find_call_id_header(msg)); sipe_dialog_parse(dialog, msg, FALSE); } else { dialog->callid = gencallid(); dialog->ourtag = gentag(); flags |= SIPE_MEDIA_CALL_INITIATOR; } if (g_hash_table_lookup(sipe_private->media_calls, dialog->callid)) { SIPE_DEBUG_ERROR("sipe_media_call_new: call already exists for " "Call-ID %s", dialog->callid); sipe_session_remove(sipe_private, session); return NULL; } call_private = g_new0(struct sipe_media_call_private, 1); call_private->sipe_private = sipe_private; call_private->session = session; SIPE_MEDIA_CALL->with = g_strdup(with); g_hash_table_insert(sipe_private->media_calls, g_strdup(dialog->callid), call_private); cname = g_strdup(sipe_private->contact + 1); cname[strlen(cname) - 1] = '\0'; call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC, SIPE_MEDIA_CALL, with, flags); sipe_backend_media_set_cname(call_private->public.backend_private, cname); call_private->ice_version = ice_version; call_private->encryption_compatible = TRUE; call_private->public.stream_initialized_cb = stream_initialized_cb; call_private->public.media_end_cb = media_end_cb; call_private->public.call_accept_cb = call_accept_cb; call_private->public.call_reject_cb = call_reject_cb; call_private->public.call_hold_cb = call_hold_cb; call_private->public.call_hangup_cb = call_hangup_cb; call_private->public.error_cb = error_cb; g_free(cname); return SIPE_MEDIA_CALL; } static gboolean find_call_cb(SIPE_UNUSED_PARAMETER const gchar *callid, struct sipe_media_call *call, const gchar *with) { return sipe_strequal(call->with, with); } struct sipe_media_call * sipe_media_call_find(struct sipe_core_private *sipe_private, const gchar *with) { return g_hash_table_find(sipe_private->media_calls, (GHRFunc)find_call_cb, (gpointer)with); } void sipe_media_hangup(struct sipe_media_call_private *call_private) { if (call_private) { sipe_backend_media_hangup(call_private->public.backend_private, FALSE); } } static gint ssrc_range_compare(const struct ssrc_range *a, const struct ssrc_range *b) { if (a->begin < b->begin) { return -1; } if (a->begin > b->begin) { return 1; } return 0; } static void ssrc_range_update(GSList **ranges, GSList *media) { for (; media; media = media->next) { struct sdpmedia *m; const char *ssrc_range; gchar **parts; m = media->data; ssrc_range = sipe_utils_nameval_find(m->attributes, "x-ssrc-range"); if (!ssrc_range) { continue; } parts = g_strsplit(ssrc_range, "-", 2); if (parts[0] && parts[1]) { struct ssrc_range *range; range = g_new0(struct ssrc_range, 1); range->begin = atoi(parts[0]); range->end = atoi(parts[1]); *ranges = sipe_utils_slist_insert_unique_sorted( *ranges, range, (GCompareFunc)ssrc_range_compare, g_free); } g_strfreev(parts); } } static struct ssrc_range * ssrc_range_allocate(GSList **ranges, guint32 len) { struct ssrc_range *range; GSList *i; range = g_new0(struct ssrc_range, 1); range->begin = 1; range->end = range->begin + (len - 1); for (i = *ranges; i; i = i->next) { struct ssrc_range *r = i->data; if (range->begin < r->begin && range->end < r->begin) { break; } range->begin = r->end + 1; range->end = range->begin + (len - 1); } /* As per [MS-SDPEXT] 3.1.5.31.1, a SSRC MUST be from 1 to 4294967040 * inclusive. */ if (range->begin > range->end || range->end > 0xFFFFFF00) { g_free(range); SIPE_DEBUG_ERROR("Couldn't allocate SSRC range of %u", len); return NULL; } *ranges = g_slist_insert_sorted(*ranges, range, (GCompareFunc)ssrc_range_compare); return range; } struct sipe_media_stream * sipe_media_stream_add(struct sipe_media_call *call, const gchar *id, SipeMediaType type, SipeIceVersion ice_version, gboolean initiator, guint32 ssrc_count) { struct sipe_core_private *sipe_private; struct sipe_media_stream_private *stream_private; struct sipe_backend_media_relays *backend_media_relays; guint min_port; guint max_port; sipe_private = SIPE_MEDIA_CALL_PRIVATE->sipe_private; backend_media_relays = sipe_backend_media_relays_convert( sipe_private->media_relays, sipe_private->media_relay_username, sipe_private->media_relay_password); min_port = sipe_private->min_media_port; max_port = sipe_private->max_media_port; switch (type) { case SIPE_MEDIA_AUDIO: min_port = sipe_private->min_audio_port; max_port = sipe_private->max_audio_port; break; case SIPE_MEDIA_VIDEO: min_port = sipe_private->min_video_port; max_port = sipe_private->max_audio_port; break; case SIPE_MEDIA_APPLICATION: if (sipe_strequal(id, "data")) { min_port = sipe_private->min_filetransfer_port; max_port = sipe_private->max_filetransfer_port; } else if (sipe_strequal(id, "applicationsharing")) { min_port = sipe_private->min_appsharing_port; max_port = sipe_private->max_appsharing_port; } break; } stream_private = g_new0(struct sipe_media_stream_private, 1); SIPE_MEDIA_STREAM->call = call; SIPE_MEDIA_STREAM->id = g_strdup(id); stream_private->write_queue = g_queue_new(); stream_private->async_reads = g_queue_new(); if (ssrc_count > 0) { SIPE_MEDIA_STREAM->ssrc_range = ssrc_range_allocate(&SIPE_MEDIA_CALL_PRIVATE->ssrc_ranges, ssrc_count); } SIPE_MEDIA_STREAM->media_source_id = SIPE_MSRTP_VSR_SOURCE_ANY; SIPE_MEDIA_STREAM->backend_private = sipe_backend_media_add_stream(SIPE_MEDIA_STREAM, type, ice_version, initiator, backend_media_relays, min_port, max_port); sipe_backend_media_relays_free(backend_media_relays); if (!SIPE_MEDIA_STREAM->backend_private) { sipe_media_stream_free(stream_private); return NULL; } if (type == SIPE_MEDIA_VIDEO) { /* Declare that we can send and receive Video Source Requests * as per [MS-SDPEXT] 3.1.5.30.2. */ sipe_media_stream_add_extra_attribute(SIPE_MEDIA_STREAM, "rtcp-fb", "* x-message app send:src recv:src"); sipe_media_stream_add_extra_attribute(SIPE_MEDIA_STREAM, "rtcp-rsize", NULL); sipe_media_stream_add_extra_attribute(SIPE_MEDIA_STREAM, "label", "main-video"); sipe_media_stream_add_extra_attribute(SIPE_MEDIA_STREAM, "x-source", "main-video"); } #ifdef HAVE_SRTP if (get_encryption_policy(sipe_private) != SIPE_ENCRYPTION_POLICY_REJECTED) { int i; stream_private->encryption_key = g_new0(guchar, SIPE_SRTP_KEY_LEN); for (i = 0; i != SIPE_SRTP_KEY_LEN; ++i) { stream_private->encryption_key[i] = rand() & 0xff; } stream_private->encryption_key_id = 1; // We don't know yet whether the stream will be // encrypted or not. Enable the require-encryption // property at stream creation time anyway, we may // disable it later if we don't receive encryption keys. sipe_backend_media_set_require_encryption(call, SIPE_MEDIA_STREAM, TRUE); } #endif SIPE_MEDIA_CALL_PRIVATE->streams = g_slist_append(SIPE_MEDIA_CALL_PRIVATE->streams, stream_private); return SIPE_MEDIA_STREAM; } static void append_2007_fallback_if_needed(struct sipe_media_call_private *call_private) { struct sipe_core_private *sipe_private = call_private->sipe_private; const gchar *marker = sip_transport_sdp_address_marker(sipe_private); const gchar *ip = sip_transport_ip_address(sipe_private); gchar *body; if (SIPE_CORE_PRIVATE_FLAG_IS(SFB) || sipe_media_get_sip_dialog(SIPE_MEDIA_CALL)->cseq != 0 || call_private->ice_version != SIPE_ICE_RFC_5245 || sipe_strequal(SIPE_MEDIA_CALL->with, sipe_private->test_call_bot_uri)) { return; } body = g_strdup_printf("Content-Type: application/sdp\r\n" "Content-Transfer-Encoding: 7bit\r\n" "Content-Disposition: session; handling=optional; ms-proxy-2007fallback\r\n" "\r\n" "o=- 0 0 IN %s %s\r\n" "s=session\r\n" "c=IN %s %s\r\n" "m=audio 0 RTP/AVP\r\n", marker, ip, marker, ip); sipe_media_add_extra_invite_section(SIPE_MEDIA_CALL, "multipart/alternative", body); } static void sipe_media_initiate_call(struct sipe_core_private *sipe_private, const char *with, SipeIceVersion ice_version, gboolean with_video) { struct sipe_media_call_private *call_private; if (sipe_core_media_get_call(SIPE_CORE_PUBLIC)) { return; } call_private = (struct sipe_media_call_private *) sipe_media_call_new(sipe_private, with, NULL, ice_version, 0); SIPE_MEDIA_CALL->call_reject_cb = av_call_reject_cb; if (!sipe_media_stream_add(SIPE_MEDIA_CALL, "audio", SIPE_MEDIA_AUDIO, call_private->ice_version, TRUE, 0)) { sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Error occurred"), _("Error creating audio stream")); sipe_media_hangup(call_private); return; } if (with_video && !sipe_media_stream_add(SIPE_MEDIA_CALL, "video", SIPE_MEDIA_VIDEO, call_private->ice_version, TRUE, VIDEO_SSRC_COUNT)) { sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Error occurred"), _("Error creating video stream")); sipe_media_hangup(call_private); return; } append_2007_fallback_if_needed(call_private); call_schedule_request_timeout(SIPE_MEDIA_CALL); // Processing continues in stream_initialized_cb } void sipe_core_media_initiate_call(struct sipe_core_public *sipe_public, const char *with, gboolean with_video) { sipe_media_initiate_call(SIPE_CORE_PRIVATE, with, SIPE_ICE_RFC_5245, with_video); } static void conference_audio_muted_cb(struct sipe_media_stream *stream, gboolean is_muted) { struct sipe_media_call *call = stream->call; if (!SIPE_MEDIA_CALL_PRIVATE->conference_session) { return; } sipe_conf_announce_audio_mute_state(SIPE_MEDIA_CALL_PRIVATE->sipe_private, SIPE_MEDIA_CALL_PRIVATE->conference_session, is_muted); } void sipe_core_media_connect_conference(struct sipe_core_public *sipe_public, struct sipe_chat_session *chat_session, gboolean with_video) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sipe_media_call_private *call_private; struct sipe_media_stream *stream; struct sip_session *session; SipeIceVersion ice_version; gchar *av_uri; if (!sipe_conf_supports_mcu_type(sipe_private, "audio-video")) { sipe_backend_notify_error(sipe_public, _("Join conference call"), _("Conference calls are not supported on this server.")); return; } session = sipe_session_find_chat(sipe_private, chat_session); if (sipe_core_media_get_call(sipe_public) || !session) { return; } av_uri = sipe_conf_build_uri(sipe_core_chat_id(sipe_public, chat_session), "audio-video"); if (!av_uri) { return; } session->is_call = TRUE; ice_version = SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013) ? SIPE_ICE_RFC_5245 : SIPE_ICE_DRAFT_6; call_private = (struct sipe_media_call_private *) sipe_media_call_new(sipe_private, av_uri, NULL, ice_version, 0); call_private->conference_session = session; SIPE_MEDIA_CALL->call_reject_cb = av_call_reject_cb; g_free(av_uri); stream = sipe_media_stream_add(SIPE_MEDIA_CALL, "audio", SIPE_MEDIA_AUDIO, call_private->ice_version, TRUE, 0); if (!stream) { sipe_backend_notify_error(sipe_public, _("Error occurred"), _("Error creating audio stream")); sipe_media_hangup(call_private); return; } stream->mute_cb = conference_audio_muted_cb; if (with_video) { stream = sipe_media_stream_add(SIPE_MEDIA_CALL, "video", SIPE_MEDIA_VIDEO, call_private->ice_version, TRUE, VIDEO_SSRC_COUNT); if (!stream) { sipe_backend_notify_error(sipe_public, _("Error occurred"), _("Error creating video stream")); sipe_media_hangup(call_private); } } // Processing continues in stream_initialized_cb } struct sipe_media_call * sipe_core_media_get_call(struct sipe_core_public *sipe_public) { struct sipe_media_call * result = NULL; GList *calls = g_hash_table_get_values(SIPE_CORE_PRIVATE->media_calls); GList *entry = calls; while (entry) { if (sipe_core_media_get_stream_by_id(entry->data, "audio")) { result = entry->data; break; } entry = entry->next; } g_list_free(calls); return result; } static gboolean phone_number_is_valid(const gchar *phone_number) { if (!phone_number || sipe_strequal(phone_number, "")) { return FALSE; } if (*phone_number == '+') { ++phone_number; } while (*phone_number != '\0') { if (!g_ascii_isdigit(*phone_number)) { return FALSE; } ++phone_number; } return TRUE; } void sipe_core_media_phone_call(struct sipe_core_public *sipe_public, const gchar *phone_number) { g_return_if_fail(sipe_public); SIPE_DEBUG_INFO("sipe_core_media_phone_call: %s", phone_number ? phone_number : "(null)"); if (phone_number_is_valid(phone_number)) { gchar *phone_uri = g_strdup_printf("sip:%s@%s;user=phone", phone_number, sipe_public->sip_domain); sipe_core_media_initiate_call(sipe_public, phone_uri, FALSE); g_free(phone_uri); } else { sipe_backend_notify_error(sipe_public, _("Unable to establish a call"), _("Invalid phone number")); } } void sipe_core_media_test_call(struct sipe_core_public *sipe_public) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; if (!sipe_private->test_call_bot_uri) { sipe_backend_notify_error(sipe_public, _("Unable to establish a call"), _("Audio Test Service is not available.")); return; } sipe_core_media_initiate_call(sipe_public, sipe_private->test_call_bot_uri, FALSE); } static struct sipe_media_call_private * sipe_media_from_sipmsg(struct sipe_core_private *sipe_private, struct sipmsg *msg) { return g_hash_table_lookup(sipe_private->media_calls, sipmsg_find_call_id_header(msg)); } static void transport_response_unsupported_sdp(struct sipe_core_private *sipe_private, struct sipmsg *msg) { sipmsg_add_header(msg, "ms-client-diagnostics", "52063;reason=\"Unsupported session description\""); sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL); } static void maybe_send_second_invite_response(struct sipe_media_call_private *call_private) { GSList *it; /* Second INVITE request had to be received and all streams must have * established candidate pairs before the response can be sent. */ if (!call_private->invitation) { return; } for (it = call_private->streams; it; it = it->next) { struct sipe_media_stream_private *stream_private = it->data; if (!stream_private->established) { return; } } send_response_with_session_description(call_private, 200, "OK"); #ifdef HAVE_XDATA for (it = call_private->streams; it; it = it->next) { struct sipe_media_stream_private *stream_private = it->data; stream_private->sdp_negotiation_concluded = TRUE; if (stream_private->writable) { // We've become writable. sipe_core_media_stream_writable(SIPE_MEDIA_STREAM, TRUE); } } #endif } struct sipe_media_call * process_incoming_invite_call(struct sipe_core_private *sipe_private, struct sipmsg *msg, const gchar *sdp) { return(process_incoming_invite_call_parsed_sdp(sipe_private, msg, sdpmsg_parse_msg(sdp))); } struct sipe_media_call * process_incoming_invite_call_parsed_sdp(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct sdpmsg *smsg) { struct sipe_media_call_private *call_private; gboolean has_new_media = FALSE; GSList *i; // Don't allow two voice calls in parallel. if (!strstr(msg->body, "m=data") && !strstr(msg->body, "m=applicationsharing")) { struct sipe_media_call *call = sipe_core_media_get_call(SIPE_CORE_PUBLIC); if (call && !is_media_session_msg(SIPE_MEDIA_CALL_PRIVATE, msg)) { sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL); sdpmsg_free(smsg); return NULL; } } call_private = sipe_media_from_sipmsg(sipe_private, msg); if (call_private) { char *self = sip_uri_self(sipe_private); if (sipe_strequal(SIPE_MEDIA_CALL->with, self)) { g_free(self); sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL); sdpmsg_free(smsg); return NULL; } g_free(self); } if (!smsg) { transport_response_unsupported_sdp(sipe_private, msg); if (call_private) { sipe_media_hangup(call_private); } return NULL; } if (!call_private) { gchar *with = sipmsg_parse_from_address(msg); SipeMediaCallFlags flags = 0; if (strstr(msg->body, "m=data") || strstr(msg->body, "m=applicationsharing")) { flags |= SIPE_MEDIA_CALL_NO_UI; } call_private = (struct sipe_media_call_private *) sipe_media_call_new(sipe_private, with, msg, smsg->ice_version, flags); if (!(flags & SIPE_MEDIA_CALL_NO_UI)) { SIPE_MEDIA_CALL->call_reject_cb = av_call_reject_cb; } g_free(with); } if (call_private->invitation) sipmsg_free(call_private->invitation); call_private->invitation = sipmsg_copy(msg); ssrc_range_update(&call_private->ssrc_ranges, smsg->media); // Create any new media streams for (i = smsg->media; i; i = i->next) { struct sdpmedia *media = i->data; gchar *id = media->name; SipeMediaType type; if ( media->port != 0 && !sipe_core_media_get_stream_by_id(SIPE_MEDIA_CALL, id)) { guint32 ssrc_count = 0; if (sipe_strequal(id, "audio")) type = SIPE_MEDIA_AUDIO; else if (sipe_strequal(id, "video")) { type = SIPE_MEDIA_VIDEO; ssrc_count = VIDEO_SSRC_COUNT; } else if (sipe_strequal(id, "data")) type = SIPE_MEDIA_APPLICATION; else if (sipe_strequal(id, "applicationsharing")) type = SIPE_MEDIA_APPLICATION; else continue; sipe_media_stream_add(SIPE_MEDIA_CALL, id, type, smsg->ice_version, FALSE, ssrc_count); has_new_media = TRUE; } } if (has_new_media) { sdpmsg_free(call_private->smsg); call_private->smsg = smsg; sip_transport_response(sipe_private, call_private->invitation, 180, "Ringing", NULL); call_schedule_ringing_timeout(SIPE_MEDIA_CALL); // Processing continues in stream_initialized_cb } else { apply_remote_message(call_private, smsg); sdpmsg_free(smsg); maybe_send_second_invite_response(call_private); } return SIPE_MEDIA_CALL; } void process_incoming_cancel_call(struct sipe_media_call_private *call_private, struct sipmsg *msg) { // We respond to the CANCEL request with 200 OK response and // with 487 Request Terminated to the remote INVITE in progress. sip_transport_response(call_private->sipe_private, msg, 200, "OK", NULL); if (call_private->invitation) { sip_transport_response(call_private->sipe_private, call_private->invitation, 487, "Request Terminated", NULL); } sipe_backend_media_reject(SIPE_MEDIA_CALL->backend_private, FALSE); } static gboolean sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans) { struct sipe_media_call_private *call_private; struct sip_dialog *dialog; int tmp_cseq; call_private = sipe_media_from_sipmsg(sipe_private, msg); if (!is_media_session_msg(call_private, msg)) return FALSE; dialog = sipe_media_get_sip_dialog(SIPE_MEDIA_CALL); if (!dialog) return FALSE; tmp_cseq = dialog->cseq; dialog->cseq = sip_transaction_cseq(trans) - 1; sip_transport_ack(sipe_private, dialog); dialog->cseq = tmp_cseq; dialog->outgoing_invite = NULL; return TRUE; } static gboolean sipe_media_send_final_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans) { struct sipe_media_call_private *call_private; #ifdef HAVE_XDATA GSList *it; #endif if (!sipe_media_send_ack(sipe_private, msg, trans)) return FALSE; call_private = sipe_media_from_sipmsg(sipe_private, msg); sipe_backend_media_accept(SIPE_MEDIA_CALL->backend_private, FALSE); #ifdef HAVE_XDATA for (it = call_private->streams; it; it = it->next) { struct sipe_media_stream_private *stream_private = it->data; stream_private->sdp_negotiation_concluded = TRUE; if (stream_private->writable) { // We've become writable. sipe_core_media_stream_writable(SIPE_MEDIA_STREAM, TRUE); } } #endif return TRUE; } void sipe_core_media_stream_candidate_pair_established(struct sipe_media_stream *stream) { struct sipe_media_call *call = stream->call; GList *active_candidates = sipe_backend_media_stream_get_active_local_candidates(stream); guint ready_components = g_list_length(active_candidates); sipe_media_candidate_list_free(active_candidates); if (ready_components != 2) { // We must have both RTP+RTCP candidate pairs established first. return; } if (SIPE_MEDIA_STREAM_PRIVATE->established) { return; } SIPE_MEDIA_STREAM_PRIVATE->established = TRUE; stream_schedule_cancel_timeout(call, SIPE_MEDIA_STREAM_PRIVATE); if (stream->candidate_pairs_established_cb) { stream->candidate_pairs_established_cb(stream); } if (sipe_backend_media_is_initiator(stream->call, NULL)) { GSList *streams = SIPE_MEDIA_CALL_PRIVATE->streams; for (; streams; streams = streams->next) { struct sipe_media_stream_private *s = streams->data; if (!s->established) { break; } } if (streams == NULL) { // All call streams have been established. sipe_invite_call(SIPE_MEDIA_CALL_PRIVATE, sipe_media_send_final_ack); } } else { maybe_send_second_invite_response(SIPE_MEDIA_CALL_PRIVATE); } } static gboolean maybe_retry_call_with_ice_version(struct sipe_core_private *sipe_private, struct sipe_media_call_private *call_private, SipeIceVersion ice_version, struct transaction *trans) { if (call_private->ice_version != ice_version && sip_transaction_cseq(trans) == 1) { GSList *i; gchar *with; gboolean with_video = FALSE; for (i = call_private->streams; i; i = i->next) { struct sipe_media_stream *stream = i->data; if (sipe_strequal(stream->id, "video")) { with_video = TRUE; } else if (!sipe_strequal(stream->id, "audio")) { /* Don't retry calls which are neither audio * nor video. */ return FALSE; } } with = g_strdup(SIPE_MEDIA_CALL->with); sipe_media_hangup(call_private); SIPE_DEBUG_INFO("Retrying call with ICEv%d.", ice_version == SIPE_ICE_DRAFT_6 ? 6 : 19); sipe_media_initiate_call(sipe_private, with, ice_version, with_video); g_free(with); return TRUE; } return FALSE; } static gboolean process_invite_call_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct transaction *trans) { const gchar *with; struct sipe_media_call_private *call_private; struct sip_dialog *dialog; struct sdpmsg *smsg; call_private = sipe_media_from_sipmsg(sipe_private,msg); if (!is_media_session_msg(call_private, msg)) return FALSE; dialog = sipe_media_get_sip_dialog(SIPE_MEDIA_CALL); with = dialog->with; dialog->outgoing_invite = NULL; if (msg->response == 603 || msg->response == 605) { // Call rejected by remote peer sipe_media_send_ack(sipe_private, msg, trans); sipe_backend_media_reject(SIPE_MEDIA_CALL->backend_private, FALSE); return TRUE; } if (msg->response >= 400) { // An error occurred const gchar *title; GString *desc = g_string_new(""); gboolean append_responsestr = FALSE; switch (msg->response) { case 480: { title = _("User unavailable"); if (sipmsg_parse_warning(msg, NULL) == 391) { g_string_append_printf(desc, _("%s does not want to be disturbed"), with); } else g_string_append_printf(desc, _("User %s is not available"), with); break; } case 415: // OCS/Lync really sends response string with 'Mutipart' typo. if (sipe_strequal(msg->responsestr, "Mutipart mime in content type not supported by Archiving CDR service") && maybe_retry_call_with_ice_version(sipe_private, call_private, SIPE_ICE_DRAFT_6, trans)) { return TRUE; } title = _("Unsupported media type"); break; case 488: { /* Check for incompatible encryption levels error. * * MS Lync 2010: * 488 Not Acceptable Here * ms-client-diagnostics: 52017;reason="Encryption levels dont match" * * older clients (and SIPE itself): * 488 Encryption Levels not compatible */ const gchar *ms_diag = sipmsg_find_header(msg, "ms-client-diagnostics"); SipeIceVersion retry_ice_version = SIPE_ICE_DRAFT_6; if (sipe_strequal(msg->responsestr, "Encryption Levels not compatible") || (ms_diag && g_str_has_prefix(ms_diag, "52017;"))) { title = _("Unable to establish a call"); g_string_append(desc, _("Encryption settings of peer are incompatible with ours.")); break; } /* Check if this is failed conference using * ICEv6 with reason "Error parsing SDP" and * retry using ICEv19. */ ms_diag = sipmsg_find_header(msg, "ms-diagnostics"); if (ms_diag && g_str_has_prefix(ms_diag, "7008;")) { retry_ice_version = SIPE_ICE_RFC_5245; } if (maybe_retry_call_with_ice_version(sipe_private, call_private, retry_ice_version, trans)) { return TRUE; } SIPE_FALLTHROUGH } default: title = _("Error occurred"); g_string_append(desc, _("Unable to establish a call")); append_responsestr = TRUE; break; } if (append_responsestr) { gchar *reason = sipmsg_get_ms_diagnostics_reason(msg); g_string_append_printf(desc, "\n%d %s", msg->response, msg->responsestr); if (reason) { g_string_append_printf(desc, "\n\n%s", reason); g_free(reason); } } sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, desc->str); g_string_free(desc, TRUE); sipe_media_send_ack(sipe_private, msg, trans); sipe_media_hangup(call_private); return TRUE; } sipe_dialog_parse(dialog, msg, TRUE); smsg = sdpmsg_parse_msg(msg->body); if (!smsg) { transport_response_unsupported_sdp(sipe_private, msg); sipe_media_hangup(call_private); return FALSE; } ssrc_range_update(&call_private->ssrc_ranges, smsg->media); apply_remote_message(call_private, smsg); sdpmsg_free(smsg); stream_schedule_timeout(SIPE_MEDIA_CALL); call_schedule_cancel_request_timeout(SIPE_MEDIA_CALL); sipe_media_send_ack(sipe_private, msg, trans); return TRUE; // Waits until sipe_core_media_candidate_pair_established() is invoked. } gboolean is_media_session_msg(struct sipe_media_call_private *call_private, struct sipmsg *msg) { if (!call_private) { return FALSE; } return sipe_media_from_sipmsg(call_private->sipe_private, msg) == call_private; } static void end_call(SIPE_UNUSED_PARAMETER gpointer key, struct sipe_media_call_private *call_private, SIPE_UNUSED_PARAMETER gpointer user_data) { struct sipe_backend_media *backend_private; backend_private = call_private->public.backend_private; if (!sipe_backend_media_is_initiator(SIPE_MEDIA_CALL, NULL) && !sipe_backend_media_accepted(backend_private)) { sip_transport_response(call_private->sipe_private, call_private->invitation, 480, "Temporarily Unavailable", NULL); } else if (call_private->session) { sipe_session_close(call_private->sipe_private, call_private->session); call_private->session = NULL; } sipe_media_hangup(call_private); } void sipe_media_handle_going_offline(struct sipe_core_private *sipe_private) { g_hash_table_foreach(sipe_private->media_calls, (GHFunc) end_call, NULL); } gboolean sipe_media_is_conference_call(struct sipe_media_call_private *call_private) { return g_strstr_len(SIPE_MEDIA_CALL->with, -1, "app:conf:audio-video:") != NULL; } struct sipe_core_private * sipe_media_get_sipe_core_private(struct sipe_media_call *call) { g_return_val_if_fail(call, NULL); return SIPE_MEDIA_CALL_PRIVATE->sipe_private; } struct sip_dialog * sipe_media_get_sip_dialog(struct sipe_media_call *call) { struct sip_session *session; g_return_val_if_fail(call, NULL); session = SIPE_MEDIA_CALL_PRIVATE->session; if (!session || !session->dialogs) { return NULL; } return session->dialogs->data; } static void sipe_media_relay_free(struct sipe_media_relay *relay) { g_free(relay->hostname); if (relay->dns_query) sipe_backend_dns_query_cancel(relay->dns_query); g_free(relay); } void sipe_media_relay_list_free(GSList *list) { for (; list; list = g_slist_delete_link(list, list)) sipe_media_relay_free(list->data); } static void relay_ip_resolved_cb(struct sipe_media_relay* relay, const gchar *ip, SIPE_UNUSED_PARAMETER guint port) { gchar *hostname = relay->hostname; relay->dns_query = NULL; if (ip && port) { relay->hostname = g_strdup(ip); SIPE_DEBUG_INFO("Media relay %s resolved to %s.", hostname, ip); } else { relay->hostname = NULL; SIPE_DEBUG_INFO("Unable to resolve media relay %s.", hostname); } g_free(hostname); } static gboolean process_get_av_edge_credentials_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { g_free(sipe_private->media_relay_username); g_free(sipe_private->media_relay_password); sipe_media_relay_list_free(sipe_private->media_relays); sipe_private->media_relay_username = NULL; sipe_private->media_relay_password = NULL; sipe_private->media_relays = NULL; if (msg->response >= 400) { SIPE_DEBUG_INFO_NOFORMAT("process_get_av_edge_credentials_response: SERVICE response is not 200. " "Failed to obtain A/V Edge credentials."); return FALSE; } if (msg->response == 200) { sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen); if (sipe_strequal("OK", sipe_xml_attribute(xn_response, "reasonPhrase"))) { const sipe_xml *xn_credentials = sipe_xml_child(xn_response, "credentialsResponse/credentials"); const sipe_xml *xn_relays = sipe_xml_child(xn_response, "credentialsResponse/mediaRelayList"); const sipe_xml *item; GSList *relays = NULL; item = sipe_xml_child(xn_credentials, "username"); sipe_private->media_relay_username = sipe_xml_data(item); item = sipe_xml_child(xn_credentials, "password"); sipe_private->media_relay_password = sipe_xml_data(item); for (item = sipe_xml_child(xn_relays, "mediaRelay"); item; item = sipe_xml_twin(item)) { struct sipe_media_relay *relay = g_new0(struct sipe_media_relay, 1); const sipe_xml *node; gchar *tmp; node = sipe_xml_child(item, "hostName"); relay->hostname = sipe_xml_data(node); node = sipe_xml_child(item, "udpPort"); if (node) { tmp = sipe_xml_data(node); if (tmp) { relay->udp_port = atoi(tmp); g_free(tmp); } } node = sipe_xml_child(item, "tcpPort"); if (node) { tmp = sipe_xml_data(node); if (tmp) { relay->tcp_port = atoi(tmp); g_free(tmp); } } relays = g_slist_append(relays, relay); relay->dns_query = sipe_backend_dns_query_a( SIPE_CORE_PUBLIC, relay->hostname, relay->udp_port, (sipe_dns_resolved_cb) relay_ip_resolved_cb, relay); SIPE_DEBUG_INFO("Media relay: %s TCP: %d UDP: %d", relay->hostname, relay->tcp_port, relay->udp_port); } sipe_private->media_relays = relays; } sipe_xml_free(xn_response); } return TRUE; } void sipe_media_get_av_edge_credentials(struct sipe_core_private *sipe_private) { // TODO: re-request credentials after duration expires? static const char CRED_REQUEST_XML[] = "" "" "%s" "%s" "480" "" ""; int request_id = rand(); gchar *self; gchar *body; if (!sipe_private->mras_uri) return; self = sip_uri_self(sipe_private); body = g_strdup_printf( CRED_REQUEST_XML, request_id, self, sipe_private->mras_uri, request_id, self, SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ? "internet" : "intranet"); g_free(self); sip_transport_service(sipe_private, sipe_private->mras_uri, "Content-Type: application/msrtc-media-relay-auth+xml\r\n", body, process_get_av_edge_credentials_response); g_free(body); } void sipe_media_add_extra_invite_section(struct sipe_media_call *call, const gchar *invite_content_type, gchar *body) { g_free(SIPE_MEDIA_CALL_PRIVATE->extra_invite_section); g_free(SIPE_MEDIA_CALL_PRIVATE->invite_content_type); SIPE_MEDIA_CALL_PRIVATE->extra_invite_section = body; SIPE_MEDIA_CALL_PRIVATE->invite_content_type = g_strdup(invite_content_type); } void sipe_media_stream_add_extra_attribute(struct sipe_media_stream *stream, const gchar *name, const gchar *value) { SIPE_MEDIA_STREAM_PRIVATE->extra_sdp = sipe_utils_nameval_add(SIPE_MEDIA_STREAM_PRIVATE->extra_sdp, name, value); } #ifdef HAVE_XDATA void sipe_core_media_stream_readable(struct sipe_media_stream *stream) { g_return_if_fail(stream); if (g_queue_is_empty(SIPE_MEDIA_STREAM_PRIVATE->async_reads) && stream->read_cb) { stream->read_cb(stream); } while (!g_queue_is_empty(SIPE_MEDIA_STREAM_PRIVATE->async_reads)) { struct async_read_data *data; guint8 *pos; gssize len; gssize bytes_read; data = g_queue_peek_head(SIPE_MEDIA_STREAM_PRIVATE->async_reads); pos = data->buffer + SIPE_MEDIA_STREAM_PRIVATE->read_pos; len = data->len - SIPE_MEDIA_STREAM_PRIVATE->read_pos; bytes_read = sipe_backend_media_stream_read(stream, pos, len); if (bytes_read == -1) { struct sipe_media_call *call = stream->call; struct sipe_core_private *sipe_private = SIPE_MEDIA_CALL_PRIVATE->sipe_private; sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Media error"), _("Error while reading from stream")); sipe_media_hangup(SIPE_MEDIA_CALL_PRIVATE); return; } SIPE_MEDIA_STREAM_PRIVATE->read_pos += bytes_read; if (SIPE_MEDIA_STREAM_PRIVATE->read_pos == data->len) { data->callback(stream, data->buffer, data->len); SIPE_MEDIA_STREAM_PRIVATE->read_pos = 0; g_queue_pop_head(SIPE_MEDIA_STREAM_PRIVATE->async_reads); g_free(data); } else { // Still not enough data to finish the read. return; } } } void sipe_media_stream_read_async(struct sipe_media_stream *stream, gpointer buffer, gsize len, sipe_media_stream_read_callback callback) { struct async_read_data *data; g_return_if_fail(stream && buffer && callback); data = g_new0(struct async_read_data, 1); data->buffer = buffer; data->len = len; data->callback = callback; g_queue_push_tail(SIPE_MEDIA_STREAM_PRIVATE->async_reads, data); } static void stream_append_buffer(struct sipe_media_stream *stream, guint8 *buffer, guint len) { GByteArray *b = g_byte_array_sized_new(len); g_byte_array_append(b, buffer, len); g_queue_push_tail(SIPE_MEDIA_STREAM_PRIVATE->write_queue, b); } gboolean sipe_media_stream_write(struct sipe_media_stream *stream, gpointer buffer, gsize len) { if (!sipe_media_stream_is_writable(stream)) { stream_append_buffer(stream, buffer, len); return FALSE; } else { guint written; written = sipe_backend_media_stream_write(stream, buffer, len); if (written == len) { return TRUE; } stream_append_buffer(stream, (guint8 *)buffer + written, len - written); return FALSE; } } void sipe_core_media_stream_writable(struct sipe_media_stream *stream, gboolean writable) { SIPE_MEDIA_STREAM_PRIVATE->writable = writable; if (!writable) { return; } while (!g_queue_is_empty(SIPE_MEDIA_STREAM_PRIVATE->write_queue)) { GByteArray *b; guint written; b = g_queue_peek_head(SIPE_MEDIA_STREAM_PRIVATE->write_queue); written = sipe_backend_media_stream_write(stream, b->data, b->len); if (written != b->len) { g_byte_array_remove_range(b, 0, written); return; } g_byte_array_unref(b); g_queue_pop_head(SIPE_MEDIA_STREAM_PRIVATE->write_queue); } if (sipe_media_stream_is_writable(stream) && stream->writable_cb) { stream->writable_cb(stream); } } gboolean sipe_media_stream_is_writable(struct sipe_media_stream *stream) { return SIPE_MEDIA_STREAM_PRIVATE->writable && SIPE_MEDIA_STREAM_PRIVATE->sdp_negotiation_concluded && g_queue_is_empty(SIPE_MEDIA_STREAM_PRIVATE->write_queue); } #endif void sipe_media_stream_set_data(struct sipe_media_stream *stream, gpointer data, GDestroyNotify free_func) { struct sipe_media_stream_private *stream_private = SIPE_MEDIA_STREAM_PRIVATE; g_return_if_fail(stream_private); if (stream_private->data && stream_private->data_free_func) { stream_private->data_free_func(stream_private->data); } stream_private->data = data; stream_private->data_free_func = free_func; } gpointer sipe_media_stream_get_data(struct sipe_media_stream *stream) { g_return_val_if_fail(stream, NULL); return SIPE_MEDIA_STREAM_PRIVATE->data; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-media.h ================================================ /** * @file sipe-media.h * * pidgin-sipe * * Copyright (C) 2010 Jakub Adam * Copyright (C) 2016-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sdpmsg; struct sipmsg; struct sipe_core_private; struct sipe_media_call_private; struct sipe_media_stream; typedef void (* sipe_media_stream_read_callback)(struct sipe_media_stream *stream, guint8 *buffer, gsize len); /** * Creates a new media call. * * @param sipe_private (in) SIPE core data. * @param with (in) SIP URI of the second participant. * @param msg (in) SIP INVITE message from the second participant or NULL if we * are the call initiator. * @param ice_version (in) version of ICE protocol to use when establishing the * connection path. * @param flags (in) bitwise-or'd media call flags. * * @return a new @c sipe_media_call structure or @c NULL on error. */ struct sipe_media_call * sipe_media_call_new(struct sipe_core_private *sipe_private, const gchar* with, struct sipmsg *msg, SipeIceVersion ice_version, SipeMediaCallFlags flags); /** * Retrieves the @c sipe_media_call structure representing the ongoing call with * the given participant. * * @param sipe_private (in) SIPE core data. * @param with (in) SIP URI of the call participant. * * @return a @c sipe_media_call structure or @c NULL if there isn't a call going * on with @c with. */ struct sipe_media_call * sipe_media_call_find(struct sipe_core_private *sipe_private, const gchar *with); /** * Adds a new media stream to a call. * * @param call (in) a media call. * @param id (in) a string identifier for the media stream. * @param type (in) a type of stream's content (audio, video, data, ...). * @param ice_version (in) a version of ICE to use when negotiating the * connection. * @param initiator (in) @c TRUE if our client is the initiator of the stream. * @param ssrc_count (in) number of RTP Synchronization source identifiers to * allocate for the stream. * * @return a new @c sipe_media_stream structure or @c NULL on error. */ struct sipe_media_stream * sipe_media_stream_add(struct sipe_media_call *call, const gchar *id, SipeMediaType type, SipeIceVersion ice_version, gboolean initiator, guint32 ssrc_count); /** * Handles incoming SIP INVITE message to start a media session. * * @param sipe_private (in) SIPE core data. * @param msg (in) a SIP INVITE message * @param sdp (in) media session description string * * @return @c sipe_media_call structure created or updated by @c msg. * The function returns @c NULL on error or if the call was rejected. */ struct sipe_media_call * process_incoming_invite_call(struct sipe_core_private *sipe_private, struct sipmsg *msg, const gchar *sdp); /** * Handles incoming SIP INVITE message to start a media session. * * @param sipe_private (in) SIPE core data. * @param msg (in) a SIP INVITE message * @param smsg (in) parsed media session description; the function takes * ownership of the sdpmsg structure and will free it when no longer * needed. * * @return @c sipe_media_call structure created or updated by @c msg. * The function returns @c NULL on error or if the call was rejected. */ struct sipe_media_call * process_incoming_invite_call_parsed_sdp(struct sipe_core_private *sipe_private, struct sipmsg *msg, struct sdpmsg *smsg); /** * Handles incoming SIP CANCEL message. * * @param call_private (in) SIPE media call data. * @param msg (in) a SIP CANCEL message */ void process_incoming_cancel_call(struct sipe_media_call_private *call_private, struct sipmsg *msg); /** * Hangs up a media session and closes all allocated resources. * * @param sipe_private (in) media call data. */ void sipe_media_hangup(struct sipe_media_call_private *call_private); /** * Call before SIP account logs of the server. Function hangs up the call and * notifies remote participant according to the actual state of call * negotiation. * * @param sipe_private (in) SIPE core data. */ void sipe_media_handle_going_offline(struct sipe_core_private *sipe_private); /** * Checks whether the given media is a conference call. * * @return @c TRUE if call is a conference, @c FALSE when it is a PC2PC call. */ gboolean sipe_media_is_conference_call(struct sipe_media_call_private *call_private); /** * Retrieves the sipe core structure the given call is associated to. * * @param call (in) media call data * * @return @c sipe_core_private structure. */ struct sipe_core_private * sipe_media_get_sipe_core_private(struct sipe_media_call *call); /** * Retrieves a SIP dialog associated with the call. * * @param call (in) media call data * * @return a @c sip_dialog structure associated with @c call. */ struct sip_dialog * sipe_media_get_sip_dialog(struct sipe_media_call *call); /** * Checks whether SIP message belongs to the session of the given media call. * * Test is done on the basis of the Call-ID of the message. * * @param call_private (in) media call data * @param msg (in) a SIP message * * @return @c TRUE if the SIP message belongs to the media session. */ gboolean is_media_session_msg(struct sipe_media_call_private *call_private, struct sipmsg *msg); /** * Sends a request to mras URI for the credentials to the A/V edge server. * Given @c sipe_core_private must have non-NULL mras_uri. When the valid * response is received, media_relay_username, media_relay_password and * media_relays attributes of the sipe core are filled. * * @param sipe_private (in) SIPE core data. */ void sipe_media_get_av_edge_credentials(struct sipe_core_private *sipe_private); /** * Turns @c call's next INVITE into a MIME multipart message with @c body as * the extra part. * * @param call (in) media call data * @param invite_content_type (in) MIME media type of the resulting message, * e.g. "multipart/alternative" or "multipart/mixed" * @param body (in) content to add as the second part of the INVITE message; * @c call takes ownership of @c body and will free it after use */ void sipe_media_add_extra_invite_section(struct sipe_media_call *call, const gchar *invite_content_type, gchar *body); /** * Adds application-specific SDP attribute to the stream. This will be sent as * a part of our SIP INVITE alongside standard media description, formatted as: * * a=name[:value] * * @param stream (in) media stream data * @param name (in) attribute name * @param value (in) optional value of the attribute */ void sipe_media_stream_add_extra_attribute(struct sipe_media_stream *stream, const gchar *name, const gchar *value); /** * Schedules asynchronous read of @c len bytes from @c stream into @c buffer. * When enough data becomes available in the stream, the buffer is filled and * @c callback gets invoked. * * It's possible to call sipe_media_stream_read_async() multiple times; every * request is placed into a FIFO queue. Media core does not call @c read_cb of * @c stream while there is some asynchronous read in progress. * * @param stream (in) media stream data * @param buffer (in) an empty data buffer * @param len (in) length of @c buffer * @param callback (in) a function to call when @c buffer gets filled with * input data */ void sipe_media_stream_read_async(struct sipe_media_stream *stream, gpointer buffer, gsize len, sipe_media_stream_read_callback callback); /** * Writes @c len bytes from @c buffer into @c stream. * * If @c stream is not in writable state, Sipe will store the data in * an internal queue which gets emptied once the stream becomes writable again. * Users should check the stream state using sipe_media_stream_is_writable() * before sending excessive data into the stream. * * @param stream (in) media stream data * @param buffer (in) data to send * @param len (in) length of @c buffer * * @return @c TRUE when @c buffer was written into the stream as a whole, * @c FALSE when some data had to be queued for later (and thus * the stream is now in unwritable state). */ gboolean sipe_media_stream_write(struct sipe_media_stream *stream, gpointer buffer, gsize len); /** * Checks whether a @c SIPE_MEDIA_APPLICATION stream is in writable state. * * @param stream (in) media stream data * * @return @c TRUE if @c stream is writable, otherwise @c FALSE. */ gboolean sipe_media_stream_is_writable(struct sipe_media_stream *stream); /** * Associates user data with the media stream. * * @param stream (in) media stream data * @param data (in) user data * @param free_func (in) function to free @c data when @c stream is destroyed */ void sipe_media_stream_set_data(struct sipe_media_stream *stream, gpointer data, GDestroyNotify free_func); /** * Returns user data associated with the media stream. * * @param stream (in) media stream data * * @return user data */ gpointer sipe_media_stream_get_data(struct sipe_media_stream *stream); /** * Deallocates the opaque list of media relay structures * * @param list (in) GSList to free */ void sipe_media_relay_list_free(GSList *list); ================================================ FILE: src/core/sipe-mime-common.c ================================================ /** * @file sipe-mime.c * * pidgin-sipe * * Copyright (C) 2015 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "sipe-common.h" #include "sipe-mime.h" #include "sipe-utils.h" struct parts_contain_cb_data { const gchar * type; gboolean result; }; static void parts_contain_cb(gpointer user_data, const GSList *fields, SIPE_UNUSED_PARAMETER const gchar *body, SIPE_UNUSED_PARAMETER gsize length) { struct parts_contain_cb_data *data = user_data; if (!data->result && sipe_strequal(data->type, sipe_utils_nameval_find(fields, "Content-Type"))) { data->result = TRUE; } } gboolean sipe_mime_parts_contain(const gchar *type, const gchar *body, const gchar *part_type) { struct parts_contain_cb_data data; data.type = part_type; data.result = FALSE; sipe_mime_parts_foreach(type, body, parts_contain_cb, &data); return data.result; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-mime.c ================================================ /** * @file sipe-mime.c * * pidgin-sipe * * Copyright (C) 2010-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "sipe-common.h" #include /* * GMIME interfaces fail to compile on ARM architecture with -Wcast-align * * Diagnostic #pragma was added in GCC 4.2.0 */ #if defined(__GNUC__) #if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ >= 5) #if defined(__ARMEL__) || defined(__ARMEB__) || defined(__mips__) || defined(__sparc__) || (defined(__powerpc__) && defined(__NO_FPRS__)) #pragma GCC diagnostic ignored "-Wcast-align" #endif #endif #endif #include #if !GMIME_CHECK_VERSION(2,6,0) #error GMime >= 2.6.0 is required to build SIPE #endif #if !GMIME_CHECK_VERSION(3,0,0) #define g_mime_part_get_content(p) g_mime_part_get_content_object(p) #define g_mime_parser_construct_part(p, o) g_mime_parser_construct_part(p) #endif #include "sipe-mime.h" #include "sipe-backend.h" #include "sipe-utils.h" void sipe_mime_init(void) { g_mime_init( #if !GMIME_CHECK_VERSION(3,0,0) 0 #endif ); } void sipe_mime_shutdown(void) { g_mime_shutdown(); } struct gmime_callback_data { sipe_mime_parts_cb callback; gpointer user_data; }; static GSList *gmime_fields_to_nameval(GMimeObject *part) { GMimeHeaderList *headers = g_mime_object_get_header_list(part); GSList *fields = NULL; #if GMIME_CHECK_VERSION(3,0,0) guint count = g_mime_header_list_get_count(headers); guint index; for (index = 0; index < count; index++) { GMimeHeader *header = g_mime_header_list_get_header_at(headers, index); fields = sipe_utils_nameval_add(fields, g_mime_header_get_name(header), g_mime_header_get_value(header)); } #else GMimeHeaderIter *iter = g_mime_header_iter_new(); if (g_mime_header_list_get_iter(headers, iter)) { do { fields = sipe_utils_nameval_add(fields, g_mime_header_iter_get_name(iter), g_mime_header_iter_get_value(iter)); } while (g_mime_header_iter_next(iter)); } g_mime_header_iter_free(iter); #endif return fields; } static void gmime_callback(SIPE_UNUSED_PARAMETER GMimeObject *parent, GMimeObject *part, gpointer user_data) { GMimeDataWrapper *data = g_mime_part_get_content((GMimePart *)part); if (data) { GMimeStream *stream = g_mime_data_wrapper_get_stream(data); if (stream) { ssize_t length = 0; const char *encoding; gchar *buffer; GString *content; encoding = g_mime_object_get_header(part, "Content-Transfer-Encoding"); if (encoding) { GMimeFilter *filter = g_mime_filter_basic_new( g_mime_content_encoding_from_string(encoding), FALSE); stream = g_mime_stream_filter_new (stream); g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream), filter); g_object_unref (filter); } /* g_mime_stream_read() might not read everything in one call */ content = g_string_new(NULL); buffer = g_malloc(4096); while ((length = g_mime_stream_read(stream, buffer, 4096)) > 0) { g_string_append_len(content, buffer, length); } g_free(buffer); if (length == 0) { struct gmime_callback_data *cd = user_data; GSList *fields = gmime_fields_to_nameval(part); cd->callback(cd->user_data, fields, content->str, content->len); sipe_utils_nameval_free(fields); } g_string_free(content, TRUE); if (encoding) { // Unref GMimeStreamFilter wrapping GMimeStream. g_object_unref(stream); } } } } void sipe_mime_parts_foreach(const gchar *type, const gchar *body, sipe_mime_parts_cb callback, gpointer user_data) { gchar *doc = g_strdup_printf("Content-Type: %s\r\n\r\n%s", type, body); GMimeStream *stream = g_mime_stream_mem_new_with_buffer(doc, strlen(doc)); if (stream) { GMimeParser *parser = g_mime_parser_new_with_stream(stream); GMimeMultipart *multipart = (GMimeMultipart *)g_mime_parser_construct_part(parser, NULL); if (multipart) { struct gmime_callback_data cd = {callback, user_data}; SIPE_DEBUG_INFO("sipe_mime_parts_foreach: %d parts", g_mime_multipart_get_count(multipart)); g_mime_multipart_foreach(multipart, gmime_callback, &cd); g_object_unref(multipart); } g_object_unref(parser); g_object_unref(stream); } g_free(doc); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-msrtp.c ================================================ /** * @file sipe-msrtp.c * * pidgin-sipe * * Copyright (C) 2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "sipe-core.h" #define _SIPE_WRITE(where, idx, size, shift, value) \ ((guint8 *) where)[idx] = (((guint##size)value) >> shift) & 0xff #define SIPE_WRITE_UINT8(where, value) \ _SIPE_WRITE(where++, 0, 8, 0, value); #define SIPE_WRITE_UINT16_BE(where, value) \ _SIPE_WRITE(where, 0, 16, 8, value); \ _SIPE_WRITE(where, 1, 16, 0, value); \ where += 2; #define SIPE_WRITE_UINT32_BE(where, value) \ _SIPE_WRITE(where, 0, 32, 24, value); \ _SIPE_WRITE(where, 1, 32, 16, value); \ _SIPE_WRITE(where, 2, 32, 8, value); \ _SIPE_WRITE(where, 3, 32, 0, value); \ where += 4; #define SIPE_WRITE_UINT64_BE(where, value) \ _SIPE_WRITE(where, 0, 64, 56, value); \ _SIPE_WRITE(where, 1, 64, 48, value); \ _SIPE_WRITE(where, 2, 64, 40, value); \ _SIPE_WRITE(where, 3, 64, 32, value); \ _SIPE_WRITE(where, 4, 64, 24, value); \ _SIPE_WRITE(where, 5, 64, 16, value); \ _SIPE_WRITE(where, 6, 64, 8, value); \ _SIPE_WRITE(where, 7, 64, 0, value); \ where += 8; enum { VSR_FLAG_NONE = 0, VSR_FLAG_H264_CGS_REWRITE = 1, VSR_FLAG_H264_CONSTRAINED_PROFILE_ONLY = 2, VSR_FLAG_RT_NO_SP_FRAMES = 4, VSR_FLAG_H264_NO_SEAMLESS_RESOLUTION_CHANGE = 8 }; enum { VSR_ASPECT_4_BY_3 = 1, VSR_ASPECT_16_BY_9 = 2, VSR_ASPECT_1_BY_1 = 4, VSR_ASPECT_3_BY_4 = 8, VSR_ASPECT_9_BY_16 = 16, VSR_ASPECT_20_BY_3 = 32, }; enum { VSR_FPS_7_5 = 1, VSR_FPS_12_5 = 2, VSR_FPS_15 = 4, VSR_FPS_25 = 8, VSR_FPS_30 = 16, VSR_FPS_50 = 32, VSR_FPS_60 = 64 }; enum { NAL_UNIT_TYPE_SEI = 6, NAL_UNIT_TYPE_PACSI = 30 }; enum { MS_LD_FPS_IDX_7_5 = 0, MS_LD_FPS_IDX_12_5 = 1, MS_LD_FPS_IDX_15 = 2, MS_LD_FPS_IDX_25 = 3, MS_LD_FPS_IDX_30 = 4, MS_LD_FPS_IDX_50 = 5, MS_LD_FPS_IDX_60 = 6 }; void sipe_core_msrtp_write_video_source_request(guint8 *buffer, guint8 payload_type, guint32 media_source_id) { static guint8 bit_rate_histogram[] = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static guint8 quality_report_histogram[] = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* VSR FCI field - from [MS-RTP] 2.2.12.2 */ /* Header */ SIPE_WRITE_UINT16_BE(buffer, 0x1); // AFB Type // Length SIPE_WRITE_UINT16_BE(buffer, SIPE_MSRTP_VSR_HEADER_LEN + SIPE_MSRTP_VSR_ENTRY_LEN); // Requested Media Source ID SIPE_WRITE_UINT32_BE(buffer, media_source_id); SIPE_WRITE_UINT16_BE(buffer, 1); // Request Id SIPE_WRITE_UINT16_BE(buffer, 0); // Reserve1 SIPE_WRITE_UINT8(buffer, 0); // Version SIPE_WRITE_UINT8(buffer, 0 << 7); // Keyframe (1bit) + Reserve2 SIPE_WRITE_UINT8(buffer, 1); // Number of Entries SIPE_WRITE_UINT8(buffer, SIPE_MSRTP_VSR_ENTRY_LEN); // Entry Length SIPE_WRITE_UINT32_BE(buffer, 0x0); // Reserve3 /* Entry */ SIPE_WRITE_UINT8(buffer, payload_type); // Payload Type SIPE_WRITE_UINT8(buffer, 1); // UCConfig Mode SIPE_WRITE_UINT8(buffer, VSR_FLAG_NONE); // Flags SIPE_WRITE_UINT8(buffer, VSR_ASPECT_4_BY_3); // Aspect Ratio Bit Mask SIPE_WRITE_UINT16_BE(buffer, 432); // Maximum Width SIPE_WRITE_UINT16_BE(buffer, 432); // Maximum Height SIPE_WRITE_UINT32_BE(buffer, 350000); // Minimum bit rate SIPE_WRITE_UINT32_BE(buffer, 0); // Reserve SIPE_WRITE_UINT32_BE(buffer, 10000); // Bit rate per level // Bit rate Histogram memcpy(buffer, bit_rate_histogram, sizeof (bit_rate_histogram)); buffer += sizeof (bit_rate_histogram); SIPE_WRITE_UINT32_BE(buffer, VSR_FPS_15); // Frame rate bit mask SIPE_WRITE_UINT16_BE(buffer, 0); // Number of MUST instances SIPE_WRITE_UINT16_BE(buffer, 1); // Number of MAY instances // Quality Report Histogram memcpy(buffer, quality_report_histogram, sizeof (quality_report_histogram)); buffer += sizeof (quality_report_histogram); SIPE_WRITE_UINT32_BE(buffer, 103680); // Maximum number of pixels } static void write_nal_unit_header(guint8 *dest, gboolean f_bit, guint8 nal_ref_idc, guint8 type) { *dest = f_bit ? 0x80 : 0x00; *dest |= nal_ref_idc << 5; *dest |= type; } static void write_ms_layer_description(guint8 *buffer, guint16 width, guint16 height, guint32 bitrate, guint8 framerate_idx, gboolean base_layer, guint16 prid, gboolean constrained_baseline) { // Coded width and height SIPE_WRITE_UINT16_BE(buffer, width); SIPE_WRITE_UINT16_BE(buffer, height); // Display width and height SIPE_WRITE_UINT16_BE(buffer, width); SIPE_WRITE_UINT16_BE(buffer, height); SIPE_WRITE_UINT32_BE(buffer, bitrate); *buffer = framerate_idx << 3; *buffer |= base_layer ? 0 : 1; ++buffer; *buffer = prid << 2; *buffer |= (constrained_baseline ? 1 : 0) << 1; } gsize sipe_core_msrtp_write_video_scalability_info(guint8 *buffer, guint8 nal_count) { static const guint8 MS_STREAM_LAYOUT_SEI_UUID[] = { 0x13, 0x9f, 0xb1, 0xa9, 0x44, 0x6a, 0x4d, 0xec, 0x8c, 0xbf, 0x65, 0xb1, 0xe1, 0x2d, 0x2c, 0xfd }; static const guint8 MS_BITSTREAM_INFO_SEI_UUID[] = { 0x05, 0xfb, 0xc6, 0xb9, 0x5a, 0x80, 0x40, 0xe5, 0xa2, 0x2a, 0xab, 0x40, 0x20, 0x26, 0x7e, 0x26 }; guint8 *ptr = buffer; // Write PACSI (RFC6190 section 4.9) SIPE_WRITE_UINT32_BE(ptr, 77); // Length of the NAL write_nal_unit_header(ptr++, FALSE, 3, NAL_UNIT_TYPE_PACSI); *ptr = 1 << 7; // Reserved bit = 1 *ptr |= 1 << 6; // I-bit = 1 if any aggregated unit has it set to 1 *ptr |= 0; // Priority = 0 ++ptr; *ptr = 1 << 7; // No Inter Layer Prediction = True *ptr |= 0 << 4; // Dependency ID = 0 *ptr |= 0; // Quality ID ++ptr; *ptr = 0; // Temporal ID *ptr |= 0 << 4; // Use Ref Base Picture = False *ptr |= 0 << 3; // Discardable = False *ptr |= 1 << 2; // Output = True *ptr |= 3; // Reserved ++ptr; // X|Y|T|A|P|C|S|E flags: DONC & First NAL = True SIPE_WRITE_UINT8(ptr, 0x22); SIPE_WRITE_UINT16_BE(ptr, 1); // Cross Session Decoder Order Number // Stream Layout SEI Message (MS-H264PF section 2.2.5) SIPE_WRITE_UINT16_BE(ptr, 45); // Size of the NAL write_nal_unit_header(ptr++, FALSE, 0, NAL_UNIT_TYPE_SEI); SIPE_WRITE_UINT8(ptr, 5); // Payload type (user data unregistered) SIPE_WRITE_UINT8(ptr, 42); // Payload size memcpy(ptr, MS_STREAM_LAYOUT_SEI_UUID, sizeof (MS_STREAM_LAYOUT_SEI_UUID)); ptr += sizeof (MS_STREAM_LAYOUT_SEI_UUID); // Layer Presence - layer with PRID 0 present SIPE_WRITE_UINT64_BE(ptr, 0x0100000000000000); SIPE_WRITE_UINT8(ptr, 1); // Layer Description Present = True SIPE_WRITE_UINT8(ptr, 16); // Layer Description Size write_ms_layer_description(ptr, 212, 160, 50250, MS_LD_FPS_IDX_7_5, TRUE, 0, TRUE); ptr += 16; // MS Bitstream Info SEI Message ([MS-H264PF] section 2.2.7) SIPE_WRITE_UINT16_BE(ptr, 21); // Size of the NAL write_nal_unit_header(ptr++, FALSE, 0, NAL_UNIT_TYPE_SEI); SIPE_WRITE_UINT8(ptr, 5); // Payload type (user data unregistered) SIPE_WRITE_UINT8(ptr, 18); // Payload size memcpy(ptr, MS_BITSTREAM_INFO_SEI_UUID, sizeof (MS_BITSTREAM_INFO_SEI_UUID)); ptr += sizeof (MS_BITSTREAM_INFO_SEI_UUID); /* Reference frame count. This should increment with each reference * frame, but clients apparently don't care about the value. */ SIPE_WRITE_UINT8(ptr, 1); // Number of NAL units described by this PACSI NAL unit. SIPE_WRITE_UINT8(ptr, nal_count); return ptr - buffer; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-notify.c ================================================ /** * @file sipe-notify.c * * pidgin-sipe * * Copyright (C) 2011-2019 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Process incoming SIP NOTIFY/BENOTIFY messages * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sipmsg.h" #include "sip-csta.h" #include "sip-soap.h" #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-cal.h" #include "sipe-conf.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-group.h" #include "sipe-groupchat.h" #include "sipe-media.h" #include "sipe-mime.h" #include "sipe-nls.h" #include "sipe-notify.h" #include "sipe-ocs2005.h" #include "sipe-ocs2007.h" #include "sipe-status.h" #include "sipe-subscriptions.h" #include "sipe-ucs.h" #include "sipe-utils.h" #include "sipe-xml.h" /* OCS2005 */ static void sipe_process_provisioning(struct sipe_core_private *sipe_private, struct sipmsg *msg) { sipe_xml *xn_provision; const sipe_xml *node; xn_provision = sipe_xml_parse(msg->body, msg->bodylen); if ((node = sipe_xml_child(xn_provision, "user"))) { SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node, "uri")); if ((node = sipe_xml_child(node, "line"))) { const gchar *line_uri = sipe_xml_attribute(node, "uri"); const gchar *server = sipe_xml_attribute(node, "server"); SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri, server); sip_csta_open(sipe_private, line_uri, server); } } sipe_xml_free(xn_provision); } /* OCS2007+ */ static void sipe_process_provisioning_v2(struct sipe_core_private *sipe_private, struct sipmsg *msg) { #define READ_INT_FROM_NODE(node_name, field) { \ gchar *s = g_strstrip(sipe_xml_data(sipe_xml_child(node, node_name))); \ sipe_private->field = s ? atoi(s) : 0; \ g_free(s); } sipe_xml *xn_provision_group_list; const sipe_xml *node; xn_provision_group_list = sipe_xml_parse(msg->body, msg->bodylen); /* provisionGroup */ for (node = sipe_xml_child(xn_provision_group_list, "provisionGroup"); node; node = sipe_xml_twin(node)) { const gchar *node_name = sipe_xml_attribute(node, "name"); /* ServerConfiguration */ if (sipe_strequal("ServerConfiguration", node_name)) { const gchar *dlx_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ? "dlxExternalUrl" : "dlxInternalUrl"; const gchar *addressbook_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ? "absExternalServerUrl" : "absInternalServerUrl"; gchar *ucPC2PCAVEncryption = NULL; gchar *ucPortRangeEnabled = NULL; g_free(sipe_private->focus_factory_uri); sipe_private->focus_factory_uri = sipe_xml_data(sipe_xml_child(node, "focusFactoryUri")); SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s", sipe_private->focus_factory_uri ? sipe_private->focus_factory_uri : ""); g_free(sipe_private->dlx_uri); sipe_private->dlx_uri = sipe_xml_data(sipe_xml_child(node, dlx_uri_str)); SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->dlx_uri=%s", sipe_private->dlx_uri ? sipe_private->dlx_uri : ""); g_free(sipe_private->addressbook_uri); sipe_private->addressbook_uri = sipe_xml_data(sipe_xml_child(node, addressbook_uri_str)); SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->addressbook_uri=%s", sipe_private->addressbook_uri ? sipe_private->addressbook_uri : ""); #ifdef HAVE_VV g_free(sipe_private->test_call_bot_uri); sipe_private->test_call_bot_uri = sipe_xml_data(sipe_xml_child(node, "botSipUriForTestCall")); SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->test_call_bot_uri=%s", sipe_private->test_call_bot_uri ? sipe_private->test_call_bot_uri : ""); g_free(sipe_private->mras_uri); sipe_private->mras_uri = g_strstrip(sipe_xml_data(sipe_xml_child(node, "mrasUri"))); SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s", sipe_private->mras_uri ? sipe_private->mras_uri : ""); if (sipe_private->mras_uri) sipe_media_get_av_edge_credentials(sipe_private); #endif ucPC2PCAVEncryption = g_strstrip(sipe_xml_data(sipe_xml_child(node, "ucPC2PCAVEncryption"))); if (sipe_strequal(ucPC2PCAVEncryption, "SupportEncryption")) { sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_OPTIONAL; } else if (sipe_strequal(ucPC2PCAVEncryption, "DoNotSupportEncryption")) { sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_REJECTED; } else { // "RequireEncryption" or any unknown value. sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_REQUIRED; } g_free(ucPC2PCAVEncryption); ucPortRangeEnabled = g_strstrip(sipe_xml_data(sipe_xml_child(node, "ucPortRangeEnabled"))); if (sipe_strequal(ucPortRangeEnabled, "true")) { READ_INT_FROM_NODE("ucMinMediaPort", min_media_port) READ_INT_FROM_NODE("ucMaxMediaPort", max_media_port) READ_INT_FROM_NODE("ucMinAudioPort", min_audio_port) READ_INT_FROM_NODE("ucMaxAudioPort", max_audio_port) READ_INT_FROM_NODE("ucMinVideoPort", min_video_port) READ_INT_FROM_NODE("ucMaxVideoPort", max_video_port) READ_INT_FROM_NODE("ucMinAppSharingPort", min_appsharing_port) READ_INT_FROM_NODE("ucMaxAppSharingPort", max_appsharing_port) READ_INT_FROM_NODE("ucMinFileTransferPort", min_filetransfer_port) READ_INT_FROM_NODE("ucMaxFileTransferPort", max_filetransfer_port) } else { sipe_private->min_media_port = 0; sipe_private->max_media_port = 0; sipe_private->min_audio_port = 0; sipe_private->max_audio_port = 0; sipe_private->min_video_port = 0; sipe_private->max_video_port = 0; sipe_private->min_appsharing_port = 0; sipe_private->max_appsharing_port = 0; sipe_private->min_filetransfer_port = 0; sipe_private->max_filetransfer_port = 0; } g_free(ucPortRangeEnabled); /* persistentChatConfiguration */ } else if (sipe_strequal("persistentChatConfiguration", node_name)) { const sipe_xml *property; gboolean enabled = FALSE; gchar *uri = NULL; for (property = sipe_xml_child(node, "propertyEntryList/property"); property; property = sipe_xml_twin(property)) { const gchar *name = sipe_xml_attribute(property, "name"); gchar *value = sipe_xml_data(property); if (sipe_strequal(name, "EnablePersistentChat")) { enabled = sipe_strequal(value, "true"); } else if (sipe_strequal(name, "DefaultPersistentChatPoolUri")) { g_free(uri); uri = value; value = NULL; } g_free(value); } if (enabled) { g_free(sipe_private->persistentChatPool_uri); sipe_private->persistentChatPool_uri = g_strdup(sipe_get_no_sip_uri(uri)); SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->persistentChatPool_uri=%s", sipe_private->persistentChatPool_uri ? sipe_private->persistentChatPool_uri : ""); } g_free(uri); } } sipe_xml_free(xn_provision_group_list); if (sipe_private->dlx_uri && sipe_private->addressbook_uri) { /* Some buddies might have been added before we received this * provisioning notify with DLX and addressbook URIs. Now we can * trigger an update of their photos. */ sipe_buddy_refresh_photos(sipe_private); } if (sipe_private->focus_factory_uri) { /* Fill the list of conferencing capabilities enabled on * the server. */ sipe_conf_get_capabilities(sipe_private); } if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) /* persistentChatPool_uri has been set at this point */ sipe_groupchat_init(sipe_private); } static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private, const gchar *data, unsigned len) { sipe_xml *xn_list; const sipe_xml *xn_resource; GHashTable *servers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); xn_list = sipe_xml_parse(data, len); for (xn_resource = sipe_xml_child(xn_list, "resource"); xn_resource; xn_resource = sipe_xml_twin(xn_resource) ) { const char *uri, *state; const sipe_xml *xn_instance; xn_instance = sipe_xml_child(xn_resource, "instance"); if (!xn_instance) continue; uri = sipe_xml_attribute(xn_resource, "uri"); state = sipe_xml_attribute(xn_instance, "state"); SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri, state); if (strstr(state, "resubscribe")) { const char *poolFqdn = sipe_xml_attribute(xn_instance, "poolFqdn"); if (poolFqdn) { //[MS-PRES] Section 3.4.5.1.3 Processing Details gchar *user = g_strdup(uri); gchar *host = g_strdup(poolFqdn); GSList *server = g_hash_table_lookup(servers, host); server = g_slist_append(server, user); g_hash_table_insert(servers, host, server); } else { sipe_subscribe_presence_single(sipe_private, uri, uri); } } } /* Send out any deferred poolFqdn subscriptions */ g_hash_table_foreach(servers, (GHFunc) sipe_subscribe_poolfqdn_resource_uri, sipe_private); g_hash_table_destroy(servers); sipe_xml_free(xn_list); } /** * Update user phone * Suitable for both 2005 and 2007 systems. * * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change. * @param phone_type * @param phone may be modified to strip white space * @param phone_display_string may be modified to strip white space */ static void sipe_update_user_phone(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *phone_type, gchar *phone, gchar *phone_display_string) { sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */ sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */ if(!phone || strlen(phone) == 0) return; if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) { phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE; phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY; } else if (sipe_strequal(phone_type, "home")) { phone_node = SIPE_BUDDY_INFO_HOME_PHONE; phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY; } else if (sipe_strequal(phone_type, "other")) { phone_node = SIPE_BUDDY_INFO_OTHER_PHONE; phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY; } else if (sipe_strequal(phone_type, "custom1")) { phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE; phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY; } sipe_buddy_update_property(sipe_private, uri, phone_node, phone); if (phone_display_string) { sipe_buddy_update_property(sipe_private, uri, phone_display_node, phone_display_string); } } static void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private, const gchar *data, unsigned len) { char *activity = NULL; const char *epid; const char *status_id = NULL; const char *name; char *uri; char *self_uri = sip_uri_self(sipe_private); int avl; int act; const char *device_name = NULL; const char *cal_start_time = NULL; const char *cal_granularity = NULL; char *cal_free_busy_base64 = NULL; struct sipe_buddy *sbuddy; const sipe_xml *node; sipe_xml *xn_presentity; const sipe_xml *xn_availability; const sipe_xml *xn_activity; const sipe_xml *xn_display_name; const sipe_xml *xn_email; const sipe_xml *xn_phone_number; const sipe_xml *xn_userinfo; const sipe_xml *xn_note; const sipe_xml *xn_oof; const sipe_xml *xn_state; const sipe_xml *xn_contact; char *note; int user_avail; const char *user_avail_nil; int res_avail; time_t user_avail_since = 0; time_t activity_since = 0; /* fix for Reuters environment on Linux */ if (data && strstr(data, "encoding=\"utf-16\"")) { char *tmp_data; tmp_data = sipe_utils_str_replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\""); xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data)); g_free(tmp_data); } else { xn_presentity = sipe_xml_parse(data, len); } xn_availability = sipe_xml_child(xn_presentity, "availability"); xn_activity = sipe_xml_child(xn_presentity, "activity"); xn_display_name = sipe_xml_child(xn_presentity, "displayName"); xn_email = sipe_xml_child(xn_presentity, "email"); xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber"); xn_userinfo = sipe_xml_child(xn_presentity, "userInfo"); xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL; xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL; user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0; user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0; user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL; xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL; xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL; note = xn_note ? sipe_xml_data(xn_note) : NULL; if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */ user_avail = 0; user_avail_since = 0; } name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */ uri = sip_uri_from_name(name); avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0); epid = sipe_xml_attribute(xn_availability, "epid"); act = sipe_xml_int_attribute(xn_activity, "aggregate", 0); status_id = sipe_ocs2005_status_from_activity_availability(act, avl); activity = g_strdup(sipe_ocs2005_activity_description(act)); res_avail = sipe_ocs2007_availability_from_status(status_id, NULL); if (user_avail > res_avail) { res_avail = user_avail; status_id = sipe_ocs2007_status_from_legacy_availability(user_avail, NULL); } if (xn_display_name) { char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName")); char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL; char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL; char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL; char *tel_uri = sip_to_tel_uri(phone_number); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number); g_free(tel_uri); g_free(phone_label); g_free(phone_number); g_free(email); g_free(display_name); } if (xn_contact) { /* tel */ for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node)) { /* Ex.: tel:+3222220000 */ const char *phone_type = sipe_xml_attribute(node, "type"); char* phone = sipe_xml_data(node); sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL); g_free(phone); } } if (xn_display_name || xn_contact) sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri); /* devicePresence */ for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) { const sipe_xml *xn_device_name; const sipe_xml *xn_calendar_info; const sipe_xml *xn_state; char *state; /* deviceName */ if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) { xn_device_name = sipe_xml_child(node, "deviceName"); device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL; } /* calendarInfo */ xn_calendar_info = sipe_xml_child(node, "calendarInfo"); if (xn_calendar_info) { const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime"); if (cal_start_time) { time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time); time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp); if (cal_start_time_t_tmp > cal_start_time_t) { cal_start_time = cal_start_time_tmp; cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity"); g_free(cal_free_busy_base64); cal_free_busy_base64 = sipe_xml_data(xn_calendar_info); SIPE_DEBUG_INFO("process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s", cal_start_time, cal_granularity, cal_free_busy_base64); } } else { cal_start_time = cal_start_time_tmp; cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity"); g_free(cal_free_busy_base64); cal_free_busy_base64 = sipe_xml_data(xn_calendar_info); SIPE_DEBUG_INFO("process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s", cal_start_time, cal_granularity, cal_free_busy_base64); } } /* state */ xn_state = sipe_xml_child(node, "states/state"); if (xn_state) { int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0); time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")); state = sipe_xml_data(xn_state); if (dev_avail_since > user_avail_since && dev_avail >= res_avail) { const gchar *new_desc; res_avail = dev_avail; if (!is_empty(state)) { if (sipe_strequal(state, sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) { g_free(activity); activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE)); } else if (sipe_strequal(state, "presenting")) { g_free(activity); activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF)); } else { activity = state; state = NULL; } activity_since = dev_avail_since; } status_id = sipe_ocs2007_status_from_legacy_availability(res_avail, NULL); new_desc = sipe_ocs2007_legacy_activity_description(res_avail); if (new_desc) { g_free(activity); activity = g_strdup(new_desc); } } g_free(state); } } /* oof */ if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */ g_free(activity); activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF)); activity_since = 0; } sbuddy = sipe_buddy_find_by_uri(sipe_private, uri); if (sbuddy) { g_free(sbuddy->activity); sbuddy->activity = activity; activity = NULL; sbuddy->activity_since = activity_since; sbuddy->user_avail = user_avail; sbuddy->user_avail_since = user_avail_since; g_free(sbuddy->note); sbuddy->note = NULL; if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); } sbuddy->is_oof_note = (xn_oof != NULL); g_free(sbuddy->device_name); sbuddy->device_name = NULL; if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); } if (!is_empty(cal_free_busy_base64)) { g_free(sbuddy->cal_start_time); sbuddy->cal_start_time = g_strdup(cal_start_time); sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0; g_free(sbuddy->cal_free_busy_base64); sbuddy->cal_free_busy_base64 = cal_free_busy_base64; cal_free_busy_base64 = NULL; g_free(sbuddy->cal_free_busy); sbuddy->cal_free_busy = NULL; } sbuddy->last_non_cal_status_id = status_id; g_free(sbuddy->last_non_cal_activity); sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity); if (sipe_strcase_equal(sbuddy->name, self_uri)) { if (!sipe_strequal(sbuddy->note, sipe_private->note)) /* not same */ { if (sbuddy->is_oof_note) SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE); else SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE); g_free(sipe_private->note); sipe_private->note = g_strdup(sbuddy->note); sipe_private->note_since = time(NULL); } sipe_status_set_token(sipe_private, sbuddy->last_non_cal_status_id); } } g_free(cal_free_busy_base64); g_free(activity); SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id); sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, sipe_status_token_to_activity(status_id), 0); if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) { sipe_ocs2005_user_info_has_updated(sipe_private, xn_userinfo); } g_free(note); sipe_xml_free(xn_presentity); g_free(uri); g_free(self_uri); } static void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private, const gchar *data, unsigned len) { const char *uri; struct sipe_buddy *sbuddy = NULL; sipe_xml *xn_categories; const sipe_xml *xn_category; const char *status = NULL; gboolean do_update_status = FALSE; gboolean has_note_cleaned = FALSE; gboolean has_free_busy_cleaned = FALSE; time_t last_active = 0; xn_categories = sipe_xml_parse(data, len); uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */ if (uri) { sbuddy = sipe_buddy_find_by_uri(sipe_private, uri); } if (!sbuddy) { /* Got presence of a buddy not in our contact list, ignore. */ sipe_xml_free(xn_categories); return; } for (xn_category = sipe_xml_child(xn_categories, "category"); xn_category ; xn_category = sipe_xml_twin(xn_category) ) { const sipe_xml *xn_node; const char *tmp; const char *attrVar = sipe_xml_attribute(xn_category, "name"); time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ? sipe_utils_str_to_time(tmp) : 0; /* contactCard */ if (sipe_strequal(attrVar, "contactCard")) { const sipe_xml *card = sipe_xml_child(xn_category, "contactCard"); if (card) { const sipe_xml *node; /* identity - Display Name and email */ node = sipe_xml_child(card, "identity"); if (node) { char* display_name = sipe_xml_data( sipe_xml_child(node, "name/displayName")); char* email = sipe_xml_data( sipe_xml_child(node, "email")); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email); g_free(display_name); g_free(email); } /* company */ node = sipe_xml_child(card, "company"); if (node) { char* company = sipe_xml_data(node); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company); g_free(company); } /* department */ node = sipe_xml_child(card, "department"); if (node) { char* department = sipe_xml_data(node); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department); g_free(department); } /* title */ node = sipe_xml_child(card, "title"); if (node) { char* title = sipe_xml_data(node); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title); g_free(title); } /* office */ node = sipe_xml_child(card, "office"); if (node) { char* office = sipe_xml_data(node); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office); g_free(office); } /* site (url) */ node = sipe_xml_child(card, "url"); if (node) { char* site = sipe_xml_data(node); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site); g_free(site); } /* phone */ for (node = sipe_xml_child(card, "phone"); node; node = sipe_xml_twin(node)) { const char *phone_type = sipe_xml_attribute(node, "type"); char* phone = sipe_xml_data(sipe_xml_child(node, "uri")); char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString")); sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string); g_free(phone); g_free(phone_display_string); } /* address */ for (node = sipe_xml_child(card, "address"); node; node = sipe_xml_twin(node)) { if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) { char* street = sipe_xml_data(sipe_xml_child(node, "street")); char* city = sipe_xml_data(sipe_xml_child(node, "city")); char* state = sipe_xml_data(sipe_xml_child(node, "state")); char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode")); char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode")); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code); g_free(street); g_free(city); g_free(state); g_free(zipcode); g_free(country_code); break; } } /* photo */ for (node = sipe_xml_child(card, "photo"); node; node = sipe_xml_twin(node)) { const gchar *type = sipe_xml_attribute(node, "type"); gchar *photo_url; gchar *hash; gboolean found = FALSE; if (sipe_strequal(type, "default") && !SIPE_CORE_PUBLIC_FLAG_IS(ALLOW_WEB_PHOTO)) { SIPE_DEBUG_INFO("process_incoming_notify_rlmi: skipping download of web profile picture for %s", uri); continue; } photo_url = sipe_xml_data(sipe_xml_child(node, "uri")); hash = sipe_xml_data(sipe_xml_child(node, "hash")); if (!is_empty(photo_url) && !is_empty(hash)) { sipe_buddy_update_photo(sipe_private, uri, hash, photo_url, NULL); found = TRUE; } g_free(hash); g_free(photo_url); if (found) break; } } } /* note */ else if (sipe_strequal(attrVar, "note")) { if (!has_note_cleaned) { has_note_cleaned = TRUE; g_free(sbuddy->note); sbuddy->note = NULL; sbuddy->is_oof_note = FALSE; sbuddy->note_since = publish_time; do_update_status = TRUE; } if (publish_time >= sbuddy->note_since) { /* clean up in case no 'note' element is supplied * which indicate note removal in client */ g_free(sbuddy->note); sbuddy->note = NULL; sbuddy->is_oof_note = FALSE; sbuddy->note_since = publish_time; xn_node = sipe_xml_child(xn_category, "note/body"); if (xn_node) { char *tmp; sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1); g_free(tmp); sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF"); sbuddy->note_since = publish_time; SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)", uri, sbuddy->note ? sbuddy->note : ""); } /* to trigger UI refresh in case no status info is supplied in this update */ do_update_status = TRUE; } } /* state */ else if(sipe_strequal(attrVar, "state")) { char *tmp; int availability; const sipe_xml *xn_availability; const sipe_xml *xn_activity; const sipe_xml *xn_device; const sipe_xml *xn_meeting_subject; const sipe_xml *xn_meeting_location; const gchar *legacy_activity; const gchar *last_active_attr; xn_node = sipe_xml_child(xn_category, "state"); if (!xn_node) continue; xn_availability = sipe_xml_child(xn_node, "availability"); if (!xn_availability) continue; xn_activity = sipe_xml_child(xn_node, "activity"); xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject"); xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation"); tmp = sipe_xml_data(xn_availability); availability = atoi(tmp); g_free(tmp); sbuddy->is_mobile = FALSE; xn_device = sipe_xml_child(xn_node, "device"); if (xn_device) { tmp = sipe_xml_data(xn_device); sbuddy->is_mobile = !g_ascii_strcasecmp(tmp, "Mobile"); g_free(tmp); } /* activity */ g_free(sbuddy->activity); sbuddy->activity = NULL; if (xn_activity) { const char *token = sipe_xml_attribute(xn_activity, "token"); const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom"); /* from token */ if (!is_empty(token)) { sbuddy->activity = g_strdup(sipe_core_activity_description(sipe_status_token_to_activity(token))); } /* from custom element */ if (xn_custom) { char *custom = sipe_xml_data(xn_custom); if (!is_empty(custom)) { g_free(sbuddy->activity); sbuddy->activity = custom; custom = NULL; } g_free(custom); } } /* meeting_subject */ g_free(sbuddy->meeting_subject); sbuddy->meeting_subject = NULL; if (xn_meeting_subject) { char *meeting_subject = sipe_xml_data(xn_meeting_subject); if (!is_empty(meeting_subject)) { sbuddy->meeting_subject = meeting_subject; meeting_subject = NULL; } g_free(meeting_subject); } /* meeting_location */ g_free(sbuddy->meeting_location); sbuddy->meeting_location = NULL; if (xn_meeting_location) { char *meeting_location = sipe_xml_data(xn_meeting_location); if (!is_empty(meeting_location)) { sbuddy->meeting_location = meeting_location; meeting_location = NULL; } g_free(meeting_location); } status = sipe_ocs2007_status_from_legacy_availability(availability, NULL); legacy_activity = sipe_ocs2007_legacy_activity_description(availability); if (sbuddy->activity && legacy_activity) { gchar *tmp2 = sbuddy->activity; sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, legacy_activity); g_free(tmp2); } else if (legacy_activity) { sbuddy->activity = g_strdup(legacy_activity); } /* lastActive */ last_active_attr = sipe_xml_attribute(xn_node, "lastActive"); if (last_active_attr) { last_active = sipe_utils_str_to_time(last_active_attr); } do_update_status = TRUE; } /* calendarData */ else if(sipe_strequal(attrVar, "calendarData")) { const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy"); const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours"); if (xn_free_busy) { if (!has_free_busy_cleaned) { has_free_busy_cleaned = TRUE; g_free(sbuddy->cal_start_time); sbuddy->cal_start_time = NULL; g_free(sbuddy->cal_free_busy_base64); sbuddy->cal_free_busy_base64 = NULL; g_free(sbuddy->cal_free_busy); sbuddy->cal_free_busy = NULL; sbuddy->cal_free_busy_published = publish_time; } if (publish_time >= sbuddy->cal_free_busy_published) { g_free(sbuddy->cal_start_time); sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime")); sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ? 15 : 0; g_free(sbuddy->cal_free_busy_base64); sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy); g_free(sbuddy->cal_free_busy); sbuddy->cal_free_busy = NULL; sbuddy->cal_free_busy_published = publish_time; SIPE_DEBUG_INFO("process_incoming_notify_rlmi: startTime=%s granularity=%d cal_free_busy_base64=\n%s", sbuddy->cal_start_time, sbuddy->cal_granularity, sbuddy->cal_free_busy_base64); } } if (xn_working_hours) { sipe_cal_parse_working_hours(xn_working_hours, sbuddy); } } } if (do_update_status) { guint activity; if (status) { SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status); activity = sipe_status_token_to_activity(status); } else { /* no status category in this update, using contact's current status */ activity = sipe_backend_buddy_get_status(SIPE_CORE_PUBLIC, uri); } sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, activity, last_active); } sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri); sipe_xml_free(xn_categories); } static void sipe_buddy_status_from_activity(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *activity, gboolean is_online) { if (is_online) { const gchar *status_id = NULL; if (activity) { if (sipe_strequal(activity, sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) { status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY); } else if (sipe_strequal(activity, sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) { status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY); } } if (!status_id) { status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE); } SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id); sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, sipe_status_token_to_activity(status_id), 0); } else { sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, SIPE_ACTIVITY_OFFLINE, 0); } } static void process_incoming_notify_pidf(struct sipe_core_private *sipe_private, const gchar *data, unsigned len) { gchar *uri; gchar *getbasic; gchar *activity = NULL; sipe_xml *pidf; const sipe_xml *basicstatus = NULL, *tuple, *status; gboolean isonline = FALSE; const sipe_xml *display_name_node; pidf = sipe_xml_parse(data, len); if (!pidf) { SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data); return; } if ((tuple = sipe_xml_child(pidf, "tuple"))) { if ((status = sipe_xml_child(tuple, "status"))) { basicstatus = sipe_xml_child(status, "basic"); } } if (!basicstatus) { SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found"); sipe_xml_free(pidf); return; } getbasic = sipe_xml_data(basicstatus); if (!getbasic) { SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found"); sipe_xml_free(pidf); return; } SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic); if (strstr(getbasic, "open")) { isonline = TRUE; } g_free(getbasic); uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */ display_name_node = sipe_xml_child(pidf, "display-name"); if (display_name_node) { char * display_name = sipe_xml_data(display_name_node); sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name); g_free(display_name); sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri); } if ((tuple = sipe_xml_child(pidf, "tuple"))) { if ((status = sipe_xml_child(tuple, "status"))) { if ((basicstatus = sipe_xml_child(status, "activities"))) { if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) { activity = sipe_xml_data(basicstatus); SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity); } } } } sipe_buddy_status_from_activity(sipe_private, uri, activity, isonline); g_free(activity); g_free(uri); sipe_xml_free(pidf); } static void sipe_presence_mime_cb(gpointer user_data, /* sipe_core_private */ const GSList *fields, const gchar *body, gsize length) { const gchar *type = sipe_utils_nameval_find(fields, "Content-Type"); if (strstr(type,"application/rlmi+xml")) { process_incoming_notify_rlmi_resub(user_data, body, length); } else if (strstr(type, "text/xml+msrtc.pidf")) { process_incoming_notify_msrtc(user_data, body, length); } else { process_incoming_notify_rlmi(user_data, body, length); } } static void sipe_process_presence(struct sipe_core_private *sipe_private, struct sipmsg *msg) { const char *ctype = sipmsg_find_content_type_header(msg); SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype ? ctype : ""); if (ctype && (strstr(ctype, "application/rlmi+xml") || strstr(ctype, "application/msrtc-event-categories+xml"))) { if (strstr(ctype, "multipart")) { sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_mime_cb, sipe_private); } else if(strstr(ctype, "application/msrtc-event-categories+xml") ) { process_incoming_notify_rlmi(sipe_private, msg->body, msg->bodylen); } else if(strstr(ctype, "application/rlmi+xml")) { process_incoming_notify_rlmi_resub(sipe_private, msg->body, msg->bodylen); } } else if(ctype && strstr(ctype, "text/xml+msrtc.pidf")) { process_incoming_notify_msrtc(sipe_private, msg->body, msg->bodylen); } else { process_incoming_notify_pidf(sipe_private, msg->body, msg->bodylen); } } /** * Fires on deregistration event initiated by server. * [MS-SIPREGE] SIP extension. * * OCS2007 Example * * Content-Type: text/registration-event * subscription-state: terminated;expires=0 * ms-diagnostics-public: 4141;reason="User disabled" * * deregistered;event=rejected */ static void sipe_process_registration_notify(struct sipe_core_private *sipe_private, struct sipmsg *msg) { const gchar *contenttype = sipmsg_find_content_type_header(msg); gchar *event = NULL; gchar *reason = NULL; gchar *warning; SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received."); if (!g_ascii_strncasecmp(contenttype, "text/registration-event", 23)) { event = sipmsg_find_part_of_header(msg->body, "event=", NULL, NULL); //@TODO have proper parameter extraction _by_name_ func, case insesitive. event = event ? event : sipmsg_find_part_of_header(msg->body, "event=", ";", NULL); } else { SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting."); return; } reason = sipmsg_get_ms_diagnostics_reason(msg); reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg); if (!reason) { // for LCS2005 if (event && sipe_strcase_equal(event, "unregistered")) { //reason = g_strdup(_("User logged out")); // [MS-OCER] reason = g_strdup(_("you are already signed in at another location")); } else if (event && sipe_strcase_equal(event, "rejected")) { reason = g_strdup(_("user disabled")); // [MS-OCER] } else if (event && sipe_strcase_equal(event, "deactivated")) { reason = g_strdup(_("user moved")); // [MS-OCER] } } g_free(event); warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given")); g_free(reason); sipe_backend_connection_error(SIPE_CORE_PUBLIC, SIPE_CONNECTION_ERROR_INVALID_USERNAME, warning); g_free(warning); } /* Replace "~" with localized version of "Other Contacts" */ static const gchar *get_group_name(const sipe_xml *node) { const gchar *name = sipe_xml_attribute(node, "name"); return(g_str_has_prefix(name, "~") ? _("Other Contacts") : name); } static void add_new_group(struct sipe_core_private *sipe_private, const sipe_xml *node) { sipe_group_add(sipe_private, get_group_name(node), NULL, NULL, sipe_xml_int_attribute(node, "id", 0)); } static void add_new_buddy(struct sipe_core_private *sipe_private, const sipe_xml *node, const gchar *uri) { const gchar *name = sipe_xml_attribute(node, "name"); struct sipe_buddy *buddy = NULL; gchar *tmp; gchar **item_groups; int i = 0; /* "name" attribute is a contact alias which user can manually assign by * renaming the item in the contact list. Empty string means no alias * and the display name from the contact card should be used instead. */ if (name && strlen(name) == 0) { name = NULL; } /* assign to group Other Contacts if nothing else received */ tmp = g_strdup(sipe_xml_attribute(node, "groups")); if (is_empty(tmp)) { struct sipe_group *group = sipe_group_find_by_name(sipe_private, _("Other Contacts")); g_free(tmp); tmp = group ? g_strdup_printf("%d", group->id) : g_strdup("1"); } item_groups = g_strsplit(tmp, " ", 0); g_free(tmp); while (item_groups[i]) { struct sipe_group *group = sipe_group_find_by_id(sipe_private, g_ascii_strtod(item_groups[i], NULL)); /* If couldn't find the right group for this contact, */ /* then just put it in the first group we have */ if (!group) group = sipe_group_first(sipe_private); if (group) { if (!buddy) buddy = sipe_buddy_add(sipe_private, uri, NULL, NULL); sipe_buddy_add_to_group(sipe_private, buddy, group, name); } else { SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list", uri); } i++; } g_strfreev(item_groups); } static gboolean sipe_process_roaming_contacts(struct sipe_core_private *sipe_private, struct sipmsg *msg) { int len = msg->bodylen; const gchar *tmp = sipmsg_find_event_header(msg); const sipe_xml *item; sipe_xml *isc; guint delta; const sipe_xml *group_node; if (!g_str_has_prefix(tmp, "vnd-microsoft-roaming-contacts")) { return FALSE; } /* Convert the contact from XML to backend Buddies */ isc = sipe_xml_parse(msg->body, len); if (!isc) { return FALSE; } /* [MS-SIP]: deltaNum MUST be non-zero */ delta = sipe_xml_int_attribute(isc, "deltaNum", 0); if (delta) { sipe_private->deltanum_contacts = delta; } /* * Process whole buddy list * * - Only sent once * * up to Lync 2010 * * Lync 2013 (and later) with buddy list not migrated * * - Lync 2013 with buddy list migrated to Unified Contact Store (UCS) * * Notify piggy-backed on SUBSCRIBE response with empty list * * NOTIFY send by server with standard list (ignored by us) */ if (sipe_strequal(sipe_xml_name(isc), "contactList")) { const gchar *ucsmode = sipe_xml_attribute(isc, "ucsmode"); SIPE_CORE_PRIVATE_FLAG_UNSET(LYNC2013); if (ucsmode) { gboolean migrated = sipe_strcase_equal(ucsmode, "migrated"); SIPE_CORE_PRIVATE_FLAG_SET(LYNC2013); SIPE_LOG_INFO_NOFORMAT("sipe_process_roaming_contacts: contact list contains 'ucsmode' attribute (indicates Lync 2013+)"); if (migrated) SIPE_LOG_INFO_NOFORMAT("sipe_process_roaming_contacts: contact list has been migrated to Unified Contact Store (UCS)"); sipe_ucs_init(sipe_private, migrated); } if (!sipe_ucs_is_migrated(sipe_private)) { /* Start processing contact list */ sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC); /* Parse groups */ for (group_node = sipe_xml_child(isc, "group"); group_node; group_node = sipe_xml_twin(group_node)) add_new_group(sipe_private, group_node); /* Make sure we have at least one group */ if (sipe_group_count(sipe_private) == 0) { sipe_group_create(sipe_private, NULL, _("Other Contacts"), NULL); } /* Parse contacts */ for (item = sipe_xml_child(isc, "contact"); item; item = sipe_xml_twin(item)) { const gchar *name = sipe_xml_attribute(item, "uri"); gchar *uri = sip_uri_from_name(name); add_new_buddy(sipe_private, item, uri); g_free(uri); } sipe_buddy_cleanup_local_list(sipe_private); /* Add self-contact if not there yet. 2005 systems. */ /* This will resemble subscription to roaming_self in 2007 systems */ if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) { gchar *self_uri = sip_uri_self(sipe_private); sipe_buddy_add(sipe_private, self_uri, NULL, NULL); g_free(self_uri); } /* Finished processing contact list */ sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC); } /* Process buddy list updates */ } else if (sipe_strequal(sipe_xml_name(isc), "contactDelta")) { /* Process new groups */ for (group_node = sipe_xml_child(isc, "addedGroup"); group_node; group_node = sipe_xml_twin(group_node)) add_new_group(sipe_private, group_node); /* Process modified groups */ for (group_node = sipe_xml_child(isc, "modifiedGroup"); group_node; group_node = sipe_xml_twin(group_node)) { struct sipe_group *group = sipe_group_find_by_id(sipe_private, (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"), NULL)); if (group) { const gchar *name = get_group_name(group_node); if (!(is_empty(name) || sipe_strequal(group->name, name)) && sipe_group_rename(sipe_private, group, name)) SIPE_DEBUG_INFO("Replaced group %d name with %s", group->id, name); } } /* Process new buddies */ for (item = sipe_xml_child(isc, "addedContact"); item; item = sipe_xml_twin(item)) { add_new_buddy(sipe_private, item, sipe_xml_attribute(item, "uri")); } /* Process modified buddies */ for (item = sipe_xml_child(isc, "modifiedContact"); item; item = sipe_xml_twin(item)) { const gchar *uri = sipe_xml_attribute(item, "uri"); struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private, uri); if (buddy) { gchar **item_groups = g_strsplit(sipe_xml_attribute(item, "groups"), " ", 0); /* this should be defined. Otherwise we would get "deletedContact" */ if (item_groups) { const gchar *name = sipe_xml_attribute(item, "name"); gboolean empty_name = is_empty(name); GSList *found = NULL; int i = 0; while (item_groups[i]) { struct sipe_group *group = sipe_group_find_by_id(sipe_private, g_ascii_strtod(item_groups[i], NULL)); /* ignore unkown groups */ if (group) { sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, group->name); /* add group to found list */ found = g_slist_prepend(found, group); if (b) { /* new alias? */ gchar *b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, b); if (!(empty_name || sipe_strequal(b_alias, name))) { sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, b, name); SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'", uri, group->name, b_alias, name); } g_free(b_alias); } else { const gchar *alias = empty_name ? uri : name; /* buddy was not in this group */ sipe_backend_buddy_add(SIPE_CORE_PUBLIC, uri, alias, group->name); sipe_buddy_insert_group(buddy, group); SIPE_DEBUG_INFO("Added buddy %s (alias '%s' to group '%s'", uri, alias, group->name); } } /* next group */ i++; } g_strfreev(item_groups); /* removed from groups? */ sipe_buddy_update_groups(sipe_private, buddy, found); g_slist_free(found); } } } /* Process deleted buddies */ for (item = sipe_xml_child(isc, "deletedContact"); item; item = sipe_xml_twin(item)) { const gchar *uri = sipe_xml_attribute(item, "uri"); struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private, uri); if (buddy) { SIPE_DEBUG_INFO("Removing buddy %s", uri); sipe_buddy_remove(sipe_private, buddy); } } /* Process deleted groups * * NOTE: all buddies will already have been removed from the * group prior to this. The log shows that OCS actually * sends two separate updates when you delete a group: * * - first one with "modifiedContact" removing buddies * from the group, leaving it empty, and * * - then one with "deletedGroup" removing the group */ for (group_node = sipe_xml_child(isc, "deletedGroup"); group_node; group_node = sipe_xml_twin(group_node)) sipe_group_remove(sipe_private, sipe_group_find_by_id(sipe_private, (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"), NULL))); } sipe_xml_free(isc); /* Subscribe to buddies, if contact list not migrated to UCS */ if (!sipe_ucs_is_migrated(sipe_private)) sipe_subscribe_presence_initial(sipe_private); /* for 2005 systems schedule contacts' status update * based on their calendar information */ if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) { sipe_ocs2005_schedule_status_update(sipe_private, time(NULL)); } return 0; } static void sipe_process_roaming_acl(struct sipe_core_private *sipe_private, struct sipmsg *msg) { guint delta; sipe_xml *xml; xml = sipe_xml_parse(msg->body, msg->bodylen); if (!xml) return; /* [MS-SIP]: deltaNum MUST be non-zero */ delta = sipe_xml_int_attribute(xml, "deltaNum", 0); if (delta) { sipe_private->deltanum_acl = delta; } sipe_xml_free(xml); } struct sipe_auth_job { gchar *who; struct sipe_core_private *sipe_private; }; void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public, const gchar* who, gboolean allow) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; if (allow) { SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who); } else { SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who); } if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) { sipe_ocs2007_change_access_level(sipe_private, (allow ? -1 : 32000), "user", sipe_get_no_sip_uri(who)); } else { sip_soap_ocs2005_setacl(sipe_private, who, allow); } } static void sipe_auth_user_cb(gpointer data) { struct sipe_auth_job *job = (struct sipe_auth_job *) data; if (!job) return; sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private, job->who, TRUE); g_free(job); } static void sipe_deny_user_cb(gpointer data) { struct sipe_auth_job *job = (struct sipe_auth_job *) data; if (!job) return; sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private, job->who, FALSE); g_free(job); } /* OCS2005- */ static void sipe_process_presence_wpending (struct sipe_core_private *sipe_private, struct sipmsg * msg) { sipe_xml *watchers; const sipe_xml *watcher; // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response if (msg->response != 0 && msg->response != 200) return; if (msg->bodylen == 0 || msg->body == NULL || sipe_strequal(sipmsg_find_event_header(msg), "msrtc.wpending")) return; watchers = sipe_xml_parse(msg->body, msg->bodylen); if (!watchers) return; for (watcher = sipe_xml_child(watchers, "watcher"); watcher; watcher = sipe_xml_twin(watcher)) { gchar * remote_user = g_strdup(sipe_xml_attribute(watcher, "uri")); gchar * alias = g_strdup(sipe_xml_attribute(watcher, "displayName")); gboolean on_list = sipe_buddy_find_by_uri(sipe_private, remote_user) != NULL; // TODO pull out optional displayName to pass as alias if (remote_user) { struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1); job->who = remote_user; job->sipe_private = sipe_private; sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC, remote_user, alias, on_list, sipe_auth_user_cb, sipe_deny_user_cb, (gpointer)job); } } sipe_xml_free(watchers); return; } /** * Dispatcher for all incoming subscription information * whether it comes from NOTIFY, BENOTIFY requests or * piggy-backed to subscription's OK responce. */ void process_incoming_notify(struct sipe_core_private *sipe_private, struct sipmsg *msg) { const gchar *content_type = sipmsg_find_content_type_header(msg); const gchar *event = sipmsg_find_event_header(msg); const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state"); SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state ? subscription_state : ""); /* implicit subscriptions */ if (content_type && g_str_has_prefix(content_type, "application/ms-imdn+xml")) { sipe_process_imdn(sipe_private, msg); /* event subscriptions */ } else if (event) { /* One-off subscriptions - sent with "Expires: 0" */ if (sipe_strcase_equal(event, "vnd-microsoft-provisioning-v2")) { sipe_process_provisioning_v2(sipe_private, msg); } else if (sipe_strcase_equal(event, "vnd-microsoft-provisioning")) { sipe_process_provisioning(sipe_private, msg); } else if (sipe_strcase_equal(event, "presence")) { sipe_process_presence(sipe_private, msg); } else if (sipe_strcase_equal(event, "registration-notify")) { sipe_process_registration_notify(sipe_private, msg); /* Subscriptions with timeout */ } else if (!subscription_state || strstr(subscription_state, "active")) { if (sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts")) { sipe_process_roaming_contacts(sipe_private, msg); } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-self")) { sipe_ocs2007_process_roaming_self(sipe_private, msg); } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-ACL")) { sipe_process_roaming_acl(sipe_private, msg); } else if (sipe_strcase_equal(event, "presence.wpending")) { sipe_process_presence_wpending(sipe_private, msg); } else if (sipe_strcase_equal(event, "conference")) { sipe_process_conference(sipe_private, msg); } } } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-notify.h ================================================ /** * @file sipe-notify.h * * pidgin-sipe * * Copyright (C) 2011-2013 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sipmsg; struct sipe_core_private; void process_incoming_notify(struct sipe_core_private *sipe_private, struct sipmsg *msg); /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-ocs2005.c ================================================ /** * @file sipe-ocs2005.c * * pidgin-sipe * * Copyright (C) 2011-2019 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * OCS2005 specific code * */ #include #include #include "sipe-common.h" #include "sip-soap.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-cal.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-ews.h" #include "sipe-ocs2005.h" #include "sipe-ocs2007.h" #include "sipe-schedule.h" #include "sipe-status.h" #include "sipe-utils.h" #include "sipe-xml.h" /** * 2005-style Activity and Availability. * * [MS-SIP] 2.2.1 * * @param activity 2005 aggregated activity. Ex.: 600 * @param availablity 2005 aggregated availablity. Ex.: 300 * * The values define the starting point of a range */ #define SIPE_OCS2005_ACTIVITY_UNKNOWN 0 #define SIPE_OCS2005_ACTIVITY_AWAY 100 #define SIPE_OCS2005_ACTIVITY_LUNCH 150 #define SIPE_OCS2005_ACTIVITY_IDLE 200 #define SIPE_OCS2005_ACTIVITY_BRB 300 #define SIPE_OCS2005_ACTIVITY_AVAILABLE 400 /* user is active */ #define SIPE_OCS2005_ACTIVITY_ON_PHONE 500 /* user is participating in a communcation session */ #define SIPE_OCS2005_ACTIVITY_BUSY 600 #define SIPE_OCS2005_ACTIVITY_AWAY2 700 #define SIPE_OCS2005_ACTIVITY_AVAILABLE2 800 #define SIPE_OCS2005_AVAILABILITY_OFFLINE 0 #define SIPE_OCS2005_AVAILABILITY_MAYBE 100 #define SIPE_OCS2005_AVAILABILITY_ONLINE 300 static guint sipe_ocs2005_activity_from_status(struct sipe_core_private *sipe_private) { const gchar *status = sipe_private->status; if (sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) { return(SIPE_OCS2005_ACTIVITY_AWAY); /*} else if (sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_LUNCH))) { return(SIPE_OCS2005_ACTIVITY_LUNCH); */ } else if (sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_BRB))) { return(SIPE_OCS2005_ACTIVITY_BRB); } else if (sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE))) { return(SIPE_OCS2005_ACTIVITY_AVAILABLE); /*} else if (sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) { return(SIPE_OCS2005_ACTIVITY_ON_PHONE); */ } else if (sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY)) || sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_DND))) { return(SIPE_OCS2005_ACTIVITY_BUSY); } else if (sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_INVISIBLE)) || sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_OFFLINE))) { return(SIPE_OCS2005_ACTIVITY_AWAY); } else { return(SIPE_OCS2005_ACTIVITY_AVAILABLE); } } static guint sipe_ocs2005_availability_from_status(struct sipe_core_private *sipe_private) { const gchar *status = sipe_private->status; if (sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_INVISIBLE)) || sipe_strequal(status, sipe_status_activity_to_token(SIPE_ACTIVITY_OFFLINE))) return(SIPE_OCS2005_AVAILABILITY_OFFLINE); else return(SIPE_OCS2005_AVAILABILITY_ONLINE); } const gchar *sipe_ocs2005_status_from_activity_availability(guint activity, guint availability) { guint type; if (availability < SIPE_OCS2005_AVAILABILITY_MAYBE) { type = SIPE_ACTIVITY_OFFLINE; } else if (activity < SIPE_OCS2005_ACTIVITY_LUNCH) { type = SIPE_ACTIVITY_AWAY; } else if (activity < SIPE_OCS2005_ACTIVITY_IDLE) { //type = SIPE_ACTIVITY_LUNCH; type = SIPE_ACTIVITY_AWAY; } else if (activity < SIPE_OCS2005_ACTIVITY_BRB) { //type = SIPE_ACTIVITY_IDLE; type = SIPE_ACTIVITY_AWAY; } else if (activity < SIPE_OCS2005_ACTIVITY_AVAILABLE) { type = SIPE_ACTIVITY_BRB; } else if (activity < SIPE_OCS2005_ACTIVITY_ON_PHONE) { type = SIPE_ACTIVITY_AVAILABLE; } else if (activity < SIPE_OCS2005_ACTIVITY_BUSY) { //type = SIPE_ACTIVITY_ON_PHONE; type = SIPE_ACTIVITY_BUSY; } else if (activity < SIPE_OCS2005_ACTIVITY_AWAY2) { type = SIPE_ACTIVITY_BUSY; } else if (activity < SIPE_OCS2005_ACTIVITY_AVAILABLE2) { type = SIPE_ACTIVITY_AWAY; } else { type = SIPE_ACTIVITY_AVAILABLE; } return(sipe_status_activity_to_token(type)); } const gchar *sipe_ocs2005_activity_description(guint activity) { if ((activity >= SIPE_OCS2005_ACTIVITY_LUNCH) && (activity < SIPE_OCS2005_ACTIVITY_IDLE)) { return(sipe_core_activity_description(SIPE_ACTIVITY_LUNCH)); } else if ((activity >= SIPE_OCS2005_ACTIVITY_IDLE) && (activity < SIPE_OCS2005_ACTIVITY_BRB)) { return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE)); } else if ((activity >= SIPE_OCS2005_ACTIVITY_ON_PHONE) && (activity < SIPE_OCS2005_ACTIVITY_BUSY)) { return(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE)); } else { return(NULL); } } void sipe_ocs2005_user_info_has_updated(struct sipe_core_private *sipe_private, const sipe_xml *xn_userinfo) { const sipe_xml *xn_states; g_free(sipe_private->ocs2005_user_states); sipe_private->ocs2005_user_states = NULL; if ((xn_states = sipe_xml_child(xn_userinfo, "states")) != NULL) { gchar *orig = sipe_private->ocs2005_user_states = sipe_xml_stringify(xn_states); /* this is a hack-around to remove added newline after inner element, * state in this case, where it shouldn't be. * After several use of sipe_xml_stringify, amount of added newlines * grows significantly. */ if (orig) { gchar c, *stripped = orig; while ((c = *orig++)) { if ((c != '\n') /* && (c != '\r') */) { *stripped++ = c; } } *stripped = '\0'; } } /* Publish initial state if not yet. * Assuming this happens on initial responce to self subscription * so we've already updated our UserInfo. */ if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) { sipe_ocs2005_presence_publish(sipe_private, FALSE); /* dalayed run */ sipe_cal_delayed_calendar_update(sipe_private); } } static gboolean sipe_is_user_available(struct sipe_core_private *sipe_private) { return(sipe_strequal(sipe_private->status, sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE))); } /** * OCS2005 presence XML messages * * Calendar publication entry * * @param legacy_dn (%s) Ex.: /o=EXCHANGE/ou=BTUK02/cn=Recipients/cn=AHHBTT * @param fb_start_time_str (%s) Ex.: 2009-12-06T17:15:00Z * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAA...... */ #define SIPE_SOAP_SET_PRESENCE_CALENDAR \ "%s" /** * Note publication entry * * @param note (%s) Ex.: Working from home */ #define SIPE_SOAP_SET_PRESENCE_NOTE_XML "%s" /** * Note's OOF publication entry */ #define SIPE_SOAP_SET_PRESENCE_OOF_XML "" /** * States publication entry for User State * * @param avail (%d) Availability 2007-style. Ex.: 9500 * @param since_time_str (%s) Ex.: 2010-01-13T10:30:05Z * @param device_id (%s) epid. Ex.: 4c77e6ec72 * @param activity_token (%s) Ex.: do-not-disturb */ #define SIPE_SOAP_SET_PRESENCE_STATES \ ""\ "%s"\ "" /** * Presentity publication entry. * * @param uri (%s) SIP URI without 'sip:' prefix. Ex.: fox@atlanta.local * @param aggr_availability (%d) Ex.: 300 * @param aggr_activity (%d) Ex.: 600 * @param host_name (%s) Uppercased. Ex.: ATLANTA * @param note_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_NOTE_XML * @param oof_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_OOF_XML * @param states_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_STATES * @param calendar_info_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_CALENDAR * @param device_id (%s) epid. Ex.: 4c77e6ec72 * @param since_time_str (%s) Ex.: 2010-01-13T10:30:05Z * @param since_time_str (%s) Ex.: 2010-01-13T10:30:05Z * @param user_input (%s) active, idle */ #define SIPE_SOAP_SET_PRESENCE \ "" \ "" \ "" \ ""\ ""\ ""\ ""\ ""\ "]]>"\ ""\ "%s%s" \ "%s" \ ""\ "%s" \ ""\ "%s"\ ""\ "" \ ""\ "" \ "" static void send_presence_soap(struct sipe_core_private *sipe_private, gboolean do_publish_calendar, gboolean do_reset_status) { struct sipe_calendar* cal = sipe_private->calendar; gchar *body; gchar *tmp; gchar *tmp2 = NULL; gchar *res_note = NULL; const gchar *res_oof = NULL; const gchar *note_pub = NULL; gchar *states = NULL; gchar *calendar_data = NULL; const gchar *epid = sip_transport_epid(sipe_private); gchar *from = sip_uri_self(sipe_private); time_t now = time(NULL); gchar *since_time_str = sipe_utils_time_to_str(now); const gchar *oof_note = cal ? sipe_ews_get_oof_note(cal) : NULL; const char *user_input; gboolean pub_oof = cal && oof_note && (!sipe_private->note || cal->updated > sipe_private->note_since); if (oof_note && sipe_private->note) { SIPE_DEBUG_INFO("cal->oof_start : %s", sipe_utils_time_to_debug_str(localtime(&(cal->oof_start)))); SIPE_DEBUG_INFO("sipe_private->note_since : %s", sipe_utils_time_to_debug_str(localtime(&(sipe_private->note_since)))); } SIPE_DEBUG_INFO("sipe_private->note : %s", sipe_private->note ? sipe_private->note : ""); if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH) || do_reset_status) sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE); /* Note */ if (pub_oof) { note_pub = oof_note; res_oof = SIPE_SOAP_SET_PRESENCE_OOF_XML; cal->published = TRUE; } else if (sipe_private->note) { if (SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) && !oof_note) { /* stale OOF note, as it's not present in cal already */ g_free(sipe_private->note); sipe_private->note = NULL; SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE); sipe_private->note_since = 0; } else { note_pub = sipe_private->note; res_oof = SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? SIPE_SOAP_SET_PRESENCE_OOF_XML : ""; } } if (note_pub) { /* to protocol internal plain text format */ tmp = sipe_backend_markup_strip_html(note_pub); res_note = g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML, tmp); g_free(tmp); } /* User State */ if (!do_reset_status) { if (sipe_private->status_set_by_user && !do_publish_calendar && SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) { const gchar *activity_token; int avail_2007 = sipe_ocs2007_availability_from_status(sipe_private->status, &activity_token); states = g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES, avail_2007, since_time_str, epid, activity_token); } else /* preserve existing publication */ { if (sipe_private->ocs2005_user_states) { states = g_strdup(sipe_private->ocs2005_user_states); } } } else { /* do nothing - then User state will be erased */ } SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH); /* CalendarInfo */ if (cal && (!is_empty(cal->legacy_dn) || !is_empty(cal->email)) && cal->fb_start && !is_empty(cal->free_busy)) { char *fb_start_str = sipe_utils_time_to_str(cal->fb_start); char *free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy); calendar_data = g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR, !is_empty(cal->legacy_dn) ? cal->legacy_dn : cal->email, fb_start_str, free_busy_base64); g_free(fb_start_str); g_free(free_busy_base64); } user_input = (sipe_private->status_set_by_user || sipe_is_user_available(sipe_private)) ? "active" : "idle"; /* generate XML */ body = g_strdup_printf(SIPE_SOAP_SET_PRESENCE, sipe_private->username, sipe_ocs2005_availability_from_status(sipe_private), sipe_ocs2005_activity_from_status(sipe_private), (tmp = g_ascii_strup(g_get_host_name(), -1)), res_note ? res_note : "", res_oof ? res_oof : "", states ? states : "", calendar_data ? calendar_data : "", epid, since_time_str, since_time_str, user_input); g_free(tmp); g_free(tmp2); g_free(res_note); g_free(states); g_free(calendar_data); g_free(since_time_str); sip_soap_raw_request_cb(sipe_private, from, body, NULL, NULL); g_free(body); } void sipe_ocs2005_presence_publish(struct sipe_core_private *sipe_private, gboolean do_publish_calendar) { send_presence_soap(sipe_private, do_publish_calendar, FALSE); } void sipe_ocs2005_reset_status(struct sipe_core_private *sipe_private) { send_presence_soap(sipe_private, FALSE, TRUE); } void sipe_ocs2005_apply_calendar_status(struct sipe_core_private *sipe_private, struct sipe_buddy *sbuddy, const char *status_id) { time_t cal_avail_since; int cal_status = sipe_cal_get_status(sbuddy, time(NULL), &cal_avail_since); int avail; gchar *self_uri; if (!sbuddy) return; if (cal_status < SIPE_CAL_NO_DATA) { SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status : %d for %s", cal_status, sbuddy->name); SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", sipe_utils_time_to_debug_str(localtime(&cal_avail_since))); } /* scheduled Cal update call */ if (!status_id) { status_id = sbuddy->last_non_cal_status_id; g_free(sbuddy->activity); sbuddy->activity = g_strdup(sbuddy->last_non_cal_activity); } if (!status_id) { SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.", sbuddy->name ? sbuddy->name : "" ); return; } /* adjust to calendar status */ if (cal_status != SIPE_CAL_NO_DATA) { SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", sipe_utils_time_to_debug_str(localtime(&sbuddy->user_avail_since))); if ((cal_status == SIPE_CAL_BUSY) && (cal_avail_since > sbuddy->user_avail_since) && sipe_ocs2007_status_is_busy(status_id)) { status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY); g_free(sbuddy->activity); sbuddy->activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_MEETING)); } avail = sipe_ocs2007_availability_from_status(status_id, NULL); SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since : %s", sipe_utils_time_to_debug_str(localtime(&sbuddy->activity_since))); if (cal_avail_since > sbuddy->activity_since) { if ((cal_status == SIPE_CAL_OOF) && sipe_ocs2007_availability_is_away(avail)) { g_free(sbuddy->activity); sbuddy->activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF)); } } } /* then set status_id actually */ SIPE_DEBUG_INFO("sipe_apply_calendar_status: to %s for %s", status_id, sbuddy->name ? sbuddy->name : "" ); sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC, sbuddy->name, sipe_status_token_to_activity(status_id), 0); /* set our account state to the one in roaming (including calendar info) */ self_uri = sip_uri_self(sipe_private); if (SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH) && sipe_strcase_equal(sbuddy->name, self_uri)) { if (sipe_strequal(status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_OFFLINE))) { /* do not let offline status switch us off */ status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_INVISIBLE); } sipe_status_and_note(sipe_private, status_id); } g_free(self_uri); } static void update_calendar_status_cb(SIPE_UNUSED_PARAMETER char *name, struct sipe_buddy *sbuddy, struct sipe_core_private *sipe_private) { sipe_ocs2005_apply_calendar_status(sipe_private, sbuddy, NULL); } /** * Updates contact's status * based on their calendar information. */ static void update_calendar_status(struct sipe_core_private *sipe_private, SIPE_UNUSED_PARAMETER void *unused) { SIPE_DEBUG_INFO_NOFORMAT("update_calendar_status() started."); sipe_buddy_foreach(sipe_private, (GHFunc) update_calendar_status_cb, sipe_private); /* repeat scheduling */ sipe_ocs2005_schedule_status_update(sipe_private, time(NULL) + 3 * 60 /* 3 min */); } /** * Schedules process of contacts' status update * based on their calendar information. * Should be scheduled to the beginning of every * 15 min interval, like: * 13:00, 13:15, 13:30, 13:45, etc. */ void sipe_ocs2005_schedule_status_update(struct sipe_core_private *sipe_private, time_t calculate_from) { #define SCHEDULE_INTERVAL 15 * 60 /* 15 min */ /* start of the beginning of closest 15 min interval. */ time_t next_start = (calculate_from / SCHEDULE_INTERVAL + 1) * SCHEDULE_INTERVAL; SIPE_DEBUG_INFO("sipe_ocs2005_schedule_status_update: calculate_from time: %s", sipe_utils_time_to_debug_str(localtime(&calculate_from))); SIPE_DEBUG_INFO("sipe_ocs2005_schedule_status_update: next start time : %s", sipe_utils_time_to_debug_str(localtime(&next_start))); sipe_schedule_seconds(sipe_private, "<+2005-cal-status>", NULL, next_start - time(NULL), update_calendar_status, NULL); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-ocs2005.h ================================================ /** * @file sipe-ocs2005.h * * pidgin-sipe * * Copyright (C) 2011 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct _sipe_xml; struct sipe_core_private; /** * OCS2005 status ID, availability & activity */ const gchar *sipe_ocs2005_status_from_activity_availability(guint activity, guint availablity); const gchar *sipe_ocs2005_activity_description(guint activity); /** * Publish status (OCS2005) */ void sipe_ocs2005_presence_publish(struct sipe_core_private *sipe_private, gboolean do_publish_calendar); void sipe_ocs2005_reset_status(struct sipe_core_private *sipe_private); void sipe_ocs2005_user_info_has_updated(struct sipe_core_private *sipe_private, const struct _sipe_xml *xn_userinfo); void sipe_ocs2005_apply_calendar_status(struct sipe_core_private *sipe_private, struct sipe_buddy *sbuddy, const char *status_id); void sipe_ocs2005_schedule_status_update(struct sipe_core_private *sipe_private, time_t calculate_from); /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-ocs2007.c ================================================ /** * @file sipe-ocs2007.c * * pidgin-sipe * * Copyright (C) 2011-2019 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * OCS2007+ specific code * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-common.h" #include "sipmsg.h" #include "sip-csta.h" #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-buddy.h" #include "sipe-cal.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-appshare.h" #include "sipe-ews.h" #include "sipe-media.h" #include "sipe-nls.h" #include "sipe-ocs2007.h" #include "sipe-schedule.h" #include "sipe-status.h" #include "sipe-utils.h" #include "sipe-xml.h" /** MS-PRES publication */ struct sipe_publication { gchar *category; guint instance; guint container; guint version; /** for 'state' category */ int availability; /** for 'state:calendarState' category */ char *cal_event_hash; /** for 'note' category */ gchar *note; /** for 'calendarData' category; 300(Team) container */ char *working_hours_xml_str; char *fb_start_str; char *free_busy_base64; }; /** * 2007-style Activity and Availability. * * [MS-PRES] 3.7.5.5 * * Conversion of legacyInterop availability ranges and activity tokens into * SIPE activity tokens. The descriptions of availability ranges are defined at: * * http://msdn.microsoft.com/en-us/library/lync/dd941370%28v=office.13%29.aspx * * The values define the starting point of a range. */ #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE 3000 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE 4500 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY 6000 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE 7500 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_DND 9000 /* do not disturb */ #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB 12000 /* be right back */ #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY 15000 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE 18000 const gchar *sipe_ocs2007_status_from_legacy_availability(guint availability, const gchar *activity) { guint type; if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE) { type = SIPE_ACTIVITY_OFFLINE; } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) { type = SIPE_ACTIVITY_AVAILABLE; } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY) { type = SIPE_ACTIVITY_INACTIVE; } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) { type = sipe_status_token_to_activity(activity); if ((type != SIPE_ACTIVITY_ON_PHONE) && (type != SIPE_ACTIVITY_IN_CONF)) type = SIPE_ACTIVITY_BUSY; } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND) { type = SIPE_ACTIVITY_BUSYIDLE; } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB) { type = sipe_status_token_to_activity(activity); if (type != SIPE_ACTIVITY_IN_PRES) { type = SIPE_ACTIVITY_DND; } } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY) { type = SIPE_ACTIVITY_BRB; } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE) { type = SIPE_ACTIVITY_AWAY; } else { type = SIPE_ACTIVITY_OFFLINE; } return sipe_status_activity_to_token(type); } const gchar *sipe_ocs2007_legacy_activity_description(guint availability) { if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) && (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY)) { return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE)); } else if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) && (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND)) { return(sipe_core_activity_description(SIPE_ACTIVITY_BUSYIDLE)); } else { return(NULL); } } /** * @param sipe_status_id (in) * @param activity_token (out) [only sipe-ocs2005.c/send_presence_soap() * requests this token] */ #define SIPE_OCS2007_AVAILABILITY_UNKNOWN 0 #define SIPE_OCS2007_AVAILABILITY_ONLINE 3500 #define SIPE_OCS2007_AVAILABILITY_BUSY 6500 #define SIPE_OCS2007_AVAILABILITY_DND 9500 /* do not disturb */ #define SIPE_OCS2007_AVAILABILITY_BRB 12500 /* be right back */ #define SIPE_OCS2007_AVAILABILITY_AWAY 15500 #define SIPE_OCS2007_AVAILABILITY_OFFLINE 18500 guint sipe_ocs2007_availability_from_status(const gchar *sipe_status_id, const gchar **activity_token) { guint availability; guint activity; if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) { availability = SIPE_OCS2007_AVAILABILITY_AWAY; activity = SIPE_ACTIVITY_AWAY; } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BRB))) { availability = SIPE_OCS2007_AVAILABILITY_BRB; activity = SIPE_ACTIVITY_BRB; } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_DND))) { availability = SIPE_OCS2007_AVAILABILITY_DND; activity = SIPE_ACTIVITY_DND; } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) { availability = SIPE_OCS2007_AVAILABILITY_BUSY; activity = SIPE_ACTIVITY_BUSY; } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE))) { availability = SIPE_OCS2007_AVAILABILITY_ONLINE; activity = SIPE_ACTIVITY_ONLINE; } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_UNSET))) { availability = SIPE_OCS2007_AVAILABILITY_UNKNOWN; activity = SIPE_ACTIVITY_UNSET; } else { /* Offline or invisible */ availability = SIPE_OCS2007_AVAILABILITY_OFFLINE; activity = SIPE_ACTIVITY_OFFLINE; } if (activity_token) { *activity_token = sipe_status_activity_to_token(activity); } return(availability); } gboolean sipe_ocs2007_status_is_busy(const gchar *status_id) { return(SIPE_OCS2007_AVAILABILITY_BUSY >= sipe_ocs2007_availability_from_status(status_id, NULL)); } gboolean sipe_ocs2007_availability_is_away(guint availability) { return(availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY); } static void send_presence_publish(struct sipe_core_private *sipe_private, const char *publications); static void free_publication(struct sipe_publication *publication) { g_free(publication->category); g_free(publication->cal_event_hash); g_free(publication->note); g_free(publication->working_hours_xml_str); g_free(publication->fb_start_str); g_free(publication->free_busy_base64); g_free(publication); } struct hash_table_delete_payload { GHashTable *hash_table; guint container; }; static void sipe_remove_category_container_publications_cb(const gchar *name, struct sipe_publication *publication, struct hash_table_delete_payload *payload) { if (publication->container == payload->container) { g_hash_table_remove(payload->hash_table, name); } } static void sipe_remove_category_container_publications(GHashTable *our_publications, const gchar *category, guint container) { struct hash_table_delete_payload payload; payload.hash_table = g_hash_table_lookup(our_publications, category); if (!payload.hash_table) return; payload.container = container; g_hash_table_foreach(payload.hash_table, (GHFunc)sipe_remove_category_container_publications_cb, &payload); } /** MS-PRES container */ struct sipe_container { guint id; guint version; GSList *members; }; /** MS-PRES container member */ struct sipe_container_member { /** user, domain, sameEnterprise, federated, publicCloud; everyone */ gchar *type; gchar *value; }; static const guint containers[] = {32000, 400, 300, 200, 100}; #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint)) static void free_container_member(struct sipe_container_member *member) { if (!member) return; g_free(member->type); g_free(member->value); g_free(member); } static void sipe_ocs2007_free_container(struct sipe_container *container) { GSList *entry; if (!container) return; entry = container->members; while (entry) { void *data = entry->data; entry = g_slist_remove(entry, data); free_container_member((struct sipe_container_member *)data); } g_free(container); } void sipe_core_buddy_menu_free(struct sipe_core_public *sipe_public) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; sipe_utils_slist_free_full(sipe_private->blist_menu_containers, (GDestroyNotify) sipe_ocs2007_free_container); sipe_private->blist_menu_containers = NULL; } static void blist_menu_remember_container(struct sipe_core_private *sipe_private, struct sipe_container *container) { sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers, container); } static struct sipe_container *create_container(guint index, const gchar *member_type, const gchar *member_value, gboolean is_group) { struct sipe_container *container = g_new0(struct sipe_container, 1); struct sipe_container_member *member = g_new0(struct sipe_container_member, 1); container->id = is_group ? (guint) -1 : containers[index]; container->members = g_slist_append(container->members, member); member->type = g_strdup(member_type); member->value = g_strdup(member_value); return(container); } void sipe_ocs2007_free(struct sipe_core_private *sipe_private) { sipe_utils_slist_free_full(sipe_private->containers, (GDestroyNotify) sipe_ocs2007_free_container); } /** * Finds locally stored MS-PRES container member */ static struct sipe_container_member * sipe_find_container_member(struct sipe_container *container, const gchar *type, const gchar *value) { struct sipe_container_member *member; GSList *entry; if (container == NULL || type == NULL) { return NULL; } entry = container->members; while (entry) { member = entry->data; if (sipe_strcase_equal(member->type, type) && sipe_strcase_equal(member->value, value)) { return member; } entry = entry->next; } return NULL; } /** * Finds locally stored MS-PRES container by id */ static struct sipe_container *sipe_find_container(struct sipe_core_private *sipe_private, guint id) { GSList *entry = sipe_private->containers; while (entry) { struct sipe_container *container = entry->data; if (id == container->id) { return container; } entry = entry->next; } return NULL; } static int sipe_find_member_access_level(struct sipe_core_private *sipe_private, const gchar *type, const gchar *value) { unsigned int i = 0; const gchar *value_mod = value; if (!type) return -1; if (sipe_strequal("user", type)) { value_mod = sipe_get_no_sip_uri(value); } for (i = 0; i < CONTAINERS_LEN; i++) { struct sipe_container_member *member; struct sipe_container *container = sipe_find_container(sipe_private, containers[i]); if (!container) continue; member = sipe_find_container_member(container, type, value_mod); if (member) return containers[i]; } return -1; } /** * Returns pointer to domain part in provided Email URL * * @param email an email URL. Example: first.last@hq.company.com * @return pointer to domain part of email URL. Coresponding example: hq.company.com * * Doesn't allocate memory */ static const gchar *sipe_get_domain(const gchar *email) { gchar *tmp; if (!email) return NULL; tmp = strstr(email, "@"); if (tmp && ((tmp+1) < (email + strlen(email)))) { return tmp+1; } else { return NULL; } } /* @TODO: replace with binary search for faster access? */ /** source: http://support.microsoft.com/kb/897567 */ static const gchar * const public_domains[] = { "aol.com", "icq.com", "love.com", "mac.com", "br.live.com", "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk", "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es", "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be", "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr", "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au", "live.com.co", "live.com.mx", "live.com.my", "live.com.pe", "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg", "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie", "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph", "live.ru", "live.se", "livemail.com.br", "livemail.tw", "messengeruser.com", "msn.com", "passport.com", "sympatico.ca", "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es", "yahoo.com", NULL}; static gboolean sipe_is_public_domain(const gchar *domain) { int i = 0; while (public_domains[i]) { if (sipe_strcase_equal(public_domains[i], domain)) { return TRUE; } i++; } return FALSE; } /** * Access Levels * 32000 - Blocked * 400 - Personal * 300 - Team * 200 - Company * 100 - Public */ const gchar *sipe_ocs2007_access_level_name(guint id) { switch (id) { case 32000: return _("Blocked"); case 400: return _("Personal"); case 300: return _("Team"); case 200: return _("Company"); case 100: return _("Public"); } return _("Unknown"); } /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */ int sipe_ocs2007_find_access_level(struct sipe_core_private *sipe_private, const gchar *type, const gchar *value, gboolean *is_group_access) { int container_id = -1; if (sipe_strequal("user", type)) { const char *domain; const char *no_sip_uri = sipe_get_no_sip_uri(value); container_id = sipe_find_member_access_level(sipe_private, "user", no_sip_uri); if (container_id >= 0) { if (is_group_access) *is_group_access = FALSE; return container_id; } domain = sipe_get_domain(no_sip_uri); container_id = sipe_find_member_access_level(sipe_private, "domain", domain); if (container_id >= 0) { if (is_group_access) *is_group_access = TRUE; return container_id; } container_id = sipe_find_member_access_level(sipe_private, "sameEnterprise", NULL); if ((container_id >= 0) && sipe_strcase_equal(sipe_private->public.sip_domain, domain)) { if (is_group_access) *is_group_access = TRUE; return container_id; } container_id = sipe_find_member_access_level(sipe_private, "publicCloud", NULL); if ((container_id >= 0) && sipe_is_public_domain(domain)) { if (is_group_access) *is_group_access = TRUE; return container_id; } container_id = sipe_find_member_access_level(sipe_private, "everyone", NULL); if ((container_id >= 0)) { if (is_group_access) *is_group_access = TRUE; return container_id; } } else { container_id = sipe_find_member_access_level(sipe_private, type, value); if (is_group_access) *is_group_access = FALSE; } return container_id; } static GSList *get_access_domains(struct sipe_core_private *sipe_private) { struct sipe_container *container; struct sipe_container_member *member; GSList *entry; GSList *entry2; GSList *res = NULL; entry = sipe_private->containers; while (entry) { container = entry->data; entry2 = container->members; while (entry2) { member = entry2->data; if (sipe_strcase_equal(member->type, "domain")) { res = sipe_utils_slist_insert_unique_sorted(res, g_strdup(member->value), (GCompareFunc)g_ascii_strcasecmp, g_free); } entry2 = entry2->next; } entry = entry->next; } return res; } static void sipe_send_container_members_prepare(const guint container_id, const guint container_version, const gchar *action, const gchar *type, const gchar *value, char **container_xmls) { gchar *value_str = value ? g_strdup_printf(" value=\"%s\"", value) : g_strdup(""); gchar *body; if (!container_xmls) return; body = g_strdup_printf( "", container_id, container_version, action, type, value_str); g_free(value_str); if ((*container_xmls) == NULL) { *container_xmls = body; } else { char *tmp = *container_xmls; *container_xmls = g_strconcat(*container_xmls, body, NULL); g_free(tmp); g_free(body); } } static void sipe_send_set_container_members(struct sipe_core_private *sipe_private, char *container_xmls) { gchar *self; gchar *contact; gchar *hdr; gchar *body; if (!container_xmls) return; self = sip_uri_self(sipe_private); body = g_strdup_printf( "" "%s" "", container_xmls); contact = get_contact(sipe_private); hdr = g_strdup_printf("Contact: %s\r\n" "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact); g_free(contact); sip_transport_service(sipe_private, self, hdr, body, NULL); g_free(hdr); g_free(body); g_free(self); } /** * @param container_id a new access level. If -1 then current access level * is just removed (I.e. the member is removed from all containers). * @param type a type of member. E.g. "user", "sameEnterprise", etc. * @param value a value for member. E.g. SIP URI for "user" member type. */ void sipe_ocs2007_change_access_level(struct sipe_core_private *sipe_private, const int container_id, const gchar *type, const gchar *value) { unsigned int i; int current_container_id = -1; char *container_xmls = NULL; /* for each container: find/delete */ for (i = 0; i < CONTAINERS_LEN; i++) { struct sipe_container_member *member; struct sipe_container *container = sipe_find_container(sipe_private, containers[i]); if (!container) continue; member = sipe_find_container_member(container, type, value); if (member) { current_container_id = containers[i]; /* delete/publish current access level */ if (container_id < 0 || container_id != current_container_id) { sipe_send_container_members_prepare(current_container_id, container->version, "remove", type, value, &container_xmls); /* remove member from our cache, to be able to recalculate AL below */ container->members = g_slist_remove(container->members, member); } } } /* recalculate AL below */ current_container_id = sipe_ocs2007_find_access_level(sipe_private, type, value, NULL); /* assign/publish new access level */ if (container_id != current_container_id && container_id >= 0) { struct sipe_container *container = sipe_find_container(sipe_private, container_id); guint version = container ? container->version : 0; sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls); } if (container_xmls) { sipe_send_set_container_members(sipe_private, container_xmls); } g_free(container_xmls); } void sipe_core_change_access_level_from_container(struct sipe_core_public *sipe_public, gpointer parameter) { struct sipe_container *container = parameter; struct sipe_container_member *member; if (!container || !container->members) return; member = ((struct sipe_container_member *)container->members->data); if (!member->type) return; SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s", container->id, member->type, member->value ? member->value : ""); sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE, container->id, member->type, member->value); } void sipe_core_change_access_level_for_domain(struct sipe_core_public *sipe_public, const gchar *domain, guint index) { /* move Blocked first */ guint i = (index == 4) ? 0 : index + 1; guint container_id = containers[i]; SIPE_DEBUG_INFO("sipe_core_change_access_level_from_id: domain=%s, container_id=(%d)%d", domain ? domain : "", index, container_id); sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE, container_id, "domain", domain); } /** * Schedules process of self status publish * based on own calendar information. * Should be scheduled to the beginning of every * 15 min interval, like: * 13:00, 13:15, 13:30, 13:45, etc. * */ static void schedule_publish_update(struct sipe_core_private *sipe_private, time_t calculate_from) { int interval = 5*60; /** start of the beginning of closest 5 min interval. */ time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval); SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s", sipe_utils_time_to_debug_str(localtime(&calculate_from))); SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s", sipe_utils_time_to_debug_str(localtime(&next_start))); sipe_schedule_seconds(sipe_private, "<+2007-cal-status>", NULL, next_start - time(NULL), sipe_ocs2007_presence_publish, NULL); } /** * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR * @param availability (%d) Ex.: 6500 */ #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \ "%d" /** * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR * @param token (%s) Ex.: in-a-meeting * @param minAvailability_attr (%s) Ex.: minAvailability="6500" * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none */ #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \ "" /** * Publishes 'calendarState' category. * @param instance (%u) Ex.: 1339299275 * @param version (%u) Ex.: 1 * @param uri (%s) Ex.: john@contoso.com * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY * @param meeting_subject (%s) Ex.: Customer Meeting * @param meeting_location (%s) Ex.: Conf Room 100 * * @param instance (%u) Ex.: 1339299275 * @param version (%u) Ex.: 1 * @param uri (%s) Ex.: john@contoso.com * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY * @param meeting_subject (%s) Ex.: Customer Meeting * @param meeting_location (%s) Ex.: Conf Room 100 */ #define SIPE_PUB_XML_STATE_CALENDAR \ ""\ ""\ "%s"\ "%s"\ ""\ "%s"\ "%s"\ ""\ ""\ ""\ ""\ "%s"\ "%s"\ ""\ "%s"\ "%s"\ ""\ "" /** * Publishes to clear 'calendarState' and 'phoneState' category * @param instance (%u) Ex.: 1251210982 * @param version (%u) Ex.: 1 */ #define SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR \ ""\ "" /** * Publishes to clear any category * @param category_name (%s) Ex.: state * @param instance (%u) Ex.: 536870912 * @param container (%u) Ex.: 3 * @param version (%u) Ex.: 1 * @param expireType (%s) Ex.: static */ #define SIPE_PUB_XML_PUBLICATION_CLEAR \ "" /** * Publishes 'note' category. * @param instance (%u) Ex.: 2135971629; 0 for personal * @param container (%u) Ex.: 200 * @param version (%u) Ex.: 2 * @param type (%s) Ex.: personal or OOF * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z" * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z" * @param body (%s) Ex.: In the office */ #define SIPE_PUB_XML_NOTE \ ""\ ""\ "%s"\ ""\ "" /** * Publishes 'phoneState' category. * @param instance (%u) Ex.: 1339299275 * @param version (%u) Ex.: 1 * @param availability (%u) Ex.: 6500 * @param token (%s) Ex.: on-the-phone * @param minAvailability (%u) generally same as availability * * @param instance (%u) Ex.: 1339299275 * @param version (%u) Ex.: 1 * @param availability (%u) Ex.: 6500 * @param token (%s) Ex.: on-the-phone * @param minAvailability (%u) generally same as availability */ #define SIPE_PUB_XML_STATE_PHONE \ ""\ ""\ "%u"\ ""\ ""\ ""\ ""\ ""\ "%u"\ ""\ ""\ "" /** * Only Busy and OOF calendar event are published. * Different instances are used for that. * * Must be g_free'd after use. */ static gchar *sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private, struct sipe_cal_event *event, const char *uri, int cal_satus) { gchar *start_time_str; int availability = 0; gchar *res; gchar *tmp = NULL; guint instance = (cal_satus == SIPE_CAL_OOF) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) : sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR); /* key is */ gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2); gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3); gpointer state = g_hash_table_lookup(sipe_private->our_publications, "state"); struct sipe_publication *publication_2 = state ? g_hash_table_lookup(state, key_2) : NULL; struct sipe_publication *publication_3 = state ? g_hash_table_lookup(state, key_3) : NULL; g_free(key_2); g_free(key_3); if (!publication_3 && !event) { /* was nothing, have nothing, exiting */ SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: " "Exiting as no publication and no event for cal_satus:%d", cal_satus); return NULL; } if (event && publication_3 && (publication_3->availability == availability) && sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event)))) { g_free(tmp); SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: " "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus); return NULL; /* nothing to update */ } g_free(tmp); if (event && (event->cal_status == SIPE_CAL_BUSY || event->cal_status == SIPE_CAL_OOF)) { gchar *availability_xml_str = NULL; gchar *activity_xml_str = NULL; gchar *escaped_subject = event->subject ? g_markup_escape_text(event->subject, -1) : NULL; gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL; if (event->cal_status == SIPE_CAL_BUSY) { availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL, SIPE_OCS2007_AVAILABILITY_BUSY); } if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) { activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY, sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING), "minAvailability=\"6500\"", "maxAvailability=\"8999\""); } else if (event->cal_status == SIPE_CAL_OOF) { activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY, sipe_status_activity_to_token(SIPE_ACTIVITY_OOF), "minAvailability=\"12000\"", ""); } start_time_str = sipe_utils_time_to_str(event->start_time); res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR, instance, publication_2 ? publication_2->version : 0, uri, start_time_str, availability_xml_str ? availability_xml_str : "", activity_xml_str ? activity_xml_str : "", escaped_subject ? escaped_subject : "", escaped_location ? escaped_location : "", instance, publication_3 ? publication_3->version : 0, uri, start_time_str, availability_xml_str ? availability_xml_str : "", activity_xml_str ? activity_xml_str : "", escaped_subject ? escaped_subject : "", escaped_location ? escaped_location : "" ); g_free(escaped_location); g_free(escaped_subject); g_free(start_time_str); g_free(availability_xml_str); g_free(activity_xml_str); } else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */ { res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR, instance, publication_2 ? publication_2->version : 0, instance, publication_3 ? publication_3->version : 0 ); } return res; } /** * Returns 'note' XML part for publication. * Must be g_free'd after use. * * Protocol format for Note is plain text. * * @param note a note in Sipe internal HTML format * @param note_type either personal or OOF */ static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private, const char *note, /* html */ const char *note_type, time_t note_start, time_t note_end, gboolean force_publish) { guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0; /* key is */ gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200); gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300); gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400); gpointer notes = g_hash_table_lookup(sipe_private->our_publications, "note"); struct sipe_publication *publication_note_200 = notes ? g_hash_table_lookup(notes, key_note_200) : NULL; struct sipe_publication *publication_note_300 = notes ? g_hash_table_lookup(notes, key_note_300) : NULL; struct sipe_publication *publication_note_400 = notes ? g_hash_table_lookup(notes, key_note_400) : NULL; char *tmp = note ? sipe_backend_markup_strip_html(note) : NULL; char *n1 = tmp ? g_markup_escape_text(tmp, -1) : NULL; const char *n2 = publication_note_200 ? publication_note_200->note : NULL; char *res, *tmp1, *tmp2, *tmp3; char *start_time_attr; char *end_time_attr; g_free(tmp); tmp = NULL; g_free(key_note_200); g_free(key_note_300); g_free(key_note_400); /* we even need to republish empty note */ if (!force_publish && sipe_strequal(n1, n2)) { SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting."); g_free(n1); return NULL; /* nothing to update */ } start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL; g_free(tmp); tmp = NULL; end_time_attr = note_end ? g_strdup_printf(" endTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_end))) : NULL; g_free(tmp); if (n1) { tmp1 = g_strdup_printf(SIPE_PUB_XML_NOTE, instance, 200, publication_note_200 ? publication_note_200->version : 0, note_type, start_time_attr ? start_time_attr : "", end_time_attr ? end_time_attr : "", n1); tmp2 = g_strdup_printf(SIPE_PUB_XML_NOTE, instance, 300, publication_note_300 ? publication_note_300->version : 0, note_type, start_time_attr ? start_time_attr : "", end_time_attr ? end_time_attr : "", n1); tmp3 = g_strdup_printf(SIPE_PUB_XML_NOTE, instance, 400, publication_note_400 ? publication_note_400->version : 0, note_type, start_time_attr ? start_time_attr : "", end_time_attr ? end_time_attr : "", n1); } else { tmp1 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR, "note", instance, 200, publication_note_200 ? publication_note_200->version : 0, "static"); tmp2 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR, "note", instance, 300, publication_note_200 ? publication_note_200->version : 0, "static"); tmp3 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR, "note", instance, 400, publication_note_200 ? publication_note_200->version : 0, "static"); } res = g_strconcat(tmp1, tmp2, tmp3, NULL); g_free(start_time_attr); g_free(end_time_attr); g_free(tmp1); g_free(tmp2); g_free(tmp3); g_free(n1); return res; } /** * Publishes 'calendarData' category's WorkingHours. * * @param version (%u) Ex.: 1 * @param email (%s) Ex.: alice@cosmo.local * @param working_hours_xml_str (%s) Ex.: "\ "%s"\ ""\ ""\ ""\ ""\ ""\ ""\ "%s"\ ""\ ""\ ""\ "%s"\ ""\ ""\ ""\ "%s"\ ""\ ""\ ""\ ""\ "" /** * Returns 'calendarData' XML part with WorkingHours for publication. * Must be g_free'd after use. */ static gchar *sipe_publish_get_category_cal_working_hours(struct sipe_core_private *sipe_private) { struct sipe_calendar* cal = sipe_private->calendar; /* key is */ gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1); gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100); gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200); gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300); gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400); gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000); gpointer tmp = g_hash_table_lookup(sipe_private->our_publications, "calendarData"); struct sipe_publication *publication_cal_1 = tmp ? g_hash_table_lookup(tmp, key_cal_1) : NULL; struct sipe_publication *publication_cal_100 = tmp ? g_hash_table_lookup(tmp, key_cal_100) : NULL; struct sipe_publication *publication_cal_200 = tmp ? g_hash_table_lookup(tmp, key_cal_200) : NULL; struct sipe_publication *publication_cal_300 = tmp ? g_hash_table_lookup(tmp, key_cal_300) : NULL; struct sipe_publication *publication_cal_400 = tmp ? g_hash_table_lookup(tmp, key_cal_400) : NULL; struct sipe_publication *publication_cal_32000 = tmp ? g_hash_table_lookup(tmp, key_cal_32000) : NULL; const char *n1 = cal ? cal->working_hours_xml_str : NULL; const char *n2 = publication_cal_300 ? publication_cal_300->working_hours_xml_str : NULL; g_free(key_cal_1); g_free(key_cal_100); g_free(key_cal_200); g_free(key_cal_300); g_free(key_cal_400); g_free(key_cal_32000); if (!cal || is_empty(cal->email) || is_empty(cal->working_hours_xml_str)) { SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting"); return NULL; } if (sipe_strequal(n1, n2)) { SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting."); return NULL; /* nothing to update */ } return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS, /* 1 */ publication_cal_1 ? publication_cal_1->version : 0, cal->email, cal->working_hours_xml_str, /* 100 - Public */ publication_cal_100 ? publication_cal_100->version : 0, /* 200 - Company */ publication_cal_200 ? publication_cal_200->version : 0, cal->email, cal->working_hours_xml_str, /* 300 - Team */ publication_cal_300 ? publication_cal_300->version : 0, cal->email, cal->working_hours_xml_str, /* 400 - Personal */ publication_cal_400 ? publication_cal_400->version : 0, cal->email, cal->working_hours_xml_str, /* 32000 - Blocked */ publication_cal_32000 ? publication_cal_32000->version : 0 ); } /** * Publishes 'calendarData' category's FreeBusy. * * @param instance (%u) Ex.: 1300372959 * @param version (%u) Ex.: 1 * * @param instance (%u) Ex.: 1300372959 * @param version (%u) Ex.: 1 * * @param instance (%u) Ex.: 1300372959 * @param version (%u) Ex.: 1 * @param email (%s) Ex.: alice@cosmo.local * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA..... * * @param instance (%u) Ex.: 1300372959 * @param version (%u) Ex.: 1 * @param email (%s) Ex.: alice@cosmo.local * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA..... * * @param instance (%u) Ex.: 1300372959 * @param version (%u) Ex.: 1 * @param email (%s) Ex.: alice@cosmo.local * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA..... * * @param instance (%u) Ex.: 1300372959 * @param version (%u) Ex.: 1 */ #define SIPE_PUB_XML_FREE_BUSY \ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ "%s"\ ""\ ""\ ""\ ""\ "%s"\ ""\ ""\ ""\ ""\ "%s"\ ""\ ""\ ""\ ""\ "" /** * Returns 'calendarData' XML part with FreeBusy for publication. * Must be g_free'd after use. */ static gchar *sipe_publish_get_category_cal_free_busy(struct sipe_core_private *sipe_private) { struct sipe_calendar* cal = sipe_private->calendar; guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA); char *fb_start_str; char *free_busy_base64; /* const char *st; */ /* const char *fb; */ char *res; /* key is */ gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1); gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100); gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200); gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300); gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400); gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000); gpointer tmp = g_hash_table_lookup(sipe_private->our_publications, "calendarData"); struct sipe_publication *publication_cal_1 = tmp ? g_hash_table_lookup(tmp, key_cal_1) : NULL; struct sipe_publication *publication_cal_100 = tmp ? g_hash_table_lookup(tmp, key_cal_100) : NULL; struct sipe_publication *publication_cal_200 = tmp ? g_hash_table_lookup(tmp, key_cal_200) : NULL; struct sipe_publication *publication_cal_300 = tmp ? g_hash_table_lookup(tmp, key_cal_300) : NULL; struct sipe_publication *publication_cal_400 = tmp ? g_hash_table_lookup(tmp, key_cal_400) : NULL; struct sipe_publication *publication_cal_32000 = tmp ? g_hash_table_lookup(tmp, key_cal_32000) : NULL; g_free(key_cal_1); g_free(key_cal_100); g_free(key_cal_200); g_free(key_cal_300); g_free(key_cal_400); g_free(key_cal_32000); if (!cal || is_empty(cal->email) || !cal->fb_start || is_empty(cal->free_busy)) { SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting"); return NULL; } fb_start_str = sipe_utils_time_to_str(cal->fb_start); free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy); /* we will rebuplish the same data to refresh publication time, * so if data from multiple sources, most recent will be choosen */ // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL; // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL; // //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64)) //{ // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting."); // g_free(fb_start_str); // g_free(free_busy_base64); // return NULL; /* nothing to update */ //} res = g_strdup_printf(SIPE_PUB_XML_FREE_BUSY, /* 1 */ cal_data_instance, publication_cal_1 ? publication_cal_1->version : 0, /* 100 - Public */ cal_data_instance, publication_cal_100 ? publication_cal_100->version : 0, /* 200 - Company */ cal_data_instance, publication_cal_200 ? publication_cal_200->version : 0, cal->email, fb_start_str, free_busy_base64, /* 300 - Team */ cal_data_instance, publication_cal_300 ? publication_cal_300->version : 0, cal->email, fb_start_str, free_busy_base64, /* 400 - Personal */ cal_data_instance, publication_cal_400 ? publication_cal_400->version : 0, cal->email, fb_start_str, free_busy_base64, /* 32000 - Blocked */ cal_data_instance, publication_cal_32000 ? publication_cal_32000->version : 0 ); g_free(fb_start_str); g_free(free_busy_base64); return res; } #ifdef HAVE_VV #define SIPE_PUB_XML_DEVICE_VV \ ""\ "
%s", msg_tmp) : NULL; g_free(msg_tmp); /* Service unavailable; Server Internal Error; Server Time-out */ if (sip_error == 606 && sip_warning == 309) { /* Not acceptable all. */ /* Message contents not allowed by policy */ label = _("Your message or invitation was not delivered, possibly because it contains a hyperlink or other content that the system administrator has blocked."); g_free(msg); msg = NULL; } else if (sip_error == 500 || sip_error == 503 || sip_error == 504 || sip_error == 603) { label = _("This message was not delivered to %s because the service is not available"); } else if (sip_error == 486) { /* Busy Here */ label = _("This message was not delivered to %s because one or more recipients do not want to be disturbed"); } else if (sip_error == 415) { /* Unsupported media type */ label = _("This message was not delivered to %s because one or more recipients don't support this type of message"); } else { label = _("This message was not delivered to %s because one or more recipients are offline"); } msg_tmp = g_strdup_printf( "%s%s\n%s" , msg_tmp2 = g_strdup_printf(label, who ? who : ""), msg ? ":" : "", msg ? msg : ""); sipe_user_present_error(sipe_private, session, msg_tmp); g_free(msg_tmp2); g_free(msg_tmp); g_free(msg); } static gboolean process_info_typing_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { /* Indicates dangling IM session which needs to be dropped */ if (msg->response == 408 || /* Request timeout */ msg->response == 480 || /* Temporarily Unavailable */ msg->response == 481) { /* Call/Transaction Does Not Exist */ gchar *with = sipmsg_parse_to_address(msg); struct sip_session *session = sipe_session_find_im(sipe_private, with); struct sip_dialog *dialog = sipe_dialog_find(session, with); if (dialog) sipe_im_cancel_dangling(sipe_private, session, dialog, with, sipe_im_cancel_unconfirmed); g_free(with); } return(TRUE); } void sipe_core_user_feedback_typing(struct sipe_core_public *sipe_public, const gchar *to, gboolean typing) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sip_session *session = sipe_session_find_im(sipe_private, to); struct sip_dialog *dialog = sipe_dialog_find(session, to); /* only enable this debug output while testing SIPE_DEBUG_INFO("sipe_core_user_feedback_typing session %p (%s) dialog %p (%s) established %s", session, session ? session->callid : "N/A", dialog, dialog ? dialog->callid : "N/A", (dialog && dialog->is_established) ? "YES" : "NO"); */ if (session && dialog && dialog->is_established) { gchar *body = g_strdup_printf("" "" " " "", typing ? "type" : "idle"); sip_transport_info(sipe_private, "Content-Type: application/xml\r\n", body, dialog, process_info_typing_response); g_free(body); } } struct sipe_user_ask_ctx { struct sipe_core_private *sipe_private; gpointer accept_cb; gpointer decline_cb; gpointer data; }; struct sipe_user_ask_ctx * sipe_user_ask(struct sipe_core_private *sipe_private, const gchar *message, const gchar *accept_label, SipeUserAskCb accept_cb, const gchar *decline_label, SipeUserAskCb decline_cb, gpointer data) { struct sipe_user_ask_ctx *ctx = g_new0(struct sipe_user_ask_ctx, 1); ctx->sipe_private = sipe_private; ctx->accept_cb = accept_cb; ctx->decline_cb = decline_cb; ctx->data = data; sipe_backend_user_ask(SIPE_CORE_PUBLIC, message, accept_label, decline_label, ctx); return ctx; } void sipe_core_user_ask_cb(gpointer context, gboolean accepted) { struct sipe_user_ask_ctx *ctx = context; if (accepted && ctx->accept_cb) ((SipeUserAskCb)ctx->accept_cb)(ctx->sipe_private, ctx->data); else if (ctx->decline_cb) ((SipeUserAskCb)ctx->decline_cb)(ctx->sipe_private, ctx->data); g_free(ctx); } struct sipe_user_ask_ctx * sipe_user_ask_choice(struct sipe_core_private *sipe_private, const gchar *message, GSList *choices, SipeUserAskChoiceCb callback, gpointer data) { struct sipe_user_ask_ctx *ctx = g_new0(struct sipe_user_ask_ctx, 1); ctx->sipe_private = sipe_private; ctx->accept_cb = callback; ctx->decline_cb = NULL; ctx->data = data; sipe_backend_user_ask_choice(SIPE_CORE_PUBLIC, message, choices, ctx); return ctx; } void sipe_core_user_ask_choice_cb(gpointer context, guint choice_id) { struct sipe_user_ask_ctx *ctx = context; if (ctx->accept_cb) { ((SipeUserAskChoiceCb)ctx->accept_cb)(ctx->sipe_private, ctx->data, choice_id); } g_free(ctx); } void sipe_user_close_ask(struct sipe_user_ask_ctx *context) { sipe_backend_user_close_ask(context); g_free(context); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-user.h ================================================ /** * @file sipe-user.h * * pidgin-sipe * * Copyright (C) 2011-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct sip_session; struct sipe_core_private; /* Opaque ask context structure */ struct sipe_user_ask_ctx; /** * Present error message in users session * * @param sipe_private SIPE core private data * @param session session for the conversation * @param message message text */ void sipe_user_present_error(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *message); /** * Present info message in users session * * @param sipe_private SIPE core private data * @param session session for the conversation * @param message message text */ void sipe_user_present_info(struct sipe_core_private *sipe_private, struct sip_session *session, const gchar *message); /** * Present error that his message wasn't delivered in users session * * @param sipe_private SIPE core private data * @param session session for the conversation * @param sip_error SIP error code or -1 * @param sip_warning SIP warning code or -1 * @param who URI of recipient * @param message message text */ void sipe_user_present_message_undelivered(struct sipe_core_private *sipe_private, struct sip_session *session, int sip_error, int sip_warning, const gchar *who, const gchar *message); typedef void (* SipeUserAskCb)(struct sipe_core_private *, gpointer data); /** * Present a query that is to be accepted or declined by the user * * @param sipe_private SIPE core private data * @param message Text of the query to be shown to user * @param accept_label Label to be displayed on UI control that accepts query * @param accept_cb callback function to be invoked when query is accepted * @param decline_label Label to be displayed on UI control that declines query * @param decline_cb callback function to be invoked when query is declined * @param data custom user data * * @return opaque sipe_user_ask_ctx pointer that can be used to close the query * before user answered it. */ struct sipe_user_ask_ctx * sipe_user_ask(struct sipe_core_private *sipe_private, const gchar *message, const gchar *accept_label, SipeUserAskCb accept_cb, const gchar *decline_label, SipeUserAskCb decline_cb, gpointer data); typedef void (* SipeUserAskChoiceCb)(struct sipe_core_private *, gpointer data, guint choice_id); /** * Present a set of options to the user to choose from. * * @param sipe_core_private SIPE core private data * @param message text message to be shown to the user * @param choices list of choice options * @param callback callback function to be invoked after user makes * a choice * @param data custom user data to pass to callback */ struct sipe_user_ask_ctx * sipe_user_ask_choice(struct sipe_core_private *sipe_private, const gchar *message, GSList *choices, SipeUserAskChoiceCb callback, gpointer data); /** * Closes the pending user query * * @param context sipe_user_ask_ctx pointer returned by sipe_user_ask() */ void sipe_user_close_ask(struct sipe_user_ask_ctx *context); ================================================ FILE: src/core/sipe-utils.c ================================================ /** * @file sipe-utils.c * * pidgin-sipe * * Copyright (C) 2009-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include "sip-transport.h" #include "sipe-backend.h" #include "sipe-core.h" /* to ensure same API for backends */ #include "sipe-core-private.h" #include "sipe-utils.h" #include "uuid.h" /* Generate 16 random bits */ #define RANDOM16BITS (rand() & 0xFFFF) gchar *gencallid(void) { return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx", RANDOM16BITS, RANDOM16BITS, RANDOM16BITS, RANDOM16BITS, RANDOM16BITS, RANDOM16BITS, RANDOM16BITS, RANDOM16BITS); } gchar *gentag(void) { return g_strdup_printf("%04d%04d", RANDOM16BITS, RANDOM16BITS); } gchar *genconfid(void) { return g_strdup_printf("%04X%04X%04X%04X%04X%04X%04X%04X", RANDOM16BITS, RANDOM16BITS, RANDOM16BITS, RANDOM16BITS, RANDOM16BITS, RANDOM16BITS, RANDOM16BITS, RANDOM16BITS); } gchar *get_contact(const struct sipe_core_private *sipe_private) { return g_strdup(sipe_private->contact); } gchar *parse_from(const gchar *hdr) { gchar *from; const gchar *tmp, *tmp2 = hdr; if (!hdr) return NULL; SIPE_DEBUG_INFO("parsing address out of %s", hdr); tmp = strchr(hdr, '<'); /* i hate the different SIP UA behaviours... */ if (tmp) { /* sip address in <...> */ tmp2 = tmp + 1; tmp = strchr(tmp2, '>'); if (tmp) { from = g_strndup(tmp2, tmp - tmp2); } else { SIPE_DEBUG_INFO_NOFORMAT("found < without > in From"); return NULL; } } else { tmp = strchr(tmp2, ';'); if (tmp) { from = g_strndup(tmp2, tmp - tmp2); } else { from = g_strdup(tmp2); } } SIPE_DEBUG_INFO("got %s", from); return from; } gchar *sip_uri_from_name(const gchar *name) { return(g_strdup_printf("sip:%s", name)); } gchar *sip_uri(const gchar *string) { return(strstr(string, "sip:") ? g_strdup(string) : sip_uri_from_name(string)); } static gchar *escape_uri_part(const gchar *in, guint len) { gchar *escaped = NULL; if (len) { gchar *s; /* reserve space for worst case, i.e. every character needs escaping */ escaped = s = g_malloc(3 * len + 1); while (len--) { gchar c = *in++; /* only allow ASCII characters */ if (!isascii(c)) { g_free(escaped); return(NULL); } /* * RFC 3986 Appendix A * * authority = [ userinfo "@" ] host [ ":" port ] * userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) * host = IP-literal / IPv4address / reg-name * reg-name = *( unreserved / pct-encoded / sub-delims ) * pct-encoded = "%" HEXDIG HEXDIG * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" * * Escape everything that isn't in "unreserved" */ if (isalnum(c) || (c == '.') || (c == '-') || (c == '_') || (c == '~')) { *s++ = c; } else { sprintf(s, "%%%1X%1X", c / 16, c % 16); s += 3; } } *s = '\0'; } return(escaped); } gchar *sip_uri_if_valid(const gchar *string) { /* strip possible sip: prefix */ const gchar *uri = sipe_get_no_sip_uri(string); const gchar *at; gchar *result = NULL; /* only XXX@YYY is valid */ if (uri && ((at = strchr(uri, '@')) != NULL)) { gchar *userinfo = escape_uri_part(uri, at - uri); if (userinfo) { gchar *host = escape_uri_part(at + 1, strlen(at + 1)); if (host) { /* name is valid for URI, convert it */ result = g_strdup_printf("sip:%s@%s", userinfo, host); g_free(host); } g_free(userinfo); } } return(result); } const gchar *sipe_get_no_sip_uri(const gchar *sip_uri) { #define SIP_PREFIX "sip:" if (!sip_uri) return NULL; if (g_str_has_prefix(sip_uri, SIP_PREFIX)) { return(sip_uri + strlen(SIP_PREFIX)); } else { return sip_uri; } } gchar *get_uuid(struct sipe_core_private *sipe_private) { return(generateUUIDfromEPID(sip_transport_epid(sipe_private))); } guint sipe_get_pub_instance(struct sipe_core_private *sipe_private, int publication_key) { unsigned res = 0; sscanf(sip_transport_epid(sipe_private), "%08x", &res); if (publication_key == SIPE_PUB_DEVICE) { /* as is */ } else if (publication_key == SIPE_PUB_STATE_MACHINE) { /* First hexadecimal digit is 0x3 */ res = (res >> 4) | 0x30000000; } else if (publication_key == SIPE_PUB_STATE_USER) { res = 0x20000000; /* fixed */ } else if (publication_key == SIPE_PUB_STATE_CALENDAR) { /* First hexadecimal digit is 0x4 */ res = (res >> 4) | 0x40000000; } else if (publication_key == SIPE_PUB_STATE_CALENDAR_OOF) { /* First hexadecimal digit is 0x5 */ res = (res >> 4) | 0x50000000; } else if (publication_key == SIPE_PUB_CALENDAR_DATA || publication_key == SIPE_PUB_NOTE_OOF) { /* First hexadecimal digit is 0x4 */ unsigned calendar_id = 0; char *mail_hash = sipe_get_epid(sipe_private->email, "", ""); sscanf(mail_hash, "%08x", &calendar_id); g_free(mail_hash); res = (calendar_id >> 4) | 0x40000000; } else if (publication_key == SIPE_PUB_STATE_PHONE_VOIP) { /* First hexadecimal digit is 0x8 */ res = (res >> 4) | 0x80000000; } return res; } gboolean sipe_is_bad_alias(const char *uri, const char *alias) { char *uri_alias; gboolean result = FALSE; if (!uri) return FALSE; if (!alias) return TRUE; if (g_str_has_prefix(alias, "sip:") || g_str_has_prefix(alias, "sips:")) return TRUE; /* check if alias is just SIP URI but without 'sip:' prefix */ uri_alias = sip_uri_from_name(alias); if (sipe_strcase_equal(uri, uri_alias)) { result = TRUE; } g_free(uri_alias); return result; } gboolean is_empty(const char *st) { if (!st || strlen(st) == 0) { return TRUE; } /* suspicious leading or trailing spaces */ else if (isspace((unsigned char) *st) || isspace((unsigned char) *(st + strlen(st) - 1))) { /* to not modify original string */ char *dup = g_strdup(st); if (strlen(g_strstrip(dup)) == 0) { g_free(dup); return TRUE; } g_free(dup); } return FALSE; } void sipe_utils_message_debug(struct sipe_transport_connection *conn, const gchar *type, const gchar *header, const gchar *body, gboolean sending) { GString *str = g_string_new(""); const char *marker = sending ? ">>>>>>>>>>" : "<<<<<<<<<<"; if (sipe_backend_debug_enabled()) { /* unsafe debugging enabled - include message contents */ gchar *time_str; gchar *tmp = NULL; #if GLIB_CHECK_VERSION(2,56,0) // resolution is microseconds GDateTime *datetime = g_date_time_new_now_utc(); gint msecs = 0; if (datetime) { tmp = g_date_time_format(datetime, "%FT%T"); msecs = g_date_time_get_microsecond(datetime); g_date_time_unref(datetime); } time_str = g_strdup_printf("%s.%06dZ", tmp ? tmp : "", msecs); g_free(tmp); #else GTimeVal currtime; g_get_current_time(&currtime); time_str = g_time_val_to_iso8601(&currtime); #endif g_string_append_printf(str, "\nMESSAGE START %s %s(%p) - %s\n", marker, type, conn, time_str); g_string_append(str, tmp = sipe_utils_str_replace(header, "\r\n", "\n")); g_free(tmp); g_string_append(str, "\n"); if (body) { g_string_append(str, tmp = sipe_utils_str_replace(body, "\r\n", "\n")); g_free(tmp); g_string_append(str, "\n"); } g_string_append_printf(str, "MESSAGE END %s %s(%p) - %s", marker, type, conn, time_str); g_free(time_str); } else { /* normal debugging - just show the important stuff */ g_string_append_printf(str, "MESSAGE %s %s(%p)", marker, type, conn); } SIPE_DEBUG_INFO_NOFORMAT(str->str); g_string_free(str, TRUE); } gboolean sipe_strequal(const gchar *left, const gchar *right) { return (g_strcmp0(left, right) == 0); } gboolean sipe_strcase_equal(const gchar *left, const gchar *right) { return ((left == NULL && right == NULL) || (left != NULL && right != NULL && g_ascii_strcasecmp(left, right) == 0)); } time_t sipe_utils_str_to_time(const gchar *timestamp) { #if GLIB_CHECK_VERSION(2,56,0) GDateTime *datetime = NULL; #else GTimeVal time; gboolean success = FALSE; #endif /* g_time_val_from_iso8601() warns about NULL pointer */ if (timestamp) { guint len; /* We have to make sure that the ISO8601 contains a time zone offset, otherwise the time is interpreted as local time, not UTC! @TODO: is there a better way to check this? */ if (((len = strlen(timestamp)) > 0) && isdigit(timestamp[len-1])) { gchar *tmp = g_strdup_printf("%sZ", timestamp); #if GLIB_CHECK_VERSION(2,56,0) datetime = g_date_time_new_from_iso8601(tmp, NULL); #else success = g_time_val_from_iso8601(tmp, &time); #endif g_free(tmp); } else { #if GLIB_CHECK_VERSION(2,56,0) datetime = g_date_time_new_from_iso8601(timestamp, NULL); #else success = g_time_val_from_iso8601(timestamp, &time); #endif } } #if GLIB_CHECK_VERSION(2,56,0) if (datetime) { time_t result = g_date_time_to_unix(datetime); g_date_time_unref(datetime); return(result); } #else if (success) return(time.tv_sec); #endif SIPE_DEBUG_ERROR("sipe_utils_str_to_time: failed to parse ISO8601 string '%s'", timestamp ? timestamp : ""); return(0); } gchar * sipe_utils_time_to_str(time_t timestamp) { gchar *result = NULL; #if GLIB_CHECK_VERSION(2,56,0) GDateTime *datetime = g_date_time_new_from_unix_utc(timestamp); if (datetime) { // resolution is seconds result = g_date_time_format(datetime, "%FT%TZ"); g_date_time_unref(datetime); } #else GTimeVal time = { timestamp, 0 }; result = g_time_val_to_iso8601(&time); #endif if (result) return(result); SIPE_DEBUG_ERROR("sipe_utils_time_to_str: failed to convert %lu to ISO8601 string", timestamp); return(g_strdup("")); } const gchar *sipe_utils_time_to_debug_str(const struct tm *tm) { gchar *buffer = asctime(tm); size_t length; if (!buffer) return(""); /* asctime() appends "\n" to the resulting string -> strip it */ length = strlen(buffer); if (length) buffer[length - 1] = '\0'; return(buffer); } size_t hex_str_to_buff(const char *hex_str, guint8 **buff) { char two_digits[3]; size_t length; size_t i; if (!buff) return 0; if (!hex_str) return 0; length = strlen(hex_str)/2; *buff = (unsigned char *)g_malloc(length); for (i = 0; i < length; i++) { two_digits[0] = hex_str[i * 2]; two_digits[1] = hex_str[i * 2 + 1]; two_digits[2] = '\0'; (*buff)[i] = (unsigned char)strtoul(two_digits, NULL, 16); } return length; } char * buff_to_hex_str(const guint8 *buff, const size_t buff_len) { char *res; size_t i, j; if (!buff) return NULL; res = g_malloc(buff_len * 2 + 1); for (i = 0, j = 0; i < buff_len; i++, j+=2) { sprintf(&res[j], "%02X", buff[i]); } res[j] = '\0'; return res; } gboolean sipe_utils_parse_lines(GSList **list, gchar **lines, const gchar *delimiter) { int i; gchar **parts; gchar *dummy; gchar *dummy2; gchar *tmp; for(i = 0; lines[i] && strlen(lines[i]) > 2; i++) { parts = g_strsplit(lines[i], delimiter, 2); if(!parts[0] || !parts[1]) { g_strfreev(parts); return FALSE; } dummy = parts[1]; dummy2 = 0; while(*dummy==' ' || *dummy=='\t') dummy++; dummy2 = g_strdup(dummy); while(lines[i+1] && (lines[i+1][0]==' ' || lines[i+1][0]=='\t')) { i++; dummy = lines[i]; while(*dummy==' ' || *dummy=='\t') dummy++; tmp = g_strdup_printf("%s %s",dummy2, dummy); g_free(dummy2); dummy2 = tmp; } *list = sipe_utils_nameval_add(*list, parts[0], dummy2); g_free(dummy2); g_strfreev(parts); } return TRUE; } GSList* sipe_utils_nameval_add(GSList* list, const gchar *name, const gchar *value) { struct sipnameval *element = g_new0(struct sipnameval,1); /* SANITY CHECK: the calling code must be fixed if this happens! */ if (!value) { SIPE_DEBUG_ERROR("sipe_utils_nameval_add: NULL value for %s", name); value = ""; } element->name = g_strdup(name); element->value = g_strdup(value); return g_slist_append(list, element); } void sipe_utils_nameval_free(GSList *list) { struct sipnameval *elem; while(list) { elem = list->data; list = g_slist_remove(list,elem); g_free(elem->name); g_free(elem->value); g_free(elem); } } const gchar * sipe_utils_nameval_find(const GSList *list, const gchar *name) { return sipe_utils_nameval_find_instance (list, name, 0); } const gchar * sipe_utils_nameval_find_instance(const GSList *list, const gchar *name, int which) { const GSList *tmp; struct sipnameval *elem; int i = 0; tmp = list; while(tmp) { elem = tmp->data; // OCS2005 can send the same header in either all caps or mixed case if (sipe_strcase_equal(elem->name, name)) { if (i == which) { return elem->value; } i++; } tmp = g_slist_next(tmp); } return NULL; } gchar *sipe_utils_str_replace(const gchar *string, const gchar *delimiter, const gchar *replacement) { gchar **split; gchar *result; if (!string || !delimiter || !replacement) return NULL; split = g_strsplit(string, delimiter, 0); result = g_strjoinv(replacement, split); g_strfreev(split); return result; } void sipe_utils_shrink_buffer(struct sipe_transport_connection *conn, const gchar *unread) { conn->buffer_used -= unread - conn->buffer; /* string terminator is not included in buffer_used */ memmove(conn->buffer, unread, conn->buffer_used + 1); } gboolean sipe_utils_ip_is_private(const char *ip) { return /* IPv4 */ g_str_has_prefix(ip, "10.") || g_str_has_prefix(ip, "172.16.") || g_str_has_prefix(ip, "192.168.") || /* IPV6 */ g_str_has_prefix(ip, "fd"); } const gchar *sipe_utils_ip_sdp_address_marker(const gchar *ip) { return(ip && strchr(ip, ':') ? "IP6" : "IP4"); } gchar *sipe_utils_presence_key(const gchar *uri) { return g_strdup_printf("<%s>", uri); } gchar * sipe_utils_uri_unescape(const gchar *string) { gchar *unescaped; gchar *tmp; if (!string) return NULL; unescaped = g_uri_unescape_string(string, NULL); if (unescaped && !g_utf8_validate(unescaped, -1, (const gchar **)&tmp)) *tmp = '\0'; return unescaped; } GSList *sipe_utils_slist_insert_unique_sorted(GSList *list, gpointer data, GCompareFunc func, GDestroyNotify destroy) { if (g_slist_find_custom(list, data, func)) { /* duplicate */ if (destroy) (*destroy)(data); return(list); } else { /* unique: list takes ownership of "data" */ return(g_slist_insert_sorted(list, data, func)); } } void sipe_utils_slist_free_full(GSList *list, GDestroyNotify free) { #if GLIB_CHECK_VERSION(2,28,0) g_slist_free_full(list, free); #else GSList *entry = list; while (entry) { (*free)(entry->data); entry = entry->next; } g_slist_free(list); #endif } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-utils.h ================================================ /** * @file sipe-utils.h * * pidgin-sipe * * Copyright (C) 2009-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Interface dependencies: * * * */ /* Forward declarations */ struct sipe_core_private; struct sipe_transport_connection; /* Our publication type keys. OCS 2007+ * Format: SIPE_PUB_{Category}[_{SubSategory}] */ /** * device * - * * - * Unique to the device. */ #define SIPE_PUB_DEVICE 0 /** * state * Machine state * * Availability, activity, end-point location, time zone, and device type. * First hexadecimal digit is 0x3; remaining seven hexadecimal digits are unique per device. */ #define SIPE_PUB_STATE_MACHINE 3 /** * state * User state * * Availability and activity. * 0x20000000 */ #define SIPE_PUB_STATE_USER 2 /** * state * Calendar state * * Availability, activity, meeting subject, and meeting location. * First hexadecimal digit is 0x4; remaining seven hexadecimal digits are unique per device. */ #define SIPE_PUB_STATE_CALENDAR 4 /** * state * Calendar state for an Out of Office meeting * * (??)Activity for when a user sets or removes an Out of Office message in Exchange. * (+)user sets in Outlook for an Out of Office meeting * First hexadecimal digit is 0x5; remaining seven hexadecimal digits are unique per device. */ #define SIPE_PUB_STATE_CALENDAR_OOF 5 /** * state * RCC Phone State * * Availability and activity for RCC call connect/disconnect or participant count changes from 0 to 2, 2 to N, N to 2, 2 to 0. * First hexadecimal digit is 0x7; remaining seven hexadecimal digits are unique per device. */ #define SIPE_PUB_STATE_PHONE_RCC 7 /** * state * VOIP Phone State * * Availability and activity for VOIP call connect/disconnect or participant count changes from 0 to 2, 2 to N, N to 2, 2 to 0. * First hexadecimal digit is 0x8; remaining seven hexadecimal digits uniquely define the SIP URI and device. */ #define SIPE_PUB_STATE_PHONE_VOIP 8 /** * calendarData * Free/busy data * * Start time, granularity, and free/busy data. * First hexadecimal digit is 0x4; last seven hexadecimal digits uniquely define the calendar. */ #define SIPE_PUB_CALENDAR_DATA 400 /** * note * Out of Office note * * Out of Office note that a user sets in Outlook using the Out of Office assistant. * First hexadecimal digit is 0x4; last seven hexadecimal digits uniquely define the calendar. */ #define SIPE_PUB_NOTE_OOF 400 /** * Returns UUID value. * * @param sipe_private (in) SIPE core private data * * @return uuid. Must be g_free()'d. */ gchar * get_uuid(struct sipe_core_private *sipe_private); /** * Generate Call ID * * @return Call ID. Must be g_free()'d. */ gchar *gencallid(void); /** * Generate Tag * * @return Tag. Must be g_free()'d. */ gchar *gentag(void); /** * Generate conference-id * 32 characters long. Value space is restricted to printable ASCII characters * * Ex.: 8386E6AEAAA41E4AA6627BA76D43B6D1 * * @return conference-id. Must be g_free()'d. */ gchar *genconfid(void); /** * Returns instance value for particular publication type. * It should be consistent for the same endpoint * but different between distinct endpoints. * * See defined constants for keys patterned SIPE_PUB_* */ guint sipe_get_pub_instance(struct sipe_core_private *sipe_private, int publication_key); /** * Get contact information from SIPE account * * @param sipe_private (in) SIPE core private data * * @return Contact. Must be g_free()'d. */ gchar *get_contact(const struct sipe_core_private *sipe_private); /** * Parses URI from SIP header * * @param hdr (in) To/From header * * @return URI with sip: prefix. Must be g_free()'d. */ gchar *parse_from(const gchar *hdr); /** * Create sip: URI from name * * @param name (in) * * @return URI with sip: prefix. Must be g_free()'d. */ gchar *sip_uri_from_name(const gchar *name); /** * Create sip: URI from SIP account user name * * @param sipe_private (in) SIPE core private data * * @return URI with sip: prefix. Must be g_free()'d. */ #define sip_uri_self(sipe_private) (sip_uri_from_name(sipe_private->username)) /** * Create sip: URI from name or sip: URI * * @param string (in) name or sip: URI * * @return URI with sip: prefix. Must be g_free()'d. */ gchar *sip_uri(const gchar *string); /** * Create sip: URI from name or sip: URI. Checks for invalid characters * * @param string (in) name or sip: URI * * @return URI with sip: prefix. Returns NULL if @c string contains invalid * characters. Must be g_free()'d. */ gchar *sip_uri_if_valid(const gchar *string); /** * Returns pointer to URI without sip: prefix if any (doesn't allocate memory) * * @param sip_uri SIP URI possibly with sip: prefix. Example: sip:first.last@hq.company.com * * @return pointer to URL without sip: prefix. Coresponding example: first.last@hq.company.com */ const gchar *sipe_get_no_sip_uri(const gchar *sip_uri); /** * Tries to figure out if contact alias which stored locally * is just SIP URI, not a proper display name or local alias. * * @param uri SIP URI with 'sip:' prefix. * @param alias as returned by purple. */ gboolean sipe_is_bad_alias(const char *uri, const char *alias); /** * Checks if provided string is empty - NULL, zero size or just series of white spaces. * Doesn't modify input string. */ gboolean is_empty(const char *st); /** * Message debugging * * @param transport the transport connection * @param type message type description (SIP or HTTP). * @param header message header * @param body message body or NULL * @param sending TRUE if outgoing message */ void sipe_utils_message_debug(struct sipe_transport_connection *conn, const gchar *type, const gchar *header, const gchar *body, gboolean sending); /** * Tests two strings for equality. * * Unlike strcmp(), this function will not crash if one or both of the * strings are @c NULL. * * Same as purple_strequal (defined only for 2.6) to maintain * our backward compatibility. * * @param left A string * @param right A string to compare with left * * @return @c TRUE if the strings are the same, else @c FALSE. * */ gboolean sipe_strequal(const gchar *left, const gchar *right); /** * Tests two strings for equality, ignoring the case * * Same as glib @c g_ascii_strcasecmp() but works correctly for @c NULL * pointers too. Plus it doesn't complain loudly about them... * * @param left A string * @param right A string to compare with left * * @return @c TRUE if the strings are the same, else @c FALSE. * */ gboolean sipe_strcase_equal(const gchar *left, const gchar *right); /** * Parses a timestamp in ISO8601 format and returns a time_t. * Assumes UTC if no timezone specified * * @param timestamp The timestamp (may be @c NULL) * * @return time_t or 0 if timestamp parsing failed */ time_t sipe_utils_str_to_time(const gchar *timestamp); /** * Converts time_t to ISO8601 string. * Timezone is UTC. * * Must be g_free()'d after use. * * Example: 2010-02-03T23:59:59Z */ gchar * sipe_utils_time_to_str(time_t timestamp); /** * Converts struct tm to human readable string * * Example: Sat Feb 28 11:07:35 2015 * * @return pointer to static buffer. Will never return @c NULL. */ const gchar *sipe_utils_time_to_debug_str(const struct tm *tm); struct sipnameval { gchar *name; gchar *value; }; /** * Parses string of hex digits to buffer. * Allocates memory. * * @param hex_str (in) string of hex digits to convert. * @param buff (out) newly allocated buffer. Must be g_free()'d after use. * * @return size of newly allocated buffer */ size_t hex_str_to_buff(const char *hex_str, guint8 **buff); /** * Composes hex string out of provided buffer. * Allocates memory. * * @param buff input buffer * @param buff_len length of buffer * * @result newly allocated hex string representing buffer. Must be g_free()'d after use. */ char * buff_to_hex_str(const guint8 *buff, const size_t buff_len); /** * Creates name-value pairs from given lines and appends them to @c list * * Lines must be in format 'name [delimiter] value' * * @param list a list of @c sipnameval structures * @param lines array of strings in format 'name: value' * @param delimiter sequence of characters between name and value * * @return @c FALSE if any of @c lines has incorrect format, @c TRUE otherwise */ gboolean sipe_utils_parse_lines(GSList **list, gchar **lines, const gchar *delimiter); /** * Adds a name-value pair to @c list * * @param list a list of @c sipnameval structures * @param name attribute's name * @param value value of attribute @c name * * @return the new start of the GSList */ GSList * sipe_utils_nameval_add(GSList *list, const gchar *name, const gchar *value); /** * Finds a value of attribute @c name in @c list * * @param list a list of @c sipnameval structures * @param name attribute to find * * @return value of @c name or NULL if @c name is not found */ const gchar * sipe_utils_nameval_find(const GSList *list, const gchar *name); /** * Returns @c which occurrence of attribute @c name in @c list * * @c which is zero based, so 0 means first occurrence of @c name in @c list. * * @param list a list of @c sipnameval structures * @param name attribute to find * @param which specifies occurrence of @name in @c list * * @return value of @c name or NULL if @c name is not found */ const gchar * sipe_utils_nameval_find_instance(const GSList *list, const gchar *name, int which); /** * Frees memory allocated by @c list * * @param list a list of @c sipnameval structures */ void sipe_utils_nameval_free(GSList *list); /** * Given a string, this replaces one substring with another * and returns a newly allocated string. * * @param string the string from which to replace stuff. * @param delimiter the substring you want replaced. * @param replacement the substring you want as replacement. * * @return string with the substitution or NULL. Must be g_free()'d after use. */ gchar *sipe_utils_str_replace(const gchar *string, const gchar *delimiter, const gchar *replacement); /** * Remove read characters from transport buffer * * @param conn the transport connection * @param unread pointer to the first character in the buffer */ void sipe_utils_shrink_buffer(struct sipe_transport_connection *conn, const gchar *unread); /** * Checks whether given IP address belongs to private block as defined in RFC1918 * * @param ip IPv4 address in "X.X.X.X" format * @return @c TRUE if address is private */ gboolean sipe_utils_ip_is_private(const char *ip); /** * Get SDP address marker for IP address * * @param ip address * * @return address marker string for IP address */ const gchar *sipe_utils_ip_sdp_address_marker(const gchar *ip); /** * Generate presence key * * @param uri presence URI * * @return key string. Must be g_free()'d after use. */ gchar *sipe_utils_presence_key(const gchar *uri); /** * Decodes a URI into a plain string. * * @param string the string to translate. * * @return the resulting string. Must be g_free()'d after use. */ gchar *sipe_utils_uri_unescape(const gchar *string); /** * Inserts in item in the list only if the value isn't already in that list * * @param list a singly linked list * @param data the item to insert. Will not be copied. * @param func function to use to compare the values * @param destroy if @c NULL call to destroy @c data if is already on list * * @return the new list head */ GSList *sipe_utils_slist_insert_unique_sorted(GSList *list, gpointer data, GCompareFunc func, GDestroyNotify destroy); /** * Same as @c g_slist_free_full() which is only defined for >= 2.28 * * @param list a singly linked list * @param free function to free list data */ void sipe_utils_slist_free_full(GSList *list, GDestroyNotify free); ================================================ FILE: src/core/sipe-webticket.c ================================================ /** * @file sipe-webticket.c * * pidgin-sipe * * Copyright (C) 2011-2016 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Specification references: * * - [MS-OCAUTHWS]: http://msdn.microsoft.com/en-us/library/ff595592.aspx * - MS Tech-Ed Europe 2010 "UNC310: Microsoft Lync 2010 Technology Explained" * http://ecn.channel9.msdn.com/o9/te/Europe/2010/pptx/unc310.pptx */ #include #include #include #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-core-private.h" #include "sipe-digest.h" #include "sipe-svc.h" #include "sipe-tls.h" #include "sipe-webticket.h" #include "sipe-utils.h" #include "sipe-xml.h" struct webticket_queued_data { sipe_webticket_callback *callback; gpointer callback_data; }; struct webticket_callback_data { gchar *service_uri; const gchar *service_port; gchar *service_auth_uri; gchar *webticket_negotiate_uri; gchar *webticket_fedbearer_uri; gboolean tried_fedbearer; gboolean requires_signing; enum { TOKEN_STATE_NONE = 0, TOKEN_STATE_SERVICE, TOKEN_STATE_FEDERATION, TOKEN_STATE_FED_BEARER, } token_state; struct sipe_tls_random entropy; sipe_webticket_callback *callback; gpointer callback_data; struct sipe_svc_session *session; GSList *queued; }; struct webticket_token { gchar *auth_uri; gchar *token; time_t expires; }; struct sipe_webticket { GHashTable *cache; GHashTable *pending; gchar *webticket_adfs_uri; gchar *adfs_token; time_t adfs_token_expires; gboolean retrieved_realminfo; gboolean shutting_down; }; void sipe_webticket_free(struct sipe_core_private *sipe_private) { struct sipe_webticket *webticket = sipe_private->webticket; if (!webticket) return; /* Web Ticket stack is shutting down: reject all new requests */ webticket->shutting_down = TRUE; g_free(webticket->webticket_adfs_uri); g_free(webticket->adfs_token); if (webticket->pending) g_hash_table_destroy(webticket->pending); if (webticket->cache) g_hash_table_destroy(webticket->cache); g_free(webticket); sipe_private->webticket = NULL; } static void free_token(gpointer data) { struct webticket_token *wt = data; g_free(wt->auth_uri); g_free(wt->token); g_free(wt); } static void sipe_webticket_init(struct sipe_core_private *sipe_private) { struct sipe_webticket *webticket; if (sipe_private->webticket) return; sipe_private->webticket = webticket = g_new0(struct sipe_webticket, 1); webticket->cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_token); webticket->pending = g_hash_table_new(g_str_hash, g_str_equal); } /* takes ownership of "token" */ static void cache_token(struct sipe_core_private *sipe_private, const gchar *service_uri, const gchar *auth_uri, gchar *token, time_t expires) { struct webticket_token *wt = g_new0(struct webticket_token, 1); wt->auth_uri = g_strdup(auth_uri); wt->token = token; wt->expires = expires; g_hash_table_insert(sipe_private->webticket->cache, g_strdup(service_uri), wt); } static const struct webticket_token *cache_hit(struct sipe_core_private *sipe_private, const gchar *service_uri) { const struct webticket_token *wt; /* make sure a cached Web Ticket is still valid for 60 seconds */ wt = g_hash_table_lookup(sipe_private->webticket->cache, service_uri); if (wt && (wt->expires < time(NULL) + 60)) { SIPE_DEBUG_INFO("cache_hit: cached token for URI %s has expired", service_uri); wt = NULL; } return(wt); } /* frees just the main request data, when this is called "queued" is cleared */ static void callback_data_free(struct webticket_callback_data *wcd) { if (wcd) { sipe_tls_free_random(&wcd->entropy); g_free(wcd->webticket_negotiate_uri); g_free(wcd->webticket_fedbearer_uri); g_free(wcd->service_auth_uri); g_free(wcd->service_uri); g_free(wcd); } } static void queue_request(struct webticket_callback_data *wcd, sipe_webticket_callback *callback, gpointer callback_data) { struct webticket_queued_data *wqd = g_new0(struct webticket_queued_data, 1); wqd->callback = callback; wqd->callback_data = callback_data; wcd->queued = g_slist_prepend(wcd->queued, wqd); } static void callback_execute(struct sipe_core_private *sipe_private, struct webticket_callback_data *wcd, const gchar *auth_uri, const gchar *wsse_security, const gchar *failure_msg) { GSList *entry = wcd->queued; /* complete main request */ wcd->callback(sipe_private, wcd->service_uri, auth_uri, wsse_security, failure_msg, wcd->callback_data); /* complete queued requests */ while (entry) { struct webticket_queued_data *wqd = entry->data; SIPE_DEBUG_INFO("callback_execute: completing queue request URI %s (Auth URI %s)", wcd->service_uri, auth_uri); wqd->callback(sipe_private, wcd->service_uri, auth_uri, wsse_security, failure_msg, wqd->callback_data); g_free(wqd); entry = entry->next; } g_slist_free(wcd->queued); /* drop request from pending hash */ g_hash_table_remove(sipe_private->webticket->pending, wcd->service_uri); } static gchar *extract_raw_xml_attribute(const gchar *xml, const gchar *name) { gchar *attr_start = g_strdup_printf("%s=\"", name); gchar *data = NULL; const gchar *start = strstr(xml, attr_start); if (start) { const gchar *value = start + strlen(attr_start); const gchar *end = strchr(value, '"'); if (end) { data = g_strndup(value, end - value); } } g_free(attr_start); return(data); } static gchar *generate_timestamp(const gchar *raw) { gchar *lifetime = sipe_xml_extract_raw(raw, "Lifetime", FALSE); gchar *timestamp = NULL; if (lifetime) timestamp = g_strdup_printf("%s", lifetime); g_free(lifetime); return(timestamp); } static gchar *generate_keydata(const gchar *raw) { return(sipe_xml_extract_raw(raw, "Assertion", TRUE)); } static gchar *generate_expires(const gchar *timestamp) { return(sipe_xml_extract_raw(timestamp, "Expires", FALSE)); } static gchar *generate_fedbearer_wsse(const gchar *raw) { gchar *timestamp = generate_timestamp(raw); gchar *keydata = sipe_xml_extract_raw(raw, "EncryptedData", TRUE); gchar *wsse_security = NULL; if (timestamp && keydata) { SIPE_DEBUG_INFO_NOFORMAT("generate_fedbearer_wsse: found timestamp & keydata"); wsse_security = g_strconcat(timestamp, keydata, NULL); } g_free(keydata); g_free(timestamp); return(wsse_security); } static void generate_federation_wsse(struct sipe_webticket *webticket, const gchar *raw) { gchar *timestamp = generate_timestamp(raw); gchar *keydata = generate_keydata(raw); /* clear old ADFS token */ g_free(webticket->adfs_token); webticket->adfs_token = NULL; if (timestamp && keydata) { gchar *expires_string = generate_expires(timestamp); if (expires_string) { SIPE_DEBUG_INFO("generate_federation_wsse: found timestamp & keydata, expires %s", expires_string); /* cache ADFS token */ webticket->adfs_token = g_strconcat(timestamp, keydata, NULL); webticket->adfs_token_expires = sipe_utils_str_to_time(expires_string); g_free(expires_string); } } g_free(keydata); g_free(timestamp); } static gchar *generate_sha1_proof_wsse(const gchar *raw, struct sipe_tls_random *entropy, time_t *expires) { gchar *timestamp = generate_timestamp(raw); gchar *keydata = generate_keydata(raw); gchar *wsse_security = NULL; if (timestamp && keydata) { gchar *expires_string = generate_expires(timestamp); if (entropy) { gchar *assertionID = extract_raw_xml_attribute(keydata, "AssertionID"); /* * WS-Trust 1.3 * * http://docs.oasis-open.org/ws-sx/ws-trust/200512/CK/PSHA1: * * "The key is computed using P_SHA1() from the TLS sepcification to generate * a bit stream using entropy from both sides. The exact form is: * * key = P_SHA1(Entropy_REQ, Entropy_RES)" */ gchar *entropy_res_base64 = sipe_xml_extract_raw(raw, "BinarySecret", FALSE); gsize entropy_res_length; guchar *entropy_response = g_base64_decode(entropy_res_base64, &entropy_res_length); guchar *key = sipe_tls_p_sha1(entropy->buffer, entropy->length, entropy_response, entropy_res_length, entropy->length); g_free(entropy_response); g_free(entropy_res_base64); SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata"); if (assertionID && key) { /* same as SIPE_DIGEST_HMAC_SHA1_LENGTH */ guchar digest[SIPE_DIGEST_SHA1_LENGTH]; gchar *base64; gchar *signed_info; gchar *canon; SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found assertionID and successfully computed the key"); /* Digest over reference element (#timestamp -> wsu:Timestamp) */ sipe_digest_sha1((guchar *) timestamp, strlen(timestamp), digest); base64 = g_base64_encode(digest, SIPE_DIGEST_SHA1_LENGTH); /* XML-Sig: SignedInfo for reference element */ signed_info = g_strdup_printf("" "" "" "" "" "" "" "" "%s" "" "", base64); g_free(base64); /* XML-Sig: SignedInfo in canonical form */ canon = sipe_xml_exc_c14n(signed_info); g_free(signed_info); if (canon) { gchar *signature; /* calculate signature */ sipe_digest_hmac_sha1(key, entropy->length, (guchar *)canon, strlen(canon), digest); base64 = g_base64_encode(digest, SIPE_DIGEST_HMAC_SHA1_LENGTH); /* XML-Sig: Signature from SignedInfo + Key */ signature = g_strdup_printf("" " %s" " %s" " " " " " %s" " " " " "", canon, base64, assertionID); g_free(base64); g_free(canon); wsse_security = g_strconcat(timestamp, keydata, signature, NULL); g_free(signature); } } g_free(key); g_free(assertionID); } else { /* token doesn't require signature */ SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata, no signing required"); wsse_security = g_strconcat(timestamp, keydata, NULL); } *expires = 0; if (expires_string) { *expires = sipe_utils_str_to_time(expires_string); g_free(expires_string); } } g_free(keydata); g_free(timestamp); return(wsse_security); } static gboolean federated_authentication(struct sipe_core_private *sipe_private, struct webticket_callback_data *wcd); static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private, struct webticket_callback_data *wcd); static void webticket_token(struct sipe_core_private *sipe_private, const gchar *uri, const gchar *raw, sipe_xml *soap_body, gpointer callback_data) { struct webticket_callback_data *wcd = callback_data; gboolean failed = TRUE; if (soap_body) { switch (wcd->token_state) { case TOKEN_STATE_NONE: SIPE_DEBUG_INFO_NOFORMAT("webticket_token: ILLEGAL STATE - should not happen..."); break; case TOKEN_STATE_SERVICE: { /* WebTicket for Web Service */ time_t expires; gchar *wsse_security = generate_sha1_proof_wsse(raw, wcd->requires_signing ? &wcd->entropy : NULL, &expires); if (wsse_security) { /* cache takes ownership of wsse_security */ cache_token(sipe_private, wcd->service_uri, wcd->service_auth_uri, wsse_security, expires); callback_execute(sipe_private, wcd, wcd->service_auth_uri, wsse_security, NULL); failed = FALSE; } break; } case TOKEN_STATE_FEDERATION: /* WebTicket from ADFS for federated authentication */ generate_federation_wsse(sipe_private->webticket, raw); if (sipe_private->webticket->adfs_token) { SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from ADFS %s", uri); if (federated_authentication(sipe_private, wcd)) { /* callback data passed down the line */ wcd = NULL; } } break; case TOKEN_STATE_FED_BEARER: { /* WebTicket for federated authentication */ gchar *wsse_security = generate_fedbearer_wsse(raw); if (wsse_security) { SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from service %s", uri); if (sipe_svc_webticket(sipe_private, wcd->session, wcd->webticket_fedbearer_uri, wsse_security, wcd->service_auth_uri, &wcd->entropy, webticket_token, wcd)) { wcd->token_state = TOKEN_STATE_SERVICE; /* callback data passed down the line */ wcd = NULL; } g_free(wsse_security); } break; } /* end of: switch (wcd->token_state) { */ } } else if (uri) { /* Retry with federated authentication? */ if (wcd->webticket_fedbearer_uri) { /* Authentication against ADFS failed? */ if (wcd->token_state == TOKEN_STATE_FEDERATION) { struct sipe_webticket *webticket = sipe_private->webticket; SIPE_LOG_WARNING_NOFORMAT("webticket_token: ADFS authentication failed - assuming Multi-Factor Authentication (MFA)"); /* forget ADFS URI */ g_free(webticket->webticket_adfs_uri); webticket->webticket_adfs_uri = NULL; } if (!wcd->tried_fedbearer) { SIPE_DEBUG_INFO("webticket_token: anonymous authentication to service %s failed, retrying with federated authentication", uri); if (initiate_fedbearer(sipe_private, wcd)) { /* callback data passed down the line */ wcd = NULL; } } } } if (wcd) { if (failed) { gchar *failure_msg = NULL; if (soap_body) { failure_msg = sipe_xml_data(sipe_xml_child(soap_body, "Body/Fault/Detail/error/internalerror/text")); /* XML data can end in */ g_strstrip(failure_msg); } callback_execute(sipe_private, wcd, uri, NULL, failure_msg); g_free(failure_msg); } callback_data_free(wcd); } } static gboolean federated_authentication(struct sipe_core_private *sipe_private, struct webticket_callback_data *wcd) { gboolean success; if ((success = sipe_svc_webticket_lmc_federated(sipe_private, wcd->session, sipe_private->webticket->adfs_token, wcd->webticket_fedbearer_uri, webticket_token, wcd))) wcd->token_state = TOKEN_STATE_FED_BEARER; /* If TRUE then callback data has been passed down the line */ return(success); } static gboolean fedbearer_authentication(struct sipe_core_private *sipe_private, struct webticket_callback_data *wcd) { struct sipe_webticket *webticket = sipe_private->webticket; gboolean success; /* make sure a cached ADFS token is still valid for 60 seconds */ if (webticket->adfs_token && (webticket->adfs_token_expires >= time(NULL) + 60)) { SIPE_DEBUG_INFO_NOFORMAT("fedbearer_authentication: reusing cached ADFS token"); success = federated_authentication(sipe_private, wcd); } else if (webticket->webticket_adfs_uri) { if ((success = sipe_svc_webticket_adfs(sipe_private, wcd->session, webticket->webticket_adfs_uri, webticket_token, wcd))) wcd->token_state = TOKEN_STATE_FEDERATION; } else { if ((success = sipe_svc_webticket_lmc(sipe_private, wcd->session, wcd->webticket_fedbearer_uri, webticket_token, wcd))) wcd->token_state = TOKEN_STATE_FED_BEARER; } /* If TRUE then callback data has been passed down the line */ return(success); } static void realminfo(struct sipe_core_private *sipe_private, const gchar *uri, SIPE_UNUSED_PARAMETER const gchar *raw, sipe_xml *realminfo, gpointer callback_data) { struct sipe_webticket *webticket = sipe_private->webticket; struct webticket_callback_data *wcd = callback_data; /* Only try retrieving of RealmInfo once */ webticket->retrieved_realminfo = TRUE; /* * We must specifically check for abort, because * realminfo == NULL is a valid response */ if (uri) { if (realminfo) { /* detect ADFS setup. See also: * * http://en.wikipedia.org/wiki/Active_Directory_Federation_Services * * NOTE: this is based on observed behaviour. * It is unkown if this is documented somewhere... */ SIPE_DEBUG_INFO("realminfo: data for user %s retrieved successfully", sipe_private->username); webticket->webticket_adfs_uri = sipe_xml_data(sipe_xml_child(realminfo, "STSAuthURL")); } if (webticket->webticket_adfs_uri) { SIPE_LOG_INFO_NOFORMAT("realminfo: ADFS setup detected"); SIPE_DEBUG_INFO("realminfo: ADFS URI: %s", webticket->webticket_adfs_uri); } else SIPE_DEBUG_INFO_NOFORMAT("realminfo: no RealmInfo found or no ADFS setup detected - try direct login"); if (fedbearer_authentication(sipe_private, wcd)) { /* callback data passed down the line */ wcd = NULL; } } if (wcd) { callback_execute(sipe_private, wcd, uri, NULL, NULL); callback_data_free(wcd); } } static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private, struct webticket_callback_data *wcd) { gboolean success; if (sipe_private->webticket->retrieved_realminfo) { /* skip retrieval and go to authentication */ wcd->tried_fedbearer = TRUE; success = fedbearer_authentication(sipe_private, wcd); } else { success = sipe_svc_realminfo(sipe_private, wcd->session, realminfo, wcd); } return(success); } static void webticket_metadata(struct sipe_core_private *sipe_private, const gchar *uri, SIPE_UNUSED_PARAMETER const gchar *raw, sipe_xml *metadata, gpointer callback_data) { struct webticket_callback_data *wcd = callback_data; if (metadata) { const sipe_xml *node; SIPE_DEBUG_INFO("webticket_metadata: metadata for service %s retrieved successfully", uri); /* Authentication ports accepted by WebTicket Service */ for (node = sipe_xml_child(metadata, "service/port"); node; node = sipe_xml_twin(node)) { const gchar *auth_uri = sipe_xml_attribute(sipe_xml_child(node, "address"), "location"); if (auth_uri) { if (sipe_strcase_equal(sipe_xml_attribute(node, "name"), "WebTicketServiceWinNegotiate")) { SIPE_DEBUG_INFO("webticket_metadata: WebTicket Windows Negotiate Auth URI %s", auth_uri); g_free(wcd->webticket_negotiate_uri); wcd->webticket_negotiate_uri = g_strdup(auth_uri); } else if (sipe_strcase_equal(sipe_xml_attribute(node, "name"), "WsFedBearer")) { SIPE_DEBUG_INFO("webticket_metadata: WebTicket FedBearer Auth URI %s", auth_uri); g_free(wcd->webticket_fedbearer_uri); wcd->webticket_fedbearer_uri = g_strdup(auth_uri); } } } if (wcd->webticket_negotiate_uri || wcd->webticket_fedbearer_uri) { gboolean success; /* Entropy: 256 random bits */ if (!wcd->entropy.buffer) sipe_tls_fill_random(&wcd->entropy, 256); if (wcd->webticket_negotiate_uri) { /* Try Negotiate authentication first */ success = sipe_svc_webticket(sipe_private, wcd->session, wcd->webticket_negotiate_uri, NULL, wcd->service_auth_uri, &wcd->entropy, webticket_token, wcd); wcd->token_state = TOKEN_STATE_SERVICE; } else { success = initiate_fedbearer(sipe_private, wcd); } if (success) { /* callback data passed down the line */ wcd = NULL; } } } if (wcd) { callback_execute(sipe_private, wcd, uri, NULL, NULL); callback_data_free(wcd); } } static void service_metadata(struct sipe_core_private *sipe_private, const gchar *uri, SIPE_UNUSED_PARAMETER const gchar *raw, sipe_xml *metadata, gpointer callback_data) { struct webticket_callback_data *wcd = callback_data; if (metadata) { const sipe_xml *node; gchar *policy = g_strdup_printf("%s_policy", wcd->service_port); gchar *ticket_uri = NULL; SIPE_DEBUG_INFO("service_metadata: metadata for service %s retrieved successfully", uri); /* WebTicket policies accepted by Web Service */ for (node = sipe_xml_child(metadata, "Policy"); node; node = sipe_xml_twin(node)) { if (sipe_strcase_equal(sipe_xml_attribute(node, "Id"), policy)) { SIPE_DEBUG_INFO_NOFORMAT("service_metadata: WebTicket policy found"); ticket_uri = sipe_xml_data(sipe_xml_child(node, "ExactlyOne/All/EndorsingSupportingTokens/Policy/IssuedToken/Issuer/Address")); if (ticket_uri) { /* this token type requires signing */ wcd->requires_signing = TRUE; } else { /* try alternative token type */ ticket_uri = sipe_xml_data(sipe_xml_child(node, "ExactlyOne/All/SignedSupportingTokens/Policy/IssuedToken/Issuer/Address")); } if (ticket_uri) { SIPE_DEBUG_INFO("service_metadata: WebTicket URI %s", ticket_uri); } break; } } g_free(policy); if (ticket_uri) { /* Authentication ports accepted by Web Service */ for (node = sipe_xml_child(metadata, "service/port"); node; node = sipe_xml_twin(node)) { if (sipe_strcase_equal(sipe_xml_attribute(node, "name"), wcd->service_port)) { const gchar *auth_uri; SIPE_DEBUG_INFO_NOFORMAT("service_metadata: authentication port found"); auth_uri = sipe_xml_attribute(sipe_xml_child(node, "address"), "location"); if (auth_uri) { SIPE_DEBUG_INFO("service_metadata: Auth URI %s", auth_uri); if (sipe_svc_metadata(sipe_private, wcd->session, ticket_uri, webticket_metadata, wcd)) { /* Remember for later */ wcd->service_auth_uri = g_strdup(auth_uri); /* callback data passed down the line */ wcd = NULL; } } break; } } g_free(ticket_uri); } } if (wcd) { callback_execute(sipe_private, wcd, uri, NULL, NULL); callback_data_free(wcd); } } static gboolean webticket_request(struct sipe_core_private *sipe_private, struct sipe_svc_session *session, const gchar *base_uri, const gchar *auth_uri, const gchar *port_name, sipe_webticket_callback *callback, gpointer callback_data) { struct sipe_webticket *webticket; gboolean ret = FALSE; sipe_webticket_init(sipe_private); webticket = sipe_private->webticket; if (webticket->shutting_down) { SIPE_DEBUG_ERROR("webticket_request: new Web Ticket request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n" "Base URI: %s\n" "Port Name: %s\n", base_uri, port_name); } else { const struct webticket_token *wt = cache_hit(sipe_private, base_uri); /* cache hit for this URI? */ if (wt) { SIPE_DEBUG_INFO("webticket_request: using cached token for URI %s (Auth URI %s)", base_uri, wt->auth_uri); callback(sipe_private, base_uri, wt->auth_uri, wt->token, NULL, callback_data); ret = TRUE; } else { GHashTable *pending = webticket->pending; struct webticket_callback_data *wcd = g_hash_table_lookup(pending, base_uri); /* is there already a pending request for this URI? */ if (wcd) { SIPE_DEBUG_INFO("webticket_request: pending request found for URI %s - queueing", base_uri); queue_request(wcd, callback, callback_data); ret = TRUE; } else { wcd = g_new0(struct webticket_callback_data, 1); ret = sipe_svc_metadata(sipe_private, session, base_uri, port_name ? service_metadata : webticket_metadata, wcd); if (ret) { wcd->service_uri = g_strdup(base_uri); wcd->service_port = port_name; wcd->service_auth_uri = g_strdup(auth_uri); wcd->callback = callback; wcd->callback_data = callback_data; wcd->session = session; wcd->token_state = TOKEN_STATE_NONE; g_hash_table_insert(pending, wcd->service_uri, /* borrowed */ wcd); /* borrowed */ } else { g_free(wcd); } } } } return(ret); } gboolean sipe_webticket_request_with_port(struct sipe_core_private *sipe_private, struct sipe_svc_session *session, const gchar *base_uri, const gchar *port_name, sipe_webticket_callback *callback, gpointer callback_data) { return(webticket_request(sipe_private, session, base_uri, NULL, /* Auth URI is determined via port_name */ port_name, callback, callback_data)); } gboolean sipe_webticket_request_with_auth(struct sipe_core_private *sipe_private, struct sipe_svc_session *session, const gchar *base_uri, const gchar *auth_uri, sipe_webticket_callback *callback, gpointer callback_data) { return(webticket_request(sipe_private, session, base_uri, auth_uri, NULL, callback, callback_data)); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-webticket.h ================================================ /** * @file sipe-webticket.h * * pidgin-sipe * * Copyright (C) 2011-2016 SIPE Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Interface dependencies: * * */ /* Forward declarations */ struct sipe_core_private; struct sipe_svc_session; /** * Web Ticket callback * * @param sipe_private SIPE core private data * @param base_uri Web Service base URI * @param auth_uri Web Service auth. URI (@c NULL when request aborted) * @param wsse_security Web Ticket XML fragment (@c NULL when request failed) * @param failure_msg Web Ticket error message (may be @c NULL) * @param callback_data callback data */ typedef void (sipe_webticket_callback)(struct sipe_core_private *sipe_private, const gchar *base_uri, const gchar *auth_uri, const gchar *wsse_security, const gchar *failure_msg, gpointer callback_data); /** * Request a Web Ticket for Web Service URI with port name * * NOTE: the callback can be called immediately if the Web Ticket is cached. * The callback data must therefore be properly initialized already. * * @param sipe_private SIPE core private data * @param base_uri Web Service base URI * @param port_name Web Service authentication port name * @param callback callback function * @param callback_data callback data * @return @c TRUE if web ticket fetch was triggered */ gboolean sipe_webticket_request_with_port(struct sipe_core_private *sipe_private, struct sipe_svc_session *session, const gchar *base_uri, const gchar *port_name, sipe_webticket_callback *callback, gpointer callback_data); /** * Request a Web Ticket for Web Service URI with Authentication URI * * NOTE: the callback can be called immediately if the Web Ticket is cached. * The callback data must therefore be properly initialized already. * * @param sipe_private SIPE core private data * @param base_uri URI * @param auth_uri Authentication URI * @param callback callback function * @param callback_data callback data * @return @c TRUE if web ticket fetch was triggered */ gboolean sipe_webticket_request_with_auth(struct sipe_core_private *sipe_private, struct sipe_svc_session *session, const gchar *base_uri, const gchar *auth_uri, sipe_webticket_callback *callback, gpointer callback_data); /** * Free webticket data * * @param sipe_private SIPE core private data */ void sipe_webticket_free(struct sipe_core_private *sipe_private); ================================================ FILE: src/core/sipe-win32dep.c ================================================ /** * @file sipe-win32dep.c * * pidgin-sipe * * Copyright (C) 2010 pier11 (fix for REG_EXPAND_SZ) * Copyright (C) 2002-2003, Herman Bloggs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file is an attempt to fix purple's wpurple_read_reg_string() * which doesn't read REG_EXPAND_SZ types from registry, only REG_SZ. * The code is identical (v2.6.6) apart from one line. * * The nead is desperate to read Lotus Notes' notes.ini file location * stored in REG_EXPAND_SZ type. */ #include "glib.h" #include "sipe-win32dep.h" #include "sipe-backend.h" static HKEY _reg_open_key(HKEY rootkey, const char *subkey, REGSAM access) { HKEY reg_key = NULL; LONG rv; if(G_WIN32_HAVE_WIDECHAR_API()) { wchar_t *wc_subkey = g_utf8_to_utf16(subkey, -1, NULL, NULL, NULL); rv = RegOpenKeyExW(rootkey, wc_subkey, 0, access, ®_key); g_free(wc_subkey); } else { char *cp_subkey = g_locale_from_utf8(subkey, -1, NULL, NULL, NULL); rv = RegOpenKeyExA(rootkey, cp_subkey, 0, access, ®_key); g_free(cp_subkey); } if (rv != ERROR_SUCCESS) { char *errmsg = g_win32_error_message(rv); SIPE_DEBUG_ERROR("Could not open reg key '%s' subkey '%s'.\nMessage: (%ld) %s\n", ((rootkey == HKEY_LOCAL_MACHINE) ? "HKLM" : (rootkey == HKEY_CURRENT_USER) ? "HKCU" : (rootkey == HKEY_CLASSES_ROOT) ? "HKCR" : "???"), subkey, rv, errmsg); g_free(errmsg); } return reg_key; } static gboolean _reg_read(HKEY reg_key, const char *valname, LPDWORD type, LPBYTE data, LPDWORD data_len) { LONG rv; if(G_WIN32_HAVE_WIDECHAR_API()) { wchar_t *wc_valname = NULL; if (valname) wc_valname = g_utf8_to_utf16(valname, -1, NULL, NULL, NULL); rv = RegQueryValueExW(reg_key, wc_valname, 0, type, data, data_len); g_free(wc_valname); } else { char *cp_valname = NULL; if(valname) cp_valname = g_locale_from_utf8(valname, -1, NULL, NULL, NULL); rv = RegQueryValueExA(reg_key, cp_valname, 0, type, data, data_len); g_free(cp_valname); } if (rv != ERROR_SUCCESS) { char *errmsg = g_win32_error_message(rv); SIPE_DEBUG_ERROR("Could not read from reg key value '%s'.\nMessage: (%ld) %s\n", valname, rv, errmsg); g_free(errmsg); } return (rv == ERROR_SUCCESS); } char *wpurple_read_reg_expand_string(HKEY rootkey, const char *subkey, const char *valname) { DWORD type; DWORD nbytes; HKEY reg_key = _reg_open_key(rootkey, subkey, KEY_QUERY_VALUE); char *result = NULL; if(reg_key) { if(_reg_read(reg_key, valname, &type, NULL, &nbytes) && (type == REG_SZ || type == REG_EXPAND_SZ)) { LPBYTE data; if(G_WIN32_HAVE_WIDECHAR_API()) data = (LPBYTE) g_new(wchar_t, ((nbytes + 1) / sizeof(wchar_t)) + 1); else data = (LPBYTE) g_malloc(nbytes + 1); if(_reg_read(reg_key, valname, &type, data, &nbytes)) { if(G_WIN32_HAVE_WIDECHAR_API()) { wchar_t *wc_temp = (wchar_t*) data; wc_temp[nbytes / sizeof(wchar_t)] = '\0'; result = g_utf16_to_utf8(wc_temp, -1, NULL, NULL, NULL); } else { char *cp_temp = (char*) data; cp_temp[nbytes] = '\0'; result = g_locale_to_utf8(cp_temp, -1, NULL, NULL, NULL); } } g_free(data); } RegCloseKey(reg_key); } return result; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-win32dep.h ================================================ /** * @file sipe-win32dep.h * * pidgin-sipe * * Copyright (C) 2010 pier11 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include char *wpurple_read_reg_expand_string(HKEY rootkey, const char *subkey, const char *valname); ================================================ FILE: src/core/sipe-xml-tests.c ================================================ /** * @file sipe-xml-tests.c * * pidgin-sipe * * Copyright (C) 2010-2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Tests for sipe-xml.c */ #include #include #include #include #include #include #include "sip-transport.h" #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-digest.h" #include "sipe-xml.h" #include "sipe-utils.h" #include "uuid.h" /* stub functions for backend API */ void sipe_backend_debug_literal(sipe_debug_level level, const gchar *msg) { printf("DEBUG %d: %s", level, msg); } void sipe_backend_debug(sipe_debug_level level, const gchar *format, ...) { va_list args; gchar *msg; va_start(args, format); msg = g_strdup_vprintf(format, args); va_end(args); sipe_backend_debug_literal(level, msg); g_free(msg); } gboolean sipe_backend_debug_enabled(void) { return TRUE; } void sipe_digest_sha1(SIPE_UNUSED_PARAMETER const guchar *data, SIPE_UNUSED_PARAMETER gsize length, SIPE_UNUSED_PARAMETER guchar *digest) {} const gchar *sip_transport_epid(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private) { return(NULL); } const gchar *sip_transport_ip_address(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private) { return(NULL); } char *generateUUIDfromEPID(SIPE_UNUSED_PARAMETER const gchar *epid) { return(NULL); } char *sipe_get_epid(SIPE_UNUSED_PARAMETER const char *self_sip_uri, SIPE_UNUSED_PARAMETER const char *hostname, SIPE_UNUSED_PARAMETER const char *ip_address) { return(NULL); } /* test helpers */ static guint succeeded = 0; static guint failed = 0; static const gchar *teststring; static sipe_xml *assert_parse(const gchar *s, gboolean ok) { sipe_xml *xml = sipe_xml_parse(s, s ? strlen(s) : 0); teststring = s ? s : "(nil)"; if ((ok && xml) || (!ok && !xml)) { succeeded++; } else { printf("[%s]\nXML parse FAILED: %p\n", teststring, xml); failed++; } return(xml); } static void assert_name(const sipe_xml *xml, const gchar *s) { const gchar *name = sipe_xml_name(xml); if (sipe_strequal(name, s)) { succeeded++; } else { printf("[%s]\nXML name FAILED: '%s' expected: '%s'\n", teststring, name ? name : "(nil)", s ? s : "(nil)"); failed++; } } static const sipe_xml *assert_child(const sipe_xml *xml, const gchar *s, gboolean ok) { const sipe_xml *child = sipe_xml_child(xml, s); if ((ok && child) || (!ok && !child)) { succeeded++; } else { printf("[%s]\nXML child FAILED: %p '%s'\n", teststring, xml, s ? s : "(nil)"); failed++; } return(child); } static void assert_data(const sipe_xml *xml, const gchar *s) { gchar *data = sipe_xml_data(xml); if (sipe_strequal(s, data)) { succeeded++; } else { printf("[%s]\nXML data FAILED: '%s' expected: '%s'\n", teststring, data ? data : "(nil)", s ? s : "(nil)"); failed++; } g_free(data); } static void assert_attribute(const sipe_xml *xml, const gchar *key, const gchar *value) { const gchar *attr = sipe_xml_attribute(xml, key); if (sipe_strequal(value, attr)) { succeeded++; } else { printf("[%s]\nXML attr FAILED: '%s': '%s' expected: '%s'\n", teststring, key ? key : "(nil)", attr ? attr : "(nil)", value ? value : "(nil)"); failed++; } } static void assert_int_attribute(const sipe_xml *xml, const gchar *key, gint value, gint fallback) { gint attr = sipe_xml_int_attribute(xml, key, fallback); if ((attr == value) || (attr == fallback)) { succeeded++; } else { printf("[%s]\nXML int attr FAILED: '%s': %d expected: %d/%d\n", teststring, key ? key : "(nil)", attr, value, fallback); failed++; } } static void assert_stringify(const sipe_xml *xml, int expected, ...) { va_list args; gchar *string = sipe_xml_stringify(xml); va_start(args, expected); while (expected-- > 0) { const gchar *alternative = va_arg(args, const gchar *); if (sipe_strequal(string, alternative)) { succeeded++; break; } else { printf("XML stringify alternative FAILED: '%s' (trying next...)\n", alternative ? alternative : "(nil)"); } } va_end(args); if (expected < 0) { printf("[%s]\nXML stringify all alternatives FAILED: '%s'\n", teststring, string ? string : "(nil)"); failed++; } g_free(string); } static void assert_raw(const gchar *raw, const gchar *tag, gboolean include_tag, const char *expected) { gchar *string = sipe_xml_extract_raw(raw, tag, include_tag); if (expected) { if (string) { if (sipe_strequal(string, expected)) { succeeded++; } else { printf("[%s]\nXML raw extract FAILED: '%s' expected: '%s'\n", raw, string, expected); failed++; } } else { printf("[%s]\nXML raw extract not found FAILED: '%s' expected: '%s'\n", raw, tag, expected); failed++; } } else { if (string) { printf("[%s]\nXML raw extract no match FAILED: '%s' matched: '%s'\n", raw, tag, string); failed++; } else { succeeded++; } } g_free(string); } /* memory leak check */ static gsize allocated = 0; static gpointer test_malloc(gsize n_bytes) { gsize *m = malloc(sizeof(gsize) + n_bytes); if (!m) return(NULL); allocated += n_bytes; m[0] = n_bytes; return(m + 1); } static void test_free(gpointer mem) { gsize *m = mem; if (!m) return; m--; allocated -= m[0]; free(m); } static gpointer test_realloc(gpointer mem, gsize n_bytes) { guint8 *n = NULL; if (n_bytes) { n = test_malloc(n_bytes); if (mem && n) { memcpy(n, mem, n_bytes); } } test_free(mem); return(n); } static GMemVTable memory_leak_check = { &test_malloc, &test_realloc, &test_free, NULL, NULL, NULL, }; int main(SIPE_UNUSED_PARAMETER int argc, SIPE_UNUSED_PARAMETER char **argv) { sipe_xml *xml; const sipe_xml *child1, *child2; #if 0 /* * No idea why the memory leak checks work on some platforms * but fail on others :-( Disable for now... */ g_mem_set_vtable(&memory_leak_check); #else (void) memory_leak_check; #endif /* empty XML */ xml = assert_parse(NULL, FALSE); assert_stringify(xml, 1, NULL); sipe_xml_free(xml); xml = assert_parse("", FALSE); sipe_xml_free(xml); xml = assert_parse("", FALSE); sipe_xml_free(xml); /* one node */ xml = assert_parse("", TRUE); assert_name(xml, "test"); assert_data(xml, NULL); assert_stringify(xml, 1, ""); sipe_xml_free(xml); xml = assert_parse("", TRUE); assert_name(xml, "test"); assert_data(xml, NULL); assert_stringify(xml, 1, teststring); sipe_xml_free(xml); xml = assert_parse("a", TRUE); assert_name(xml, "test"); assert_data(xml, "a"); assert_stringify(xml, 1, teststring); sipe_xml_free(xml); xml = assert_parse("a\nb", TRUE); assert_name(xml, "test"); assert_data(xml, "a\nb"); assert_stringify(xml, 1, teststring); sipe_xml_free(xml); /* child node */ xml = assert_parse("ab", TRUE); assert_name(xml, "test"); assert_data(xml, "a"); child1 = assert_child(xml, NULL, FALSE); child1 = assert_child(xml, "child", TRUE); assert_name(child1, "child"); assert_data(child1, "b"); child1 = assert_child(xml, "shouldnotmatch", FALSE); assert_data(child1, NULL); assert_stringify(xml, 1, teststring); sipe_xml_free(xml); xml = assert_parse("a", TRUE); assert_name(xml, "test"); assert_data(xml, "a"); child1 = assert_child(xml, "child", TRUE); assert_name(child1, "child"); assert_data(child1, NULL); child1 = assert_child(xml, "shouldnotmatch", FALSE); assert_data(child1, NULL); assert_stringify(xml, 1, teststring); sipe_xml_free(xml); xml = assert_parse("abc", TRUE); assert_name(xml, "test"); assert_data(xml, "a"); child1 = assert_child(xml, "child", TRUE); assert_name(child1, "child"); assert_data(child1, "b"); child1 = assert_child(child1, "inner", TRUE); assert_name(child1, "inner"); assert_data(child1, "c"); child1 = assert_child(xml, "child/inner", TRUE); assert_name(child1, "inner"); assert_data(child1, "c"); assert_stringify(xml, 1, teststring); sipe_xml_free(xml); xml = assert_parse("abcd", TRUE); assert_name(xml, "test"); assert_data(xml, "a"); child1 = assert_child(xml, "child", TRUE); assert_name(child1, "child"); assert_data(child1, "b"); child2 = assert_child(child1, "inner/innerinner", TRUE); assert_name(child2, "innerinner"); assert_data(child2, "d"); child1 = assert_child(child1, "inner", TRUE); assert_name(child1, "inner"); assert_data(child1, "c"); child1 = assert_child(child1, "innerinner", TRUE); assert_name(child1, "innerinner"); assert_data(child1, "d"); child1 = assert_child(xml, "child/inner", TRUE); assert_name(child1, "inner"); assert_data(child1, "c"); child1 = assert_child(xml, "child/inner/innerinner", TRUE); assert_name(child1, "innerinner"); assert_data(child1, "d"); assert_stringify(xml, 1, teststring); sipe_xml_free(xml); /* attributes */ xml = assert_parse("a", TRUE); assert_name(xml, "test"); assert_data(xml, "a"); assert_attribute(xml, NULL, NULL); assert_attribute(xml, "a", ""); assert_attribute(xml, "b", NULL); assert_stringify(xml, 1, teststring); sipe_xml_free(xml); xml = assert_parse("a", TRUE); assert_name(xml, "test"); assert_data(xml, "a"); assert_attribute(xml, "a", "1"); assert_int_attribute(xml, "a", 1, 0); assert_attribute(xml, "b", "abc"); assert_attribute(xml, "c", NULL); assert_int_attribute(xml, "d", 100, 200); /* the attribute order depends on glib hashing :-( */ assert_stringify(xml, 2, teststring, "a"); sipe_xml_free(xml); /* attributes with namespace */ xml = assert_parse("", TRUE); assert_name(xml, "row"); assert_data(xml, NULL); assert_attribute(xml, "uri", "sip:"); assert_attribute(xml, "displayName", "X"); assert_attribute(xml, "title", "Y"); assert_attribute(xml, "office", "Z"); assert_attribute(xml, "phone", "0"); assert_attribute(xml, "company", "A"); assert_attribute(xml, "city", "B"); assert_attribute(xml, "state", "C"); assert_attribute(xml, "country", "D"); assert_attribute(xml, "email", "E"); sipe_xml_free(xml); xml = assert_parse("15500", TRUE); assert_name(xml, "state"); assert_data(xml, NULL); assert_attribute(xml, "type", "aggregateState"); assert_attribute(xml, "lastActive", "date"); assert_attribute(xml, "xsi", "http://one"); assert_attribute(xml, "xmlns", "http://two"); child1 = assert_child(xml, "availability", TRUE); assert_name(child1, "availability"); assert_data(child1, "15500"); sipe_xml_free(xml); /* broken XML */ xml = assert_parse("t", FALSE); sipe_xml_free(xml); xml = assert_parse("<>", FALSE); sipe_xml_free(xml); xml = assert_parse("<>", FALSE); sipe_xml_free(xml); xml = assert_parse("", FALSE); sipe_xml_free(xml); xml = assert_parse("", FALSE); sipe_xml_free(xml); /* XML raw extract */ assert_raw("data", "tag", FALSE, "data"); assert_raw("data", "tag", TRUE, "data"); assert_raw("data", "tag1", FALSE, NULL); assert_raw("data", "tag1", TRUE, NULL); assert_raw("data", "tag", FALSE, "data"); assert_raw("data", "tag", TRUE, "data"); assert_raw("data", "tag1", FALSE, NULL); assert_raw("data", "tag1", TRUE, NULL); assert_raw("data", "ns:tag", FALSE, "data"); assert_raw("data", "ns:tag", TRUE, "data"); assert_raw("data", "ns:tag1", FALSE, NULL); assert_raw("data", "ns:tag1", TRUE, NULL); assert_raw("datadata", "tag", FALSE, "data"); assert_raw("datadata", "tag", TRUE, "data"); assert_raw("datadatadata", "tag", FALSE, "data"); assert_raw("datadatadata", "tag", TRUE, "data"); assert_raw("datadata", "tag1", FALSE, NULL); assert_raw("datadata", "tag1", TRUE, NULL); assert_raw("datadatadata", "tag1", FALSE, NULL); assert_raw("datadatadata", "tag1", TRUE, NULL); assert_raw("datadata", "tag", FALSE, "data"); assert_raw("datadata", "tag", TRUE, "data"); assert_raw("datadatadata", "tag", FALSE, "data"); assert_raw("datadatadata", "tag", TRUE, "data"); assert_raw("datadata", "tag1", FALSE, NULL); assert_raw("datadata", "tag1", TRUE, NULL); assert_raw("datadatadata", "tag1", FALSE, NULL); assert_raw("datadatadata", "tag1", TRUE, NULL); /* broken XML raw extract */ assert_raw("tag>data", "tag", FALSE, NULL); assert_raw(":tag>data", "tag", FALSE, NULL); assert_raw("datadata", "tag", FALSE, NULL); assert_raw("data", "tag", FALSE, NULL); assert_raw("data", "tag", FALSE, NULL); if (allocated) { printf("MEMORY LEAK: %" G_GSIZE_FORMAT " still allocated\n", allocated); failed++; } else { printf("MEMORY LEAK CHECK OK\n"); succeeded++; } printf("Result: %d PASSED %d FAILED\n", succeeded, failed); return(failed); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-xml.c ================================================ /** * @file sipe-xml.c * * pidgin-sipe * * Copyright (C) 2010-2015 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This code is loosely based on libpurple xmlnode.c */ #include #include #include #include "libxml/parser.h" #include "libxml/c14n.h" #include "libxml/xmlversion.h" #include "glib.h" #include "sipe-backend.h" #include "sipe-utils.h" #include "sipe-xml.h" struct _sipe_xml { gchar *name; sipe_xml *parent; sipe_xml *sibling; sipe_xml *first; sipe_xml *last; GString *data; GHashTable *attributes; }; struct _parser_data { sipe_xml *root; sipe_xml *current; gboolean error; }; /* our string equal function is case insensitive -> hash must be too! */ static guint sipe_ascii_strdown_hash(gconstpointer key) { gchar *lc = g_ascii_strdown((const gchar *) key, -1); guint bucket = g_str_hash(lc); g_free(lc); return(bucket); } static void callback_start_element(void *user_data, const xmlChar *name, const xmlChar **attrs) { struct _parser_data *pd = user_data; const char *tmp; sipe_xml *node; if (!name || pd->error) return; node = g_new0(sipe_xml, 1); if ((tmp = strchr((char *)name, ':')) != NULL) { name = (xmlChar *)tmp + 1; } node->name = g_strdup((gchar *)name); if (!pd->root) { pd->root = node; } else { sipe_xml *current = pd->current; node->parent = current; if (current->last) { current->last->sibling = node; } else { current->first = node; } current->last = node; } if (attrs) { const xmlChar *key; node->attributes = g_hash_table_new_full(sipe_ascii_strdown_hash, (GEqualFunc) sipe_strcase_equal, g_free, g_free); while ((key = *attrs++) != NULL) { if ((tmp = strchr((char *)key, ':')) != NULL) { key = (xmlChar *)tmp + 1; } /* libxml2 decodes all entities except &. & is replaced by the equivalent & */ g_hash_table_insert(node->attributes, g_strdup((gchar *) key), sipe_utils_str_replace((gchar *) *attrs++, "&", "&")); } } pd->current = node; } static void callback_end_element(void *user_data, const xmlChar *name) { struct _parser_data *pd = user_data; if (!name || !pd->current || pd->error) return; if (pd->current->parent) pd->current = pd->current->parent; } static void callback_characters(void *user_data, const xmlChar *text, int text_len) { struct _parser_data *pd = user_data; sipe_xml *node; if (!pd->current || pd->error || !text || !text_len) return; node = pd->current; if (node->data) node->data = g_string_append_len(node->data, (gchar *)text, text_len); else node->data = g_string_new_len((gchar *)text, text_len); } static void callback_error(void *user_data, const char *msg, ...) { struct _parser_data *pd = user_data; gchar *errmsg; va_list args; pd->error = TRUE; va_start(args, msg); errmsg = g_strdup_vprintf(msg, args); va_end(args); SIPE_DEBUG_ERROR("error parsing xml string: %s", errmsg); g_free(errmsg); } static void callback_serror(void *user_data, xmlErrorPtr error) { struct _parser_data *pd = user_data; if (error && (error->level == XML_ERR_ERROR || error->level == XML_ERR_FATAL)) { pd->error = TRUE; SIPE_DEBUG_ERROR("XML parser error: Domain %i, code %i, level %i: %s", error->domain, error->code, error->level, error->message ? error->message : "(null)"); } else if (error) { SIPE_DEBUG_WARNING("XML parser error: Domain %i, code %i, level %i: %s", error->domain, error->code, error->level, error->message ? error->message : "(null)"); } else { /* *sigh* macro expects at least two parameters */ SIPE_DEBUG_WARNING_NOFORMAT("XML parser error"); } } /* API doesn't accept const data structure */ static xmlSAXHandler parser = { NULL, /* internalSubset */ NULL, /* isStandalone */ NULL, /* hasInternalSubset */ NULL, /* hasExternalSubset */ NULL, /* resolveEntity */ NULL, /* getEntity */ NULL, /* entityDecl */ NULL, /* notationDecl */ NULL, /* attributeDecl */ NULL, /* elementDecl */ NULL, /* unparsedEntityDecl */ NULL, /* setDocumentLocator */ NULL, /* startDocument */ NULL, /* endDocument */ callback_start_element, /* startElement */ callback_end_element, /* endElement */ NULL, /* reference */ callback_characters, /* characters */ NULL, /* ignorableWhitespace */ NULL, /* processingInstruction */ NULL, /* comment */ NULL, /* warning */ callback_error, /* error */ NULL, /* fatalError */ NULL, /* getParameterEntity */ NULL, /* cdataBlock */ NULL, /* externalSubset */ XML_SAX2_MAGIC, /* initialized */ NULL, /* _private */ NULL, /* startElementNs */ NULL, /* endElementNs */ callback_serror, /* serror */ }; sipe_xml *sipe_xml_parse(const gchar *string, gsize length) { sipe_xml *result = NULL; if (string && length) { struct _parser_data *pd = g_new0(struct _parser_data, 1); if (xmlSAXUserParseMemory(&parser, pd, string, length)) pd->error = TRUE; if (pd->error) { sipe_xml_free(pd->root); } else { result = pd->root; } g_free(pd); } return result; } void sipe_xml_free(sipe_xml *node) { sipe_xml *child; if (!node) return; /* we don't support partial tree deletion */ if (node->parent != NULL) { SIPE_DEBUG_ERROR_NOFORMAT("sipe_xml_free: partial delete attempt! Expect crash or memory leaks..."); } /* free children */ child = node->first; while (child) { sipe_xml *tmp = child->sibling; child->parent = NULL; /* detach from tree, see above */ sipe_xml_free(child); child = tmp; } /* free node */ g_free(node->name); if (node->data) g_string_free(node->data, TRUE); if (node->attributes) g_hash_table_destroy(node->attributes); g_free(node); } static void sipe_xml_stringify_attribute(gpointer key, gpointer value, gpointer user_data) { g_string_append_printf(user_data, " %s=\"%s\"", (const gchar *) key, (const gchar *) value); } static void sipe_xml_stringify_node(GString *s, const sipe_xml *node) { g_string_append_printf(s, "<%s", node->name); if (node->attributes) { g_hash_table_foreach(node->attributes, (GHFunc) sipe_xml_stringify_attribute, s); } if (node->data || node->first) { const sipe_xml *child; g_string_append_printf(s, ">%s", node->data ? node->data->str : ""); for (child = node->first; child; child = child->sibling) sipe_xml_stringify_node(s, child); g_string_append_printf(s, "", node->name); } else { g_string_append(s, "/>"); } } gchar *sipe_xml_stringify(const sipe_xml *node) { GString *s; if (!node) return NULL; s = g_string_new(""); sipe_xml_stringify_node(s, node); return g_string_free(s, FALSE); } const sipe_xml *sipe_xml_child(const sipe_xml *parent, const gchar *name) { gchar **names; const sipe_xml *child = NULL; if (!parent || !name) return NULL; /* 0: child name */ /* 1: trailing path (optional) */ names = g_strsplit(name, "/", 2); for (child = parent->first; child; child = child->sibling) { if (sipe_strequal(names[0], child->name)) break; } /* recurse into path */ if (child && names[1]) child = sipe_xml_child(child, names[1]); g_strfreev(names); return child; } const sipe_xml *sipe_xml_twin(const sipe_xml *node) { sipe_xml *sibling; if (!node) return NULL; for (sibling = node->sibling; sibling; sibling = sibling->sibling) { if (sipe_strequal(node->name, sibling->name)) return sibling; } return NULL; } const gchar *sipe_xml_name(const sipe_xml *node) { return(node ? node->name : NULL); } const gchar *sipe_xml_attribute(const sipe_xml *node, const gchar *attr) { if (!node || !attr || !node->attributes) return NULL; return(g_hash_table_lookup(node->attributes, attr)); } guint sipe_xml_int_attribute(const sipe_xml *node, const gchar *attr, guint fallback) { const gchar *value = sipe_xml_attribute(node, attr); return(value ? g_ascii_strtoull(value, NULL, 10) : fallback); } gchar *sipe_xml_data(const sipe_xml *node) { if (!node || !node->data || !node->data->str) return NULL; return g_strdup(node->data->str); } /** * Set to 1 to enable debugging code and then add this line to your code: * * sipe_xml_dump(node, NULL); */ #if 0 void sipe_xml_dump(const sipe_xml *node, const gchar *path) { const sipe_xml *child; gchar *new_path; if (!node) return; new_path = g_strdup_printf("%s/%s", path ? path : "", node->name); if (node->attributes) { GList *attrs = g_hash_table_get_keys(node->attributes); GString *buf = g_string_new(""); GList *entry = attrs; while (entry) { g_string_append_printf(buf, "%s ", (gchar *)entry->data); entry = entry->next; } SIPE_DEBUG_INFO("%s [%s]", new_path, buf->str); g_string_free(buf, TRUE); g_list_free(attrs); } else { SIPE_DEBUG_INFO_NOFORMAT(new_path); } for (child = node->first; child; child = child->sibling) sipe_xml_dump(child, new_path); g_free(new_path); } #endif /* * Other XML convenience functions not based on libpurple xmlnode.c */ gchar *sipe_xml_exc_c14n(const gchar *string) { /* Parse string to XML document */ xmlDocPtr doc = xmlReadMemory(string, strlen(string), "", NULL, 0); gchar *canon = NULL; if (doc) { xmlChar *buffer; int size; /* Apply canonicalization */ size = xmlC14NDocDumpMemory(doc, NULL, #if LIBXML_VERSION > 20703 /* new API: int mode (a xmlC14NMode) */ XML_C14N_EXCLUSIVE_1_0, #else /* old API: int exclusive */ 1, #endif NULL, 0, &buffer); xmlFreeDoc(doc); if (size >= 0) { SIPE_DEBUG_INFO("sipe_xml_exc_c14n:\noriginal: %s\ncanonicalized: %s", string, buffer); canon = g_strndup((gchar *) buffer, size); xmlFree(buffer); } else { SIPE_DEBUG_ERROR("sipe_xml_exc_c14n: failed to canonicalize xml string:\n%s", string); } } else { SIPE_DEBUG_ERROR("sipe_xml_exc_c14n: error parsing xml string:\n%s", string); } return(canon); } static gchar *sipe_xml_extract_exact_raw(const gchar *xml, const gchar *tag, gboolean include_tag) { gchar *tag_start = g_strdup_printf("<%s", tag); gchar *tag_end = g_strdup_printf("", tag); gchar *data = NULL; const gchar *start = strstr(xml, tag_start); if (start) { const gchar *end = strstr(start + strlen(tag_start), tag_end); if (end) { if (include_tag) { data = g_strndup(start, end + strlen(tag_end) - start); } else { const gchar *tmp = strchr(start + strlen(tag_start), '>') + 1; data = g_strndup(tmp, end - tmp); } } } g_free(tag_end); g_free(tag_start); return data; } static gchar *sipe_xml_extract_any_raw(const gchar *xml, const gchar *tag, gboolean include_tag) { gchar *tag_start = g_strdup_printf(":%s", tag); gchar *data = NULL; const gchar *start = strstr(xml, tag_start); if (start) { const gchar *p = start - 1; /* search for beginning of tag */ while ((*p != '<') && (p >= xml)) p--; /* namespace identifier found? */ if ((p >= xml) && (p != (start - 1))) { gchar *ns = g_strndup(p + 1, start - p); gchar *tag_end = g_strdup_printf("", ns, tag); const gchar *end = strstr(start + strlen(tag_start), tag_end); g_free(ns); if (end) { if (include_tag) { data = g_strndup(p, end + strlen(tag_end) - p); } else { const gchar *tmp = strchr(start + strlen(tag_start), '>') + 1; data = g_strndup(tmp, end - tmp); } } g_free(tag_end); } } g_free(tag_start); return data; } gchar *sipe_xml_extract_raw(const gchar *xml, const gchar *tag, gboolean include_tag) { /* first try exact match */ gchar *data = sipe_xml_extract_exact_raw(xml, tag, include_tag); /* otherwise match tag in any name space */ if (!data) data = sipe_xml_extract_any_raw(xml, tag, include_tag); return(data); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipe-xml.h ================================================ /** * @file sipe-xml.h * * pidgin-sipe * * Copyright (C) 2010-11 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ typedef struct _sipe_xml sipe_xml; /** * Parse XML from a string. * * @param string String with the XML to be parsed. * @param length Length of the string. * * @return Parsed XML information. Must be @c sipe_xml_free()'d. */ sipe_xml *sipe_xml_parse(const gchar *string, gsize length); /** * Free XML information. * * @param string XML information to be freed. */ void sipe_xml_free(sipe_xml *xml); /** * Convert XML information to string. * * @param xml XML information. * * @return XML converted to a string. Must be @c g_free()'d. */ gchar *sipe_xml_stringify(const sipe_xml *xml); /** * Gets a child node named name. * * @param parent The parent node. * @param name relative XPATH of the child (a, a/b, a/b/c, etc.). * * @return The child or @c NULL. Never try to @c sipe_xml_free() it! */ const sipe_xml *sipe_xml_child(const sipe_xml *parent, const gchar *name); /** * Gets the next node with the same name as node. * * @param node The node of a twin to find. * * @return The twin of node or @c NULL. */ const sipe_xml *sipe_xml_twin(const sipe_xml *node); /** * Gets the name from the current XML node. * * @param node The node to get the name from. * * @return The name of the node */ const gchar *sipe_xml_name(const sipe_xml *node); /** * Gets an attribute from the current XML node. * * @param node The node to get an attribute from. * @param attr The attribute to get. * * @return The value of the attribute or @c NULL. */ const gchar *sipe_xml_attribute(const sipe_xml *node, const gchar *attr); /** * Gets an attribute from the current XML node and convert it to an * unsigned integer. * * @param node The node to get an attribute from. * @param attr The attribute to get. * @param fallback Default value if the attribute doesn't exist. * * @return Attribute value converted to an integer or the fallback value. */ guint sipe_xml_int_attribute(const sipe_xml *node, const gchar *attr, guint fallback); /** * Gets escaped data from the current XML node. * * @param node The node to get data from. * * @return The data from the node or @c NULL. Must be @c g_free()'d. */ gchar *sipe_xml_data(const sipe_xml *node); /** * For debugging while writing XML processing code. * NOTE: the code for this function is flagged out by default! * * @param node The node to start dumping from * @param path The path to this node (can be NULL) */ void sipe_xml_dump(const sipe_xml *node, const gchar *path); /* Other XML convenience functions */ /** * Apply "Exclusive XML Canonicalization" to a XML string * See also http://www.w3.org/TR/xml-exc-c14n/ * * @param string String with the XML to be canonicalized. * * @return canonicalized XML string. Must be @c g_free()'d. */ gchar *sipe_xml_exc_c14n(const gchar *string); /** * Extracts raw data between a pair of XML tags. * * @param xml XML document * @param tag XML tag enclosing the data * @param include_tag whether the enclosing tags should be included in the result * * @return a first substring from the XML document enclosed by @c tag. * Must be @c g_free()'d. */ gchar *sipe_xml_extract_raw(const gchar *xml, const gchar *tag, gboolean include_tag); ================================================ FILE: src/core/sipmsg.c ================================================ /** * @file sipmsg.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * Copyright (C) 2008 Novell, Inc. * Copyright (C) 2005 Thomas Butter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "sipmsg.h" #include "sipe-backend.h" #include "sipe-mime.h" #include "sipe-rtf.h" #include "sipe-utils.h" struct sipmsg *sipmsg_parse_msg(const gchar *msg) { const char *tmp = strstr(msg, "\r\n\r\n"); char *line; struct sipmsg *smsg; if(!tmp) return NULL; line = g_strndup(msg, tmp - msg); smsg = sipmsg_parse_header(line); smsg->body = g_strdup(tmp + 4); g_free(line); return smsg; } struct sipmsg *sipmsg_parse_header(const gchar *header) { struct sipmsg *msg = g_new0(struct sipmsg,1); gchar **lines = g_strsplit(header,"\r\n",0); gchar **parts; const gchar *contentlength; if(!lines[0]) { g_strfreev(lines); g_free(msg); return NULL; } parts = g_strsplit(lines[0], " ", 3); if(!parts[0] || !parts[1] || !parts[2]) { g_strfreev(parts); g_strfreev(lines); g_free(msg); return NULL; } if(strstr(parts[0],"SIP") || strstr(parts[0],"HTTP")) { /* numeric response */ msg->responsestr = g_strdup(parts[2]); msg->response = strtol(parts[1],NULL,10); } else { /* request */ msg->method = g_strdup(parts[0]); msg->target = g_strdup(parts[1]); msg->response = 0; } g_strfreev(parts); if (sipe_utils_parse_lines(&msg->headers, lines + 1, ":") == FALSE) { g_strfreev(lines); sipmsg_free(msg); return NULL; } g_strfreev(lines); contentlength = sipmsg_find_header(msg, "Content-Length"); if (contentlength) { msg->bodylen = strtol(contentlength,NULL,10); } else { const gchar *tmp = sipmsg_find_header(msg, "Transfer-Encoding"); if (tmp && sipe_strcase_equal(tmp, "chunked")) { msg->bodylen = SIPMSG_BODYLEN_CHUNKED; } else { tmp = sipmsg_find_content_type_header(msg); if (tmp) { /* * This is a fatal error situation: the message * is corrupted and we can't proceed. Set the * response code to a special value so that the * caller can abort correctly. */ SIPE_DEBUG_ERROR_NOFORMAT("sipmsg_parse_header: Content-Length header not found. Aborting!"); msg->response = SIPMSG_RESPONSE_FATAL_ERROR; return(msg); } else { msg->bodylen = 0; } } } if(msg->response) { const gchar *tmp; tmp = sipmsg_find_cseq_header(msg); if(!tmp) { /* SHOULD NOT HAPPEN */ msg->method = 0; } else { parts = g_strsplit(tmp, " ", 2); msg->method = g_strdup(parts[1]); g_strfreev(parts); } } return msg; } struct sipmsg *sipmsg_copy(const struct sipmsg *other) { struct sipmsg *msg = g_new0(struct sipmsg, 1); GSList *list; msg->response = other->response; msg->responsestr = g_strdup(other->responsestr); msg->method = g_strdup(other->method); msg->target = g_strdup(other->target); list = other->headers; while(list) { struct sipnameval *elem = list->data; sipmsg_add_header_now(msg, elem->name, elem->value); list = list->next; } list = other->new_headers; while(list) { struct sipnameval *elem = list->data; sipmsg_add_header(msg, elem->name, elem->value); list = list->next; } msg->bodylen = other->bodylen; msg->body = g_strdup(other->body); msg->signature = g_strdup(other->signature); msg->rand = g_strdup(other->rand); msg->num = g_strdup(other->num); return msg; } char *sipmsg_to_string(const struct sipmsg *msg) { GSList *cur; GString *outstr = g_string_new(""); struct sipnameval *elem; if(msg->response) g_string_append_printf(outstr, "SIP/2.0 %d Unknown\r\n", msg->response); else g_string_append_printf(outstr, "%s %s SIP/2.0\r\n", msg->method, msg->target); cur = msg->headers; while(cur) { elem = cur->data; /*Todo: remove the LFCR in a good way*/ /*if(sipe_strequal(elem->name,"Proxy-Authorization")) g_string_append_printf(outstr, "%s: %s", elem->name, elem->value); else */ g_string_append_printf(outstr, "%s: %s\r\n", elem->name, elem->value); cur = g_slist_next(cur); } g_string_append_printf(outstr, "\r\n%s", msg->bodylen ? msg->body : ""); return g_string_free(outstr, FALSE); } /** * Adds header to current message headers */ void sipmsg_add_header_now(struct sipmsg *msg, const gchar *name, const gchar *value) { struct sipnameval *element = g_new0(struct sipnameval,1); /* SANITY CHECK: the calling code must be fixed if this happens! */ if (!value) { SIPE_DEBUG_ERROR("sipmsg_add_header_now: NULL value for %s", name); value = ""; } element->name = g_strdup(name); element->value = g_strdup(value); msg->headers = g_slist_append(msg->headers, element); } /** * Adds header to separate storage for future merge */ void sipmsg_add_header(struct sipmsg *msg, const gchar *name, const gchar *value) { struct sipnameval *element = g_new0(struct sipnameval,1); /* SANITY CHECK: the calling code must be fixed if this happens! */ if (!value) { SIPE_DEBUG_ERROR("sipmsg_add_header: NULL value for %s", name); value = ""; } element->name = g_strdup(name); element->value = g_strdup(value); msg->new_headers = g_slist_append(msg->new_headers, element); } /** * Removes header if it's not in keepers array */ void sipmsg_strip_headers(struct sipmsg *msg, const gchar *keepers[]) { GSList *entry; struct sipnameval *elem; entry = msg->headers; while(entry) { int i = 0; gboolean keeper = FALSE; elem = entry->data; while (keepers[i]) { if (!g_ascii_strcasecmp(elem->name, keepers[i])) { keeper = TRUE; break; } i++; } if (!keeper) { GSList *to_delete = entry; SIPE_DEBUG_INFO("sipmsg_strip_headers: removing %s", elem->name); entry = g_slist_next(entry); msg->headers = g_slist_delete_link(msg->headers, to_delete); g_free(elem->name); g_free(elem->value); g_free(elem); } else { entry = g_slist_next(entry); } } } /** * Merges newly added headers to message */ void sipmsg_merge_new_headers(struct sipmsg *msg) { while(msg->new_headers) { msg->headers = g_slist_append(msg->headers, msg->new_headers->data); msg->new_headers = g_slist_remove(msg->new_headers, msg->new_headers->data); } } void sipmsg_free(struct sipmsg *msg) { if (msg) { sipe_utils_nameval_free(msg->headers); sipe_utils_nameval_free(msg->new_headers); g_free(msg->signature); g_free(msg->rand); g_free(msg->num); g_free(msg->responsestr); g_free(msg->method); g_free(msg->target); g_free(msg->body); g_free(msg); } } void sipmsg_remove_header_now(struct sipmsg *msg, const gchar *name) { struct sipnameval *elem; GSList *tmp = msg->headers; while(tmp) { elem = tmp->data; // OCS2005 can send the same header in either all caps or mixed case if (sipe_strcase_equal(elem->name, name)) { msg->headers = g_slist_remove(msg->headers, elem); g_free(elem->name); g_free(elem->value); g_free(elem); return; } tmp = g_slist_next(tmp); } return; } const gchar *sipmsg_find_header(const struct sipmsg *msg, const gchar *name) { return sipe_utils_nameval_find_instance (msg->headers, name, 0); } const gchar *sipmsg_find_header_instance(const struct sipmsg *msg, const gchar *name, int which) { return sipe_utils_nameval_find_instance(msg->headers, name, which); } gchar *sipmsg_find_part_of_header(const char *hdr, const char * before, const char * after, const char * def) { const char *tmp; const char *tmp2; gchar *res2; if (!hdr) { return NULL; } //printf("partof %s w/ %s before and %s after\n", hdr, before, after); tmp = before == NULL ? hdr : strstr(hdr, before); if (!tmp) { //printf ("not found, returning null\n"); return (gchar *)def; } if (before != NULL) { tmp += strlen(before); //printf ("tmp now %s\n", tmp); } if (after != NULL && (tmp2 = strstr(tmp, after))) { gchar * res = g_strndup(tmp, tmp2 - tmp); //printf("returning %s\n", res); return res; } res2 = g_strdup(tmp); //printf("returning %s\n", res2); return res2; } int sipmsg_parse_cseq(struct sipmsg *msg) { int res = -1; gchar **items; items = g_strsplit(sipmsg_find_cseq_header(msg), " ", 1); if (items[0]) { res = atoi(items[0]); } g_strfreev(items); return res; } /** * Parse EndPoints header from INVITE request * Returns a list of end points: contact URI plus optional epid. * You must free the values and the list. * * Example headers: * EndPoints: "alice alisson" , ;epid=ebca82d94d, * EndPoints: "alice, alisson" , * EndPoints: "alice alisson" , "Super, Man" * * @param header (in) EndPoints header contents * * @return GSList with struct sipendpoint as elements */ GSList *sipmsg_parse_endpoints_header(const gchar *header) { GSList *list = NULL; gchar **parts = g_strsplit(header, ",", 0); gchar *part; int i; for (i = 0; (part = parts[i]) != NULL; i++) { /* Does the part contain a URI? */ gchar *contact = sipmsg_find_part_of_header(part, "<", ">", NULL); if (contact) { struct sipendpoint *end_point = g_new(struct sipendpoint, 1); end_point->contact = contact; end_point->epid = sipmsg_find_part_of_header(part, "epid=", NULL, NULL); list = g_slist_append(list, end_point); } } g_strfreev(parts); return(list); } void sipmsg_parse_p_asserted_identity(const gchar *header, gchar **sip_uri, gchar **tel_uri) { gchar **parts, **p; *sip_uri = NULL; *tel_uri = NULL; if (g_ascii_strncasecmp(header, "tel:", 4) == 0) { *tel_uri = g_strdup(header); return; } parts = g_strsplit(header, ",", 0); for (p = parts; *p; p++) { gchar *uri = sipmsg_find_part_of_header(*p, "<", ">", NULL); if (!uri) continue; if (g_ascii_strncasecmp(uri, "sip:", 4) == 0) { if (*sip_uri) { SIPE_DEBUG_WARNING_NOFORMAT("More than one " "sip: URI found in P-Asserted-Identity!"); } else { *sip_uri = uri; uri = NULL; } } else if (g_ascii_strncasecmp(uri, "tel:", 4) == 0){ if (*tel_uri) { SIPE_DEBUG_WARNING_NOFORMAT("More than one " "tel: URI found in P-Asserted-Identity!"); } else { *tel_uri = uri; uri = NULL; } } g_free(uri); } g_strfreev(parts); } /* * sipmsg_find_auth_header will return the particular WWW-Authenticate * header specified by *name. * * Use this function when you want to look for a specific authentication * method such as NTLM or Kerberos */ const gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name) { GSList *tmp; struct sipnameval *elem; int name_len; if (!name) { SIPE_DEBUG_INFO_NOFORMAT("sipmsg_find_auth_header: no authentication scheme specified"); return NULL; } name_len = strlen(name); tmp = msg->headers; while(tmp) { elem = tmp->data; /* SIPE_DEBUG_INFO("Current header: %s", elem->value); */ if (elem && elem->name && (sipe_strcase_equal(elem->name,"WWW-Authenticate") || sipe_strcase_equal(elem->name,"Authentication-Info")) ) { if (!g_ascii_strncasecmp((gchar *)elem->value, name, name_len)) { /* SIPE_DEBUG_INFO("elem->value: %s", elem->value); */ return elem->value; } } /* SIPE_DEBUG_INFO_NOFORMAT("moving to next header"); */ tmp = g_slist_next(tmp); } SIPE_DEBUG_INFO("sipmsg_find_auth_header: '%s' not found", name); return NULL; } /** * Parses headers-like 'msgr' attribute of INVITE's 'ms_text_format' header. * Then retrieves value of 'X-MMS-IM-Format'. * 'msgr' typically looks like: * X-MMS-IM-Format: FN=Microsoft%20Sans%20Serif; EF=BI; CO=800000; CS=0; PF=22 */ static gchar *sipmsg_get_x_mms_im_format(gchar *msgr) { gchar *msgr2; gsize msgr_dec64_len; guchar *msgr_dec64; gchar *msgr_utf8; gchar **lines; gchar **parts; gchar *x_mms_im_format; gchar *tmp; if (!msgr) return NULL; msgr2 = g_strdup(msgr); while (strlen(msgr2) % 4 != 0) { gchar *tmp_msgr2 = msgr2; msgr2 = g_strdup_printf("%s=", msgr2); g_free(tmp_msgr2); } msgr_dec64 = g_base64_decode(msgr2, &msgr_dec64_len); msgr_utf8 = g_convert((gchar *) msgr_dec64, msgr_dec64_len, "UTF-8", "UTF-16LE", NULL, NULL, NULL); g_free(msgr_dec64); g_free(msgr2); lines = g_strsplit(msgr_utf8,"\r\n\r\n",0); g_free(msgr_utf8); //@TODO: make extraction like parsing of message headers. parts = g_strsplit(lines[0],"X-MMS-IM-Format:",0); x_mms_im_format = g_strdup(parts[1]); g_strfreev(parts); g_strfreev(lines); tmp = x_mms_im_format; if (x_mms_im_format) { while(*x_mms_im_format==' ' || *x_mms_im_format=='\t') x_mms_im_format++; } x_mms_im_format = g_strdup(x_mms_im_format); g_free(tmp); return x_mms_im_format; } gchar *sipmsg_get_msgr_string(const gchar *x_mms_im_format) { gchar *msgr_orig; gsize msgr_utf16_len; gchar *msgr_utf16; gchar *msgr_enc; gchar *res; int len; if (!x_mms_im_format) return NULL; msgr_orig = g_strdup_printf("X-MMS-IM-Format: %s\r\n\r\n", x_mms_im_format); msgr_utf16 = g_convert(msgr_orig, -1, "UTF-16LE", "UTF-8", NULL, &msgr_utf16_len, NULL); g_free(msgr_orig); msgr_enc = g_base64_encode((guchar *) msgr_utf16, msgr_utf16_len); g_free(msgr_utf16); len = strlen(msgr_enc); while (msgr_enc[len - 1] == '=') len--; res = g_strndup(msgr_enc, len); g_free(msgr_enc); return res; } static void msn_parse_format(const char *mime, char **pre_ret, char **post_ret); /** * Translates X-MMS-IM format to HTML presentation. */ static gchar *sipmsg_apply_x_mms_im_format(const char *x_mms_im_format, gchar *body) { char *pre, *post; gchar *res; if (!x_mms_im_format) { return body ? g_strdup(body) : NULL; } msn_parse_format(x_mms_im_format, &pre, &post); res = g_strdup_printf("%s%s%s", pre ? pre : "", body ? body : "", post ? post : ""); g_free(pre); g_free(post); return res; } struct html_message_data { gchar *ms_text_format; gchar *body; gboolean preferred; }; static void get_html_message_mime_cb(gpointer user_data, const GSList *fields, const gchar *body, gsize length) { const gchar *type = sipe_utils_nameval_find(fields, "Content-Type"); struct html_message_data *data = user_data; if (!data->preferred) { gboolean copy = FALSE; /* preferred formats */ if (g_str_has_prefix(type, "text/html") || g_str_has_prefix(type, "text/rtf")) { copy = TRUE; data->preferred = TRUE; /* fallback format */ } else if (g_str_has_prefix(type, "text/plain")) { copy = TRUE; } if (copy) { g_free(data->ms_text_format); g_free(data->body); data->ms_text_format = g_strdup(type); data->body = g_strndup(body, length); } } } /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */ gchar *get_html_message(const gchar *ms_text_format_in, const gchar *body_in) { gchar *msgr; gchar *res; gchar *ms_text_format = NULL; gchar *body = NULL; if (g_str_has_prefix(ms_text_format_in, "multipart/related") || g_str_has_prefix(ms_text_format_in, "multipart/alternative")) { struct html_message_data data = { NULL, NULL, FALSE }; sipe_mime_parts_foreach(ms_text_format_in, body_in, get_html_message_mime_cb, &data); ms_text_format = data.ms_text_format; body = data.body; } else { ms_text_format = g_strdup(ms_text_format_in); body = g_strdup(body_in); } if (body) { res = body; } else { gchar *tmp = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL); gsize len; if (!tmp) { g_free(ms_text_format); return NULL; } res = (gchar *) g_base64_decode(tmp, &len); g_free(tmp); if (!res) { g_free(ms_text_format); return NULL; } } if (g_str_has_prefix(ms_text_format, "text/html")) { /* * HTML uses tags for formatting, not line breaks. But * clients still might render them, so we need to remove * them to avoid incorrect text rendering. */ gchar *d = res; const gchar *s = res; gchar c; /* No ANSI C nor glib function seems to exist for this :-( */ while ((c = *s++)) if ((c != '\n') && (c != '\r')) *d++ = c; *d = c; } else if (g_str_has_prefix(ms_text_format, "text/rtf")) { char *tmp = res; res = sipe_rtf_to_html(res); g_free(tmp); } else { char *tmp = res; res = g_markup_escape_text(res, -1); // as this is not html g_free(tmp); } msgr = sipmsg_find_part_of_header(ms_text_format, "msgr=", ";", NULL); if (msgr) { gchar *x_mms_im_format = sipmsg_get_x_mms_im_format(msgr); gchar *tmp = res; g_free(msgr); res = sipmsg_apply_x_mms_im_format(x_mms_im_format, res); g_free(tmp); g_free(x_mms_im_format); } g_free(ms_text_format); return res; } static gchar * get_reason(struct sipmsg *msg, const gchar *header) { const gchar *diagnostics = sipmsg_find_header(msg, header); if (diagnostics) return sipmsg_find_part_of_header(diagnostics, "reason=\"", "\"", NULL); return NULL; } gchar * sipmsg_get_ms_diagnostics_reason(struct sipmsg *msg) { return get_reason(msg, "ms-diagnostics"); } gchar * sipmsg_get_ms_diagnostics_public_reason(struct sipmsg *msg) { return get_reason(msg, "ms-diagnostics-public"); } int sipmsg_parse_warning(struct sipmsg *msg, gchar **reason) { /* * Example header: * Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client" */ const gchar *hdr = sipmsg_find_header(msg, "Warning"); int code = -1; if (reason) *reason = NULL; if (hdr) { gchar **parts = g_strsplit(hdr, " ", 3); if (parts[0]) { code = atoi(parts[0]); if (reason && parts[1] && parts[2]) { size_t len = strlen(parts[2]); if (len > 2 && parts[2][0] == '"' && parts[2][len - 1] == '"') *reason = g_strndup(parts[2] + 1, len - 2); } } g_strfreev(parts); } return code; } const gchar *sipmsg_find_call_id_header(const struct sipmsg *msg) { return(sipmsg_find_header(msg, "Call-ID")); } const gchar *sipmsg_find_content_type_header(const struct sipmsg *msg) { return(sipmsg_find_header(msg, "Content-Type")); } const gchar *sipmsg_find_cseq_header(const struct sipmsg *msg) { return(sipmsg_find_header(msg, "CSeq")); } const gchar *sipmsg_find_event_header(const struct sipmsg *msg) { return(sipmsg_find_header(msg, "Event")); } const gchar *sipmsg_find_expires_header(const struct sipmsg *msg) { return(sipmsg_find_header(msg, "Expires")); } const gchar *sipmsg_find_from_header(const struct sipmsg *msg) { return(sipmsg_find_header(msg, "From")); } const gchar *sipmsg_find_to_header(const struct sipmsg *msg) { return(sipmsg_find_header(msg, "To")); } gchar *sipmsg_parse_address_from_header(const struct sipmsg *msg, const gchar *name) { return(parse_from(sipmsg_find_header(msg, name))); } gchar *sipmsg_parse_contact_address(const struct sipmsg *msg) { return(sipmsg_parse_address_from_header(msg, "Contact")); } gchar *sipmsg_parse_from_address(const struct sipmsg *msg) { return(sipmsg_parse_address_from_header(msg, "From")); } gchar *sipmsg_parse_to_address(const struct sipmsg *msg) { return(sipmsg_parse_address_from_header(msg, "To")); } void sipmsg_update_to_header_tag(struct sipmsg *msg) { const gchar *old = sipmsg_find_to_header(msg); gchar *tag = gentag(); gchar *new = g_strdup_printf("%s;tag=%s", old, tag); g_free(tag); sipmsg_remove_header_now(msg, "To"); sipmsg_add_header_now(msg, "To", new); g_free(new); } //------------------------------------------------------------------------------------------ //TEMP solution to include it here (copy from purple's msn protocol //How to reuse msn's util methods from sipe? /* from internal.h */ #define MSG_LEN 2048 #define BUF_LEN MSG_LEN void msn_parse_format(const char *mime, char **pre_ret, char **post_ret) { char *cur; GString *pre = g_string_new(NULL); GString *post = g_string_new(NULL); unsigned int colors[3]; if (pre_ret != NULL) *pre_ret = NULL; if (post_ret != NULL) *post_ret = NULL; cur = strstr(mime, "FN="); if (cur && (*(cur = cur + 3) != ';')) { pre = g_string_append(pre, ""); post = g_string_prepend(post, ""); } cur = strstr(mime, "EF="); if (cur && (*(cur = cur + 3) != ';')) { while (*cur && *cur != ';') { pre = g_string_append_c(pre, '<'); pre = g_string_append_c(pre, *cur); pre = g_string_append_c(pre, '>'); post = g_string_prepend_c(post, '>'); post = g_string_prepend_c(post, *cur); post = g_string_prepend_c(post, '/'); post = g_string_prepend_c(post, '<'); cur++; } } cur = strstr(mime, "CO="); if (cur && (*(cur = cur + 3) != ';')) { int i; i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]); if (i > 0) { char tag[64]; if (i == 1) { colors[1] = 0; colors[2] = 0; } else if (i == 2) { unsigned int temp = colors[0]; colors[0] = colors[1]; colors[1] = temp; colors[2] = 0; } else if (i == 3) { unsigned int temp = colors[2]; colors[2] = colors[0]; colors[0] = temp; } /* hh is undefined in mingw's gcc 4.4 * https://sourceforge.net/tracker/index.php?func=detail&aid=2818436&group_id=2435&atid=102435 */ g_snprintf(tag, sizeof(tag), "", (unsigned char)colors[0], (unsigned char)colors[1], (unsigned char)colors[2]); pre = g_string_append(pre, tag); post = g_string_prepend(post, ""); } } cur = strstr(mime, "RL="); if (cur && (*(cur = cur + 3) != ';')) { if (*cur == '1') { /* RTL text was received */ pre = g_string_append(pre, ""); post = g_string_prepend(post, ""); } } cur = sipe_utils_uri_unescape(pre->str); g_string_free(pre, TRUE); if (pre_ret != NULL) *pre_ret = cur; else g_free(cur); cur = sipe_utils_uri_unescape(post->str); g_string_free(post, TRUE); if (post_ret != NULL) *post_ret = cur; else g_free(cur); } static const char * encode_spaces(const char *str) { static char buf[BUF_LEN]; const char *c; char *d; g_return_val_if_fail(str != NULL, NULL); for (c = str, d = buf; *c != '\0'; c++) { if (*c == ' ') { *d++ = '%'; *d++ = '2'; *d++ = '0'; } else *d++ = *c; } *d = '\0'; return buf; } void sipe_parse_html(const char *html, char **attributes, char **message) { int len, retcount = 0; const char *c; char *msg; char *fontface = NULL; char fonteffect[4]; char fontcolor[7]; char direction = '0'; gboolean has_bold = FALSE; gboolean has_italic = FALSE; gboolean has_underline = FALSE; gboolean has_strikethrough = FALSE; g_return_if_fail(html != NULL); g_return_if_fail(attributes != NULL); g_return_if_fail(message != NULL); #define _HTML_UNESCAPE \ if (!g_ascii_strncasecmp(c, "<", 4)) { \ msg[retcount++] = '<'; \ c += 4; \ } else if (!g_ascii_strncasecmp(c, ">", 4)) { \ msg[retcount++] = '>'; \ c += 4; \ } else if (!g_ascii_strncasecmp(c, " ", 6)) { \ msg[retcount++] = ' '; \ c += 6; \ } else if (!g_ascii_strncasecmp(c, """, 6)) { \ msg[retcount++] = '"'; \ c += 6; \ } else if (!g_ascii_strncasecmp(c, "&", 5)) { \ msg[retcount++] = '&'; \ c += 5; \ } else if (!g_ascii_strncasecmp(c, "'", 6)) { \ msg[retcount++] = '\''; \ c += 6; \ } else { \ msg[retcount++] = *c++; \ } len = strlen(html); msg = g_malloc0(len + 1); memset(fontcolor, 0, sizeof(fontcolor)); strcat(fontcolor, "0"); memset(fonteffect, 0, sizeof(fonteffect)); for (c = html; *c != '\0';) { if (*c == '<') { if (!g_ascii_strncasecmp(c + 1, "br>", 3)) { msg[retcount++] = '\r'; msg[retcount++] = '\n'; c += 4; } else if (!g_ascii_strncasecmp(c + 1, "div>", 4)) { msg[retcount++] = '\r'; msg[retcount++] = '\n'; c += 5; if (!g_ascii_strncasecmp(c, "
", 10)) { /* This is an empty paragraph; replace it with * one line break. */ c += 10; } } else if (!g_ascii_strncasecmp(c + 1, "i>", 2)) { if (!has_italic) { strcat(fonteffect, "I"); has_italic = TRUE; } c += 3; } else if (!g_ascii_strncasecmp(c + 1, "b>", 2)) { if (!has_bold) { strcat(fonteffect, "B"); has_bold = TRUE; } c += 3; } else if (!g_ascii_strncasecmp(c + 1, "u>", 2)) { if (!has_underline) { strcat(fonteffect, "U"); has_underline = TRUE; } c += 3; } else if (!g_ascii_strncasecmp(c + 1, "s>", 2)) { if (!has_strikethrough) { strcat(fonteffect, "S"); has_strikethrough = TRUE; } c += 3; } else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8)) { c += 9; if (!g_ascii_strncasecmp(c, "mailto:", 7)) c += 7; while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2)) if (*c == '&') { _HTML_UNESCAPE; } else msg[retcount++] = *c++; if (*c != '\0') c += 2; /* ignore descriptive string */ while ((*c != '\0') && g_ascii_strncasecmp(c, "", 4)) c++; if (*c != '\0') c += 4; } else if (!g_ascii_strncasecmp(c + 1, "span", 4)) { /* Bi-directional text support using CSS properties in span tags */ c += 5; while (*c != '\0' && *c != '>') { while (*c == ' ') c++; if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9)) { c += 9; direction = '1'; } else if (!g_ascii_strncasecmp(c, "style=\"", 7)) { /* Parse inline CSS attributes */ int attr_len = 0; c += 7; while (*(c + attr_len) != '\0' && *(c + attr_len) != '"') attr_len++; if (*(c + attr_len) == '"') { char *css_attributes; char *attr_dir; css_attributes = g_strndup(c, attr_len); attr_dir = sipe_backend_markup_css_property(css_attributes, "direction"); g_free(css_attributes); if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3))) direction = '1'; g_free(attr_dir); } } else { c++; } } if (*c == '>') c++; } else if (!g_ascii_strncasecmp(c + 1, "font", 4)) { c += 5; while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1)) c++; if (!g_ascii_strncasecmp(c, "color=\"#", 7)) { c += 8; fontcolor[0] = *(c + 4); fontcolor[1] = *(c + 5); fontcolor[2] = *(c + 2); fontcolor[3] = *(c + 3); fontcolor[4] = *c; fontcolor[5] = *(c + 1); c += 8; } else if (!g_ascii_strncasecmp(c, "face=\"", 6)) { const char *end = NULL; const char *comma = NULL; unsigned int namelen = 0; c += 6; end = strchr(c, '\"'); comma = strchr(c, ','); if (comma == NULL || comma > end) namelen = (unsigned int)(end - c); else namelen = (unsigned int)(comma - c); g_free(fontface); fontface = g_strndup(c, namelen); c = end + 2; } else { /* Drop all unrecognized/misparsed font tags */ while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2)) c++; if (*c != '\0') c += 2; } } else { while ((*c != '\0') && (*c != '>')) c++; if (*c != '\0') c++; } } else if (*c == '&') { _HTML_UNESCAPE; } else msg[retcount++] = *c++; } if (fontface == NULL) fontface = g_strdup("MS Sans Serif"); *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c", encode_spaces(fontface), fonteffect, fontcolor, direction); *message = msg; g_free(fontface); #undef _HTML_UNESCAPE } // End of TEMP /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/sipmsg.h ================================================ /** * @file sipmsg.h * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * Copyright (C) 2008 Novell, Inc. * Copyright (C) 2005, Thomas Butter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Interface dependencies: * * */ #define SIPMSG_RESPONSE_FATAL_ERROR -1 #define SIPMSG_BODYLEN_CHUNKED -1 struct sipmsg { int response; /* 0 means request, otherwise response code */ gchar *responsestr; gchar *method; gchar *target; GSList *headers; GSList *new_headers; int bodylen; gchar *body; gchar *signature; gchar *rand; gchar *num; }; struct sipendpoint { gchar *contact; gchar *epid; }; struct sipmsg *sipmsg_parse_msg(const gchar *msg); struct sipmsg *sipmsg_parse_header(const gchar *header); struct sipmsg *sipmsg_copy(const struct sipmsg *other); void sipmsg_add_header_now(struct sipmsg *msg, const gchar *name, const gchar *value); void sipmsg_add_header(struct sipmsg *msg, const gchar *name, const gchar *value); void sipmsg_strip_headers(struct sipmsg *msg, const gchar *keepers[]); void sipmsg_merge_new_headers(struct sipmsg *msg); void sipmsg_free(struct sipmsg *msg); /** * Parses CSeq from SIP message * * @param msg (in) SIP message * * @return int type CSeq value (i.e. without method). */ int sipmsg_parse_cseq(struct sipmsg *msg); GSList *sipmsg_parse_endpoints_header(const gchar *header); /** * Parses sip: and tel: URI out of P-Asserted-Identity header from INVITE request. * You must free the values. * * Example headers: * P-Asserted-Identity: "Cullen Jennings" * P-Asserted-Identity: tel:+14085264000 * P-Asserted-Identity: "Lunch, Lucas" , * * @param header (in) P-Asserted-Identity header contents * @param sip_uri (out) parsed sip: URI or NULL if missing * @param tel_uri (out) parsed tel: URI or NULL if missing */ void sipmsg_parse_p_asserted_identity(const gchar *header, gchar **sip_uri, gchar **tel_uri); const gchar *sipmsg_find_header(const struct sipmsg *msg, const gchar *name); const gchar *sipmsg_find_header_instance(const struct sipmsg *msg, const gchar *name, int which); gchar *sipmsg_find_part_of_header(const char *hdr, const char * before, const char * after, const char * def); const gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name); void sipmsg_remove_header_now(struct sipmsg *msg, const gchar *name); char *sipmsg_to_string(const struct sipmsg *msg); /** * Formats message to html if not yet. * Either - keep as is if text/html, or escape text, or escape text and apply format string if any * * @param body in case of 'ms_text_format is Content-Type header' or NULL otherwise * @param ms_text_format either ms-text-format ot Content-Type header. * * Allocates memory. Must be feed when done. */ gchar *get_html_message(const gchar *ms_text_format, const gchar *body); /** * Returns UTF-16LE/'modified base64' encoded X-MMS-IM-Format * based on input x_mms_im_format. */ gchar *sipmsg_get_msgr_string(const gchar *x_mms_im_format); /** * Parses the Purple message formatting (html) into the MSN format. * * @param html The html message to format. * @param attributes The returned attributes string. * @param message The returned message string. * * @return The new message. */ void sipe_parse_html(const char *html, char **attributes, char **message); /** * Extracts reason string from ms-diagnostics header of SIP message * * @param msg SIP message * * @return reason string. Must be g_free()'d after use. */ gchar *sipmsg_get_ms_diagnostics_reason(struct sipmsg *msg); /** * Extracts reason string from ms-diagnostics-public header of SIP message * * @param msg SIP message * * @return reason string. Must be g_free()'d after use. */ gchar *sipmsg_get_ms_diagnostics_public_reason(struct sipmsg *msg); /** * Parses Warning header of SIP message, if present. * * @param msg (in) SIP message * @param reason (out) parsed warning text or NULL if missing. Must be g_free()'d * after use. * * @return warning code or -1 if warning header is not present in message. */ int sipmsg_parse_warning(struct sipmsg *msg, gchar **reason); /** * Helpers to find some common headers in SIP message * * @param msg (in) SIP message * * @return header text or NULL if header is not present message. */ const gchar *sipmsg_find_call_id_header(const struct sipmsg *msg); const gchar *sipmsg_find_content_type_header(const struct sipmsg *msg); const gchar *sipmsg_find_cseq_header(const struct sipmsg *msg); const gchar *sipmsg_find_event_header(const struct sipmsg *msg); const gchar *sipmsg_find_expires_header(const struct sipmsg *msg); const gchar *sipmsg_find_from_header(const struct sipmsg *msg); const gchar *sipmsg_find_to_header(const struct sipmsg *msg); /** * Parse addresses out of SIP message headers * * @param msg (in) SIP message * @param name (in) header name * * @return address or @c NULL if header not found or parse failure. * Must be @c g_free()'d after use. */ gchar *sipmsg_parse_contact_address(const struct sipmsg *msg); gchar *sipmsg_parse_from_address(const struct sipmsg *msg); gchar *sipmsg_parse_to_address(const struct sipmsg *msg); gchar *sipmsg_parse_address_from_header(const struct sipmsg *msg, const gchar *name); /** * Generate and append new tag to the "To:" header in the message * * @param msg (in) SIP message */ void sipmsg_update_to_header_tag(struct sipmsg *msg); ================================================ FILE: src/core/uuid.c ================================================ /** * @file uuid.c * * pidgin-sipe * * Copyright (C) 2008-2015 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "sipe-digest.h" #include "uuid.h" /* * This assumes that the structure is correctly packed on all target * platforms, i.e. sizeof(uuid_t) == 16 * * See also the test added to "configure". On Windows platform we know * that #pragma pack() exists and therefore can use it in the code. * */ #ifdef _WIN32 #pragma pack(push, 1) #endif typedef struct { guint32 time_low; guint16 time_mid; guint16 time_hi_and_version; guint8 clock_seq_hi_and_reserved; guint8 clock_seq_low; guint8 node[6]; } uuid_t; #ifdef _WIN32 #pragma pack(pop) #endif static void printUUID(uuid_t *uuid, char *string) { int i; size_t pos; sprintf(string, "%08x-%04x-%04x-%02x%02x-", uuid->time_low , uuid->time_mid, uuid->time_hi_and_version , uuid->clock_seq_hi_and_reserved , uuid->clock_seq_low ); pos = strlen(string); for(i=0;i<6;i++) { pos += sprintf(&string[pos], "%02x", uuid->node[i]); } } static void createUUIDfromHash(uuid_t *uuid, const unsigned char *hash) { memcpy(uuid, hash, sizeof(uuid_t)); uuid->time_hi_and_version &= GUINT16_TO_LE(0x0FFF); uuid->time_hi_and_version |= GUINT16_TO_LE(0x5000); uuid->clock_seq_hi_and_reserved &= 0x3F; uuid->clock_seq_hi_and_reserved |= 0x80; } char *generateUUIDfromEPID(const gchar *epid) { #define UUID_STRING_LENGTH 36 /* derived from "fcacfb03-8a73-46ef-91b1-e5ebeeaba4fe" */ uuid_t result = { 0xfcacfb03, 0x8a73, 0x46ef, 0x91, 0xb1, { 0xe5, 0xeb, 0xee, 0xab, 0xa4, 0xfe } }; gchar *buf; guchar digest[SIPE_DIGEST_SHA1_LENGTH]; guint digest_length = sizeof(uuid_t) + strlen(epid); guint buf_length = digest_length; result.time_low = GUINT32_FROM_LE(result.time_low); result.time_mid = GUINT16_FROM_LE(result.time_mid); result.time_hi_and_version = GUINT16_FROM_LE(result.time_hi_and_version); /* buffer must be able to hold at least the UUID string */ if (buf_length < UUID_STRING_LENGTH) buf_length = UUID_STRING_LENGTH; buf = g_malloc(buf_length + 1); memcpy(buf, &result, sizeof(uuid_t)); strcpy(buf + sizeof(uuid_t), epid); sipe_digest_sha1((guchar *)buf, digest_length, digest); createUUIDfromHash(&result, digest); result.time_low = GUINT32_TO_LE(result.time_low); result.time_mid = GUINT16_TO_LE(result.time_mid); result.time_hi_and_version = GUINT16_TO_LE(result.time_hi_and_version); printUUID(&result, buf); return(buf); } /** * Generates epid from user SIP URI, hostname and IP address. * Thus epid will be the same each start and * not needed to be persistent. * * Using MAC address proved to be poorly portable solution. * * Must be g_free()'d */ char *sipe_get_epid(const char *self_sip_uri, const char *hostname, const char *ip_address) { /* 6 last digits of hash */ #define SIPE_EPID_HASH_START 14 #define SIPE_EPID_HASH_END SIPE_DIGEST_SHA1_LENGTH #define SIPE_EPID_LENGTH (2 * (SIPE_EPID_HASH_END - SIPE_EPID_HASH_START + 1)) int i,j; char out[SIPE_EPID_LENGTH + 1]; char *buf = g_strdup_printf("%s:%s:%s", self_sip_uri, hostname, ip_address); guchar hash[SIPE_DIGEST_SHA1_LENGTH]; sipe_digest_sha1((guchar *) buf, strlen(buf), hash); for (i = SIPE_EPID_HASH_START, j = 0; i < SIPE_EPID_HASH_END; i++, j += 2) { g_sprintf(&out[j], "%02x", hash[i]); } out[SIPE_EPID_LENGTH] = 0; g_free(buf); return g_strdup(out); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/core/uuid.h ================================================ /** * @file uuid.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Interface dependencies: * * */ /* Must be g_free'd */ char *generateUUIDfromEPID(const gchar *epid); /* Must be g_free'd */ char *sipe_get_epid(const char *self_sip_uri, const char *hostname, const char *ip_address); ================================================ FILE: src/miranda/INSTALL ================================================ FIXME: miranda client code compile instructions... ================================================ FILE: src/miranda/miranda-buddy.c ================================================ /** * @file miranda-buddy.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "miranda-version.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_protomod.h" #include "m_database.h" #include "m_clist.h" #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" #define ADD_PROP(key,value) g_hash_table_insert(info_to_property_table, (gpointer)key, value) static GHashTable *info_to_property_table = NULL; static void init_property_hash(void) { info_to_property_table = g_hash_table_new(NULL, NULL); // ADD_PROP(SIPE_BUDDY_INFO_DISPLAY_NAME, ALIAS_PROP); ADD_PROP(SIPE_BUDDY_INFO_EMAIL , "e-mail"); ADD_PROP(SIPE_BUDDY_INFO_WORK_PHONE , "CompanyPhone"); // ADD_PROP(SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, PHONE_DISPLAY_PROP); // ADD_PROP(SIPE_BUDDY_INFO_SITE , SITE_PROP); ADD_PROP(SIPE_BUDDY_INFO_COMPANY , "Company"); ADD_PROP(SIPE_BUDDY_INFO_DEPARTMENT , "CompanyDepartment"); ADD_PROP(SIPE_BUDDY_INFO_JOB_TITLE , "CompanyPosition"); // ADD_PROP(SIPE_BUDDY_INFO_OFFICE , OFFICE_PROP); ADD_PROP(SIPE_BUDDY_INFO_STREET , "CompanyStreet"); ADD_PROP(SIPE_BUDDY_INFO_CITY , "CompanyCity"); ADD_PROP(SIPE_BUDDY_INFO_STATE , "CompanyState"); ADD_PROP(SIPE_BUDDY_INFO_ZIPCODE , "CompanyZIP"); ADD_PROP(SIPE_BUDDY_INFO_COUNTRY , "CompanyCountry"); /* Summary values: SetValue(hwndDlg,IDC_NICK,hContact,szProto,"Nick",0); SetValue(hwndDlg,IDC_FIRSTNAME,hContact,szProto,"FirstName",0); SetValue(hwndDlg,IDC_LASTNAME,hContact,szProto,"LastName",0); SetValue(hwndDlg,IDC_EMAIL,hContact,szProto,"e-mail",0); SetValue(hwndDlg,IDC_AGE,hContact,szProto,"Age",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_GENDER,hContact,szProto,"Gender",SVS_GENDER); SetValue(hwndDlg,IDC_DOBDAY,hContact,szProto,"BirthDay",0); SetValue(hwndDlg,IDC_DOBMONTH,hContact,szProto,"BirthMonth",SVS_MONTH); SetValue(hwndDlg,IDC_DOBYEAR,hContact,szProto,"BirthYear",0); Location values: SetValue(hwndDlg,IDC_STREET,hContact,szProto,"Street",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_CITY,hContact,szProto,"City",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_STATE,hContact,szProto,"State",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_ZIP,hContact,szProto,"ZIP",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_COUNTRY,hContact,szProto,"Country",SVS_COUNTRY); SetValue(hwndDlg,IDC_LANGUAGE1,hContact,szProto,"Language1",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_LANGUAGE2,hContact,szProto,"Language2",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_LANGUAGE3,hContact,szProto,"Language3",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_TIMEZONE,hContact,szProto,"Timezone",SVS_TIMEZONE); Work values: SetValue(hwndDlg,IDC_COMPANY,hContact,szProto,"Company",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_DEPARTMENT,hContact,szProto,"CompanyDepartment",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_POSITION,hContact,szProto,"CompanyPosition",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_STREET,hContact,szProto,"CompanyStreet",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_CITY,hContact,szProto,"CompanyCity",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_STATE,hContact,szProto,"CompanyState",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_ZIP,hContact,szProto,"CompanyZIP",SVS_ZEROISUNSPEC); SetValue(hwndDlg,IDC_COUNTRY,hContact,szProto,"CompanyCountry",SVS_COUNTRY); SetValue(hwndDlg,IDC_WEBPAGE,hContact,szProto,"CompanyHomepage",SVS_ZEROISUNSPEC); Background: SetValue(hwndDlg,IDC_WEBPAGE,hContact,szProto,"Homepage",SVS_ZEROISUNSPEC); Contact: if(DBGetContactSettingTString(hContact,szProto,"e-mail",&dbv)) mir_snprintf(idstr, SIZEOF(idstr), "e-mail%d", i ); mir_snprintf(idstr, SIZEOF(idstr), "Mye-mail%d",i); if(!DBGetContactSettingTString(hContact,szProto,"Phone",&dbv)) { if(!DBGetContactSettingTString(hContact,szProto,"Fax",&dbv)) { if(!DBGetContactSettingTString(hContact,szProto,"Cellular",&dbv)) { if(!DBGetContactSettingTString(hContact,szProto,"CompanyPhone",&dbv)) { if(!DBGetContactSettingTString(hContact,szProto,"CompanyFax",&dbv)) { mir_snprintf(idstr, SIZEOF(idstr), "MyPhone%d",i); */ } static const gchar * sipe_info_to_miranda_property(sipe_buddy_info_fields info) { if (!info_to_property_table) init_property_hash(); return (const char *)g_hash_table_lookup(info_to_property_table, (gconstpointer)info); } sipe_backend_buddy sipe_miranda_buddy_find(SIPPROTO *pr, const gchar *name, const gchar *group) { HANDLE hContact; hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); while (hContact) { gchar* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if (szProto != NULL && !lstrcmpA(szProto, pr->proto.m_szModuleName)) { DBVARIANT dbv; if ( !DBGetContactSettingString( hContact, pr->proto.m_szModuleName, SIP_UNIQUEID, &dbv )) { int tCompareResult = lstrcmpiA( dbv.pszVal, name ); DBFreeVariant( &dbv ); if ( !tCompareResult ) { if (!group) { SIPE_DEBUG_INFO("buddy_name <%s> group <%s> found <%08x>", name, group, hContact); return hContact; } if ( !DBGetContactSettingStringUtf(hContact, "CList", "Group", &dbv )) { int tCompareResult = lstrcmpiA( dbv.pszVal, group ); DBFreeVariant( &dbv ); if ( !tCompareResult ) { SIPE_DEBUG_INFO("buddy_name <%s> group <%s> found <%08x> in group", name, group, hContact); return hContact; } } else { SIPE_DEBUG_INFO("buddy_name <%s> group <%s> ERROR getting contact group", name, group); return NULL; } } } } hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); } SIPE_DEBUG_INFO("buddy_name <%s> group <%s> NOT FOUND", name, group); return NULL; } sipe_backend_buddy sipe_backend_buddy_find(struct sipe_core_public *sipe_public, const gchar *name, const gchar *group) { return sipe_miranda_buddy_find(sipe_public->backend_private, name, group); } GSList* sipe_miranda_buddy_find_all(SIPPROTO *pr, const gchar *buddy_name, const gchar *group_name) { GSList *res = NULL; HANDLE hContact; hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); while (hContact) { gchar* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if (szProto != NULL && !lstrcmpA(szProto, pr->proto.m_szModuleName)) { if (DBGetContactSettingByte(hContact, pr->proto.m_szModuleName, "ChatRoom", 0) == 0) { DBVARIANT dbv; if (!buddy_name) res = g_slist_append(res, hContact); else if ( !DBGetContactSettingString( hContact, pr->proto.m_szModuleName, SIP_UNIQUEID, &dbv )) { int tCompareResult = lstrcmpiA( dbv.pszVal, buddy_name ); DBFreeVariant( &dbv ); if ( !tCompareResult ) { if (!group_name) res = g_slist_append(res, hContact); else if ( !DBGetContactSettingStringUtf(hContact, "CList", "Group", &dbv )) { int tCompareResult = lstrcmpiA( dbv.pszVal, group_name ); DBFreeVariant( &dbv ); if ( !tCompareResult ) res = g_slist_append(res, hContact); } } } else { SIPE_DEBUG_INFO_NOFORMAT("Could not get SIP id from contact"); } } } hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); } SIPE_DEBUG_INFO("name <%s> group <%s> found <%d> buddies", buddy_name, group_name, g_slist_length(res)); return res; } GSList* sipe_backend_buddy_find_all(struct sipe_core_public *sipe_public, const gchar *buddy_name, const gchar *group_name) { return sipe_miranda_buddy_find_all(sipe_public->backend_private, buddy_name, group_name); } gchar* sipe_backend_buddy_get_name(struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { DBVARIANT dbv; HANDLE hContact = (HANDLE)who; gchar *alias; SIPPROTO *pr = sipe_public->backend_private; const gchar *module = pr->proto.m_szModuleName; if ( !DBGetContactSettingString( hContact, module, SIP_UNIQUEID, &dbv )) { alias = g_strdup(dbv.pszVal); DBFreeVariant( &dbv ); return alias; } return NULL; } gchar* sipe_backend_buddy_get_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { DBVARIANT dbv; HANDLE hContact = (HANDLE)who; gchar *alias; SIPPROTO *pr = sipe_public->backend_private; const gchar *module = pr->proto.m_szModuleName; if ( DBGetContactSettingString( hContact, module, "Nick", &dbv ) && DBGetContactSettingString( hContact, module, "Alias", &dbv ) && DBGetContactSettingString( hContact, module, SIP_UNIQUEID, &dbv )) return NULL; alias = g_strdup(dbv.pszVal); DBFreeVariant( &dbv ); return alias; } gchar* sipe_backend_buddy_get_server_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { DBVARIANT dbv; HANDLE hContact = (HANDLE)who; char *alias; SIPPROTO *pr = sipe_public->backend_private; const gchar *module = pr->proto.m_szModuleName; if ( !DBGetContactSettingString( hContact, module, "Alias", &dbv )) { alias = g_strdup(dbv.pszVal); DBFreeVariant( &dbv ); return alias; } return NULL; } gchar* sipe_backend_buddy_get_local_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { DBVARIANT dbv; HANDLE hContact = (HANDLE)who; char *alias; SIPPROTO *pr = sipe_public->backend_private; const gchar *module = pr->proto.m_szModuleName; if ( DBGetContactSettingString( hContact, module, "Nick", &dbv ) && DBGetContactSettingString( hContact, module, SIP_UNIQUEID, &dbv )) return NULL; alias = g_strdup(dbv.pszVal); DBFreeVariant( &dbv ); return alias; } gchar* sipe_backend_buddy_get_group_name(struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { DBVARIANT dbv; HANDLE hContact = (HANDLE)who; gchar *alias; SIPPROTO *pr = sipe_public->backend_private; const gchar *module = pr->proto.m_szModuleName; if ( !DBGetContactSettingString( hContact, "CList", "Group", &dbv )) { alias = g_strdup(dbv.pszVal); DBFreeVariant( &dbv ); return alias; } return NULL; } guint sipe_backend_buddy_get_status(struct sipe_core_public *sipe_public, const gchar *uri) { SIPPROTO *pr = sipe_public->backend_private; sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public, uri, NULL); WORD rv = SIPE_ACTIVITY_UNSET; sipe_miranda_getWord(pr, buddy, "Status", &rv); return MirandaStatusToSipe(rv); } void sipe_backend_buddy_set_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who, const gchar *alias) { SIPPROTO *pr = sipe_public->backend_private; HANDLE hContact = (HANDLE)who; SIPE_DEBUG_INFO("Set alias of contact <%08x> to <%s>", who, alias); sipe_miranda_setContactStringUtf( pr, hContact, "Nick", alias ); } void sipe_backend_buddy_set_server_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who, const gchar *alias) { HANDLE hContact = (HANDLE)who; SIPPROTO *pr = sipe_public->backend_private; SIPE_DEBUG_INFO("Set alias of contact <%08x> to <%s>", who, alias); sipe_miranda_setContactStringUtf( pr, hContact, "Alias", alias ); } gchar* sipe_backend_buddy_get_string(struct sipe_core_public *sipe_public, sipe_backend_buddy buddy, const sipe_buddy_info_fields key) { SIPPROTO *pr = sipe_public->backend_private; const gchar *module = pr->proto.m_szModuleName; const gchar *prop_name = sipe_info_to_miranda_property(key); char *tmp; char *prop_str; if (!prop_name) return NULL; tmp = sipe_miranda_getContactString(pr, buddy, prop_name); prop_str = g_strdup(tmp); mir_free(tmp); return prop_str; } void sipe_backend_buddy_set_string(struct sipe_core_public *sipe_public, sipe_backend_buddy buddy, const sipe_buddy_info_fields key, const gchar *val) { SIPPROTO *pr = sipe_public->backend_private; const gchar *module = pr->proto.m_szModuleName; const gchar *prop_name = sipe_info_to_miranda_property(key); SIPE_DEBUG_INFO("buddy <%08x> key <%d = %s> val <%s>", buddy, key, prop_name, val); if (!prop_name) return; sipe_miranda_setContactString(pr, buddy, prop_name, val); } void sipe_backend_buddy_refresh_properties(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *uri, SIPE_UNUSED_PARAMETER last_active) { /* nothing to do here: already taken care of by Miranda */ } void sipe_backend_buddy_list_processing_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { } void sipe_backend_buddy_list_processing_finish(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { } sipe_backend_buddy sipe_backend_buddy_add(struct sipe_core_public *sipe_public, const gchar *name, const gchar *alias, const gchar *groupname) { SIPPROTO *pr = sipe_public->backend_private; HANDLE hContact; SIPE_DEBUG_INFO("Adding miranda contact for buddy <%s> alias <%s> in <%s>", name, alias, groupname); hContact = ( HANDLE )CallService( MS_DB_CONTACT_ADD, 0, 0 ); CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hContact,( LPARAM )pr->proto.m_szModuleName ); sipe_miranda_setContactString( pr, hContact, SIP_UNIQUEID, name ); // name if (alias) sipe_miranda_setContactStringUtf( pr, hContact, "Nick", alias ); DBWriteContactSettingString( hContact, "CList", "Group", groupname ); sipe_miranda_setContactString( pr, hContact, "Group", groupname ); return (sipe_backend_buddy)hContact; } void sipe_backend_buddy_remove(struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { CallService( MS_DB_CONTACT_DELETE, (WPARAM)who, 0 ); } void sipe_backend_buddy_request_authorization(struct sipe_core_public *sipe_public, const gchar *who, const gchar *alias, gboolean on_list, sipe_backend_buddy_request_authorization_cb auth_cb, sipe_backend_buddy_request_authorization_cb deny_cb, void *data) { SIPPROTO *pr = sipe_public->backend_private; CCSDATA ccs; PROTORECVEVENT pre = {0}; HANDLE hContact; BYTE *pblob; hContact = sipe_backend_buddy_find( sipe_public, who, NULL ); if (!hContact) { SIPE_DEBUG_INFO("Adding miranda contact for incoming talker <%s>", who); hContact = ( HANDLE )CallService( MS_DB_CONTACT_ADD, 0, 0 ); CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hContact,( LPARAM )pr->proto.m_szModuleName ); DBWriteContactSettingByte( hContact, "CList", "NotOnList", 1 ); sipe_miranda_setContactString( pr, hContact, SIP_UNIQUEID, who ); // name } ccs.szProtoService = PSR_AUTH; ccs.hContact = hContact; ccs.wParam = 0; ccs.lParam = (LPARAM) ⪯ pre.flags = PREF_UTF; pre.timestamp = time(NULL); pre.lParam = sizeof(DWORD)+sizeof(HANDLE)+strlen(who)+strlen(alias)+5; pre.szMessage = malloc(pre.lParam); pblob = pre.szMessage; *(DWORD*)pblob = 0; /* UIN */ pblob += sizeof(DWORD); *(HANDLE*)pblob = hContact; /* contact */ pblob += sizeof(HANDLE); strcpy(pblob, who); /* nick */ pblob += strlen(pblob) + 1; strcpy(pblob, alias); /* first name */ pblob += strlen(pblob) + 1; strcpy(pblob, ""); /* last name */ pblob += strlen(pblob) + 1; strcpy(pblob, ""); /* email */ pblob += strlen(pblob) + 1; strcpy(pblob, ""); /* msg */ pblob += strlen(pblob) + 1; CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); _NIF(); auth_cb(data); } void sipe_backend_buddy_request_add(struct sipe_core_public *sipe_public, const gchar *who, const gchar *alias) { _NIF(); } gboolean sipe_backend_buddy_is_blocked(struct sipe_core_public *sipe_public, const gchar *who) { _NIF(); return FALSE; } void sipe_backend_buddy_set_blocked_status(struct sipe_core_public *sipe_public, const gchar *who, gboolean blocked) { _NIF(); } void sipe_backend_buddy_set_status(struct sipe_core_public *sipe_public, const gchar *who, guint activity) { SIPPROTO *pr = sipe_public->backend_private; GSList *contacts = sipe_backend_buddy_find_all(sipe_public, who, NULL); CONTACTS_FOREACH(contacts) sipe_miranda_setWord(pr, hContact, "Status", SipeStatusToMiranda(activity)); CONTACTS_FOREACH_END; } gboolean sipe_backend_buddy_group_add(struct sipe_core_public *sipe_public, const gchar *group_name) { TCHAR *mir_group_name = mir_a2t(group_name); HANDLE hGroup = (HANDLE)CallService(MS_CLIST_GROUPCREATE, 0, (LPARAM)mir_group_name); mir_free(mir_group_name); return (hGroup?TRUE:FALSE); } gboolean sipe_backend_buddy_group_rename(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *old_name, SIPE_UNUSED_PARAMETER const gchar *new_name) { /* @TODO */ return(FALSE); } void sipe_backend_buddy_group_remove(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *group_name) { /* @TODO */ } struct sipe_backend_buddy_info *sipe_backend_buddy_info_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { return((struct sipe_backend_buddy_info *)g_hash_table_new_full(NULL,NULL,NULL,g_free)); } void sipe_backend_buddy_info_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_info *info, sipe_buddy_info_fields description, const gchar *value) { g_hash_table_insert((GHashTable*)info, (gpointer)description, g_strdup(value)); } void sipe_backend_buddy_info_break(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_info *info) { /* Nothin to do */ } static void set_if_defined(SIPPROTO *pr, GHashTable *store, HANDLE hContact, sipe_buddy_info_fields field, char *label) { char *value = (char *)g_hash_table_lookup(store, (gpointer)field); if (value) sipe_miranda_setContactStringUtf(pr, hContact, label, value); } void sipe_backend_buddy_info_finalize(struct sipe_core_public *sipe_public, struct sipe_backend_buddy_info *info, const gchar *uri) { SIPPROTO *pr = sipe_public->backend_private; HANDLE hContact = sipe_miranda_buddy_find(pr, uri, NULL); /* (HANDLE) data; */ DBVARIANT dbv; GHashTable *results = (GHashTable*)info; GHashTableIter iter; const char *id, *value; g_hash_table_iter_init( &iter, results); while (g_hash_table_iter_next (&iter, (gpointer *)&id, (gpointer *)&value)) { SIPE_DEBUG_INFO("miranda_sipe_get_info_cb: user info field <%d> = <%s>", id, value ? value : "(none)"); } set_if_defined(pr, results, hContact, SIPE_BUDDY_INFO_EMAIL, "e-mail"); set_if_defined(pr, results, hContact, SIPE_BUDDY_INFO_CITY, "City"); set_if_defined(pr, results, hContact, SIPE_BUDDY_INFO_STATE, "State"); set_if_defined(pr, results, hContact, SIPE_BUDDY_INFO_COUNTRY, "Country"); set_if_defined(pr, results, hContact, SIPE_BUDDY_INFO_COMPANY, "Company"); set_if_defined(pr, results, hContact, SIPE_BUDDY_INFO_JOB_TITLE, "CompanyPosition"); set_if_defined(pr, results, hContact, SIPE_BUDDY_INFO_WORK_PHONE, "CompanyPhone"); set_if_defined(pr, results, hContact, SIPE_BUDDY_INFO_STREET, "CompanyStreet"); set_if_defined(pr, results, hContact, SIPE_BUDDY_INFO_ZIPCODE, "CompanyZIP"); set_if_defined(pr, results, hContact, SIPE_BUDDY_INFO_DEPARTMENT, "CompanyDepartment"); if ( !DBGetContactSettingString( hContact, pr->proto.m_szModuleName, SIP_UNIQUEID, &dbv )) { GString *content = g_string_new(NULL); WORD wstatus; gchar *status; /* GSList *info; */ gboolean is_online; sipe_miranda_getWord(pr, hContact, "Status", &wstatus); status = (gchar*)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)wstatus, (LPARAM)GSMDF_PREFIXONLINE); is_online = g_str_has_prefix(status, "Online: ") || !g_ascii_strcasecmp(status, "Online"); /* info = sipe_core_buddy_info(sipe_public, dbv.pszVal, g_str_has_prefix(status, "Online: ") ? status+8 : status, is_online); while (info) { struct sipe_buddy_info *sbi = info->data; g_string_append_printf(content, "%s: %s\r\n", sbi->label, sbi->text); g_free(sbi->text); g_free(sbi); info = g_slist_delete_link(info, info); } sipe_miranda_setContactStringUtf(pr, hContact, "About", content->str); */ g_string_free(content, TRUE); } sipe_miranda_SendBroadcast(pr, hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) 0); } struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { return(NULL); } struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_menu *menu, const gchar *label, enum sipe_buddy_menu_type type, gpointer parameter) { _NIF(); return(NULL); } struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_separator(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_menu *menu, const gchar *label) { _NIF(); return(NULL); } struct sipe_backend_buddy_menu *sipe_backend_buddy_sub_menu_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_menu *menu, const gchar *label, struct sipe_backend_buddy_menu *sub) { _NIF(); return(NULL); } void sipe_backend_buddy_tooltip_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_tooltip *tooltip, const gchar *description, const gchar *value) { _NIF(); } int sipe_miranda_buddy_delete(SIPPROTO *pr, WPARAM wParam, LPARAM lParam) { DBVARIANT dbv; HANDLE hContact = (HANDLE)wParam; char *name; char *groupname; SIPE_DEBUG_INFO("Deleting contact <%08x>", hContact); if ( DBGetContactSettingString( hContact, pr->proto.m_szModuleName, SIP_UNIQUEID, &dbv )) return 0; name = g_strdup(dbv.pszVal); DBFreeVariant( &dbv ); if ( DBGetContactSettingString( hContact, "CList", "Group", &dbv )) { g_free(name); return 0; } groupname = g_strdup(dbv.pszVal); DBFreeVariant( &dbv ); LOCK; sipe_core_buddy_remove(pr->sip, name, groupname); UNLOCK; return 0; } unsigned GetAwayMsgThread(SIPPROTO *pr, HANDLE hContact) { const gchar *status; gchar *name = sipe_miranda_getContactString(pr, hContact, SIP_UNIQUEID); gchar *tmp = NULL; if (!name) { SIPE_DEBUG_INFO("Could not find name for contact <%08x>", hContact); sipe_miranda_SendProtoAck(pr, hContact, 1, ACKRESULT_FAILED, ACKTYPE_AWAYMSG, NULL); return 0; } LOCK; status = sipe_core_buddy_status(pr->sip, name, SIPE_ACTIVITY_BUSYIDLE, "dummy test string"); UNLOCK; if (status) tmp = sipe_miranda_eliminate_html(status, strlen(status)); sipe_miranda_SendProtoAck(pr, hContact, 1, ACKRESULT_SUCCESS, ACKTYPE_AWAYMSG, tmp); mir_free(tmp); mir_free(name); return 0; } HANDLE sipe_miranda_GetAwayMsg( SIPPROTO *pr, HANDLE hContact ) { CloseHandle((HANDLE)mir_forkthreadowner(&GetAwayMsgThread, pr, hContact, NULL )); return (HANDLE)1; } int sipe_miranda_GetInfo( SIPPROTO *pr, HANDLE hContact, int infoType ) { DBVARIANT dbv; SIPE_DEBUG_INFO("GetInfo: infotype <%x>", infoType); if (!pr->sip) return 0; if ( !DBGetContactSettingString( hContact, pr->proto.m_szModuleName, SIP_UNIQUEID, &dbv )) { LOCK; sipe_core_buddy_get_info(pr->sip, dbv.pszVal); UNLOCK; DBFreeVariant( &dbv ); } return 0; } gboolean sipe_backend_uses_photo(void) { return FALSE; } void sipe_backend_buddy_set_photo(struct sipe_core_public *sipe_public, const gchar *who, gpointer photo_data, gsize data_len, const gchar *photo_hash) { g_free(photo_data); } const gchar *sipe_backend_buddy_get_photo_hash(struct sipe_core_public *sipe_public, const gchar *who) { const gchar *result = NULL; return result; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-chat.c ================================================ /** * @file miranda-chat.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_chat.h" #include "m_utils.h" #include "m_system.h" #include "miranda-private.h" void sipe_backend_chat_session_destroy(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *session) { /* Nothing to do here */ } void sipe_backend_chat_add(struct sipe_backend_chat_session *backend_session, const gchar *uri, gboolean is_new) { SIPPROTO *pr = backend_session->pr; struct sipe_core_public *sipe_public = pr->sip; gchar *self = sipe_miranda_uri_self(pr); GCDEST gcd = {0}; GCEVENT gce = {0}; int retval; HANDLE hContact = sipe_backend_buddy_find( sipe_public, uri, NULL ); gchar *nick = sipe_miranda_getContactString(pr, hContact, "Nick"); SIPE_DEBUG_INFO("sipe_backend_chat_add: Adding user <%s> to chat <%s>", uri, backend_session->conv); gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = backend_session->conv; gcd.iType = GC_EVENT_JOIN; gce.cbSize = sizeof(gce); gce.pDest = &gcd; gce.pszNick = nick; gce.pszUID = uri; gce.pszStatus = "Normal"; gce.bIsMe = !strcmp(self, uri); g_free(self); retval = CallService( MS_GC_EVENT, 0, (LPARAM)&gce ); if (retval) { SIPE_DEBUG_WARNING("sipe_backend_chat_add: Failed to add user to chat: <%d>", retval); } mir_free(nick); } void sipe_backend_chat_close(struct sipe_backend_chat_session *backend_session) { SIPPROTO *pr; GCEVENT gce = {0}; GCDEST gcd = {0}; struct sipe_chat_session *session; if (!backend_session) { SIPE_DEBUG_WARNING_NOFORMAT("Attempted to close NULL backend_session"); return; } pr = backend_session->pr; gce.cbSize = sizeof(gce); gce.pDest = &gcd; gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = backend_session->conv; gcd.iType = GC_EVENT_CONTROL; session = (struct sipe_chat_session*)CallServiceSync( MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gce ); } struct sipe_backend_chat_session *sipe_backend_chat_create(struct sipe_core_public *sipe_public, struct sipe_chat_session *session, const gchar *title, const gchar *nick) { SIPPROTO *pr = sipe_public->backend_private; GCSESSION gs; GCDEST gcd = {0}; GCEVENT gce = {0}; gchar *id = g_strdup(title); /* FIXME: Generate ID */ struct sipe_backend_chat_session *conv = g_new0(struct sipe_backend_chat_session,1); gs.cbSize = sizeof(gs); gs.iType = GCW_CHATROOM; gs.pszModule = pr->proto.m_szModuleName; gs.pszName = title; gs.pszID = id; gs.pszStatusbarText = NULL; gs.dwFlags = 0; gs.dwItemData = (DWORD)session; if (CallServiceSync( MS_GC_NEWSESSION, 0, (LPARAM)&gs )) { SIPE_DEBUG_ERROR("Failed to create chat session <%d> <%s>", id, title); } gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = id; gce.cbSize = sizeof(gce); gce.pDest = &gcd; gcd.iType = GC_EVENT_ADDGROUP; gce.pszStatus = "Normal"; if (CallService( MS_GC_EVENT, 0, (LPARAM)&gce )) { SIPE_DEBUG_WARNING_NOFORMAT("Failed to add normal status to chat session"); } gce.pszStatus = "Presenter"; if (CallService( MS_GC_EVENT, 0, (LPARAM)&gce )) { SIPE_DEBUG_WARNING_NOFORMAT("Failed to add presenter status to chat session"); } gcd.iType = GC_EVENT_CONTROL; if (CallServiceSync( MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gce )) { SIPE_DEBUG_WARNING_NOFORMAT("Failed to initdone chat session"); } if (CallServiceSync( MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gce )) { SIPE_DEBUG_ERROR_NOFORMAT("Failed to set chat session online"); } conv->conv = id; conv->pr = pr; return conv; } gboolean sipe_backend_chat_find(struct sipe_backend_chat_session *backend_session, const gchar *uri) { SIPPROTO *pr = backend_session->pr; GC_INFO gci = {0}; gchar *context; const gchar *user; gci.Flags = BYID | USERS; gci.pszID = mir_a2t(backend_session->conv); gci.pszModule = pr->proto.m_szModuleName; if(CallServiceSync( MS_GC_GETINFO, 0, (LPARAM)&gci )) { SIPE_DEBUG_ERROR_NOFORMAT("Failed to get chat user list"); return FALSE; } if (!gci.pszUsers) return FALSE; user = strtok_s(gci.pszUsers, " ", &context); while (user) { SIPE_DEBUG_INFO("sipe_backend_chat_find: Found user <%s>", user); if (!strcmp(uri, user)) { mir_free(gci.pszUsers); return TRUE; } user = strtok_s(NULL, " ", &context); } mir_free(gci.pszUsers); return FALSE; } gboolean sipe_backend_chat_is_operator(struct sipe_backend_chat_session *backend_session, const gchar *uri) { _NIF(); return TRUE; } void sipe_backend_chat_message(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *from, time_t when, const gchar *html) { SIPPROTO *pr = backend_session->pr; gchar *self = sipe_miranda_uri_self(pr); gchar *msg; GCDEST gcd = {0}; GCEVENT gce = {0}; HANDLE hContact = sipe_backend_buddy_find( sipe_public, from, NULL ); gchar *nick = sipe_miranda_getContactString(pr, hContact, "Nick"); gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = backend_session->conv; gcd.iType = GC_EVENT_MESSAGE; msg = sipe_miranda_eliminate_html(html, strlen(html)); gce.cbSize = sizeof(gce); gce.pDest = &gcd; gce.pszNick = nick; gce.pszUID = from; gce.pszText = msg; gce.bIsMe = !strcmp(self, from); // gce.time = mtime; // FIXME: Generate timestamp g_free(self); CallService( MS_GC_EVENT, 0, (LPARAM)&gce ); mir_free(nick); mir_free(msg); } void sipe_backend_chat_operator(struct sipe_backend_chat_session *backend_session, const gchar *uri) { SIPPROTO *pr; GCEVENT gce = {0}; GCDEST gcd = {0}; HANDLE hContact; gchar *nick; struct sipe_core_public *sipe_public; if (!backend_session) { SIPE_DEBUG_WARNING_NOFORMAT("Attempted to set operator on NULL backend_session"); return; } pr = backend_session->pr; sipe_public = pr->sip; hContact = sipe_backend_buddy_find( sipe_public, uri, NULL ); nick = sipe_miranda_getContactString(pr, hContact, "Nick"); gce.cbSize = sizeof(gce); gce.pDest = &gcd; gce.pszNick = nick; gce.pszUID = uri; gce.pszText = "Presenter"; gce.pszStatus = "Presenter"; gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = backend_session->conv; gcd.iType = GC_EVENT_ADDSTATUS; if (CallServiceSync( MS_GC_EVENT, 0, (LPARAM)&gce )) { SIPE_DEBUG_WARNING_NOFORMAT("Failed to set presenter status"); } mir_free(nick); } void sipe_backend_chat_rejoin(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *nick, const gchar *title) { _NIF(); } /** * Connection re-established: tell core what chats need to be rejoined */ void sipe_backend_chat_rejoin_all(struct sipe_core_public *sipe_public) { _NIF(); } void sipe_backend_chat_remove(struct sipe_backend_chat_session *backend_session, const gchar *uri) { SIPPROTO *pr = backend_session->pr; struct sipe_core_public *sipe_public = pr->sip; gchar *self = sipe_miranda_uri_self(pr); GCDEST gcd = {0}; GCEVENT gce = {0}; HANDLE hContact = sipe_backend_buddy_find( sipe_public, uri, NULL ); gchar *nick = sipe_miranda_getContactString(pr, hContact, "Nick"); SIPE_DEBUG_INFO("sipe_backend_chat_remove: Removing user <%s> from chat <%s>", uri, backend_session->conv); gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = backend_session->conv; gcd.iType = GC_EVENT_PART; gce.cbSize = sizeof(gce); gce.pDest = &gcd; gce.pszNick = nick; gce.pszUID = uri; gce.pszStatus = 0; gce.bIsMe = !strcmp(self, uri); g_free(self); CallService( MS_GC_EVENT, 0, (LPARAM)&gce ); mir_free(nick); } void sipe_backend_chat_show(struct sipe_backend_chat_session *backend_session) { _NIF(); } void sipe_backend_chat_topic(struct sipe_backend_chat_session *backend_session, const gchar *topic) { SIPPROTO *pr = backend_session->pr; GCDEST gcd = {0}; GCEVENT gce = {0}; SIPE_DEBUG_INFO("sipe_backend_chat_topic: conv <%s> topic <%s>", backend_session->conv, topic); gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = backend_session->conv; gcd.iType = GC_EVENT_TOPIC; gce.cbSize = sizeof(gce); gce.pDest = &gcd; gce.pszNick = NULL; gce.pszUID = NULL; gce.pszText = topic; CallService( MS_GC_EVENT, 0, (LPARAM)&gce ); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-connection.c ================================================ /** * @file miranda-connection.c * * pidgin-sipe * * Copyright (C) 2010-11 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "sipe-backend.h" #include "sipe-core.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "miranda-private.h" void sipe_miranda_connection_destroy(SIPPROTO *pr) { int oldStatus; SIPE_DEBUG_INFO("valid <%d> state <%d>", pr->valid, pr->state); if (!pr->valid) return; set_buddies_offline(pr); sipe_miranda_close(pr); pr->state = SIPE_MIRANDA_DISCONNECTED; pr->valid = FALSE; oldStatus = pr->proto.m_iStatus; pr->proto.m_iDesiredStatus = ID_STATUS_OFFLINE; pr->proto.m_iStatus = pr->proto.m_iDesiredStatus; sipe_miranda_SendBroadcast(pr, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, pr->proto.m_iStatus); } static void disconnect_cb(SIPPROTO *pr) { SIPE_DEBUG_INFO_NOFORMAT(""); if (!pr->valid) return; if (pr->state == SIPE_MIRANDA_DISCONNECTED) return; pr->disconnecting = TRUE; sipe_miranda_connection_destroy(pr); pr->valid = FALSE; pr->disconnecting = FALSE; pr->disconnect_timeout = NULL; } void sipe_miranda_connection_error_reason(SIPPROTO *pr, sipe_connection_error error, const gchar *msg) { if (!pr->disconnect_timeout) { SIPE_DEBUG_INFO("valid <%d> state <%d> error <%d> message <%s>", pr->valid, pr->state, error, msg); pr->disconnect_timeout = sipe_miranda_schedule_mseconds(disconnect_cb, 1000, pr); } } void sipe_backend_connection_completed(struct sipe_core_public *sipe_public) { SIPPROTO *pr = sipe_public->backend_private; int oldStatus = pr->proto.m_iStatus; pr->state = SIPE_MIRANDA_CONNECTED; pr->proto.m_iStatus = pr->proto.m_iDesiredStatus; sipe_miranda_SendBroadcast(pr, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, pr->proto.m_iStatus); } void sipe_backend_connection_error(struct sipe_core_public *sipe_public, sipe_connection_error error, const gchar *msg) { SIPE_DEBUG_INFO("reason <%d> message <%s>", error, msg); sipe_miranda_connection_error_reason(sipe_public->backend_private, error, msg); } gboolean sipe_backend_connection_is_disconnecting(struct sipe_core_public *sipe_public) { SIPPROTO *pr = sipe_public->backend_private; return (pr->disconnecting); } gboolean sipe_backend_connection_is_valid(struct sipe_core_public *sipe_public) { SIPPROTO *pr = sipe_public->backend_private; return (pr->state == SIPE_MIRANDA_CONNECTED); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-debug.c ================================================ /** * @file miranda-debug.c * * pidgin-sipe * * Copyright (C) 2010-2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_netlib.h" #include "sipe-backend.h" #include "miranda-private.h" extern HANDLE sipe_miranda_incoming_netlibuser; extern CRITICAL_SECTION sipe_miranda_debug_CriticalSection; void sipe_backend_debug_literal(sipe_debug_level level, const gchar *message) { if (sipe_miranda_incoming_netlibuser) { gchar *msg = g_strdup_printf("[%5d] %s", GetCurrentThreadId(), message); EnterCriticalSection(&sipe_miranda_debug_CriticalSection); CallService(MS_NETLIB_LOG, (WPARAM)sipe_miranda_incoming_netlibuser, (LPARAM)msg); LeaveCriticalSection(&sipe_miranda_debug_CriticalSection); g_free(msg); } } void sipe_backend_debug(sipe_debug_level level, const gchar *format, ...) G_GNUC_PRINTF(2, 3) { va_list ap; va_start(ap,format); if ((level < SIPE_DEBUG_LEVEL_LOWEST) || sipe_backend_debug_enabled()) { gchar *msg = g_strdup_vprintf(format, ap); sipe_backend_debug_literal(level, msg); g_free(msg); } va_end(ap); } gboolean sipe_backend_debug_enabled(void) { return TRUE; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-dnsquery.c ================================================ /** * @file miranda-dnsquery.c * * pidgin-sipe * * Copyright (C) 2010-12 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_utils.h" #include "sipe-backend.h" #include "miranda-private.h" typedef DNS_STATUS (WINAPI *DNSQUERYA)(IN PCSTR pszName, IN WORD wType, IN DWORD Options, IN PIP4_ARRAY aipServers OPTIONAL, IN OUT PDNS_RECORD *ppQueryResults OPTIONAL, IN OUT PVOID *pReserved OPTIONAL); typedef void (WINAPI *DNSFREELIST)(IN OUT PDNS_RECORD pRecordList, IN DNS_FREE_TYPE FreeType); typedef struct srv_reply_t { char *host; int port; } srv_reply; static srv_reply* srv_lookup(WORD wType, const gchar* service, const gchar* protocol, const gchar* domain ) { srv_reply *res = NULL; HINSTANCE hDnsapi = LoadLibraryA( "dnsapi.dll" ); DNSQUERYA pDnsQuery; DNSFREELIST pDnsRecordListFree; gchar temp[256]; DNS_RECORD *results = NULL; DNS_STATUS status; if ( hDnsapi == NULL ) return res; pDnsQuery = (DNSQUERYA)GetProcAddress(hDnsapi, "DnsQuery_A"); pDnsRecordListFree = (DNSFREELIST)GetProcAddress(hDnsapi, "DnsRecordListFree"); if ( pDnsQuery == NULL ) { //dnsapi.dll is not the needed dnsapi ;) FreeLibrary( hDnsapi ); return res; } mir_snprintf( temp, SIZEOF(temp), "_%s._%s.%s", service, protocol, domain ); status = pDnsQuery(temp, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &results, NULL); if (FAILED(status)||!results || results[0].Data.Srv.pNameTarget == 0||results[0].wType != DNS_TYPE_SRV) { FreeLibrary(hDnsapi); return res; } res = g_new0(srv_reply,1); res->host = g_strdup((const gchar*)results[0].Data.Srv.pNameTarget); res->port = (int)results[0].Data.Srv.wPort; pDnsRecordListFree(results, DnsFreeRecordList); FreeLibrary(hDnsapi); return res; } struct sipe_dns_query *sipe_backend_dns_query_a(struct sipe_core_public *sipe_public, const gchar *hostname, guint port, sipe_dns_resolved_cb callback, gpointer data) { srv_reply* sr = srv_lookup( DNS_TYPE_A, "protocol", "transport", "domain" ); SIPE_DEBUG_INFO("Type A lookup for host <%s> port <%d>", hostname, port); if (sr) { callback( data, sr->host, sr->port); g_free(sr->host); g_free(sr); } else { callback( data, NULL, 0); } return NULL; } struct sipe_dns_query *sipe_backend_dns_query_srv(struct sipe_core_public *sipe_public, const gchar *protocol, const gchar *transport, const gchar *domain, sipe_dns_resolved_cb callback, gpointer data) { srv_reply* sr = srv_lookup( DNS_TYPE_SRV, protocol, transport, domain ); SIPE_DEBUG_INFO("Type SRV lookup for proto <%s> transport <%s> domain <%s>", protocol, transport, domain); if (sr) { callback( data, sr->host, sr->port); g_free(sr->host); g_free(sr); } else { callback( data, NULL, 0); } return NULL; } void sipe_backend_dns_query_cancel(struct sipe_dns_query *query) { _NIF(); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-ft.c ================================================ /** * @file miranda-ft.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * Copyright (C) 2010 Jakub Adam * Copyright (C) 2010 Tomáš Hrabčík * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "miranda-version.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_database.h" #include "m_protomod.h" #include "m_netlib.h" #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" #define FT_SIPE_DEBUG_INFO(fmt, ...) sipe_backend_debug(SIPE_DEBUG_LEVEL_INFO, "[FT:%08x] %s: " fmt, ft, __func__, __VA_ARGS__) #define FT_SIPE_DEBUG_INFO_NOFORMAT(msg) sipe_backend_debug(SIPE_DEBUG_LEVEL_INFO, "[FT:%08x] %s: %s", ft, __func__, msg) #define FT_INITIAL_BUFFER_SIZE 4096 #define FT_MAX_BUFFER_SIZE 65535 typedef enum { SIPE_MIRANDA_XFER_STATUS_UNKNOWN = 0, /**< Unknown, the xfer may be null. */ // SIPE_MIRANDA_XFER_STATUS_NOT_STARTED, /**< It hasn't started yet. */ // SIPE_MIRANDA_XFER_STATUS_ACCEPTED, /**< Receive accepted, but destination file not selected yet */ SIPE_MIRANDA_XFER_STATUS_STARTED, /**< purple_xfer_start has been called. */ SIPE_MIRANDA_XFER_STATUS_DONE, /**< The xfer completed successfully. */ SIPE_MIRANDA_XFER_STATUS_CANCEL_LOCAL, /**< The xfer was cancelled by us. */ SIPE_MIRANDA_XFER_STATUS_CANCEL_REMOTE /**< The xfer was cancelled by the other end, or we couldn't connect. */ } sipe_miranda_xfer_status; struct sipe_backend_file_transfer { gboolean incoming; HANDLE fd; SIPPROTO *pr; struct sipe_file_transfer *ft; gsize file_size; size_t bytes_sent; size_t bytes_remaining; size_t current_buffer_size; struct sipe_miranda_sel_entry *watcher; gchar *filename; gchar *local_filename; FILE *dest_fp; HANDLE hContact; time_t start_time; time_t end_time; GByteArray *buffer; PROTOFILETRANSFERSTATUS st; sipe_miranda_xfer_status status; }; static void update_progress(struct sipe_backend_file_transfer *xfer) { #if 0 ZeroMemory(pfts, sizeof(PROTOFILETRANSFERSTATUS)); pfts->flags = PFTS_UTF | (ft->sending ? PFTS_SENDING : PFTS_RECEIVING); /* Standard FT is Ansi only */ if (ft->sending) pfts->pszFiles = ft->pszFiles; else pfts->pszFiles = NULL; /* FIXME */ pfts->currentFileTime = ft->dwThisFileDate; #endif xfer->st.flags = (xfer->incoming ? PFTS_RECEIVING : PFTS_SENDING); xfer->st.szWorkingDir = "none"; xfer->st.szCurrentFile = xfer->filename; xfer->st.totalFiles = 1; xfer->st.totalBytes = xfer->file_size; xfer->st.totalProgress = xfer->bytes_sent; xfer->st.currentFileNumber = 1; xfer->st.currentFileSize = xfer->file_size; xfer->st.currentFileProgress = xfer->bytes_sent; ProtoBroadcastAck(xfer->pr->proto.m_szModuleName, xfer->hContact, ACKTYPE_FILE, ACKRESULT_DATA, (HANDLE)xfer, (LPARAM)&xfer->st); } static void increase_buffer_size(struct sipe_backend_file_transfer *xfer) { xfer->current_buffer_size = MIN(xfer->current_buffer_size * 1.5, FT_MAX_BUFFER_SIZE); } void sipe_backend_ft_error(struct sipe_file_transfer *ft, const gchar *errmsg) { gchar *msg; FT_SIPE_DEBUG_INFO("file transfer error: <%s>", errmsg); if (ft->backend_private->incoming) { msg = g_strdup_printf("Incoming file transfer failed"); } else { msg = g_strdup_printf("Outgoing file transfer failed"); } sipe_miranda_AddEvent(ft->backend_private->pr, ft->backend_private->hContact, SIPE_EVENTTYPE_ERROR_NOTIFY, time(NULL), DBEF_UTF, strlen(msg), (PBYTE)msg); g_free(msg); } const gchar *sipe_backend_ft_get_error(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft) { _NIF(); return strerror(errno); /* FIXME: Only valid for the file side i think */ } static void free_xfer_struct(struct sipe_backend_file_transfer *xfer) { struct sipe_file_transfer *ft = xfer->ft; if (ft) { if (xfer->watcher) { sipe_miranda_input_remove(xfer->watcher); xfer->watcher = 0; } if (ft->deallocate) { ft->deallocate(ft); } xfer->ft = NULL; } } static void cancel_remote(struct sipe_backend_file_transfer *xfer) { struct sipe_file_transfer *ft = xfer->ft; gchar *msg; FT_SIPE_DEBUG_INFO_NOFORMAT(""); if (!xfer) return; xfer->status = SIPE_MIRANDA_XFER_STATUS_CANCEL_REMOTE; xfer->end_time = time(NULL); msg = g_strdup_printf("File transfer cancelled by peer"); sipe_miranda_AddEvent(ft->backend_private->pr, ft->backend_private->hContact, SIPE_EVENTTYPE_ERROR_NOTIFY, time(NULL), DBEF_UTF, strlen(msg), (PBYTE)msg); g_free(msg); free_xfer_struct(xfer); if (xfer->watcher != 0) { sipe_miranda_input_remove(xfer->watcher); xfer->watcher = 0; } if (xfer->fd) Netlib_CloseHandle(xfer->fd); if (xfer->dest_fp != NULL) { fclose(xfer->dest_fp); xfer->dest_fp = NULL; } xfer->bytes_remaining = 0; g_free(xfer->filename); /* g_free(xfer); FIXME: needs refcounting like purple i guess */ } void sipe_backend_ft_deallocate(struct sipe_file_transfer *ft) { struct sipe_backend_file_transfer *xfer = ft->backend_private; /* If file transfer is not finished, cancel it */ if (xfer->status != SIPE_MIRANDA_XFER_STATUS_DONE && xfer->status != SIPE_MIRANDA_XFER_STATUS_CANCEL_LOCAL && xfer->status != SIPE_MIRANDA_XFER_STATUS_CANCEL_REMOTE) { cancel_remote(xfer); } } gssize sipe_backend_ft_read(struct sipe_file_transfer *ft, guchar *data, gsize size) { gssize bytes_read; FT_SIPE_DEBUG_INFO("reading up to <%d> bytes", size); bytes_read = Netlib_Recv(ft->backend_private->fd, data, size, MSG_NODUMP); FT_SIPE_DEBUG_INFO("came back from read <%d>", bytes_read); if (bytes_read == 0) { /* Sender canceled transfer before it was finished */ FT_SIPE_DEBUG_INFO_NOFORMAT("no read cause sender cancelled"); return -2; } else if (bytes_read == SOCKET_ERROR) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { return 0; } else { FT_SIPE_DEBUG_INFO("Error reading <%d>", err); return -1; } } FT_SIPE_DEBUG_INFO("read <%d> bytes [%02x:%c]", bytes_read, *data, *data); return bytes_read; } gssize sipe_backend_ft_write(struct sipe_file_transfer *ft, const guchar *data, gsize size) { int bytes_written; FT_SIPE_DEBUG_INFO("writing <%d> bytes", size); bytes_written = Netlib_Send(ft->backend_private->fd, data, size, MSG_NODUMP ); if (bytes_written == SOCKET_ERROR) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { return 0; } else { FT_SIPE_DEBUG_INFO("Error writing <%u>", err); return -1; } } FT_SIPE_DEBUG_INFO("wrote <%d> bytes", bytes_written); return bytes_written; } static void cancel_local(struct sipe_backend_file_transfer *xfer) { struct sipe_file_transfer *ft = xfer->ft; gchar *msg; FT_SIPE_DEBUG_INFO_NOFORMAT(""); xfer->status = SIPE_MIRANDA_XFER_STATUS_CANCEL_LOCAL; xfer->end_time = time(NULL); msg = g_strdup_printf("File transfer cancelled"); sipe_miranda_AddEvent(ft->backend_private->pr, ft->backend_private->hContact, SIPE_EVENTTYPE_ERROR_NOTIFY, time(NULL), DBEF_UTF, strlen(msg), (PBYTE)msg); g_free(msg); free_xfer_struct(xfer); if (xfer->watcher != 0) { sipe_miranda_input_remove(xfer->watcher); xfer->watcher = 0; } if (xfer->fd) Netlib_CloseHandle(xfer->fd); if (xfer->dest_fp != NULL) { fclose(xfer->dest_fp); xfer->dest_fp = NULL; } xfer->bytes_remaining = 0; g_free(xfer->filename); g_free(xfer); } void sipe_backend_ft_set_completed(struct sipe_file_transfer *ft) { _NIF(); } void sipe_backend_ft_cancel_local(struct sipe_file_transfer *ft) { cancel_local(ft->backend_private); } void sipe_backend_ft_cancel_remote(struct sipe_file_transfer *ft) { cancel_remote(ft->backend_private); } static struct sipe_backend_file_transfer * new_xfer(SIPPROTO *pr, struct sipe_file_transfer *ft, HANDLE hContact) { struct sipe_backend_file_transfer *xfer = g_new0(struct sipe_backend_file_transfer, 1); xfer->current_buffer_size = FT_INITIAL_BUFFER_SIZE; xfer->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE); xfer->ft = ft; xfer->hContact = hContact; xfer->pr = pr; xfer->st.cbSize = sizeof(PROTOFILETRANSFERSTATUS); xfer->st.hContact = hContact; return xfer; } void sipe_backend_ft_incoming(struct sipe_core_public *sipe_public, struct sipe_file_transfer *ft, const gchar *who, const gchar *file_name, gsize file_size) { SIPPROTO *pr = sipe_public->backend_private; PROTORECVFILET pre = {0}; CCSDATA ccs; HANDLE hContact; FT_SIPE_DEBUG_INFO("Incoming ft <%08x> from <%s> file <%s> size <%d>", ft, who, file_name, file_size); hContact = sipe_backend_buddy_find( sipe_public, who, NULL ); if (!hContact) { FT_SIPE_DEBUG_INFO("Adding miranda contact for incoming transfer from <%s>", who); hContact = ( HANDLE )CallService( MS_DB_CONTACT_ADD, 0, 0 ); CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hContact,( LPARAM )pr->proto.m_szModuleName ); DBWriteContactSettingByte( hContact, "CList", "NotOnList", 1 ); sipe_miranda_setContactString( pr, hContact, SIP_UNIQUEID, who ); // name } ft->backend_private = new_xfer(pr, ft, hContact); ft->backend_private->incoming = TRUE; ft->backend_private->file_size = file_size; ft->backend_private->bytes_remaining = file_size; ft->backend_private->bytes_sent = 0; ft->backend_private->filename = g_strdup(file_name); pre.flags = PREF_TCHAR; pre.timestamp = time(NULL); pre.tszDescription = mir_a2t(file_name); pre.fileCount = 1; pre.ptszFiles = &pre.tszDescription; pre.lParam = (LPARAM)ft; ccs.szProtoService = PSR_FILE; ccs.hContact = hContact; ccs.wParam = 0; ccs.lParam = (LPARAM)⪯ CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); } void sipe_backend_ft_outgoing(struct sipe_core_public *sipe_public, struct sipe_file_transfer *ft, const gchar *who, const gchar *file_name) { SIPPROTO *pr = sipe_public->backend_private; HANDLE hContact; int result; struct __stat64 buf; LOCK; hContact = sipe_backend_buddy_find( sipe_public, who, NULL ); ft->backend_private = new_xfer(pr, ft, hContact); ft->backend_private->incoming = FALSE; result = _tstat64( file_name, &buf ); if (result != 0) { FT_SIPE_DEBUG_INFO("Could not stat file, error<%d>", result); ft->backend_private->file_size = 0; } else { ft->backend_private->file_size = buf.st_size; ft->backend_private->bytes_remaining = ft->backend_private->file_size; ft->backend_private->bytes_sent = 0; } ft->backend_private->local_filename = g_strdup(file_name); ft->backend_private->filename = g_path_get_basename(ft->backend_private->local_filename); FT_SIPE_DEBUG_INFO("set filename to <%s>", ft->backend_private->filename); ft->init(ft, ft->backend_private->filename, ft->backend_private->file_size, who); sipe_miranda_SendBroadcast(pr, hContact, ACKTYPE_FILE, ACKRESULT_CONNECTING, (HANDLE)ft->backend_private, 0); UNLOCK; } gboolean sipe_backend_ft_incoming_accept(struct sipe_file_transfer *ft, const gchar *ip, unsigned short port_min, unsigned short port_max) { _NIF(); return FALSE; } static void set_completed(struct sipe_backend_file_transfer *xfer, gboolean completed) { if (completed == TRUE) { char *msg = NULL; xfer->status = SIPE_MIRANDA_XFER_STATUS_DONE; if (xfer->filename != NULL) { char *filename = g_markup_escape_text(xfer->filename, -1); if (xfer->local_filename && xfer->incoming) { char *local = g_markup_escape_text(xfer->local_filename, -1); msg = g_strdup_printf("Transfer of file %s complete", local, filename); g_free(local); } else msg = g_strdup_printf("Transfer of file %s complete", filename); g_free(filename); } else msg = g_strdup("File transfer complete"); sipe_miranda_AddEvent(xfer->pr, xfer->hContact, SIPE_EVENTTYPE_ERROR_NOTIFY, time(NULL), DBEF_UTF, strlen(msg), (PBYTE)msg); sipe_miranda_SendBroadcast(xfer->pr, xfer->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, (HANDLE)xfer, 0); g_free(msg); } update_progress(xfer); } static void do_transfer(struct sipe_backend_file_transfer *xfer) { guchar *buffer = NULL; gssize r = 0; struct sipe_file_transfer *ft = xfer->ft; FT_SIPE_DEBUG_INFO("incoming <%d>", xfer->incoming); if (xfer->incoming) { FT_SIPE_DEBUG_INFO_NOFORMAT("incoming branch"); r = ft->read(xfer->ft, &buffer, xfer->bytes_remaining, xfer->current_buffer_size); if (r > 0) { size_t wc; wc = fwrite(buffer, 1, r, xfer->dest_fp); if (wc != r) { SIPE_DEBUG_ERROR("Unable to write whole buffer."); cancel_local(xfer); g_free(buffer); return; } if ((xfer->file_size > 0) && ((xfer->bytes_sent+r) >= xfer->file_size)) set_completed(xfer, TRUE); } else if(r < 0) { cancel_remote(xfer); g_free(buffer); return; } } else { size_t result = 0; size_t s = MIN(xfer->bytes_remaining, xfer->current_buffer_size); gboolean read = TRUE; FT_SIPE_DEBUG_INFO("outgoing branch, size <%u>", s); /* this is so the prpl can keep the connection open if it needs to for some odd reason. */ if (s == 0) { if (xfer->watcher) { sipe_miranda_input_remove(xfer->watcher); xfer->watcher = 0; } return; } if (xfer->buffer) { if (xfer->buffer->len < s) { s -= xfer->buffer->len; read = TRUE; } else { read = FALSE; } } if (read) { buffer = g_malloc(s); result = fread(buffer, 1, s, xfer->dest_fp); if (result != s) { FT_SIPE_DEBUG_INFO_NOFORMAT("Unable to read whole buffer."); cancel_local(xfer); g_free(buffer); return; } } if (xfer->buffer) { g_byte_array_append(xfer->buffer, buffer, result); g_free(buffer); buffer = xfer->buffer->data; result = xfer->buffer->len; } s = MIN(xfer->bytes_remaining, result); r = ft->write(ft, buffer, s); if ((xfer->bytes_remaining - r) == 0) set_completed(xfer, TRUE); if (r >= 0 && (xfer->bytes_sent+r) >= xfer->file_size && xfer->status != SIPE_MIRANDA_XFER_STATUS_DONE) set_completed(xfer, TRUE); if (r == -1) { cancel_remote(xfer); if (!xfer->buffer) /* We don't free buffer if priv->buffer is set, because in that case buffer doesn't belong to us. */ g_free(buffer); return; } else if (r == result) { /* * We managed to write the entire buffer. This means our * network is fast and our buffer is too small, so make it * bigger. */ increase_buffer_size(xfer); } if (xfer->buffer) { /* * Remove what we wrote * If we wrote the whole buffer the byte array will be empty * Otherwise we'll keep what wasn't sent for next time. */ buffer = NULL; g_byte_array_remove_range(xfer->buffer, 0, r); } } FT_SIPE_DEBUG_INFO_NOFORMAT("back to common code"); if (r > 0) { if (xfer->file_size > 0) xfer->bytes_remaining -= r; xfer->bytes_sent += r; g_free(buffer); update_progress(xfer); } if (xfer->status == SIPE_MIRANDA_XFER_STATUS_DONE) { xfer->end_time = time(NULL); if (xfer->ft->end && xfer->ft->end(xfer->ft)) { /* We're done with this transfer */ free_xfer_struct(xfer); } else if (xfer->incoming) { _unlink(xfer->local_filename); } if (xfer->watcher != 0) { sipe_miranda_input_remove(xfer->watcher); xfer->watcher = 0; } if (xfer->fd) Netlib_CloseHandle(xfer->fd); if (xfer->dest_fp != NULL) { fclose(xfer->dest_fp); xfer->dest_fp = NULL; } g_free(xfer->filename); g_free(xfer); } } static void transfer_cb(gpointer data, gint source, sipe_miranda_input_condition condition) { struct sipe_backend_file_transfer *xfer = data; SIPE_DEBUG_INFO_NOFORMAT(""); do_transfer(xfer); } static void begin_transfer(struct sipe_file_transfer *ft) { struct sipe_backend_file_transfer *xfer = ft->backend_private; SIPPROTO *pr = xfer->pr; xfer->dest_fp = fopen(xfer->local_filename, xfer->incoming ? "wb" : "rb"); if (xfer->dest_fp == NULL) { int err = errno; gchar *msg; if (xfer->incoming) { msg = g_strdup_printf("Error reading %s: \n%s.\n", xfer->local_filename, g_strerror(err)); } else { msg = g_strdup_printf("Error writing %s: \n%s.\n", xfer->local_filename, g_strerror(err)); } sipe_miranda_AddEvent(ft->backend_private->pr, ft->backend_private->hContact, SIPE_EVENTTYPE_ERROR_NOTIFY, time(NULL), DBEF_UTF, strlen(msg), (PBYTE)msg); g_free(msg); FT_SIPE_DEBUG_INFO("error opening local file: %s", g_strerror(errno)); cancel_local(xfer); return; } fseek(xfer->dest_fp, xfer->bytes_sent, SEEK_SET); xfer->start_time = time(NULL); LOCK; FT_SIPE_DEBUG_INFO("incoming <%d> size <%d>", ft->backend_private->incoming, ft->backend_private->file_size); if (!ft->backend_private->incoming) { /* Set socket to nonblocking */ SOCKET sock = CallService(MS_NETLIB_GETSOCKET, (WPARAM)xfer->fd, (LPARAM)0); unsigned long parm = 1; if (ioctlsocket(sock, FIONBIO, &parm) == SOCKET_ERROR) { FT_SIPE_DEBUG_INFO("Error ioctlsocket <%d>", WSAGetLastError()); } FT_SIPE_DEBUG_INFO("outgoing ft <%08x> size <%d>", ft, ft->backend_private->file_size); } if (ft->start) { ft->start(ft, ft->backend_private->file_size); } UNLOCK; if (xfer->fd) xfer->watcher = sipe_miranda_input_add(xfer->fd, xfer->incoming?SIPE_MIRANDA_INPUT_READ:SIPE_MIRANDA_INPUT_WRITE, transfer_cb, xfer); FT_SIPE_DEBUG_INFO("watcher [%08x]", xfer->watcher); } static void ft_connected_callback(HANDLE fd, void* data, const gchar *reason) { struct sipe_file_transfer *ft = (struct sipe_file_transfer *)data; struct sipe_backend_file_transfer *xfer = ft->backend_private; SIPPROTO *pr = ft->backend_private->pr; if (!fd) { cancel_local(xfer); } else { ft->backend_private->fd = fd; begin_transfer(ft); } } void sipe_backend_ft_start(struct sipe_file_transfer *ft, struct sipe_backend_fd *fd, const char* ip, unsigned port) { ft->backend_private->status = SIPE_MIRANDA_XFER_STATUS_STARTED; if (ip && port) { FT_SIPE_DEBUG_INFO("Should connect to <%s:%d>", ip, port); sipe_miranda_connect(ft->backend_private->pr, ip, port, FALSE, 5, ft_connected_callback, ft); return; } FT_SIPE_DEBUG_INFO("Should use incoming fd <%08x>", fd); ft->backend_private->fd = fd; begin_transfer(ft); } gboolean sipe_backend_ft_is_incoming(struct sipe_file_transfer *ft) { FT_SIPE_DEBUG_INFO("ft <%08x> incoming <%d>", ft, ft->backend_private->incoming); return ft->backend_private->incoming; } HANDLE sipe_miranda_SendFile( SIPPROTO *pr, HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles ) { DBVARIANT dbv; if ( !DBGetContactSettingString( hContact, pr->proto.m_szModuleName, SIP_UNIQUEID, &dbv )) { struct sipe_file_transfer *ft; ft = sipe_core_ft_create_outgoing(pr->sip, dbv.pszVal, TCHAR2CHAR(ppszFiles[0])); FT_SIPE_DEBUG_INFO("SendFile: desc <%ls> name <%s> size <%d> to <%s>", szDescription, TCHAR2CHAR(ppszFiles[0]), ft->backend_private->file_size, dbv.pszVal); DBFreeVariant( &dbv ); return ft->backend_private; } return NULL; } int sipe_miranda_RecvFile( SIPPROTO *pr, HANDLE hContact, PROTOFILEEVENT* evt ) { CCSDATA ccs = { hContact, PSR_FILE, 0, (LPARAM)evt }; return CallService(MS_PROTO_RECVFILET, 0, (LPARAM)&ccs); } HANDLE sipe_miranda_FileAllow( SIPPROTO *pr, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath ) { struct sipe_file_transfer *ft = (struct sipe_file_transfer *)hTransfer; FT_SIPE_DEBUG_INFO("Incoming ft <%08x> allowed", ft); ft->backend_private->local_filename = g_strdup_printf("%s%s", TCHAR2CHAR(szPath), ft->backend_private->filename); sipe_miranda_SendBroadcast(pr, hContact, ACKTYPE_FILE, ACKRESULT_CONNECTING, (HANDLE)ft->backend_private, 0); ft->init(ft, ft->backend_private->filename, ft->backend_private->file_size, NULL); return ft->backend_private; } int sipe_miranda_FileDeny( SIPPROTO *pr, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason ) { struct sipe_file_transfer *ft = (struct sipe_file_transfer *)hTransfer; FT_SIPE_DEBUG_INFO("FileDeny: reason <%s>", szReason); if (ft->backend_private->incoming && ft->request_denied) ft->request_denied(ft); free_xfer_struct(ft->backend_private); return 0; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-groupchat.c ================================================ /** * @file miranda-groupchat.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_chat.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" void sipe_backend_groupchat_room_add(struct sipe_core_public *sipe_public, const gchar *uri, const gchar *name, const gchar *description, guint users, guint32 flags) { SIPPROTO *pr = sipe_public->backend_private; GCSESSION gs; GCEVENT gce = {0}; GCDEST gcd = {0}; gs.cbSize = sizeof(gs); gs.iType = GCW_CHATROOM; gs.pszModule = pr->proto.m_szModuleName; gs.pszName = name; gs.pszID = uri; gs.pszStatusbarText = description; gs.dwFlags = 0; gs.dwItemData = 0; if (CallServiceSync( MS_GC_NEWSESSION, 0, (LPARAM)&gs )) { SIPE_DEBUG_ERROR("sipe_backend_groupchat_room_add: Failed to create chat session <%d> <%s>", uri, name); } gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = uri; gce.cbSize = sizeof(gce); gce.pDest = &gcd; gcd.iType = GC_EVENT_CONTROL; if (CallService( MS_GC_EVENT, 0, (LPARAM)&gce )) { SIPE_DEBUG_WARNING_NOFORMAT("sipe_backend_groupchat_room_add: Failed to add normal status to chat session"); } gce.pszStatus = "Presenter"; if (CallService( MS_GC_EVENT, 0, (LPARAM)&gce )) { SIPE_DEBUG_WARNING_NOFORMAT("sipe_backend_groupchat_room_add: Failed to add presenter status to chat session"); } gcd.iType = GC_EVENT_CONTROL; if (CallServiceSync( MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gce )) { SIPE_DEBUG_WARNING_NOFORMAT("sipe_backend_groupchat_room_add: Failed to initdone chat session"); } if (CallServiceSync( MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gce )) { SIPE_DEBUG_ERROR_NOFORMAT("sipe_backend_groupchat_room_add: Failed to set chat session online\n"); } } void sipe_backend_groupchat_room_terminate(struct sipe_core_public *sipe_public) { _NIF(); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-im.c ================================================ /** * @file miranda-im.c * * pidgin-sipe * * Copyright (C) 2010-11 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_system.h" #include "m_database.h" #include "m_protomod.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" void sipe_backend_im_message(struct sipe_core_public *sipe_public, const gchar *from, const gchar *html) { SIPPROTO *pr = sipe_public->backend_private; CCSDATA ccs; PROTORECVEVENT pre = {0}; HANDLE hContact; gchar *msg; hContact = sipe_backend_buddy_find( sipe_public, from, NULL ); if (!hContact) { SIPE_DEBUG_INFO("Adding miranda contact for incoming talker <%s>", from); hContact = ( HANDLE )CallService( MS_DB_CONTACT_ADD, 0, 0 ); CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hContact,( LPARAM )pr->proto.m_szModuleName ); DBWriteContactSettingByte( hContact, "CList", "NotOnList", 1 ); sipe_miranda_setContactString( pr, hContact, SIP_UNIQUEID, from ); // name } msg = sipe_miranda_eliminate_html(html, strlen(html)); pre.szMessage = msg; // pre.flags = PREF_UTF + (isRtl ? PREF_RTL : 0); pre.timestamp = (DWORD)time(NULL); pre.lParam = 0; ccs.szProtoService = PSR_MESSAGE; ccs.hContact = hContact; ccs.wParam = 0; ccs.lParam = (LPARAM)⪯ CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); mir_free(msg); } int sipe_miranda_SendMsg(SIPPROTO *pr, HANDLE hContact, int flags, const char* msg ) { DBVARIANT dbv; SIPE_DEBUG_INFO("SendMsg: flags <%x> msg <%s>", flags, msg); if ( !DBGetContactSettingString( hContact, pr->proto.m_szModuleName, SIP_UNIQUEID, &dbv )) { LOCK; sipe_core_im_send(pr->sip, dbv.pszVal, msg); UNLOCK; sipe_miranda_SendProtoAck( pr, hContact, 1, ACKRESULT_SUCCESS, ACKTYPE_MESSAGE, NULL ); DBFreeVariant(&dbv); } else { sipe_miranda_SendProtoAck( pr, hContact, 1, ACKRESULT_FAILED, ACKTYPE_MESSAGE, NULL ); } return 1; } int sipe_miranda_RecvMsg(SIPPROTO *pr, HANDLE hContact, PROTORECVEVENT* pre) { // char *msg = EliminateHtml( pre->szMessage, strlen(pre->szMessage)); // mir_free(pre->szMessage); // pre->szMessage = msg; CCSDATA ccs = { hContact, PSR_MESSAGE, 0, ( LPARAM )pre }; return CallService( MS_PROTO_RECVMSG, 0, ( LPARAM )&ccs ); } void sipe_backend_im_topic(struct sipe_core_public *sipe_public, const gchar *with, const gchar *topic) { SIPPROTO *pr = sipe_public->backend_private; HANDLE hContact; hContact = sipe_backend_buddy_find( sipe_public, with, NULL ); if (!hContact) { _NIF(); } else { sipe_miranda_AddEvent(pr, hContact, SIPE_EVENTTYPE_IM_TOPIC, time(NULL), DBEF_UTF, strlen(topic), (PBYTE)topic); } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-input.c ================================================ /** * @file miranda-input.c * * pidgin-sipe * * Copyright (C) 2010-11 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "sipe-backend.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_netlib.h" #include "miranda-private.h" #define ENTRY_SIG 0x88442211 static NETLIBSELECTEX m_select = {0}; static GHashTable *m_readhash = NULL; static GHashTable *m_writehash = NULL; static GList *m_entries = NULL; typedef struct sipe_miranda_sel_entry { int sig; HANDLE fd; sipe_miranda_input_function func; gpointer user_data; gboolean async; /* Private. For locking only */ HANDLE hDoneEvent; gint source; sipe_miranda_input_condition cond; }; static void __stdcall input_cb_async(void *data) { struct sipe_miranda_sel_entry *entry = (struct sipe_miranda_sel_entry*)data; if (entry->fd == NULL) { SIPE_DEBUG_INFO("[IE:%08x] Entry already removed. Not calling read/write function", entry); } else { SIPE_DEBUG_INFO("[IE:%08x] Calling real read/write function", entry); entry->func(entry->user_data, entry->source, entry->cond); } SetEvent(entry->hDoneEvent); } static unsigned __stdcall inputloop(void* data) { int cnt; struct sipe_miranda_sel_entry *entry; INT_PTR lstRes; m_select.cbSize = sizeof(m_select); m_select.dwTimeout = 6000; while( m_select.hReadConns[0] || m_select.hWriteConns[0]) { int rc=0; int wc=0; for ( rc=0 ; m_select.hReadConns[rc] ; rc++ ); for ( wc=0 ; m_select.hWriteConns[wc] ; wc++ ); SIPE_DEBUG_INFO("About to run select on <%d> read and <%d> write", rc, wc); lstRes = CallService(MS_NETLIB_SELECTEX, 0, (LPARAM)&m_select); if (lstRes < 0) { SIPE_DEBUG_INFO_NOFORMAT("Connection failed while waiting."); break; } else if (lstRes == 0) { SIPE_DEBUG_INFO_NOFORMAT("Select Timeout."); } else { SIPE_DEBUG_INFO_NOFORMAT("Back from select"); for ( cnt=0 ; m_select.hReadConns[cnt] ; cnt++ ) { DWORD wr; if (!m_select.hReadStatus[cnt]) continue; SIPE_DEBUG_INFO("FD at position <%d> ready to read.", cnt); entry = (struct sipe_miranda_sel_entry*)g_hash_table_lookup(m_readhash, (gconstpointer)m_select.hReadConns[cnt]); if (!entry) { SIPE_DEBUG_INFO_NOFORMAT("ERROR: no read handler found."); continue; } SIPE_DEBUG_INFO("[IE:%08x] About to call read function.", entry); entry->source = (gint)m_select.hReadConns[cnt]; entry->cond = SIPE_MIRANDA_INPUT_READ; entry->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (entry->async) { CallFunctionAsync(input_cb_async, entry); wr = WaitForSingleObject(entry->hDoneEvent, INFINITE); } else { input_cb_async(entry); } CloseHandle(entry->hDoneEvent); SIPE_DEBUG_INFO("[IE:%08x] read function returned.", entry); } for ( cnt=0 ; m_select.hWriteConns[cnt] ; cnt++ ) { if (!m_select.hWriteStatus[cnt]) continue; SIPE_DEBUG_INFO("FD at position <%d> ready to write.", cnt); entry = (struct sipe_miranda_sel_entry*)g_hash_table_lookup(m_writehash, (gconstpointer)m_select.hWriteConns[cnt]); if (!entry) { SIPE_DEBUG_INFO_NOFORMAT("ERROR: no write handler found."); continue; } SIPE_DEBUG_INFO("[IE:%08x] About to call write function.", entry); entry->source = (gint)m_select.hWriteConns[cnt]; entry->cond = SIPE_MIRANDA_INPUT_WRITE; entry->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (entry->async) { CallFunctionAsync(input_cb_async, entry); WaitForSingleObject(entry->hDoneEvent, INFINITE); } else { input_cb_async(entry); } CloseHandle(entry->hDoneEvent); SIPE_DEBUG_INFO("[IE:%08x] write function returned.", entry); } } /* Free all removed entries */ while (m_entries) g_list_delete_link(m_entries, g_list_last(m_entries)); } return 0; } struct sipe_miranda_sel_entry* sipe_miranda_input_add(HANDLE fd, sipe_miranda_input_condition cond, sipe_miranda_input_function func, gpointer user_data) { int rcnt = 0; int wcnt = 0; struct sipe_miranda_sel_entry *entry; if (!m_readhash) m_readhash = g_hash_table_new(NULL, NULL); if (!m_writehash) m_writehash = g_hash_table_new(NULL, NULL); if ((cond != SIPE_MIRANDA_INPUT_READ) && (cond != SIPE_MIRANDA_INPUT_WRITE)) { SIPE_DEBUG_INFO("Invalid input condition <%d> cond.", cond); return 0; } entry = g_new0(struct sipe_miranda_sel_entry,1); entry->sig = ENTRY_SIG; entry->func = func; entry->user_data = user_data; entry->fd = fd; entry->async = FALSE; if (cond == SIPE_MIRANDA_INPUT_READ) { for ( wcnt=0 ; m_select.hWriteConns[wcnt] ; wcnt++ ); for ( rcnt=0 ; m_select.hReadConns[rcnt] && m_select.hReadConns[rcnt]!=(HANDLE)fd ; rcnt++ ); g_hash_table_replace( m_readhash, (gpointer)fd, entry ); m_select.hReadStatus[rcnt] = FALSE; m_select.hReadConns[rcnt] = (HANDLE)fd; } else if (cond == SIPE_MIRANDA_INPUT_WRITE) { for ( rcnt=0 ; m_select.hReadConns[rcnt] ; rcnt++ ); for ( wcnt=0 ; m_select.hWriteConns[wcnt] && m_select.hWriteConns[wcnt]!=(HANDLE)fd ; wcnt++ ); g_hash_table_replace( m_writehash, (gpointer)fd, entry ); m_select.hWriteStatus[rcnt] = FALSE; m_select.hWriteConns[rcnt] = (HANDLE)fd; } if (!(rcnt+wcnt)) CloseHandle((HANDLE) mir_forkthreadex( inputloop, NULL, 8192, NULL )); SIPE_DEBUG_INFO_NOFORMAT("Added input handler."); return entry; } gboolean sipe_miranda_input_remove(struct sipe_miranda_sel_entry *entry) { int cnt; if (!entry) { SIPE_DEBUG_INFO_NOFORMAT("Not a valid entry. NULL."); return FALSE; } if (entry->sig != ENTRY_SIG) { SIPE_DEBUG_INFO("Not a valid entry. Sig is <%08x>.", entry->sig); return FALSE; } if (g_hash_table_lookup(m_readhash, (gconstpointer)entry->fd) == entry) { for ( cnt=0 ; m_select.hReadConns[cnt] && m_select.hReadConns[cnt]!=(HANDLE)entry->fd ; cnt++ ); for ( ; m_select.hReadConns[cnt] ; cnt++ ) m_select.hReadConns[cnt] = m_select.hReadConns[cnt+1]; g_hash_table_remove(m_readhash, (gconstpointer)entry->fd); } if (g_hash_table_lookup(m_writehash, (gconstpointer)entry->fd) == entry) { for ( cnt=0 ; m_select.hWriteConns[cnt] && m_select.hWriteConns[cnt]!=(HANDLE)entry->fd ; cnt++ ); for ( ; m_select.hWriteConns[cnt] ; cnt++ ) m_select.hWriteConns[cnt] = m_select.hWriteConns[cnt+1]; g_hash_table_remove(m_writehash, (gconstpointer)entry->fd); } /* Set fd to NULL so we won't try to call the callback if we're currently waiting to get back to the main thread */ entry->fd = NULL; /* Add it to the list of entries that can be freed after the next select * loop in the thread that's handling the actual select */ g_list_append( m_entries, entry ); return TRUE; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-markup.c ================================================ /** * @file miranda-markup.c * * pidgin-sipe * * Copyright (C) 2010-11 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "glib.h" #include #include "sipe-backend.h" gchar *sipe_backend_markup_css_property(const gchar *style, const gchar *opt) { const gchar *css_str = style; const gchar *css_value_start; const gchar *css_value_end; gchar *tmp; gchar *ret; g_return_val_if_fail(opt != NULL, NULL); if (!css_str) return NULL; /* find the CSS property */ while (1) { /* skip whitespace characters */ while (*css_str && g_ascii_isspace(*css_str)) css_str++; if (!g_ascii_isalpha(*css_str)) return NULL; if (g_ascii_strncasecmp(css_str, opt, strlen(opt))) { /* go to next css property positioned after the next ';' */ while (*css_str && *css_str != '"' && *css_str != ';') css_str++; if(*css_str != ';') return NULL; css_str++; } else break; } /* find the CSS value position in the string */ css_str += strlen(opt); while (*css_str && g_ascii_isspace(*css_str)) css_str++; if (*css_str != ':') return NULL; css_str++; while (*css_str && g_ascii_isspace(*css_str)) css_str++; if (*css_str == '\0' || *css_str == '"' || *css_str == ';') return NULL; /* mark the CSS value */ css_value_start = css_str; while (*css_str && *css_str != '"' && *css_str != ';') css_str++; css_value_end = css_str - 1; /* Removes trailing whitespace */ while (css_value_end > css_value_start && g_ascii_isspace(*css_value_end)) css_value_end--; tmp = g_strndup(css_value_start, css_value_end - css_value_start + 1); // ret = purple_unescape_html(tmp); // g_free(tmp); ret = tmp; return ret; } gchar *sipe_backend_markup_strip_html(const gchar *html) { char *tmp = g_malloc(strlen(html)+1); const char *src; char *tgt; gboolean in_tag = FALSE; for ( src=html, tgt=tmp ; *src ; src++ ) { if (*src == '<') in_tag = TRUE; if (!in_tag) *tgt++ = *src; if (*src == '>') in_tag = FALSE; } *tgt = '\0'; tgt = g_strdup(tmp); g_free(tmp); return tgt; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-media.c ================================================ /** * @file miranda-media.c * * pidgin-sipe * * Copyright (C) 2010-2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" struct sipe_backend_media { int dummy; }; struct sipe_backend_media * sipe_backend_media_new(struct sipe_core_public *sipe_public, struct sipe_media_call *call, const gchar *participant, SipeMediaCallFlags flags) { struct sipe_backend_media *m = g_new0(struct sipe_backend_media,1); return m; } void sipe_backend_media_free(struct sipe_backend_media *media) { _NIF(); } void sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname) { _NIF(); } struct sipe_backend_media_relays * sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password) { _NIF(); return NULL; } void sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays) { _NIF(); } struct sipe_backend_media_stream * sipe_backend_media_add_stream(struct sipe_media_stream *stream, SipeMediaType type, SipeIceVersion ice_version, gboolean initiator, struct sipe_backend_media_relays *media_relays, guint min_port, guint max_port) { _NIF(); return NULL; } void sipe_backend_media_add_remote_candidates(struct sipe_media_call *media, struct sipe_media_stream *stream, GList *candidates) { _NIF(); } gboolean sipe_backend_media_is_initiator(struct sipe_media_call *media, struct sipe_media_stream *stream) { _NIF(); return FALSE; } gboolean sipe_backend_media_accepted(struct sipe_backend_media *media) { _NIF(); return FALSE; } gboolean sipe_backend_stream_initialized(struct sipe_media_call *media, struct sipe_media_stream *stream) { _NIF(); return FALSE; } GList * sipe_backend_media_stream_get_active_local_candidates(struct sipe_media_stream *stream) { _NIF(); return NULL; } GList * sipe_backend_media_stream_get_active_remote_candidates(struct sipe_media_stream *stream) { _NIF(); return NULL; } sipe_backend_media_set_encryption_keys(struct sipe_media_call *media, struct sipe_media_stream *stream, const guchar *encryption_key, const guchar *decryption_key) { _NIF(); } void sipe_backend_media_set_require_encryption(struct sipe_media_call *media, struct sipe_media_stream *stream, const gboolean require_encryption) { _NIF(); } void sipe_backend_stream_hold(struct sipe_media_call *media, struct sipe_media_stream *stream, gboolean local) { _NIF(); } void sipe_backend_stream_unhold(struct sipe_media_call *media, struct sipe_media_stream *stream, gboolean local) { _NIF(); } gboolean sipe_backend_stream_is_held(struct sipe_media_stream *stream) { _NIF(); return FALSE; } void sipe_backend_media_stream_end(struct sipe_media_call *media, struct sipe_media_stream *stream) { _NIF(); } void sipe_backend_media_stream_free(struct sipe_backend_media_stream *stream) { _NIF(); } struct sipe_backend_codec * sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate, guint channels) { _NIF(); return NULL; } void sipe_backend_codec_free(struct sipe_backend_codec *codec) { _NIF(); } int sipe_backend_codec_get_id(struct sipe_backend_codec *codec) { _NIF(); return 0; } gchar * sipe_backend_codec_get_name(struct sipe_backend_codec *codec) { _NIF(); return NULL; } guint sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec) { _NIF(); return 0; } void sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec, const gchar *name, const gchar *value) { _NIF(); } GList * sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec) { _NIF(); return NULL; } gboolean sipe_backend_set_remote_codecs(struct sipe_media_call *media, struct sipe_media_stream *stream, GList *codecs) { _NIF(); return FALSE; } GList* sipe_backend_get_local_codecs(struct sipe_media_call *media, struct sipe_media_stream *stream) { _NIF(); return NULL; } struct sipe_backend_candidate * sipe_backend_candidate_new(const gchar *foundation, SipeComponentType component, SipeCandidateType type, SipeNetworkProtocol proto, const gchar *ip, guint port, const gchar *username, const gchar *password) { _NIF(); return NULL; } void sipe_backend_candidate_free(struct sipe_backend_candidate *candidate) { _NIF(); } gchar * sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate) { _NIF(); return NULL; } gchar * sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate) { _NIF(); return NULL; } gchar * sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate) { _NIF(); return NULL; } gchar * sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate) { _NIF(); return NULL; } guint sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate) { _NIF(); return 0; } gchar * sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate) { _NIF(); return FALSE; } guint sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate) { _NIF(); return 0; } guint32 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate) { _NIF(); return 0; } void sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority) { _NIF(); } SipeComponentType sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate) { _NIF(); return SIPE_COMPONENT_NONE; } SipeCandidateType sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate) { _NIF(); return SIPE_CANDIDATE_TYPE_ANY; } SipeNetworkProtocol sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate) { _NIF(); return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE; } GList * sipe_backend_get_local_candidates(struct sipe_media_call *media, struct sipe_media_stream *stream) { _NIF(); return FALSE; } void sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local) { _NIF(); } void sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local) { _NIF(); } void sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local) { _NIF(); } SipeEncryptionPolicy sipe_backend_media_get_encryption_policy(struct sipe_core_public *sipe_public) { return SIPE_ENCRYPTION_POLICY_REJECTED; } gssize sipe_backend_media_stream_read(struct sipe_media_stream *stream, guint8 *buffer, gsize len) { _NIF(); } gssize sipe_backend_media_stream_write(struct sipe_media_stream *stream, guint8 *buffer, gsize len) { _NIF(); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-network.c ================================================ /** * @file miranda-network.c * * pidgin-sipe * * Copyright (C) 2010-2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_system.h" #include "m_netlib.h" #include "sipe-common.h" #include "sipe-core.h" #include "sipe-backend.h" #include "miranda-private.h" extern HANDLE sipe_miranda_incoming_netlibuser; const gchar *sipe_miranda_get_local_ip(void) { struct hostent *localHost = gethostbyname(""); return inet_ntoa(*(struct in_addr *)*localHost->h_addr_list); } struct sipe_backend_listendata { sipe_listen_start_cb listen_cb; sipe_client_connected_cb connect_cb; gpointer data; unsigned short port_min; unsigned short port_max; HANDLE boundport; HANDLE fd; WORD port; /* Private. For locking only */ HANDLE hDoneEvent; HANDLE hDoneEventL; }; static void __stdcall client_connected_cb_async(void *data) { struct sipe_backend_listendata *ldata = (struct sipe_backend_listendata *)data; SIPE_DEBUG_INFO("[CN:%08x] About to call real connect callback", ldata); if (ldata->connect_cb) ldata->connect_cb((struct sipe_backend_fd *)ldata->fd, ldata->data); /* Can't close the handle before the SetEvent or we'll deadlock */ SetEvent(ldata->hDoneEvent); /* MS_NETLIB_CLOSEHANDLE doesn't come back until the accept thread is done. That in turn doesn't end until the pfnNewConnectionV2 function comes back. So we know that client_connected_callback will be done and it's safe to free ldata here. */ CallService(MS_NETLIB_CLOSEHANDLE,(WPARAM)ldata->boundport,0); g_free(ldata); } static void client_connected_callback(HANDLE hNewConnection, DWORD dwRemoteIP, void *data) { struct sipe_backend_listendata *ldata = (struct sipe_backend_listendata *)data; SIPE_DEBUG_INFO("[CN:%08x] Remote connection from <%08x>", ldata, dwRemoteIP); ldata->fd = hNewConnection; ldata->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); CallFunctionAsync(client_connected_cb_async, ldata); WaitForSingleObject(ldata->hDoneEvent, INFINITE); CloseHandle(ldata->hDoneEvent); } static void __stdcall listen_cb_async(void *data) { struct sipe_backend_listendata *ldata = (struct sipe_backend_listendata *)data; ldata->listen_cb(ldata->port, ldata->data); SetEvent(ldata->hDoneEventL); } static unsigned __stdcall listen_callback(void* data) { NETLIBBIND nlb = {0}; NETLIBUSERSETTINGS nls = {0}; WORD iptype; struct sipe_backend_listendata *ldata = (struct sipe_backend_listendata *)data; nls.cbSize = sizeof(NETLIBUSERSETTINGS); CallService(MS_NETLIB_GETUSERSETTINGS, (WPARAM)sipe_miranda_incoming_netlibuser, (LPARAM)&nls); nls.specifyIncomingPorts = 1; nls.szIncomingPorts = mir_alloc(20); mir_snprintf( nls.szIncomingPorts, 20, "%d-%d", ldata->port_min, ldata->port_max); CallService(MS_NETLIB_SETUSERSETTINGS, (WPARAM)sipe_miranda_incoming_netlibuser, (LPARAM)&nls); nlb.cbSize = sizeof(NETLIBBIND); nlb.pfnNewConnectionV2 = client_connected_callback; nlb.pExtra = ldata; SetLastError(ERROR_INVALID_PARAMETER); // this must be here - NetLib does not set any error :(( ldata->boundport = (HANDLE)CallService(MS_NETLIB_BINDPORT, (WPARAM)sipe_miranda_incoming_netlibuser, (LPARAM)&nlb); ldata->port = nlb.wPort; sipe_miranda_getGlobalWord("iptype", &iptype); if (iptype == SIPE_MIRANDA_IP_PROG) { gchar rc[20]; DWORD bytesread = sizeof(rc); gchar *cmd = sipe_miranda_getGlobalString("ipprog"); gchar *cmdline = g_strdup_printf("%s listen %d", cmd, nlb.wPort); mir_free(cmd); if (!sipe_miranda_cmd(cmdline, rc, &bytesread)) { SIPE_DEBUG_INFO("Could not run child program <%s> (%d)", cmdline, GetLastError()); } } if (ldata->listen_cb) { ldata->hDoneEventL = CreateEvent(NULL, FALSE, FALSE, NULL); CallFunctionAsync(listen_cb_async, ldata); WaitForSingleObject(ldata->hDoneEventL, INFINITE); CloseHandle(ldata->hDoneEventL); } return 0; } struct sipe_backend_listendata * sipe_backend_network_listen_range(unsigned short port_min, unsigned short port_max, sipe_listen_start_cb listen_cb, sipe_client_connected_cb connect_cb, gpointer data) { struct sipe_backend_listendata *ldata; ldata = g_new0(struct sipe_backend_listendata, 1); ldata->listen_cb = listen_cb; ldata->connect_cb = connect_cb; ldata->data = data; ldata->port_min = port_min; ldata->port_max = port_max; CloseHandle((HANDLE) mir_forkthreadex( listen_callback, ldata, 65536, NULL )); return ldata; } void sipe_backend_network_listen_cancel(struct sipe_backend_listendata *ldata) { _NIF(); } struct sipe_backend_fd * sipe_backend_fd_from_int(int fd) { _NIF(); return NULL; } gboolean sipe_backend_fd_is_valid(struct sipe_backend_fd *fd) { return (fd != NULL); } void sipe_backend_fd_free(struct sipe_backend_fd *fd) { /* N/A; sipe_backend_fd is the actual HANDLE */ } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-notify.c ================================================ /** * @file miranda-notify.c * * pidgin-sipe * * Copyright (C) 2010-11 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_chat.h" #include "m_database.h" #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" static void notify_message(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *who, const gchar *message, int eventtype, const gchar *prefix ) { SIPPROTO *pr = sipe_public->backend_private; if (backend_session) { GCDEST gcd = {0}; GCEVENT gce = {0}; gchar *msg; gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = backend_session->conv; gcd.iType = GC_EVENT_INFORMATION; msg = mir_alloc(strlen(message)+strlen(prefix)+1); mir_snprintf(msg, strlen(message)+strlen(prefix)+1, "%s%s", prefix, message); gce.cbSize = sizeof(gce); gce.pDest = &gcd; gce.pszText = msg; // gce.time = mtime; // FIXME: Generate timestamp CallService( MS_GC_EVENT, 0, (LPARAM)&gce ); mir_free(msg); } else { HANDLE hContact = sipe_backend_buddy_find( sipe_public, who, NULL ); if (hContact) { sipe_miranda_AddEvent(pr, hContact, eventtype, time(NULL), DBEF_UTF, strlen(message), (PBYTE)message); } } } void sipe_backend_notify_message_error(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *who, const gchar *message) { notify_message(sipe_public, backend_session, who, message, SIPE_EVENTTYPE_ERROR_NOTIFY, "Error: "); } void sipe_backend_notify_message_info(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *who, const gchar *message) { notify_message(sipe_public, backend_session, who, message, SIPE_EVENTTYPE_INFO_NOTIFY, "Info: "); } void sipe_backend_notify_error(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const gchar *title, const gchar *msg) { sipe_miranda_msgbox(msg, title); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-plugin.c ================================================ /** * @file miranda-plugin.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma comment(lib, "Secur32.lib") #ifdef HAVE_GSSAPI_GSSAPI_H #pragma comment(lib, "krb5_32.lib") #pragma comment(lib, "gssapi32.lib") #pragma comment(lib, "comerr32.lib") #endif #include #include #include #include #include #include #include "miranda-version.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_protomod.h" #include "m_system.h" #include "m_database.h" #include "m_options.h" #include "m_netlib.h" #include "m_chat.h" #include "m_clist.h" #include "m_langpack.h" #include "m_message.h" #include "m_genmenu.h" #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" #include "miranda-resource.h" /* FIXME: Not here */ void CreateProtoService(const SIPPROTO *pr, const char* szService, SipSimpleServiceFunc serviceProc); HANDLE sipe_miranda_incoming_netlibuser = NULL; CRITICAL_SECTION sipe_miranda_debug_CriticalSection; gchar *sipe_backend_version(void) { char version[200]; if (CallService(MS_SYSTEM_GETVERSIONTEXT, sizeof(version), (LPARAM)version)) { strcpy(version, "Unknown"); } return g_strdup_printf("Miranda %s SIPLCS " __DATE__ " " __TIME__, version ); } /* * Miranda globals * * Global variables related to miranda core or UI */ static BOOL (WINAPI *pfnEnableThemeDialogTexture)(HANDLE, DWORD) = 0; HINSTANCE hInst; PLUGINLINK* pluginLink; struct MM_INTERFACE mmi; int hLangpack; /* * Dialog boxes */ static void EnableDlgItem(HWND hwndDlg, UINT control, gboolean enable) { EnableWindow(GetDlgItem(hwndDlg, control), enable); } static void CheckDlgItem(HWND hwndDlg, UINT control, int state) { Button_SetCheck(GetDlgItem(hwndDlg, control), state); } INT_PTR CALLBACK DlgProcSipSimpleOptsAbout(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_INITDIALOG: { SIPPROTO *pr = (SIPPROTO *)lParam; SETTEXTEX tex; gchar *tmp, *about; LOCK; tmp = sipe_core_about(); about = sipe_miranda_html2rtf(tmp); g_free(tmp); UNLOCK; TranslateDialogDefault(hwndDlg); SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); tex.flags = ST_DEFAULT; tex.codepage = 437; SendDlgItemMessage(hwndDlg, IDC_ABOUTSIPE, EM_SETTEXTEX, (WPARAM)&tex, (LPARAM)about ); g_free(about); } } return FALSE; } static INT_PTR CALLBACK DlgProcSipSimpleOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { static int lock=0; switch(msg) { case WM_INITDIALOG: { const SIPPROTO *pr = (const SIPPROTO *)lParam; char *str; gboolean state; WORD iptype; TranslateDialogDefault(hwndDlg); SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); lock++; #if defined(HAVE_GSSAPI_GSSAPI_H) || defined(HAVE_SSPI) state = sipe_miranda_getBool(pr, "sso", FALSE); if (state) { CheckDlgItem(hwndDlg, IDC_USESSO, BST_CHECKED); EnableDlgItem(hwndDlg, IDC_LOGIN, FALSE); EnableDlgItem(hwndDlg, IDC_PASSWORD, FALSE); } else { #endif CheckDlgItem(hwndDlg, IDC_USESSO, BST_UNCHECKED); EnableDlgItem(hwndDlg, IDC_LOGIN, TRUE); EnableDlgItem(hwndDlg, IDC_PASSWORD, TRUE); #if defined(HAVE_GSSAPI_GSSAPI_H) || defined(HAVE_SSPI) } #endif str = sipe_miranda_getString(pr, "username"); SetDlgItemTextA(hwndDlg, IDC_HANDLE, str); SendDlgItemMessage(hwndDlg, IDC_HANDLE, EM_SETLIMITTEXT, 50, 0); mir_free(str); str = sipe_miranda_getString(pr, "login"); SetDlgItemTextA(hwndDlg, IDC_LOGIN, str); SendDlgItemMessage(hwndDlg, IDC_LOGIN, EM_SETLIMITTEXT, 50, 0); mir_free(str); str = sipe_miranda_getString(pr, "password"); if (str) CallService(MS_DB_CRYPT_DECODESTRING, strlen(str),(LPARAM)str); SetDlgItemTextA(hwndDlg, IDC_PASSWORD, str); SendDlgItemMessage(hwndDlg, IDC_PASSWORD, EM_SETLIMITTEXT, 16, 0); mir_free(str); SendDlgItemMessage(hwndDlg, IDC_AUTHTYPE, CB_ADDSTRING, 0, (LPARAM)_T("Auto")); SendDlgItemMessage(hwndDlg, IDC_AUTHTYPE, CB_ADDSTRING, 0, (LPARAM)_T("NTLM")); #if defined(HAVE_GSSAPI_GSSAPI_H) || defined(HAVE_SSPI) SendDlgItemMessage(hwndDlg, IDC_AUTHTYPE, CB_ADDSTRING, 0, (LPARAM)_T("Kerberos")); #endif SendDlgItemMessage(hwndDlg, IDC_AUTHTYPE, CB_ADDSTRING, 0, (LPARAM)_T("TLS-DSK")); sipe_miranda_getWord(pr, NULL, "authscheme", &iptype); if (iptype == SIPE_AUTHENTICATION_TYPE_NTLM) SendDlgItemMessage(hwndDlg, IDC_AUTHTYPE, CB_SELECTSTRING, -1, (LPARAM)_T("NTLM")); else if (iptype == SIPE_AUTHENTICATION_TYPE_KERBEROS) SendDlgItemMessage(hwndDlg, IDC_AUTHTYPE, CB_SELECTSTRING, -1, (LPARAM)_T("Kerberos")); else if (iptype == SIPE_AUTHENTICATION_TYPE_TLS_DSK) SendDlgItemMessage(hwndDlg, IDC_AUTHTYPE, CB_SELECTSTRING, -1, (LPARAM)_T("TLS-DSK")); else SendDlgItemMessage(hwndDlg, IDC_AUTHTYPE, CB_SELECTSTRING, -1, (LPARAM)_T("Auto")); SendDlgItemMessage(hwndDlg, IDC_CONNTYPE, CB_ADDSTRING, 0, (LPARAM)_T("Auto")); SendDlgItemMessage(hwndDlg, IDC_CONNTYPE, CB_ADDSTRING, 0, (LPARAM)_T("SSL/TLS")); SendDlgItemMessage(hwndDlg, IDC_CONNTYPE, CB_ADDSTRING, 0, (LPARAM)_T("TCP")); str = sipe_miranda_getString(pr, "transport"); if (!str || !strcmp(str, "auto")) SendDlgItemMessage(hwndDlg, IDC_CONNTYPE, CB_SELECTSTRING, -1, (LPARAM)_T("Auto")); else if (!strcmp(str, "tls")) SendDlgItemMessage(hwndDlg, IDC_CONNTYPE, CB_SELECTSTRING, -1, (LPARAM)_T("SSL/TLS")); else if (!strcmp(str, "tcp")) SendDlgItemMessage(hwndDlg, IDC_CONNTYPE, CB_SELECTSTRING, -1, (LPARAM)_T("TCP")); str = sipe_miranda_getGlobalString("public_ip"); SetDlgItemTextA(hwndDlg, IDC_PUBLICIP, str); SendDlgItemMessage(hwndDlg, IDC_PUBLICIP, EM_SETLIMITTEXT, 20, 0); mir_free(str); str = sipe_miranda_getGlobalString("ipprog"); SetDlgItemTextA(hwndDlg, IDC_IPPROGEXE, str); SendDlgItemMessage(hwndDlg, IDC_IPPROGEXE, EM_SETLIMITTEXT, 60, 0); mir_free(str); str = sipe_miranda_get_local_ip(); SetDlgItemTextA(hwndDlg, IDC_IPLOCALFOUND, str); sipe_miranda_getGlobalWord("iptype", &iptype); if (iptype == SIPE_MIRANDA_IP_LOCAL) { CheckRadioButton(hwndDlg, IDC_IPLOCAL, IDC_IPPROG, IDC_IPLOCAL); EnableDlgItem(hwndDlg, IDC_PUBLICIP, FALSE); EnableDlgItem(hwndDlg, IDC_IPPROGEXE, FALSE); } else if (iptype == SIPE_MIRANDA_IP_MANUAL) { CheckRadioButton(hwndDlg, IDC_IPLOCAL, IDC_IPPROG, IDC_IPMANUAL); EnableDlgItem(hwndDlg, IDC_PUBLICIP, TRUE); EnableDlgItem(hwndDlg, IDC_IPPROGEXE, FALSE); } else { CheckRadioButton(hwndDlg, IDC_IPLOCAL, IDC_IPPROG, IDC_IPPROG); EnableDlgItem(hwndDlg, IDC_PUBLICIP, FALSE); EnableDlgItem(hwndDlg, IDC_IPPROGEXE, TRUE); } lock--; return TRUE; } case WM_COMMAND: { int code = wParam >> 16; int id = wParam & 0xffff; if (LOWORD(wParam) == IDC_IPLOCAL) { CheckRadioButton(hwndDlg, IDC_IPLOCAL, IDC_IPPROG, IDC_IPLOCAL); EnableDlgItem(hwndDlg, IDC_PUBLICIP, FALSE); EnableDlgItem(hwndDlg, IDC_IPPROGEXE, FALSE); SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); } else if (LOWORD(wParam) == IDC_IPMANUAL) { CheckRadioButton(hwndDlg, IDC_IPLOCAL, IDC_IPPROG, IDC_IPMANUAL); EnableDlgItem(hwndDlg, IDC_PUBLICIP, TRUE); EnableDlgItem(hwndDlg, IDC_IPPROGEXE, FALSE); SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); } else if (LOWORD(wParam) == IDC_IPPROG) { CheckRadioButton(hwndDlg, IDC_IPLOCAL, IDC_IPPROG, IDC_IPPROG); EnableDlgItem(hwndDlg, IDC_PUBLICIP, FALSE); EnableDlgItem(hwndDlg, IDC_IPPROGEXE, TRUE); SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); } else if (LOWORD(wParam) == IDC_USESSO) { if (IsDlgButtonChecked(hwndDlg, IDC_USESSO) == BST_CHECKED) { EnableDlgItem(hwndDlg, IDC_LOGIN, FALSE); EnableDlgItem(hwndDlg, IDC_PASSWORD, FALSE); SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); } else { CheckRadioButton(hwndDlg, IDC_SSO, IDC_MSO, IDC_MSO); EnableDlgItem(hwndDlg, IDC_LOGIN, TRUE); EnableDlgItem(hwndDlg, IDC_PASSWORD, TRUE); } } else if (!lock && (code == EN_CHANGE || code == CBN_SELCHANGE)) { SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); } return TRUE; } case WM_NOTIFY: { if (((LPNMHDR)lParam)->code == (UINT)PSN_APPLY) { char buf[100]; TCHAR tbuf[100]; const SIPPROTO *pr = (const SIPPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); GetDlgItemTextA(hwndDlg, IDC_HANDLE, buf, sizeof(buf)); sipe_miranda_setString(pr, "username", buf); GetDlgItemTextA(hwndDlg, IDC_LOGIN, buf, sizeof(buf)); sipe_miranda_setString(pr, "login", buf); GetDlgItemTextA(hwndDlg, IDC_PASSWORD, buf, sizeof(buf)); CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(buf),(LPARAM)buf); sipe_miranda_setString(pr, "password", buf); SendDlgItemMessage(hwndDlg, IDC_CONNTYPE, WM_GETTEXT, 100, (LPARAM)tbuf ); if (!_tcscmp(tbuf, _T("Auto"))) sipe_miranda_setString(pr, "transport", "auto"); else if (!_tcscmp(tbuf, _T("SSL/TLS"))) sipe_miranda_setString(pr, "transport", "tls"); else if (!_tcscmp(tbuf, _T("TCP"))) sipe_miranda_setString(pr, "transport", "tcp"); SendDlgItemMessage(hwndDlg, IDC_AUTHTYPE, WM_GETTEXT, 100, (LPARAM)tbuf ); if (!_tcscmp(tbuf, _T("NTLM"))) sipe_miranda_setWord(pr, NULL, "authscheme", SIPE_AUTHENTICATION_TYPE_NTLM); else if (!_tcscmp(tbuf, _T("Kerberos"))) sipe_miranda_setWord(pr, NULL, "authscheme", SIPE_AUTHENTICATION_TYPE_KERBEROS); else if (!_tcscmp(tbuf, _T("TLS-DSK"))) sipe_miranda_setWord(pr, NULL, "authscheme", SIPE_AUTHENTICATION_TYPE_TLS_DSK); else sipe_miranda_setWord(pr, NULL, "authscheme", SIPE_AUTHENTICATION_TYPE_AUTOMATIC); GetDlgItemTextA(hwndDlg, IDC_PUBLICIP, buf, sizeof(buf)); sipe_miranda_setGlobalString("public_ip", buf); GetDlgItemTextA(hwndDlg, IDC_IPPROGEXE, buf, sizeof(buf)); sipe_miranda_setGlobalString("ipprog", buf); if (IsDlgButtonChecked(hwndDlg, IDC_IPLOCAL) == BST_CHECKED) { sipe_miranda_setGlobalWord("iptype", SIPE_MIRANDA_IP_LOCAL); } else if (IsDlgButtonChecked(hwndDlg, IDC_IPMANUAL) == BST_CHECKED) { sipe_miranda_setGlobalWord("iptype", SIPE_MIRANDA_IP_MANUAL); } else { sipe_miranda_setGlobalWord("iptype", SIPE_MIRANDA_IP_PROG); } return TRUE; } return TRUE; } } return FALSE; } INT_PTR CALLBACK DlgProcAccMgrUI(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_INITDIALOG: { const SIPPROTO *pr = (const SIPPROTO *)lParam; char *str; gboolean sso; TranslateDialogDefault(hwndDlg); SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); sso = sipe_miranda_getBool(pr, "sso", FALSE); if (sso) { CheckRadioButton(hwndDlg, IDC_SSO, IDC_MSO, IDC_SSO); EnableDlgItem(hwndDlg, IDC_LOGIN, FALSE); EnableDlgItem(hwndDlg, IDC_PASSWORD, FALSE); } else { CheckRadioButton(hwndDlg, IDC_SSO, IDC_MSO, IDC_MSO); EnableDlgItem(hwndDlg, IDC_LOGIN, TRUE); EnableDlgItem(hwndDlg, IDC_PASSWORD, TRUE); } str = sipe_miranda_getString(pr, "username"); SetDlgItemTextA(hwndDlg, IDC_HANDLE, str); mir_free(str); str = sipe_miranda_getString(pr, "login"); SetDlgItemTextA(hwndDlg, IDC_LOGIN, str); mir_free(str); str = sipe_miranda_getString(pr, "password"); if (str) CallService(MS_DB_CRYPT_DECODESTRING, strlen(str),(LPARAM)str); SetDlgItemTextA(hwndDlg, IDC_PASSWORD, str); mir_free(str); SendDlgItemMessage(hwndDlg, IDC_HANDLE, EM_SETLIMITTEXT, 50, 0); SendDlgItemMessage(hwndDlg, IDC_LOGIN, EM_SETLIMITTEXT, 50, 0); SendDlgItemMessage(hwndDlg, IDC_PASSWORD, EM_SETLIMITTEXT, 16, 0); return TRUE; } case WM_COMMAND: if (LOWORD(wParam) == IDC_SSO) { EnableDlgItem(hwndDlg, IDC_LOGIN, FALSE); EnableDlgItem(hwndDlg, IDC_PASSWORD, FALSE); SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); } else if (LOWORD(wParam) == IDC_MSO) { EnableDlgItem(hwndDlg, IDC_LOGIN, TRUE); EnableDlgItem(hwndDlg, IDC_PASSWORD, TRUE); SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); } else if (HIWORD(wParam) == EN_CHANGE && (HWND)lParam == GetFocus()) { switch(LOWORD(wParam)) { case IDC_HANDLE: case IDC_LOGIN: case IDC_PASSWORD: SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); break; } } break; case WM_NOTIFY: if (((LPNMHDR)lParam)->code == (UINT)PSN_APPLY) { char buf[100]; const SIPPROTO *pr = (const SIPPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); if (IsDlgButtonChecked(hwndDlg, IDC_SSO) == BST_CHECKED) { sipe_miranda_setBool(pr, "sso", TRUE); } else { sipe_miranda_setBool(pr, "sso", FALSE); } GetDlgItemTextA(hwndDlg, IDC_HANDLE, buf, sizeof(buf)); sipe_miranda_setString(pr, "username", buf); GetDlgItemTextA(hwndDlg, IDC_LOGIN, buf, sizeof(buf)); sipe_miranda_setString(pr, "login", buf); GetDlgItemTextA(hwndDlg, IDC_PASSWORD, buf, sizeof(buf)); CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(buf),(LPARAM)buf); sipe_miranda_setString(pr, "password", buf); return TRUE; } break; } return FALSE; } /* * Miranda service support functions * * Functions called by our service functions */ static void fix_contact_groups(SIPPROTO *pr) { GSList *contacts = sipe_miranda_buddy_find_all(pr, NULL, NULL); char *group; CONTACTS_FOREACH(contacts) group = DBGetString(hContact, "CList", "Group"); sipe_miranda_setContactString(pr, hContact, "Group", group); mir_free(group); CONTACTS_FOREACH_END } static INT_PTR sipe_miranda_start_chat(SIPPROTO *pr, WPARAM wParam, LPARAM lParam) { HANDLE hContact = (HANDLE)wParam; struct sipe_core_public *sipe_public = pr->sip; DBVARIANT dbv; if ( !DBGetContactSettingString( hContact, pr->proto.m_szModuleName, SIP_UNIQUEID, &dbv )) { LOCK; sipe_core_buddy_new_chat(sipe_public, dbv.pszVal); UNLOCK; DBFreeVariant( &dbv ); return TRUE; } return FALSE; } static void OnModulesLoaded(SIPPROTO *pr) { TCHAR descr[MAX_PATH]; NETLIBUSER nlu = {0}; GCREGISTER gcr; DBEVENTTYPEDESCR eventType = {0}; SIPE_DEBUG_INFO_NOFORMAT("OnEvent::OnModulesLoaded"); nlu.cbSize = sizeof(nlu); nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_TCHAR; nlu.szSettingsModule = pr->proto.m_szModuleName; _sntprintf(descr, SIZEOF(descr), TranslateT("%s server connection"), pr->proto.m_tszUserName ); nlu.ptszDescriptiveName = descr; pr->m_hServerNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu); gcr.cbSize = sizeof(gcr); gcr.dwFlags = 0; gcr.pszModule = pr->proto.m_szModuleName; gcr.pszModuleDispName = "Sip/Simple"; gcr.iMaxText = 0; gcr.nColors = 0; if (CallService(MS_GC_REGISTER, 0, (LPARAM)&gcr)) { SIPE_DEBUG_INFO_NOFORMAT("OnEvent::OnModulesLoaded Failed to register chat"); } // Register custom database events eventType.cbSize = DBEVENTTYPEDESCR_SIZE; eventType.module = pr->proto.m_szModuleName; eventType.eventType = SIPE_EVENTTYPE_ERROR_NOTIFY; eventType.descr = "Message error notification"; eventType.textService = SIPE_DB_GETEVENTTEXT_ERROR_NOTIFY; eventType.flags = DETF_HISTORY | DETF_MSGWINDOW; // for now keep default "message" icon CallService(MS_DB_EVENT_REGISTERTYPE, 0, (LPARAM)&eventType); eventType.cbSize = DBEVENTTYPEDESCR_SIZE; eventType.module = pr->proto.m_szModuleName; eventType.eventType = SIPE_EVENTTYPE_INFO_NOTIFY; eventType.descr = "Message info notification"; eventType.textService = SIPE_DB_GETEVENTTEXT_INFO_NOTIFY; eventType.flags = DETF_HISTORY | DETF_MSGWINDOW; // for now keep default "message" icon CallService(MS_DB_EVENT_REGISTERTYPE, 0, (LPARAM)&eventType); eventType.cbSize = DBEVENTTYPEDESCR_SIZE; eventType.module = pr->proto.m_szModuleName; eventType.eventType = SIPE_EVENTTYPE_IM_TOPIC; eventType.descr = "Chat topic set"; eventType.textService = SIPE_DB_GETEVENTTEXT_IM_TOPIC; eventType.flags = DETF_HISTORY | DETF_MSGWINDOW; // for now keep default "message" icon CallService(MS_DB_EVENT_REGISTERTYPE, 0, (LPARAM)&eventType); } int OnOptionsInit(const SIPPROTO *pr, WPARAM wParam, LPARAM lParam) { OPTIONSDIALOGPAGE odp = {0}; HMODULE hUxTheme = 0; if (IsWinVerXPPlus()) { hUxTheme = GetModuleHandleA("uxtheme.dll"); if (hUxTheme) pfnEnableThemeDialogTexture = (BOOL (WINAPI *)(HANDLE, DWORD))GetProcAddress(hUxTheme, "EnableThemeDialogTexture"); } odp.cbSize = sizeof(odp); odp.position = -800000000; odp.hInstance = hInst; odp.ptszGroup = LPGENT("Network"); odp.dwInitParam = (LPARAM)pr; odp.ptszTitle = pr->proto.m_tszUserName; odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR; odp.ptszTab = LPGENT("Account"); odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SIPSIMPLE); odp.pfnDlgProc = DlgProcSipSimpleOpts; CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp ); odp.ptszTab = LPGENT("About"); odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SIPSIMPLE_ABOUT); odp.pfnDlgProc = DlgProcSipSimpleOptsAbout; CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp ); #if 0 odp.ptszTab = LPGENT("Features"); odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_ICQFEATURES); odp.pfnDlgProc = DlgProcIcqFeaturesOpts; CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp ); odp.ptszTab = LPGENT("Privacy"); odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_ICQPRIVACY); odp.pfnDlgProc = DlgProcIcqPrivacyOpts; CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp ); if (bPopUpService) { odp.position = 100000000; odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_POPUPS); odp.groupPosition = 900000000; odp.pfnDlgProc = DlgProcIcqPopupOpts; odp.ptszGroup = LPGENT("Popups"); odp.ptszTab = NULL; CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp ); } #endif return 0; } void sipe_miranda_close(SIPPROTO *pr) { struct sipe_core_public *sipe_public = pr->sip; if (sipe_public) { LOCK; sipe_core_deallocate(sipe_public); pr->sip = NULL; UNLOCK; // sipe_purple_chat_destroy_rejoin(purple_private); // g_free(purple_private); } } void set_buddies_offline(const SIPPROTO* pr) { HANDLE hContact; hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); while (hContact) { char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if (szProto != NULL && !lstrcmpA(szProto, pr->proto.m_szModuleName)) { if (DBGetContactSettingByte(hContact, pr->proto.m_szModuleName, "ChatRoom", 0) == 0) DBWriteContactSettingWord(hContact, pr->proto.m_szModuleName, "Status", ID_STATUS_OFFLINE); } hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); } } int __stdcall show_vlc(void *data); void sipe_miranda_login(SIPPROTO *pr) { gchar *username = sipe_miranda_getString(pr, "username"); gchar *login = sipe_miranda_getString(pr, "login"); gchar *email = sipe_miranda_getString(pr, "email"); gchar *email_url = sipe_miranda_getString(pr, "email_url"); const gchar *errmsg; gchar *password; gchar *tmp = (char*)mir_calloc(1024); int tmpstatus; int ttype; guint authentication_type = SIPE_AUTHENTICATION_TYPE_AUTOMATIC; struct sipe_core_public *sipe_public; // CloseHandle((HANDLE) mir_forkthreadex(show_vlc, NULL, 65536, NULL)); if (sipe_miranda_getStaticString(pr, NULL, "password", tmp, 1024 )) tmp[0] = '\0'; CallService(MS_DB_CRYPT_DECODESTRING, sizeof(tmp),(LPARAM)tmp); password = g_strdup(tmp); mir_free(tmp); LOCK; pr->sip = sipe_core_allocate(username, // /* @TODO: is this correct? // "sso" is only available when SSPI/Kerberos support is compiled in */ sipe_miranda_getBool(pr, "sso", FALSE), login, password, email, email_url, &errmsg); if (pr->sip) pr->sip->backend_private = pr; sipe_public = pr->sip; UNLOCK; mir_free(username); mir_free(login); mir_free(email); mir_free(email_url); g_free(password); if (!pr->sip) { sipe_miranda_connection_error_reason(pr, SIPE_CONNECTION_ERROR_INVALID_USERNAME, errmsg); return; } //sipe_miranda_chat_setup_rejoin(pr); /* default is Auto */ sipe_miranda_getWord(pr, NULL, "authscheme", &authentication_type); /* Set display name */ sipe_miranda_setStringUtf(pr, "Nick", pr->sip->sip_name); /* Update connection progress */ tmpstatus = pr->proto.m_iStatus; pr->proto.m_iStatus = ID_STATUS_CONNECTING; sipe_miranda_SendBroadcast(pr, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)tmpstatus, ID_STATUS_CONNECTING); tmp = sipe_miranda_getString(pr, "transport"); if (sipe_strequal(tmp, "auto")) { ttype = SIPE_TRANSPORT_AUTO; } else if (sipe_strequal(tmp, "tls")) { ttype = SIPE_TRANSPORT_TLS; } else { ttype = SIPE_TRANSPORT_TCP; } mir_free(tmp); LOCK; sipe_core_transport_sip_connect(pr->sip, ttype, authentication_type, NULL, NULL); UNLOCK; } void sipsimple_search_contact_cb( GList *columns, GList *results, GHashTable *opts, void *data ) { SIPPROTO *pr = (SIPPROTO *)data; GList *row, *col; HANDLE hProcess = g_hash_table_lookup(opts, "searchid"); PROTOSEARCHRESULT psr = { 0 }; psr.cbSize = sizeof(psr); row = results; while (row) { gchar **name; col = (GList*)row->data; psr.id = (PROTOCHAR*)col->data; col = g_list_next(col); name = g_strsplit_set(col->data, ",", 2); psr.nick = (FNAMECHAR*)col->data; psr.firstName = (PROTOCHAR*)(name[0] ? name[1] : NULL); psr.lastName = (PROTOCHAR*)name[0]; col = g_list_next(col); /* company */ col = g_list_next(col); /* country */ col = g_list_next(col); psr.email = (PROTOCHAR*)col->data; row = g_list_next(row); sipe_miranda_SendBroadcast(pr, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, hProcess, (LPARAM) & psr); g_strfreev(name); } sipe_miranda_SendBroadcast(pr, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, hProcess, 0); } static int OnGroupChange(SIPPROTO *pr, WPARAM w, LPARAM l ) { CLISTGROUPCHANGE *gi = (CLISTGROUPCHANGE*)l; HANDLE hContact = (HANDLE)w; DBVARIANT dbv; /* No contact => it's a group add/rename/remove */ if (!hContact) { gchar *oldname, *newname; /* No old name => add */ if (!gi->pszOldName) { return 0; } /* No new name => delete */ else if (!gi->pszNewName) { SIPE_DEBUG_INFO("Removing group <%ls>", gi->pszOldName); oldname = mir_t2a(gi->pszOldName); LOCK; sipe_core_group_remove(pr->sip, oldname); UNLOCK; mir_free(oldname); return 0; } SIPE_DEBUG_INFO("Renaming group <%S> to <%S>", gi->pszOldName, gi->pszNewName); oldname = mir_t2a(gi->pszOldName); newname = mir_t2a(gi->pszNewName); LOCK; sipe_core_group_rename(pr->sip, oldname, newname); UNLOCK; mir_free(oldname); mir_free(newname); return 0; } if ( !DBGetContactSettingString( hContact, pr->proto.m_szModuleName, SIP_UNIQUEID, &dbv )) { gchar *oldgroup; gchar *who = g_strdup(dbv.pszVal); DBFreeVariant( &dbv ); if (oldgroup = sipe_miranda_getContactString(pr, hContact, "Group")) { SIPE_DEBUG_INFO("Moving buddy <%s> from group <%ls> to group <%ls>", who, oldgroup, gi->pszNewName); LOCK; sipe_core_buddy_group(pr->sip, who, oldgroup, TCHAR2CHAR(gi->pszNewName)); UNLOCK; mir_free(oldgroup); } else { gchar *name = mir_t2a(gi->pszNewName); if (!g_str_has_prefix(name, "sip:")) { gchar *newname = sip_uri_from_name(name); mir_free(name); name = mir_strdup(newname); g_free(newname); } SIPE_DEBUG_INFO("Really adding buddy <%s> to list in group <%s>", who, name); LOCK; sipe_core_buddy_add(pr->sip, who, name); UNLOCK; mir_free(name); } g_free(who); } return TRUE; } static int sipe_miranda_build_chat_menu(SIPPROTO *pr, WPARAM w, LPARAM lParam ) { GCMENUITEMS *gcmi= (GCMENUITEMS*) lParam; if (gcmi->Type == MENU_ON_NICKLIST) { static struct gc_item Item[] = { {"&Make Leader", 1, MENU_ITEM, FALSE}, }; gcmi->nItems = sizeof(Item)/sizeof(Item[0]); gcmi->Item = &Item[0]; } return 0; } static int OnChatEvent(SIPPROTO *pr, WPARAM w, LPARAM l ) { GCHOOK *hook = (GCHOOK*)l; GCDEST *dst = hook->pDest; if (dst->iType == GC_USER_MESSAGE) { GCDEST gcd = {0}; GCEVENT gce = {0}; struct sipe_chat_session *session; gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = dst->pszID; gcd.iType = GC_EVENT_GETITEMDATA; gce.cbSize = sizeof(gce); gce.pDest = &gcd; if ((session = (struct sipe_chat_session*)CallService( MS_GC_EVENT, 0, (LPARAM)&gce )) == NULL) { SIPE_DEBUG_WARNING_NOFORMAT("Failed to get chat session"); return 0; } LOCK; sipe_core_chat_send(pr->sip, session, hook->pszText); UNLOCK; return TRUE; } else if (dst->iType == GC_USER_PRIVMESS) { } else if (dst->iType == GC_USER_NICKLISTMENU) { if (hook->dwData == 1) { SIPE_DEBUG_INFO("make leader <%s>", hook->pszUID); } } return FALSE; } int OnPreBuildContactMenu(SIPPROTO *pr, WPARAM wParam, LPARAM lParam) { HANDLE hContact = (HANDLE)wParam; int chatcount = CallService(MS_GC_GETSESSIONCOUNT, 0, (LPARAM)pr->proto.m_szModuleName); int idx; CLISTMENUITEM mi = {0}; GC_INFO gci = {0}; gpointer tmp; mi.cbSize = sizeof(mi); gci.pszModule = pr->proto.m_szModuleName; /* Remove the old list */ while (pr->contactMenuChatItems) { SIPE_DEBUG_INFO("Removing old menuitem <%08x>", pr->contactMenuChatItems->data); CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)pr->contactMenuChatItems->data, 0); pr->contactMenuChatItems = g_slist_remove(pr->contactMenuChatItems, pr->contactMenuChatItems->data); } /* Add the main entry */ mi.pszName = "Invite to chat"; mi.flags = CMIF_NOTOFFLINE; mi.position = 20; tmp = (gpointer)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); pr->contactMenuChatItems = g_slist_append(pr->contactMenuChatItems, tmp); mi.pszName = "New chat"; mi.hParentMenu = pr->contactMenuChatItems->data; mi.flags = CMIF_ROOTHANDLE; mi.popupPosition = 0; mi.position=-10; mi.pszService = g_strdup_printf("%s/StartChat", pr->proto.m_szModuleName); mi.pszContactOwner = pr->proto.m_szModuleName; tmp = (gpointer)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); g_free(mi.pszService); pr->contactMenuChatItems = g_slist_append(pr->contactMenuChatItems, tmp); for (idx=0 ; idx Menuitem <%08x>", idx, pr->contactMenuChatItems); gci.iItem = idx; gci.Flags = BYINDEX | NAME | ID; if(!CallServiceSync( MS_GC_GETINFO, 0, (LPARAM)&gci )) { SIPE_DEBUG_INFO("Chat <%s>", gci.pszName); mi.pszName = gci.pszName; mi.hParentMenu = pr->contactMenuChatItems->data; mi.flags = CMIF_ROOTHANDLE; mi.popupPosition = g_strdup(gci.pszID); mi.position = idx; mi.pszService = g_strdup_printf("%s/InviteToChat", pr->proto.m_szModuleName); mi.pszContactOwner = pr->proto.m_szModuleName; tmp = (gpointer)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); g_free(mi.pszService); pr->contactMenuChatItems = g_slist_append(pr->contactMenuChatItems, tmp); } } return 0; } INT_PTR SvcCreateAccMgrUI(const SIPPROTO *pr, WPARAM wParam, LPARAM lParam) { return (INT_PTR)CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_ACCMGRUI), (HWND)lParam, DlgProcAccMgrUI, (LPARAM)pr); } /* * Miranda service functions * * The functions in our plugin that get called directly by core Miranda */ static DWORD_PTR GetCaps( SIPPROTO *pr, int type, HANDLE hContact ) { switch (type) { case PFLAGNUM_1: return PF1_IM | PF1_CHAT | PF1_FILE | PF1_MODEMSG | PF1_SERVERCLIST | PF1_ADDED | PF1_BASICSEARCH | PF1_ADDSEARCHRES | PF1_SEARCHBYEMAIL | PF1_USERIDISEMAIL | PF1_SEARCHBYNAME | PF1_EXTSEARCH ; case PFLAGNUM_2: return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_OUTTOLUNCH | PF2_ONTHEPHONE | PF2_IDLE; case PFLAGNUM_3: return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_OUTTOLUNCH | PF2_ONTHEPHONE | PF2_IDLE; case PFLAGNUM_4: return PF4_NOCUSTOMAUTH | PF4_IMSENDUTF | PF4_SUPPORTTYPING | PF4_SUPPORTIDLE; case PFLAGNUM_5: return 0; case PFLAG_UNIQUEIDSETTING: return (DWORD_PTR) SIP_UNIQUEID; break; default: SIPE_DEBUG_INFO("GetCaps: unknown type <%x>", type); } return 0; } static HICON GetIcon( SIPPROTO *pr, int iconIndex ) { SIPE_DEBUG_INFO("GetIcon: unknown index <%x>", iconIndex); return NULL; } static int OnEvent( SIPPROTO *pr, PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam ) { SIPE_DEBUG_INFO("OnEvent: type <%x>", eventType); switch (eventType) { case EV_PROTO_ONLOAD: OnModulesLoaded(pr); break; case EV_PROTO_ONREADYTOEXIT: break; case EV_PROTO_ONEXIT: break; case EV_PROTO_ONRENAME: break; case EV_PROTO_ONOPTIONS: return OnOptionsInit( pr, wParam, lParam ); break; case EV_PROTO_ONERASE: break; } return 0; } static HANDLE AddToListByEvent( SIPPROTO *pr, int flags, int iContact, HANDLE hDbEvent ) { DBEVENTINFO dbei = {0}; dbei.cbSize = sizeof(dbei); if ((dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvent, 0)) == -1) return 0; dbei.pBlob = (PBYTE)_alloca(dbei.cbBlob + 1); dbei.pBlob[dbei.cbBlob] = '\0'; if (CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei)) return 0; // failed to get event if (strcmp(dbei.szModule, pr->proto.m_szModuleName)) return 0; // this event is not ours _NIF(); SIPE_DEBUG_INFO("AddToListByEvent: flags <%x> iContact <%x>", flags, iContact); return NULL; } static int Authorize( SIPPROTO *pr, HANDLE hContact ) { _NIF(); SIPE_DEBUG_INFO_NOFORMAT("Authorize"); return 0; } static int AuthDeny( SIPPROTO *pr, HANDLE hContact, const PROTOCHAR* szReason ) { _NIF(); SIPE_DEBUG_INFO("AuthDeny: reason <%s>", szReason); return 0; } static int AuthRecv( SIPPROTO *pr, HANDLE hContact, PROTORECVEVENT* evt ) { _NIF(); SIPE_DEBUG_INFO_NOFORMAT("AuthRecv"); return 0; } static int AuthRequest( SIPPROTO *pr, HANDLE hContact, const PROTOCHAR* szMessage ) { _NIF(); SIPE_DEBUG_INFO("AuthRequest: message <%s>", szMessage); return 0; } static HANDLE ChangeInfo( SIPPROTO *pr, int iInfoType, void* pInfoData ) { _NIF(); SIPE_DEBUG_INFO("ChangeInfo: infotype <%x>", iInfoType); return NULL; } static int FileCancel( SIPPROTO *pr, HANDLE hContact, HANDLE hTransfer ) { _NIF(); SIPE_DEBUG_INFO_NOFORMAT("FileCancel"); return 0; } static int FileResume( SIPPROTO *pr, HANDLE hTransfer, int* action, const PROTOCHAR** szFilename ) { _NIF(); SIPE_DEBUG_INFO("FileResume: action <%x>", action); return 0; } static HANDLE SearchBasic( SIPPROTO *pr, const PROTOCHAR* id ) { return NULL; } static HWND CreateExtendedSearchUI( SIPPROTO *pr, HWND owner ) { return CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SEARCHUI), (HWND)owner, NULL, (LPARAM)pr); } static HANDLE AddToList( SIPPROTO *pr, int flags, PROTOSEARCHRESULT* psr ) { HANDLE hContact; gchar *id = g_strdup(TCHAR2CHAR(psr->id)); /* Prepend sip: if needed */ if (strncmp("sip:", id, 4)) { gchar *tmp = id; id = sip_uri_from_name(tmp); g_free(tmp); } hContact = sipe_miranda_buddy_find(pr, id, NULL); if (hContact) { g_free(id); return hContact; } hContact = ( HANDLE )CallService( MS_DB_CONTACT_ADD, 0, 0 ); CallService( MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)pr->proto.m_szModuleName ); sipe_miranda_setContactString( pr, hContact, SIP_UNIQUEID, id ); // name if (psr->nick) { /* server_alias */ gchar *tmp = mir_t2a(psr->nick); sipe_miranda_setContactStringUtf( pr, hContact, "Nick", tmp ); mir_free(tmp); } g_free(id); return hContact; } int sipe_miranda_window_closed(SIPPROTO *pr, WPARAM wParam, LPARAM lParam) { MessageWindowEventData* evt = (MessageWindowEventData*)lParam; SIPE_DEBUG_INFO("contact <%08x> module <%s> type <%02x> flags <%02x>", evt->hContact, evt->szModule, evt->uType, evt->uFlags); return 0; } static int sipe_miranda_invite_to_chat(const SIPPROTO *pr, WPARAM wParam, LPARAM lParam) { HANDLE hContact = (HANDLE)wParam; gchar *id = (gchar*)lParam; GCDEST gcd = {0}; GCEVENT gce = {0}; struct sipe_chat_session *session; gchar *uid; gcd.pszModule = pr->proto.m_szModuleName; gcd.pszID = id; gcd.iType = GC_EVENT_GETITEMDATA; gce.cbSize = sizeof(gce); gce.pDest = &gcd; if ((session = (struct sipe_chat_session*)CallService( MS_GC_EVENT, 0, (LPARAM)&gce )) == NULL) { SIPE_DEBUG_WARNING_NOFORMAT("Failed to get chat session"); return 0; } uid = sipe_miranda_getContactString(pr, hContact, SIP_UNIQUEID); sipe_core_chat_invite(pr->sip, session, uid); mir_free(uid); g_free(id); return 0; } /* * Main Miranda interface * * The structures and functions that allow Miranda to recovnize and load * our plugin. */ /* Plugin information structure */ PLUGININFOEX pluginInfo = { sizeof(PLUGININFOEX), "Office Communicator Protocol", PLUGIN_MAKE_VERSION(0,11,2,1), "Support for Microsoft Office Communicator", "Miranda support by Jochen De Smet, for core sipe support see homepage", "jochen.libsipe@leahnim.org", "(C)2009-2011", "https://sourceforge.net/projects/sipe", UNICODE_AWARE, 0, //doesn't replace anything built-in #if defined( _UNICODE ) { 0x842395ed, 0x4e56, 0x40e5, { 0x94, 0x25, 0x28, 0x29, 0xd8, 0xab, 0xae, 0xa5 } } // {842395ED-4E56-40e5-9425-2829D8ABAEA5} #else { 0x1ef8af37, 0xdec1, 0x4757, { 0x89, 0x78, 0xe8, 0xad, 0xd0, 0xd8, 0x6e, 0x7f } } // {1EF8AF37-DEC1-4757-8978-E8ADD0D86E7F} #endif }; __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion) { // Only load for 0.8.0.29 or greater // We need the core stubs for PS_GETNAME and PS_GETSTATUS if (mirandaVersion < PLUGIN_MAKE_VERSION(0, 9, 0, 0)) { MessageBoxA( NULL, "SIP/Simple plugin cannot be loaded. It requires Miranda IM 0.9.0.0 or later.", "SIP/Simple Plugin", MB_OK | MB_ICONWARNING | MB_SETFOREGROUND | MB_TOPMOST ); return NULL; } return &pluginInfo; } static const MUUID interfaces[] = {MIID_PROTOCOL, MIID_LAST}; __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void) { return interfaces; } static PROTO_INTERFACE* sipsimpleProtoInit( const char* pszProtoName, const TCHAR* tszUserName ) { gchar *tmp; SIPPROTO *pr = (SIPPROTO *)mir_calloc(sizeof(SIPPROTO)); pr->proto.vtbl = (PROTO_INTERFACE_VTBL*)mir_calloc(sizeof(PROTO_INTERFACE_VTBL)); SIPE_DEBUG_INFO("protoname <%s> username <%ls>", pszProtoName, tszUserName); if (!InitializeCriticalSectionAndSpinCount(&pr->CriticalSection, 0)) { SIPE_DEBUG_ERROR_NOFORMAT("Can't initialize critical section"); return NULL; } tmp = sipe_miranda_getString(pr, "transport"); if (!tmp) { sipe_miranda_setString(pr, "transport", "auto"); } else { mir_free(tmp); } /* To make it easy to detect when a SIPPROTO* isn't a SIPPROTO* */ strncpy(pr->_SIGNATURE, "AbandonAllHope..", sizeof(pr->_SIGNATURE)); pr->main_thread_id = GetCurrentThreadId(); pr->proto.m_iVersion = 2; pr->proto.m_szModuleName = mir_strdup(pszProtoName); pr->proto.m_tszUserName = mir_tstrdup(tszUserName); pr->proto.m_szProtoName = mir_strdup(pszProtoName); // set_buddies_offline(pr); fix_contact_groups(pr); /* Fill the function table */ #define PROTO_FUNC(name,func) ((struct sipe_backend_private)(pr->proto)).vtbl->name = func; pr->proto.vtbl->AddToList = AddToList; pr->proto.vtbl->AddToListByEvent = AddToListByEvent; pr->proto.vtbl->Authorize = Authorize; pr->proto.vtbl->AuthDeny = AuthDeny; pr->proto.vtbl->AuthRecv = AuthRecv; pr->proto.vtbl->AuthRequest = AuthRequest; pr->proto.vtbl->ChangeInfo = ChangeInfo; pr->proto.vtbl->FileAllow = sipe_miranda_FileAllow; pr->proto.vtbl->FileCancel = FileCancel; pr->proto.vtbl->FileDeny = sipe_miranda_FileDeny; pr->proto.vtbl->FileResume = FileResume; pr->proto.vtbl->GetCaps = GetCaps; pr->proto.vtbl->GetIcon = GetIcon; pr->proto.vtbl->GetInfo = sipe_miranda_GetInfo; pr->proto.vtbl->SearchBasic = SearchBasic; pr->proto.vtbl->SearchByEmail = sipe_miranda_SearchByEmail; pr->proto.vtbl->SearchByName = sipe_miranda_SearchByName; pr->proto.vtbl->SearchAdvanced = sipe_miranda_SearchAdvanced; pr->proto.vtbl->CreateExtendedSearchUI = CreateExtendedSearchUI; pr->proto.vtbl->RecvMsg = sipe_miranda_RecvMsg; pr->proto.vtbl->SendMsg = sipe_miranda_SendMsg; pr->proto.vtbl->SetStatus = sipe_miranda_SetStatus; pr->proto.vtbl->GetAwayMsg = sipe_miranda_GetAwayMsg; pr->proto.vtbl->SetAwayMsg = sipe_miranda_SetAwayMsg; pr->proto.vtbl->UserIsTyping = sipe_miranda_UserIsTyping; pr->proto.vtbl->SendFile = sipe_miranda_SendFile; pr->proto.vtbl->RecvFile = sipe_miranda_RecvFile; pr->proto.vtbl->OnEvent = OnEvent; /* Setup services */ CreateProtoService(pr, PS_CREATEACCMGRUI, &SvcCreateAccMgrUI ); CreateProtoService(pr, "/InviteToChat", &sipe_miranda_invite_to_chat); CreateProtoService(pr, "/StartChat",&sipe_miranda_start_chat); #define HOOKEVENT(evt,func) HookEventObj(evt, func, pr) HOOKEVENT(ME_OPT_INITIALISE, &OnOptionsInit); HOOKEVENT(ME_CLIST_GROUPCHANGE, &OnGroupChange); HOOKEVENT(ME_GC_EVENT, &OnChatEvent); HOOKEVENT(ME_CLIST_PREBUILDCONTACTMENU, &OnPreBuildContactMenu); HOOKEVENT(ME_DB_CONTACT_DELETED, &sipe_miranda_buddy_delete); HOOKEVENT(ME_MSG_WINDOWEVENT, &sipe_miranda_window_closed); HOOKEVENT(ME_GC_BUILDMENU, &sipe_miranda_build_chat_menu); return (PROTO_INTERFACE*)pr; } static int sipsimpleProtoUninit( PROTO_INTERFACE* _pr ) { SIPPROTO *pr = (SIPPROTO *)_pr; DeleteCriticalSection(&pr->CriticalSection); Netlib_CloseHandle(pr->m_hServerNetlibUser); mir_free(pr->proto.m_szProtoName); mir_free(pr->proto.m_szModuleName); mir_free(pr->proto.m_tszUserName); mir_free(pr->proto.vtbl); mir_free(pr); return 0; } __declspec(dllexport) int Load(PLUGINLINK *link) { PROTOCOLDESCRIPTOR pd = {0}; NETLIBUSER nlu = {0}; char *tmp; WORD iptype; pluginLink = link; sipe_core_init(""); mir_getMMI( &mmi ); /* Register the module */ pd.cbSize = sizeof(pd); pd.szName = SIPSIMPLE_PROTOCOL_NAME; pd.type = PROTOTYPE_PROTOCOL; pd.fnInit = sipsimpleProtoInit; pd.fnUninit = sipsimpleProtoUninit; CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM)&pd); /* Protocolwide netlib user for incoming connections (also abused for logging) */ nlu.cbSize = sizeof(nlu); nlu.flags = NUF_INCOMING | NUF_TCHAR | NUF_NOOPTIONS; nlu.szSettingsModule = SIPSIMPLE_PROTOCOL_NAME; nlu.minIncomingPorts = 10; InitializeCriticalSectionAndSpinCount(&sipe_miranda_debug_CriticalSection, 0); sipe_miranda_incoming_netlibuser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu); tmp = sipe_miranda_getGlobalString("public_ip"); if (!tmp) { sipe_miranda_setGlobalString("public_ip", "0.0.0.0"); } else { mir_free(tmp); } if (!sipe_miranda_getGlobalWord("iptype", &iptype)) { sipe_miranda_setGlobalWord("iptype", SIPE_MIRANDA_IP_LOCAL); } return 0; } __declspec(dllexport) int Unload(void) { Netlib_CloseHandle(sipe_miranda_incoming_netlibuser); sipe_miranda_incoming_netlibuser = NULL; DeleteCriticalSection(&sipe_miranda_debug_CriticalSection); sipe_core_destroy(); return 0; } BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) { hInst = hinstDLL; return TRUE; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-private.h ================================================ #ifdef _MSC_VER #define __func__ __FUNCTION__ #endif #define SIPSIMPLE_PROTOCOL_NAME LPGEN("Office Communicator") #define SIP_UNIQUEID "sip_screenname" #define SIPE_EVENTTYPE_ERROR_NOTIFY 2002 #define SIPE_DB_GETEVENTTEXT_ERROR_NOTIFY "SIP/SIMPLE/GetEventTextErrorNotify" #define SIPE_EVENTTYPE_INFO_NOTIFY 2003 #define SIPE_DB_GETEVENTTEXT_INFO_NOTIFY "SIP/SIMPLE/GetEventTextInfoNotify" #define SIPE_EVENTTYPE_IM_TOPIC 2010 #define SIPE_DB_GETEVENTTEXT_IM_TOPIC "SIP/SIMPLE/GetEventTextIMTopic" #define SIPE_MIRANDA_IP_LOCAL 0 #define SIPE_MIRANDA_IP_MANUAL 1 #define SIPE_MIRANDA_IP_PROG 2 typedef enum { SIPE_MIRANDA_DISCONNECTED = 0, /**< Disconnected. */ SIPE_MIRANDA_CONNECTED, /**< Connected. */ SIPE_MIRANDA_CONNECTING /**< Connecting. */ } sipe_miranda_ConnectionState; struct sipe_miranda_connection_info; typedef struct sipe_backend_private { PROTO_INTERFACE proto; struct sipe_core_public *sip; CRITICAL_SECTION CriticalSection; HANDLE m_hServerNetlibUser; sipe_miranda_ConnectionState state; gboolean valid; gboolean disconnecting; HANDLE disconnect_timeout; GSList *contactMenuChatItems; DWORD main_thread_id; char _SIGNATURE[16]; } SIPPROTO; struct sipe_backend_chat_session { SIPPROTO *pr; gchar *conv; }; typedef enum { SIPE_MIRANDA_INPUT_READ = 1 << 0, /**< A read condition. */ SIPE_MIRANDA_INPUT_WRITE = 1 << 1 /**< A write condition. */ } sipe_miranda_input_condition; /** The type of callbacks to handle events on file descriptors, as passed to * sipe_miranda_input_add(). The callback will receive the @c user_data * passed to sipe_miranda_input_add(), the file descriptor on which the event * occurred, and the condition that was satisfied to cause the callback to be * invoked. */ typedef void (*sipe_miranda_input_function)(gpointer, gint, sipe_miranda_input_condition); typedef struct sipe_miranda_sel_entry; #define CONTACTS_FOREACH(list) { \ GSList *entry = list; \ while (entry) { \ HANDLE hContact = entry->data; \ entry = entry->next; #define CONTACTS_FOREACH_END }} typedef INT_PTR (*SipSimpleServiceFunc)( SIPPROTO*, WPARAM, LPARAM ); typedef int (*SipSimpleEventFunc)( SIPPROTO*, WPARAM, LPARAM ); typedef void (*SipSimpleThreadFunc)( SIPPROTO*, void* ); #define _NI(string) SIPE_DEBUG_INFO( "%s:%s (%d) ##NOT IMPLEMENTED## %s", __FILE__, __FUNCTION__, __LINE__, #string ) #define _NIF() _NI("") #define _LOCK(crit) do { SIPE_DEBUG_INFO("[L:%08x] About to lock", crit); EnterCriticalSection(crit); SIPE_DEBUG_INFO("[L:%08x] Locked", crit); } while (0) #define _UNLOCK(crit) do { SIPE_DEBUG_INFO("[L:%08x] About to unlock", crit); LeaveCriticalSection(crit); SIPE_DEBUG_INFO("[L:%08x] Unlocked", crit); } while (0) #define LOCK _LOCK(&pr->CriticalSection) #define UNLOCK _UNLOCK(&pr->CriticalSection) #define _TRACE do { SIPE_DEBUG_INFO_NOFORMAT("TRACE") } while (0); void sipe_miranda_close( SIPPROTO *pr); TCHAR* CHAR2TCHAR( const char *chr ); char* TCHAR2CHAR( const TCHAR *tchr ); HANDLE sipe_miranda_AddEvent(const SIPPROTO *pr, HANDLE hContact, WORD wType, DWORD dwTime, DWORD flags, DWORD cbBlob, PBYTE pBlob); gchar* sipe_miranda_getContactString(const SIPPROTO *pr, HANDLE hContact, const gchar* name); gchar* sipe_miranda_getString(const SIPPROTO *pr, const gchar* name); int sipe_miranda_getStaticString(const SIPPROTO *pr, HANDLE hContact, const gchar* valueName, gchar* dest, unsigned dest_len); gchar* sipe_miranda_getGlobalString(const gchar* name); WORD sipe_miranda_getGlobalWord(const gchar* name, WORD* rv); WORD sipe_miranda_getWord(const SIPPROTO *pr, HANDLE hContact, const gchar* name, WORD* rv); DWORD sipe_miranda_getDword( const SIPPROTO *pr, HANDLE hContact, const gchar* name, DWORD* rv); gboolean sipe_miranda_getBool(const SIPPROTO *pr, const gchar *name, gboolean defval); void sipe_miranda_setContactString(const SIPPROTO *pr, HANDLE hContact, const gchar* name, const gchar* value); void sipe_miranda_setContactStringUtf(const SIPPROTO *pr, HANDLE hContact, const gchar* valueName, const gchar* parValue ); void sipe_miranda_setString(const SIPPROTO *pr, const gchar* name, const gchar* value); void sipe_miranda_setStringUtf(const SIPPROTO *pr, const gchar* name, const gchar* value); void sipe_miranda_setGlobalString(const gchar* name, const gchar* value); void sipe_miranda_setGlobalStringUtf(const gchar* valueName, const gchar* parValue ); int sipe_miranda_setGlobalWord(const gchar* szSetting, WORD wValue); int sipe_miranda_setWord(const SIPPROTO *pr, HANDLE hContact, const gchar* szSetting, WORD wValue); int sipe_miranda_setBool(const SIPPROTO *pr, const gchar *name, gboolean value); struct sipe_miranda_sel_entry* sipe_miranda_input_add(HANDLE fd, sipe_miranda_input_condition cond, sipe_miranda_input_function func, gpointer user_data); gboolean sipe_miranda_input_remove(struct sipe_miranda_sel_entry *entry); int sipe_miranda_SendBroadcast(SIPPROTO *pr, HANDLE hContact,int type,int result,HANDLE hProcess,LPARAM lParam); void sipe_miranda_SendProtoAck( SIPPROTO *pr, HANDLE hContact, DWORD dwCookie, int nAckResult, int nAckType, const char* pszMessage); void sipe_miranda_msgbox(const char *msg, const char *caption); gchar *sipe_miranda_uri_self(SIPPROTO *pr); void sipe_miranda_login(SIPPROTO *pr); struct sipe_miranda_connection_info *sipe_miranda_connect(SIPPROTO *pr, const gchar *host, int port, gboolean tls, int timeout, void (*callback)(HANDLE fd, void *data, const gchar *reason), void *data); gboolean sipe_miranda_cmd(gchar *cmd, gchar *buf, DWORD *maxlen); gchar* sipe_miranda_eliminate_html(const gchar *string, int len); gchar* sipe_miranda_html2rtf(const gchar *text); unsigned short sipe_miranda_network_get_port_from_fd( HANDLE fd ); const gchar *sipe_miranda_get_local_ip(void); void sipe_miranda_connection_destroy(SIPPROTO *pr); void sipe_miranda_connection_error_reason(SIPPROTO *pr, sipe_connection_error error, const gchar *msg); gpointer sipe_miranda_schedule_mseconds(void (*callback)(gpointer), guint timeout, gpointer data); /* Buddy utility functions */ sipe_backend_buddy sipe_miranda_buddy_find(SIPPROTO *pr, const gchar *name, const gchar *group); GSList* sipe_miranda_buddy_find_all(SIPPROTO *pr, const gchar *buddy_name, const gchar *group_name); /* Plugin interface functions */ int sipe_miranda_SetStatus( SIPPROTO *pr, int iNewStatus ); int sipe_miranda_SendMsg(SIPPROTO *pr, HANDLE hContact, int flags, const char* msg); int sipe_miranda_RecvMsg(SIPPROTO *pr, HANDLE hContact, PROTORECVEVENT* pre); int sipe_miranda_SetAwayMsg(SIPPROTO *pr, int m_iStatus, const PROTOCHAR* msg); HANDLE sipe_miranda_GetAwayMsg( SIPPROTO *pr, HANDLE hContact ); HANDLE sipe_miranda_SendFile( SIPPROTO *pr, HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles ); int sipe_miranda_RecvFile( SIPPROTO *pr, HANDLE hContact, PROTOFILEEVENT* evt ); int sipe_miranda_FileDeny( SIPPROTO *pr, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason ); HANDLE sipe_miranda_FileAllow( SIPPROTO *pr, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath ); int sipe_miranda_UserIsTyping( SIPPROTO *pr, HANDLE hContact, int type ); int sipe_miranda_GetInfo( SIPPROTO *pr, HANDLE hContact, int infoType ); HANDLE sipe_miranda_SearchByEmail( SIPPROTO *pr, const PROTOCHAR* email ); HANDLE sipe_miranda_SearchByName( SIPPROTO *pr, const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName); HWND sipe_miranda_SearchAdvanced( SIPPROTO *pr, HWND owner ); /* Plugin event functions */ int sipe_miranda_buddy_delete(SIPPROTO *pr, WPARAM wParam, LPARAM lParam); int SipeStatusToMiranda(guint activity); guint MirandaStatusToSipe(int status); ================================================ FILE: src/miranda/miranda-resource.h ================================================ #ifndef IDC_STATIC #define IDC_STATIC (-1) #endif #define IDD_ACCMGRUI 135 #define IDD_OPT_SIPSIMPLE 137 #define IDD_OPT_SIPSIMPLE_ABOUT 139 #define IDD_SEARCHUI 142 #define IDC_USESSO 1000 #define IDC_CONNTYPE 1001 #define IDC_AUTHTYPE 1002 #define IDC_ABOUTSIPE 1003 #define IDC_IPPROGEXE 1005 #define IDC_IPLOCALFOUND 1006 #define IDC_PASSWORD 1020 #define IDC_LOGIN 1021 #define IDC_HANDLE 1022 #define IDC_PUBLICIP 1024 #define IDC_SSO 1030 #define IDC_MSO 1031 #define IDC_IPLOCAL 1040 #define IDC_IPMANUAL 1041 #define IDC_IPPROG 1042 #define IDC_SEARCH_FN 1044 #define IDC_SEARCH_LN 1045 #define IDC_SEARCH_COMPANY 1046 #define IDC_SEARCH_COUNTRY 1047 ================================================ FILE: src/miranda/miranda-schedule.c ================================================ /** * @file miranda-schedule.c * * pidgin-sipe * * Copyright (C) 2010-11 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "newpluginapi.h" #include "m_system.h" #include "m_protosvc.h" #include "m_protoint.h" #include "miranda-private.h" struct time_entry { gpointer core_data; guint timeout; HANDLE sem; gboolean cancelled; SIPPROTO *pr; void (*callback)(gpointer); /* Private. For locking only */ HANDLE hDoneEvent; }; static void __stdcall timeout_cb_async(void *data) { struct time_entry *entry = (struct time_entry*)data; SIPPROTO *pr = entry->pr; if (entry->cancelled == TRUE) { SIPE_DEBUG_INFO("Entry <%08x> already cancelled. Not calling timeout function", entry); } else { SIPE_DEBUG_INFO("Calling timeout function for entry <%08x>", entry); LOCK; entry->callback(entry->core_data); UNLOCK; } SetEvent(entry->hDoneEvent); } static unsigned __stdcall timeoutfunc(void* data) { struct time_entry *entry = (struct time_entry*)data; DWORD ret; SIPPROTO *pr = entry->pr; SIPE_DEBUG_INFO("timeout start; <%08x> timeout is <%d>", entry, entry->timeout); entry->sem = CreateSemaphore(NULL, 0, 100, NULL); ret = WaitForSingleObjectEx( entry->sem, entry->timeout, FALSE); if (entry->cancelled == TRUE) { SIPE_DEBUG_INFO("<%08x> Timeout cancelled by caller", entry); } else if (ret == WAIT_TIMEOUT) { SIPE_DEBUG_INFO("<%08x> about to run", entry); if (entry->cancelled == TRUE) { SIPE_DEBUG_INFO("<%08x> Timeout cancelled by caller in the nick of time", entry); } else { entry->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); CallFunctionAsync(timeout_cb_async, entry); WaitForSingleObject(entry->hDoneEvent, INFINITE); CloseHandle(entry->hDoneEvent); } SIPE_DEBUG_INFO("<%08x> exiting", entry); } else { SIPE_DEBUG_INFO("<%08x> Something unexpected happened: <%d>", entry, ret); } CloseHandle(entry->sem); g_free(entry); return 0; } gpointer sipe_miranda_schedule_mseconds(void (*callback)(gpointer), guint timeout, gpointer data) { struct time_entry *entry; entry = g_new0(struct time_entry,1); entry->timeout = timeout; entry->core_data = data; entry->cancelled = FALSE; entry->pr = data; /* FIXME: Assumes data = SIPPROTO * */ entry->callback = callback; SIPE_DEBUG_INFO("Scheduling timeout in <%u>ms for entry <%08x>", timeout, entry); CloseHandle((HANDLE) mir_forkthreadex( timeoutfunc, entry, 65536, NULL )); return entry; } gpointer sipe_backend_schedule_mseconds(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, guint timeout, gpointer data) { struct time_entry *entry; entry = g_new0(struct time_entry,1); entry->timeout = timeout; entry->core_data = data; entry->cancelled = FALSE; entry->pr = sipe_public->backend_private; entry->callback = sipe_core_schedule_execute; CloseHandle((HANDLE) mir_forkthreadex( timeoutfunc, entry, 65536, NULL )); return entry; } gpointer sipe_backend_schedule_seconds(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, guint timeout, gpointer data) { return sipe_backend_schedule_mseconds( sipe_public, timeout*1000, data); } void sipe_backend_schedule_cancel(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, gpointer data) { struct time_entry *entry = (struct time_entry*) data; if (entry && entry->sem) { SIPE_DEBUG_INFO("Cancelling timeout <%08x>", entry); entry->cancelled = TRUE; ReleaseSemaphore(entry->sem, 1, NULL); } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-search.c ================================================ /** * @file miranda-search.c * * pidgin-sipe * * Copyright (C) 2011-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "miranda-version.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_system.h" #include "m_utils.h" #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" #include "miranda-resource.h" struct sipe_backend_search_results { int dummy; }; void sipe_backend_search_failed(struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_search_token *token, const gchar *msg) { sipe_miranda_SendBroadcast(sipe_public->backend_private, NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)1, 0); sipe_backend_notify_error(sipe_public, msg, NULL); } struct sipe_backend_search_results *sipe_backend_search_results_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_search_token *token) { return g_new0(struct sipe_backend_search_results, 1); } void sipe_backend_search_results_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_search_results *results, const gchar *uri, const gchar *name, const gchar *company, const gchar *country, const gchar *email) { SIPPROTO *pr = sipe_public->backend_private; PROTOSEARCHRESULT psr = { 0 }; HANDLE hProcess = (HANDLE)1; /* g_hash_table_lookup(opts, "searchid"); */ gchar **nameparts; psr.cbSize = sizeof(psr); psr.id = (PROTOCHAR*)uri; nameparts = g_strsplit_set(name, ",", 2); psr.nick = (FNAMECHAR*)name; psr.firstName = (PROTOCHAR*)(nameparts[1] ? nameparts[1] : NULL); psr.lastName = (PROTOCHAR*)nameparts[0]; psr.email = (PROTOCHAR*)email; sipe_miranda_SendBroadcast(pr, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, hProcess, (LPARAM) & psr); g_strfreev(nameparts); } void sipe_backend_search_results_finalize(struct sipe_core_public *sipe_public, struct sipe_backend_search_results *results, const gchar *description, gboolean more) { SIPPROTO *pr = sipe_public->backend_private; HANDLE hProcess = (HANDLE)1; /* g_hash_table_lookup(opts, "searchid"); */ sipe_miranda_SendBroadcast(pr, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, hProcess, 0); g_free(results); } HANDLE sipe_miranda_SearchByEmail( SIPPROTO *pr, const PROTOCHAR* email ) { char *mail; SIPE_DEBUG_INFO("SearchByEmail: email <%S>", email); if (!pr->sip) return NULL; mail = mir_t2a(email); LOCK; sipe_core_buddy_search(pr->sip, NULL, NULL, NULL, mail, NULL, NULL, NULL); UNLOCK; mir_free(mail); return (HANDLE)1; } HANDLE sipe_miranda_SearchByName( SIPPROTO *pr, const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName) { char *given_name; char *surname; SIPE_DEBUG_INFO("SearchByName: nick <%S> firstname <%S> lastname <%S>", nick, firstName, lastName); if (!pr->sip) return NULL; given_name = mir_t2a(firstName); surname = mir_t2a(lastName); LOCK; sipe_core_buddy_search(pr->sip, NULL, given_name, surname, NULL, NULL, NULL, NULL); UNLOCK; mir_free(given_name); mir_free(surname); return (HANDLE)1; } HWND sipe_miranda_SearchAdvanced( SIPPROTO *pr, HWND owner ) { char buf[512]; GHashTable *query = g_hash_table_new_full(NULL,NULL,NULL,g_free); GString *msg; if (!pr->sip) return NULL; msg = g_string_new("SearchAdvanced: "); GetDlgItemTextA(owner, IDC_SEARCH_FN, buf, sizeof(buf)); if (strlen(buf)) { g_string_append_printf(msg, "firstname <%s> ", buf); g_hash_table_insert(query, "givenName", g_strdup(buf)); } GetDlgItemTextA(owner, IDC_SEARCH_LN, buf, sizeof(buf)); if (strlen(buf)) { g_string_append_printf(msg, "lastname <%s> ", buf); g_hash_table_insert(query, "sn", g_strdup(buf)); } GetDlgItemTextA(owner, IDC_SEARCH_COMPANY, buf, sizeof(buf)); if (strlen(buf)) { g_string_append_printf(msg, "company <%s> ", buf); g_hash_table_insert(query, "company", g_strdup(buf)); } GetDlgItemTextA(owner, IDC_SEARCH_COUNTRY, buf, sizeof(buf)); if (strlen(buf)) { g_string_append_printf(msg, "country <%s> ", buf); g_hash_table_insert(query, "c", g_strdup(buf)); } SIPE_DEBUG_INFO_NOFORMAT(msg->str); g_string_free(msg, TRUE); LOCK; sipe_backend_search_failed(pr->sip, NULL, "Not implemented"); /* ret = (HANDLE)sipe_core_buddy_search( pr->sip, NULL, query, sipsimple_search_contact_cb, pr); */ UNLOCK; return (HANDLE)1; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-setting.c ================================================ /** * @file miranda-setting.c * * pidgin-sipe * * Copyright (C) 2010-2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "sipe-core.h" #include "sipe-backend.h" #include "miranda-version.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_system.h" #include "m_database.h" #include "miranda-private.h" /** * Map sipe_setting values to miranda account setting keys * * This needs to be kept in sync with * * api/sipe-backend.h */ static const gchar * const setting_name[SIPE_SETTING_LAST] = { "email_url", /* SIPE_SETTING_EMAIL_URL */ "login", /* SIPE_SETTING_EMAIL_LOGIN */ "password", /* SIPE_SETTING_EMAIL_PASSWORD */ "groupchat_user", /* SIPE_SETTING_GROUPCHAT_USER */ "NOTDEFINED", /* SIPE_SETTING_RDP_CLIENT */ "useragent" /* SIPE_SETTING_USER_AGENT */ }; const gchar *sipe_backend_setting(struct sipe_core_public *sipe_public, sipe_setting type) { SIPPROTO *pr = sipe_public->backend_private; gchar *ret; gchar *tmp; if (type == SIPE_SETTING_EMAIL_PASSWORD) { tmp = (char*)mir_calloc(1024); if (sipe_miranda_getStaticString(pr, NULL, "password", tmp, 1024 )) tmp[0] = '\0'; CallService(MS_DB_CRYPT_DECODESTRING, sizeof(tmp),(LPARAM)tmp); } else { tmp = sipe_miranda_getString(pr, setting_name[type] ); } ret = g_strdup(tmp); mir_free(tmp); return ret; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-status.c ================================================ /** * @file miranda-status.c * * pidgin-sipe * * Copyright (C) 2011-2015 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "miranda-version.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_system.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" guint sipe_backend_status(struct sipe_core_public *sipe_public) { SIPPROTO* pr = sipe_public->backend_private; return MirandaStatusToSipe(pr->proto.m_iStatus); } int sipe_miranda_SetStatus( SIPPROTO *pr, int iNewStatus ) { int oldStatus; if (!pr->m_hServerNetlibUser) return 0; if (pr->proto.m_iDesiredStatus == iNewStatus) return 0; oldStatus = pr->proto.m_iStatus; pr->proto.m_iDesiredStatus = iNewStatus; SIPE_DEBUG_INFO("SetStatus: newstatus <%x>", iNewStatus); if (iNewStatus == ID_STATUS_OFFLINE) { pr->disconnecting = TRUE; sipe_miranda_connection_destroy(pr); pr->valid = FALSE; pr->disconnecting = FALSE; } else { if (pr->proto.m_iStatus == ID_STATUS_OFFLINE) { pr->valid = TRUE; pr->state = SIPE_MIRANDA_CONNECTING; pr->proto.m_iStatus = ID_STATUS_CONNECTING; sipe_miranda_SendBroadcast(pr, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, pr->proto.m_iStatus); sipe_miranda_login(pr); } else if (pr->state == SIPE_MIRANDA_CONNECTED) { pr->proto.m_iStatus = pr->proto.m_iDesiredStatus; sipe_miranda_SendBroadcast(pr, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, pr->proto.m_iStatus); LOCK; if (pr->proto.m_iStatus != ID_STATUS_OFFLINE) { gchar *note = sipe_miranda_getString(pr, "note"); sipe_core_status_set(pr->sip, TRUE, MirandaStatusToSipe(iNewStatus), note); mir_free(note); } UNLOCK; } } /* //Will send an ack with: //type=ACKTYPE_STATUS, result=ACKRESULT_SUCCESS, hProcess=(HANDLE)previousMode, lParam=newMode //when the change completes. This ack is sent for all changes, not just ones //caused by calling this function. //Note that newMode can be ID_STATUS_CONNECTING<=newModebackend_private; int iNewStatus = SipeStatusToMiranda(activity); if (!pr->m_hServerNetlibUser) return FALSE; if (pr->proto.m_iDesiredStatus == iNewStatus) return FALSE; return(TRUE); } void sipe_backend_status_and_note(struct sipe_core_public *sipe_public, guint activity, const gchar *message) { sipe_miranda_SetStatus(sipe_public->backend_private, SipeStatusToMiranda(activity)); } ================================================ FILE: src/miranda/miranda-transport.c ================================================ /** * @file miranda-transport.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "sipe-common.h" #include "sipe-core.h" #include "sipe-backend.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_netlib.h" #include "miranda-private.h" #define MIRANDA_TRANSPORT ((struct sipe_transport_miranda *) conn) #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport) #define BUFFER_SIZE_INCREMENT 4096 struct sipe_transport_miranda { /* public part shared with core */ struct sipe_transport_connection public; /* miranda private part */ transport_connected_cb *connected; transport_input_cb *input; transport_error_cb *error; HANDLE fd; struct sipe_miranda_sel_entry *inputhandler; SIPPROTO *pr; /* Private. For locking only */ HANDLE hDoneEvent; }; static void __stdcall transport_input_cb_async(void *data) { struct sipe_transport_miranda *transport = (struct sipe_transport_miranda *)data; struct sipe_transport_connection *conn = SIPE_TRANSPORT_CONNECTION; SIPPROTO *pr = transport->pr; LOCK; transport->input(conn); UNLOCK; SetEvent(transport->hDoneEvent); } static void miranda_sipe_input_cb(gpointer data, SIPE_UNUSED_PARAMETER gint source, SIPE_UNUSED_PARAMETER sipe_miranda_input_condition cond) { struct sipe_transport_miranda *transport = (struct sipe_transport_miranda *)data; struct sipe_transport_connection *conn = SIPE_TRANSPORT_CONNECTION; SIPPROTO *pr = transport->pr; int len; int readlen; gboolean firstread = TRUE; LOCK; if (!pr->valid) { UNLOCK; return; } if (!transport->fd) { UNLOCK; return; } do { /* Increase input buffer size as needed */ if (conn->buffer_length < conn->buffer_used + BUFFER_SIZE_INCREMENT) { conn->buffer_length += BUFFER_SIZE_INCREMENT; conn->buffer = g_realloc(conn->buffer, conn->buffer_length); SIPE_DEBUG_INFO("miranda_sipe_input_cb: new buffer length %" G_GSIZE_FORMAT, conn->buffer_length); } /* Try to read as much as there is space left in the buffer */ /* minus 1 for the string terminator */ readlen = conn->buffer_length - conn->buffer_used - 1; len = Netlib_Recv(transport->fd, conn->buffer + conn->buffer_used, readlen, MSG_NODUMP); if (len == SOCKET_ERROR) { SIPE_DEBUG_INFO("miranda_sipe_input_cb: read error"); if (transport) transport->error(SIPE_TRANSPORT_CONNECTION, "Read error"); UNLOCK; return; } else if (firstread && (len == 0)) { SIPE_DEBUG_ERROR_NOFORMAT("miranda_sipe_input_cb: server has disconnected"); transport->error(SIPE_TRANSPORT_CONNECTION, "Server has disconnected"); UNLOCK; return; } conn->buffer_used += len; firstread = FALSE; /* Equivalence indicates that there is possibly more data to read */ } while (len == readlen); conn->buffer[conn->buffer_used] = '\0'; UNLOCK; transport->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); CallFunctionAsync(transport_input_cb_async, transport); WaitForSingleObject(transport->hDoneEvent, INFINITE); CloseHandle(transport->hDoneEvent); } static void connected_callback(HANDLE fd, void* data, const gchar *reason) { struct sipe_transport_miranda *transport = (struct sipe_transport_miranda*)data; SIPPROTO *pr = transport->pr; if (!pr) return; LOCK; if (!fd) { transport->error(SIPE_TRANSPORT_CONNECTION, reason); } else { transport->fd = fd; transport->public.client_port = sipe_miranda_network_get_port_from_fd( transport->fd ); transport->inputhandler = sipe_miranda_input_add(transport->fd, SIPE_MIRANDA_INPUT_READ, miranda_sipe_input_cb, transport ); transport->connected(SIPE_TRANSPORT_CONNECTION); } UNLOCK; } struct sipe_transport_connection * sipe_backend_transport_connect(struct sipe_core_public *sipe_public, const sipe_connect_setup *setup) { struct sipe_transport_miranda *transport = g_new0(struct sipe_transport_miranda, 1); SIPPROTO *pr = sipe_public->backend_private; NETLIBOPENCONNECTION ncon = {0}; transport->public.type = setup->type; transport->public.user_data = setup->user_data; transport->connected = setup->connected; transport->input = setup->input; transport->error = setup->error; transport->pr = pr; sipe_miranda_connect(pr, setup->server_name, setup->server_port, (setup->type == SIPE_TRANSPORT_TLS), 5, connected_callback, transport); return(SIPE_TRANSPORT_CONNECTION); } void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn) { struct sipe_transport_miranda *transport = MIRANDA_TRANSPORT; SIPE_DEBUG_INFO("Disconnecting transport <%08x>", transport); if (!transport) return; Netlib_CloseHandle(transport->fd); transport->fd = NULL; if (transport->inputhandler) sipe_miranda_input_remove(transport->inputhandler); g_free(transport->public.buffer); g_free(transport); } gchar *sipe_backend_transport_ip_address(struct sipe_transport_connection *conn) { // @TODO: provide correct implementation for Miranda return("0.0.0.0"); } void sipe_backend_transport_message(struct sipe_transport_connection *conn, const gchar *buffer) { struct sipe_transport_miranda *transport = MIRANDA_TRANSPORT; guint written = 0; do { int len = Netlib_Send(transport->fd, buffer + written, strlen(buffer + written), MSG_NODUMP); if (len == SOCKET_ERROR) { SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_transport_message: error, exiting"); transport->error(SIPE_TRANSPORT_CONNECTION, "Write error"); return; } written += len; } while (written < strlen(buffer)); } void sipe_backend_transport_flush(struct sipe_transport_connection *conn) { /* N/A */ } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-user.c ================================================ /** * @file miranda-user.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "miranda-version.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_database.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" void sipe_backend_user_feedback_typing(struct sipe_core_public *sipe_public, const gchar *from) { HANDLE hContact = sipe_backend_buddy_find( sipe_public, from, NULL ); if (!hContact) return; CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact, (LPARAM)6); } void sipe_backend_user_feedback_typing_stop(struct sipe_core_public *sipe_public, const gchar *from) { HANDLE hContact = sipe_backend_buddy_find( sipe_public, from, NULL ); if (!hContact) return; CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact, (LPARAM)0); } void sipe_backend_user_ask(struct sipe_core_public *sipe_public, const gchar *message, const gchar *accept_label, const gchar *decline_label, gpointer key) { _NIF(); } void sipe_backend_user_ask_choice(struct sipe_core_public *sipe_public, const gchar *message, GSList *choices, gpointer key) { _NIF(); } void sipe_backend_user_close_ask(gpointer key) { _NIF(); } int sipe_miranda_SetAwayMsg(SIPPROTO *pr, int m_iStatus, const PROTOCHAR* msg) { const gchar *note = TCHAR2CHAR(msg); SIPE_DEBUG_INFO("SetAwayMsg: status <%x> msg <%ls>", m_iStatus, msg); sipe_miranda_setString(pr, "note", note); LOCK; if (pr->state == SIPE_MIRANDA_CONNECTED) sipe_core_status_set(pr->sip, FALSE, MirandaStatusToSipe(pr->proto.m_iStatus), note); UNLOCK; return 0; } int sipe_miranda_UserIsTyping( SIPPROTO *pr, HANDLE hContact, int type ) { SIPE_DEBUG_INFO("type <%x>", type); if (hContact) { DBVARIANT dbv; char *name; if ( !DBGetContactSettingString( hContact, pr->proto.m_szModuleName, SIP_UNIQUEID, &dbv )) { name = g_strdup(dbv.pszVal); DBFreeVariant(&dbv); } else { return 1; } switch (type) { case PROTOTYPE_SELFTYPING_ON: LOCK; sipe_core_user_feedback_typing(pr->sip, name, TRUE); UNLOCK; g_free(name); return 0; case PROTOTYPE_SELFTYPING_OFF: /* Not supported anymore? */ LOCK; sipe_core_user_feedback_typing(pr->sip, name, FALSE); UNLOCK; g_free(name); return 0; } g_free(name); } return 1; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-utils.c ================================================ /** * @file miranda-utils.c * * pidgin-sipe * * Copyright (C) 2010-11 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "miranda-version.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_database.h" #include "m_netlib.h" #include "m_langpack.h" #include "m_protomod.h" #include "glib.h" #include "sipe-backend.h" #include "sipe-core.h" #include "miranda-private.h" /* * Table to hold HTML entities we want to convert */ static GHashTable *entities = NULL; /** * Various shortcut functions to get database values */ gchar* sipe_miranda_getGlobalString(const gchar* name) { return DBGetString( NULL, SIPSIMPLE_PROTOCOL_NAME, name ); } gchar* sipe_miranda_getContactString(const SIPPROTO *pr, HANDLE hContact, const gchar* name) { return DBGetString( hContact, pr->proto.m_szModuleName, name ); } gchar* sipe_miranda_getString(const SIPPROTO *pr, const gchar* name) { return sipe_miranda_getContactString( pr, NULL, name ); } DWORD sipe_miranda_getDword(const SIPPROTO *pr, HANDLE hContact, const gchar* name, DWORD* rv) { DBVARIANT dbv; DBCONTACTGETSETTING cgs; cgs.szModule = pr->proto.m_szModuleName; cgs.szSetting = name; cgs.pValue=&dbv; if(CallService(MS_DB_CONTACT_GETSETTING,(WPARAM)hContact,(LPARAM)&cgs)) return 0; if (rv) { *rv = dbv.dVal; return 1; } else { return dbv.dVal; } } WORD sipe_miranda_getGlobalWord(const gchar* name, WORD* rv) { DBVARIANT dbv; DBCONTACTGETSETTING cgs; cgs.szModule = SIPSIMPLE_PROTOCOL_NAME; cgs.szSetting = name; cgs.pValue=&dbv; if(CallService(MS_DB_CONTACT_GETSETTING, (WPARAM)NULL,(LPARAM)&cgs)) return 0; if (rv) { *rv = dbv.wVal; return 1; } else { return dbv.wVal; } } WORD sipe_miranda_getWord(const SIPPROTO *pr, HANDLE hContact, const gchar* name, WORD* rv) { DBVARIANT dbv; DBCONTACTGETSETTING cgs; cgs.szModule = pr->proto.m_szModuleName; cgs.szSetting = name; cgs.pValue=&dbv; if(CallService(MS_DB_CONTACT_GETSETTING,(WPARAM)hContact,(LPARAM)&cgs)) return 0; if (rv) { *rv = dbv.wVal; return 1; } else { return dbv.wVal; } } gboolean sipe_miranda_getBool(const SIPPROTO *pr, const gchar *name, gboolean defval) { WORD ret; if (sipe_miranda_getWord( pr, NULL, name, &ret )) return ret?TRUE:FALSE; return defval; } int sipe_miranda_getStaticString(const SIPPROTO *pr, HANDLE hContact, const gchar* valueName, gchar* dest, unsigned dest_len) { DBVARIANT dbv; DBCONTACTGETSETTING sVal; dbv.pszVal = dest; dbv.cchVal = (WORD)dest_len; dbv.type = DBVT_ASCIIZ; sVal.pValue = &dbv; sVal.szModule = pr->proto.m_szModuleName; sVal.szSetting = valueName; if (CallService(MS_DB_CONTACT_GETSETTINGSTATIC, (WPARAM)hContact, (LPARAM)&sVal) != 0) return 1; return (dbv.type != DBVT_ASCIIZ); } /** * Various shortcut functions to set database values */ void sipe_miranda_setGlobalString(const gchar* name, const gchar* value) { DBWriteContactSettingString(NULL, SIPSIMPLE_PROTOCOL_NAME, name, value); } void sipe_miranda_setGlobalStringUtf(const gchar* valueName, const gchar* parValue ) { DBWriteContactSettingStringUtf( NULL, SIPSIMPLE_PROTOCOL_NAME, valueName, parValue ); } void sipe_miranda_setContactString(const SIPPROTO *pr, HANDLE hContact, const gchar* name, const gchar* value) { DBWriteContactSettingString(hContact, pr->proto.m_szModuleName, name, value); } void sipe_miranda_setContactStringUtf(const SIPPROTO *pr, HANDLE hContact, const gchar* valueName, const gchar* parValue ) { DBWriteContactSettingStringUtf( hContact, pr->proto.m_szModuleName, valueName, parValue ); } void sipe_miranda_setString(const SIPPROTO *pr, const gchar* name, const gchar* value) { sipe_miranda_setContactString( pr, NULL, name, value ); } void sipe_miranda_setStringUtf(const SIPPROTO *pr, const gchar* name, const gchar* value) { sipe_miranda_setContactStringUtf( pr, NULL, name, value ); } int sipe_miranda_setGlobalWord(const gchar* szSetting, WORD wValue) { return DBWriteContactSettingWord(NULL, SIPSIMPLE_PROTOCOL_NAME, szSetting, wValue); } int sipe_miranda_setWord(const SIPPROTO *pr, HANDLE hContact, const gchar* szSetting, WORD wValue) { return DBWriteContactSettingWord(hContact, pr->proto.m_szModuleName, szSetting, wValue); } int sipe_miranda_setBool(const SIPPROTO *pr, const gchar *name, gboolean value) { return DBWriteContactSettingWord(NULL, pr->proto.m_szModuleName, name, value?1:0); } /* * Initialize our table of HTML entities */ #define ADDENT(a,b) g_hash_table_insert(entities, a, b) static void initEntities(void) { entities = g_hash_table_new(g_str_hash, g_str_equal); ADDENT("nbsp"," "); ADDENT("quot","\""); ADDENT("lt","<"); ADDENT("gt",">"); ADDENT("apos","'"); } /* * WARNING: Returns miranda-allocated string, not glib one */ gchar* sipe_miranda_eliminate_html(const gchar *string, int len) { gchar *tmp = (char*)mir_alloc(len + 1); int i,j; BOOL tag = FALSE; gchar *res; if (!entities) initEntities(); for (i=0,j=0;i", 4) || !_strnicmp(string + i, "
", 5))) { // insert newline tmp[j] = '\r'; j++; tmp[j] = '\n'; j++; } tag = TRUE; } else if (tag && string[i] == '>') { tag = FALSE; } else if (!tag) { char *tkend; if ((string[i] == '&') && (tkend = strstr((char*)&string[i], ";"))) { gchar *rep; gchar c = *tkend; *tkend = '\0'; rep = (char*)g_hash_table_lookup(entities, &string[i+1]); if (rep) { strcpy(&tmp[j], rep); j += strlen(rep); i += strlen(&string[i]); *tkend = c; } else { *tkend = c; tmp[j] = string[i]; j++; } } else { tmp[j] = string[i]; j++; } } tmp[j] = '\0'; } res = tmp; return res; } unsigned short sipe_miranda_network_get_port_from_fd( HANDLE fd ) { SOCKET sock = CallService(MS_NETLIB_GETSOCKET, (WPARAM)fd, (LPARAM)0); struct sockaddr_in sockbuf; int namelen = sizeof(sockbuf); getsockname(sock, (struct sockaddr *)&sockbuf, &namelen); SIPE_DEBUG_INFO("<%x> <%x><%x><%s>", namelen, sockbuf.sin_family, sockbuf.sin_port, inet_ntoa(sockbuf.sin_addr) ); return sockbuf.sin_port; } /* Misc functions */ TCHAR _tcharbuf[32768]; TCHAR* CHAR2TCHAR( const char *chr ) { #ifdef UNICODE if (!chr) return NULL; mbstowcs( _tcharbuf, chr, strlen(chr)+1 ); return _tcharbuf; #else return chr; #endif } char _charbuf[32768]; char* TCHAR2CHAR( const TCHAR *tchr ) { #ifdef UNICODE if (!tchr) return NULL; wcstombs( _charbuf, tchr, wcslen(tchr)+1 ); return _charbuf; #else return tchr; #endif } HANDLE sipe_miranda_AddEvent(const SIPPROTO *pr, HANDLE hContact, WORD wType, DWORD dwTime, DWORD flags, DWORD cbBlob, PBYTE pBlob) { DBEVENTINFO dbei = {0}; dbei.cbSize = sizeof(dbei); dbei.szModule = pr->proto.m_szModuleName; dbei.timestamp = dwTime; dbei.flags = flags; dbei.eventType = wType; dbei.cbBlob = cbBlob; dbei.pBlob = pBlob; return (HANDLE)CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei); } struct msgboxinfo { TCHAR *msg; TCHAR *caption; }; static unsigned __stdcall msgboxThread(void* arg) { struct msgboxinfo *err = (struct msgboxinfo*)arg; if (!err) return 0; MessageBox(NULL, err->msg, err->caption, MB_OK); mir_free(err->msg); mir_free(err->caption); g_free(err); return 0; } void sipe_miranda_msgbox(const char *msg, const char *caption) { struct msgboxinfo *info = g_new(struct msgboxinfo,1); info->msg = mir_a2t(msg); info->caption = mir_a2t(caption); CloseHandle((HANDLE) mir_forkthreadex( msgboxThread, info, 8192, NULL )); } char* sipe_miranda_acktype_strings[] = { "ACKTYPE_MESSAGE", "ACKTYPE_URL", "ACKTYPE_FILE", "ACKTYPE_CHAT", "ACKTYPE_AWAYMSG", "ACKTYPE_AUTHREQ", "ACKTYPE_ADDED", "ACKTYPE_GETINFO", "ACKTYPE_SETINFO", "ACKTYPE_LOGIN", "ACKTYPE_SEARCH", "ACKTYPE_NEWUSER", "ACKTYPE_STATUS", "ACKTYPE_CONTACTS", "ACKTYPE_AVATAR", "ACKTYPE_EMAIL" }; char* sipe_miranda_ackresult_strings[] = { "ACKRESULT_SUCCESS", "ACKRESULT_FAILED", "ACKRESULT_CONNECTING", "ACKRESULT_CONNECTED", "ACKRESULT_INITIALISING", "ACKRESULT_SENTREQUEST", "ACKRESULT_DATA", "ACKRESULT_NEXTFILE", "ACKRESULT_FILERESUME", "ACKRESULT_DENIED", "ACKRESULT_STATUS", "ACKRESULT_LISTENING", "ACKRESULT_CONNECTPROXY", "ACKRESULT_SEARCHRESULT" }; int sipe_miranda_SendBroadcast(SIPPROTO *pr, HANDLE hContact,int type,int result,HANDLE hProcess,LPARAM lParam) { ACKDATA ack = {0}; ack.cbSize = sizeof(ACKDATA); ack.szModule = pr->proto.m_szModuleName; ack.hContact = hContact; ack.type = type; ack.result = result; ack.hProcess = hProcess; ack.lParam = lParam; SIPE_DEBUG_INFO("broadcasting contact <%08x> type <%d:%s> result <%d:%s> par1 <%08x> par2 <%08x>", hContact, type, sipe_miranda_acktype_strings[type], result, sipe_miranda_ackresult_strings[result>99 ? result-98 : result], hProcess, lParam); return CallServiceSync(MS_PROTO_BROADCASTACK,0,(LPARAM)&ack); } struct sipe_miranda_connection_info { SIPPROTO *pr; gchar *server_name; int server_port; int timeout; gboolean tls; void (*callback)(HANDLE fd, void *data, const gchar *reason); void *data; /* Private. For locking only */ HANDLE hDoneEvent; HANDLE fd; const gchar *reason; }; static void __stdcall connection_cb_async(void *data) { struct sipe_miranda_connection_info *entry = (struct sipe_miranda_connection_info*)data; SIPE_DEBUG_INFO("[C:%08x] Calling real connected function", entry); entry->callback(entry->fd, entry->data, entry->reason); SetEvent(entry->hDoneEvent); } static unsigned __stdcall sipe_miranda_connected_callback(void* data) { struct sipe_miranda_connection_info *info = (struct sipe_miranda_connection_info*)data; SIPPROTO *pr = info->pr; NETLIBOPENCONNECTION ncon = {0}; ncon.cbSize = sizeof(ncon); ncon.flags = NLOCF_V2; ncon.szHost = info->server_name; ncon.wPort = info->server_port; ncon.timeout = info->timeout; info->fd = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)pr->m_hServerNetlibUser, (LPARAM)&ncon); if (info->fd == NULL) { SIPE_DEBUG_INFO("[C:%08x] Connection to <%s:%d> failed", info, info->server_name, info->server_port); info->reason = "Connection failed"; } else { SIPE_DEBUG_INFO("[C:%08x] connected <%d>", info, (int)info->fd); if (info->tls) { if (!CallService(MS_NETLIB_STARTSSL, (WPARAM)info->fd, 0)) { Netlib_CloseHandle(info->fd); info->fd = NULL; info->reason = "Failed to enabled SSL"; } else { SIPE_DEBUG_INFO("[C:%08x] SSL enabled", info); } } else { info->reason = NULL; } } info->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); CallFunctionAsync(connection_cb_async, info); WaitForSingleObject(info->hDoneEvent, INFINITE); CloseHandle(info->hDoneEvent); g_free(info->server_name); g_free(info); return 0; } struct sipe_miranda_connection_info * sipe_miranda_connect(SIPPROTO *pr, const gchar *host, int port, gboolean tls, int timeout, void (*callback)(HANDLE fd, void *data, const gchar *reason), void *data) { struct sipe_miranda_connection_info *info = g_new0(struct sipe_miranda_connection_info, 1); SIPE_DEBUG_INFO("[C:%08x] Connecting to <%s:%d> tls <%d> timeout <%d>", info, host, port, tls, timeout); info->pr = pr; info->server_name = g_strdup(host); info->server_port = port; info->timeout = timeout; info->tls = tls; info->callback = callback; info->data = data; CloseHandle((HANDLE) mir_forkthreadex( sipe_miranda_connected_callback, info, 65536, NULL )); return info; } struct sipe_miranda_ack_args { HANDLE hContact; int nAckType; int nAckResult; HANDLE hSequence; gchar *pszMessage; const gchar *modname; }; static unsigned __stdcall ProtocolAckThread(struct sipe_miranda_ack_args* args) { ProtoBroadcastAck(args->modname, args->hContact, args->nAckType, args->nAckResult, args->hSequence, (LPARAM)args->pszMessage); if (args->nAckResult == ACKRESULT_SUCCESS) SIPE_DEBUG_INFO_NOFORMAT("ProtocolAckThread: Sent ACK"); else if (args->nAckResult == ACKRESULT_FAILED) SIPE_DEBUG_INFO_NOFORMAT("ProtocolAckThread: Sent NACK"); g_free(args->pszMessage); g_free(args); return 0; } void sipe_miranda_SendProtoAck( SIPPROTO *pr, HANDLE hContact, DWORD dwCookie, int nAckResult, int nAckType, const char* pszMessage) { struct sipe_miranda_ack_args* pArgs = g_new0(struct sipe_miranda_ack_args, 1); pArgs->hContact = hContact; pArgs->hSequence = (HANDLE)dwCookie; pArgs->nAckResult = nAckResult; pArgs->nAckType = nAckType; pArgs->pszMessage = g_strdup(pszMessage); pArgs->modname = pr->proto.m_szModuleName; CloseHandle((HANDLE) mir_forkthreadex(ProtocolAckThread, pArgs, 65536, NULL)); } gboolean sipe_miranda_cmd(gchar *cmd, gchar *buf, DWORD *maxlen) { STARTUPINFOA si = {0}; PROCESS_INFORMATION pi = {0}; SECURITY_ATTRIBUTES sa = {0}; HANDLE rd,wr; sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; if (!CreatePipe(&rd, &wr, &sa, 0)) { SIPE_DEBUG_INFO_NOFORMAT("Could not create pipe"); return FALSE; } SetHandleInformation(rd, HANDLE_FLAG_INHERIT, 0); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdOutput = wr; si.hStdError = wr; si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); if (!CreateProcessA(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { SIPE_DEBUG_INFO("Could not run child program <%s> (%d)", cmd, GetLastError()); return FALSE; } CloseHandle(pi.hThread); CloseHandle(pi.hProcess); if (!ReadFile(rd, buf, *maxlen, maxlen, NULL)) { SIPE_DEBUG_INFO("Could not read from child program <%s>", cmd); return FALSE; } return TRUE; } gchar* sipe_miranda_html2rtf(const gchar *text) { const gchar *intro = "{\\rtf1\\ansi"; const gchar *link1 = "{\\field{\\*\\fldinst{HYPERLINK \""; const gchar *link2 = "}}{\\fldrslt {\\ul\\cf2 "; gchar *tmp = g_malloc(strlen(text)+1); int maxlen = strlen(text); const gchar *i = text; int j = 0; gboolean skiptag = FALSE; gboolean escape = FALSE; gboolean copystring = FALSE; gboolean link_stage2 = FALSE; strncpy(tmp+j, intro, maxlen-j); j += strlen(intro); while (*i) { if (j+100>=maxlen) /* 100 is max substitution size */ { maxlen += 128; tmp = g_realloc(tmp, maxlen); } if (skiptag && !escape && *i != '>') { i++; } else if (skiptag && !escape) { i++; skiptag = FALSE; } else if (copystring) { if (!escape && *i == '"') copystring = FALSE; if (escape) escape = FALSE; else if (*i == '\\') escape = TRUE; *(tmp+j) = *i; j++; i++; } else if (link_stage2) { strcpy(tmp+j, link2); j += strlen(link2); link_stage2 = FALSE; skiptag = TRUE; } else if (g_str_has_prefix(i,"
")) { strcpy(tmp+j, "\\par\n"); j += 5; i += 5; } else if (g_str_has_prefix(i,"")) { strcpy(tmp+j, "\\b"); j += 2; i += 3; } else if (g_str_has_prefix(i,"")) { strcpy(tmp+j, "\\b0"); j += 3; i += 4; } else if (g_str_has_prefix(i,"")) { strcpy(tmp+j, "\\fs20"); j += 5; i += 7; } else if (g_str_has_prefix(i,"")) { strcpy(tmp+j, "}}}\\cf0 "); j += 7; i += 4; } else if (*i == '<') { skiptag = TRUE; } else { if (escape) { escape = FALSE; } else if (*i == '\\') { escape = TRUE; } if (!skiptag) { *(tmp+j) = *i; j++; } i++; } } *(tmp+j++) = '}'; *(tmp+j++) = '\0'; tmp = g_realloc(tmp, j); return tmp; } int SipeStatusToMiranda(guint activity) { switch (activity) { case SIPE_ACTIVITY_OFFLINE: return ID_STATUS_OFFLINE; case SIPE_ACTIVITY_AVAILABLE: return ID_STATUS_ONLINE; case SIPE_ACTIVITY_ON_PHONE: return ID_STATUS_ONTHEPHONE; case SIPE_ACTIVITY_DND: case SIPE_ACTIVITY_URGENT_ONLY: return ID_STATUS_DND; case SIPE_ACTIVITY_AWAY: case SIPE_ACTIVITY_OOF: return ID_STATUS_NA; case SIPE_ACTIVITY_LUNCH: return ID_STATUS_OUTTOLUNCH; case SIPE_ACTIVITY_BUSY: case SIPE_ACTIVITY_IN_MEETING: case SIPE_ACTIVITY_IN_CONF: return ID_STATUS_OCCUPIED; case SIPE_ACTIVITY_INVISIBLE: return ID_STATUS_INVISIBLE; case SIPE_ACTIVITY_BRB: return ID_STATUS_AWAY; case SIPE_ACTIVITY_UNSET: return ID_STATUS_OFFLINE; case SIPE_ACTIVITY_INACTIVE: case SIPE_ACTIVITY_ONLINE: case SIPE_ACTIVITY_BUSYIDLE: return ID_STATUS_ONLINE; default: /* None of those? We'll have to guess. Online seems ok. */ return ID_STATUS_ONLINE; } /* Don't have SIPE equivalent of these: - ID_STATUS_FREECHAT */ } guint MirandaStatusToSipe(int status) { switch (status) { case ID_STATUS_OFFLINE: return SIPE_ACTIVITY_OFFLINE; case ID_STATUS_ONLINE: case ID_STATUS_FREECHAT: return SIPE_ACTIVITY_AVAILABLE; case ID_STATUS_ONTHEPHONE: return SIPE_ACTIVITY_ON_PHONE; case ID_STATUS_DND: return SIPE_ACTIVITY_DND; case ID_STATUS_NA: return SIPE_ACTIVITY_AWAY; case ID_STATUS_AWAY: return SIPE_ACTIVITY_BRB; case ID_STATUS_OUTTOLUNCH: return SIPE_ACTIVITY_LUNCH; case ID_STATUS_OCCUPIED: return SIPE_ACTIVITY_BUSY; case ID_STATUS_INVISIBLE: return SIPE_ACTIVITY_INVISIBLE; default: return SIPE_ACTIVITY_UNSET; } } gchar *sipe_miranda_uri_self(SIPPROTO *pr) { gchar *username = sipe_miranda_getString(pr, "username"); gchar *uri = g_strdup_printf("sip:%s", username); mir_free(username); return uri; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/miranda/miranda-version.h ================================================ /** * @file miranda-version.h * * pidgin-sipe * * Copyright (C) 2011 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define MIRANDA_VER 0x900 ================================================ FILE: src/miranda/miranda.rc ================================================ // Generated by ResEdit 1.5.4 // Copyright (C) 2006-2010 // http://www.resedit.net #include #include #include #include "miranda-resource.h" // // Dialog resources // LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US IDD_ACCMGRUI DIALOGEX 0, 0, 186, 134 STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILDWINDOW EXSTYLE WS_EX_CONTROLPARENT FONT 8, "MS Shell Dlg", 400, 0, 1 { LTEXT "Username:", IDC_STATIC, 5, 5, 45, 12, SS_LEFT EDITTEXT IDC_HANDLE, 50, 5, 116, 12, ES_AUTOHSCROLL AUTORADIOBUTTON "Use windows credentials", IDC_SSO, 5, 20, 94, 8 AUTORADIOBUTTON "Manually specify parameters", IDC_MSO, 5, 35, 105, 8 LTEXT "Login:", IDC_STATIC, 20, 50, 45, 12, SS_LEFT EDITTEXT IDC_LOGIN, 65, 50, 116, 12, ES_AUTOHSCROLL LTEXT "Password:", IDC_STATIC, 20, 65, 45, 12, SS_LEFT EDITTEXT IDC_PASSWORD, 65, 65, 116, 12, ES_AUTOHSCROLL | ES_PASSWORD } LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US IDD_SEARCHUI DIALOGEX 0, 0, 109, 148 STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILDWINDOW EXSTYLE WS_EX_CONTROLPARENT FONT 8, "Ms Shell Dlg" { LTEXT "First Name", IDC_STATIC, 3, 5, 44, 12, SS_LEFT EDITTEXT IDC_SEARCH_FN, 3, 15, 104, 12, ES_AUTOHSCROLL LTEXT "Last Name", IDC_STATIC, 3, 30, 44, 12, SS_LEFT EDITTEXT IDC_SEARCH_LN, 3, 40, 104, 12, ES_AUTOHSCROLL LTEXT "Company", IDC_STATIC, 3, 55, 44, 12, SS_LEFT EDITTEXT IDC_SEARCH_COMPANY, 3, 65, 104, 12, ES_AUTOHSCROLL LTEXT "Country", IDC_STATIC, 3, 80, 44, 12, SS_LEFT EDITTEXT IDC_SEARCH_COUNTRY, 3, 90, 104, 12, ES_AUTOHSCROLL } LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US IDD_OPT_SIPSIMPLE DIALOG 0, 0, 255, 230 STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILDWINDOW EXSTYLE WS_EX_CONTROLPARENT FONT 8, "Ms Shell Dlg" { GROUPBOX "Account options", IDC_STATIC, 5, 5, 245, 70 AUTOCHECKBOX "Use windows credentials for authentication", IDC_USESSO, 15, 20, 155, 12 LTEXT "Authentication scheme", IDC_STATIC, 20, 35, 75, 12, SS_LEFT COMBOBOX IDC_AUTHTYPE, 100, 35, 75, 30, CBS_DROPDOWNLIST | CBS_HASSTRINGS LTEXT "Connection type", IDC_STATIC, 20, 55, 75, 12, SS_LEFT COMBOBOX IDC_CONNTYPE, 100, 55, 75, 30, CBS_DROPDOWNLIST | CBS_HASSTRINGS GROUPBOX "User information", IDC_STATIC, 5, 80, 245, 65 LTEXT "Username:", IDC_STATIC, 15, 95, 55, 12, SS_LEFT EDITTEXT IDC_HANDLE, 75, 95, 135, 12, ES_AUTOHSCROLL LTEXT "Login:", IDC_STATIC, 15, 110, 55, 12, SS_LEFT EDITTEXT IDC_LOGIN, 75, 110, 135, 12, ES_AUTOHSCROLL LTEXT "Password:", IDC_STATIC, 15, 125, 55, 12, SS_LEFT EDITTEXT IDC_PASSWORD, 75, 125, 135, 12, ES_AUTOHSCROLL | ES_PASSWORD GROUPBOX "Public IP (Protocol global)", IDC_STATIC, 5, 150, 245, 75 AUTORADIOBUTTON "Fetch local IP", IDC_IPLOCAL, 15, 165, 55, 12 LTEXT "", IDC_IPLOCALFOUND, 90, 165, 55, 12, SS_LEFT AUTORADIOBUTTON "Set manually", IDC_IPMANUAL, 15, 180, 55, 12 EDITTEXT IDC_PUBLICIP, 90, 180, 135, 12, ES_AUTOHSCROLL AUTORADIOBUTTON "Run program", IDC_IPPROG, 15, 195, 55, 12 EDITTEXT IDC_IPPROGEXE, 90, 195, 135, 12, ES_AUTOHSCROLL } LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US IDD_OPT_SIPSIMPLE_ABOUT DIALOG 0, 0, 255, 230 STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILDWINDOW EXSTYLE WS_EX_CONTROLPARENT FONT 8, "Ms Shell Dlg" { CONTROL "Rich Edit", IDC_ABOUTSIPE, RICHEDIT_CLASS, WS_TABSTOP | WS_BORDER | ES_NUMBER | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | WS_VSCROLL, 5, 8, 240, 216 } ================================================ FILE: src/miranda/sipe-miranda.c ================================================ #include #include #include #include #include #include "miranda-version.h" #include "newpluginapi.h" #include "m_protosvc.h" #include "m_protoint.h" #include "m_system.h" #include "m_database.h" #include "m_langpack.h" #include "m_options.h" #include "m_clist.h" #include "m_chat.h" #include "m_netlib.h" #include "m_protomod.h" #include "sipe-core.h" #include "sipe-backend.h" #include "miranda-private.h" #include "miranda-resource.h" /* Miranda interface globals */ void CreateProtoService(SIPPROTO *pr, const char* szService, SipSimpleServiceFunc serviceProc) { char str[ MAXMODULELABELLENGTH ]; mir_snprintf(str, sizeof(str), "%s%s", pr->proto.m_szModuleName, szService); CreateServiceFunctionObj(str, (MIRANDASERVICEOBJ)*(void**)&serviceProc, pr); } /**************************************************************************** * Struct that defines our interface with libsipe ****************************************************************************/ /* Protocol interface functions */ int RecvContacts( SIPPROTO *pr, HANDLE hContact, PROTORECVEVENT* evt ) { _NIF(); return 0; } int RecvUrl( SIPPROTO *pr, HANDLE hContact, PROTORECVEVENT* evt ) { _NIF(); return 0; } int SendContacts( SIPPROTO *pr, HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList ) { _NIF(); SIPE_DEBUG_INFO("SendContacts: flags <%x> ncontacts <%x>", flags, nContacts); return 0; } int SendUrl( SIPPROTO *pr, HANDLE hContact, int flags, const char* url ) { _NIF(); SIPE_DEBUG_INFO("SendUrl: iflags <%x> url <%s>", flags, url); return 0; } int SetApparentMode( SIPPROTO *pr, HANDLE hContact, int mode ) { _NIF(); SIPE_DEBUG_INFO("SetApparentMode: mode <%x>", mode); return 0; } int RecvAwayMsg( SIPPROTO *pr, HANDLE hContact, int mode, PROTORECVEVENT* evt ) { _NIF(); SIPE_DEBUG_INFO("RecvAwayMsg: mode <%x>", mode); return 0; } int SendAwayMsg( SIPPROTO *pr, HANDLE hContact, HANDLE hProcess, const char* msg ) { _NIF(); SIPE_DEBUG_INFO("SendAwayMsg: msg <%s>", msg); return 0; } ================================================ FILE: src/miranda/vlc.c ================================================ #include #include #include #include extern HINSTANCE hInst; LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } int __stdcall show_vlc(void *data) { libvlc_instance_t * inst; libvlc_media_player_t *mp; libvlc_media_t *m; int rc; HANDLE w; WNDCLASSEX wc; /* Load the VLC engine */ inst = libvlc_new (0, NULL); /* Create a new item */ m = libvlc_media_new_path (inst, "C:\\Temp\\[001_1-01] Broken Bow (Part 1).mpg"); /* Create a media player playing environement */ mp = libvlc_media_player_new_from_media (m); /* No need to keep the media now */ libvlc_media_release (m); //Step 1: Registering the Window Class wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = NULL; wc.lpszClassName = L"myclassstuff"; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&wc)) { MessageBox(NULL, L"Window Registration Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } w = CreateWindow(L"myclassstuff", L"VLC Window", WS_OVERLAPPEDWINDOW, 50, 50, 400, 400, NULL, NULL, hInst, NULL); if (!w) { DWORD d = GetLastError(); printf ("===================== %08x ==============\n", d); } // libvlc_media_player_set_hwnd (mp, w); // ShowWindow(w, SW_SHOW); // UpdateWindow(w); #if 0 /* This is a non working code that show how to hooks into a window, * if we have a window around */ libvlc_media_player_set_xdrawable (mp, xdrawable); /* or on windows */ /* or on mac os */ libvlc_media_player_set_nsobject (mp, view); #endif /* play the media_player */ rc = libvlc_media_player_play (mp); Sleep (60000); /* Let it play a bit */ /* Stop playing */ libvlc_media_player_stop (mp); /* Free the media_player */ libvlc_media_player_release (mp); libvlc_release (inst); return 0; } ================================================ FILE: src/purple/Makefile.am ================================================ pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) noinst_LTLIBRARIES = libsipe_backend.la pkg_LTLIBRARIES = libsipe.la EXTRA_DIST = \ pidgin-sipe.metainfo.xml MAINTAINERCLEANFILES = \ Makefile.in libsipe_backend_la_SOURCES = \ purple-private.h \ purple-buddy.c \ purple-chat.c \ purple-connection.c \ purple-debug.c \ purple-dnsquery.c \ purple-ft.c \ purple-groupchat.c \ purple-im.c \ purple-markup.c \ purple-network.c \ purple-notify.c \ purple-schedule.c \ purple-search.c \ purple-setting.c \ purple-status.c \ purple-transport.c \ purple-user.c libsipe_la_SOURCES = \ purple-plugin-common.c if SIPE_PURPLE3 libsipe_la_SOURCES += purple-plugin3.c else libsipe_la_SOURCES += purple-plugin.c endif AM_CFLAGS = $(st) libsipe_backend_la_CFLAGS = \ $(DEBUG_CFLAGS) \ $(QUALITY_CFLAGS) \ $(GLIB_CFLAGS) \ $(DBUS_CFLAGS) \ $(PURPLE_CFLAGS) \ -I$(srcdir)/../api if !SIPE_OS_WIN32 libsipe_backend_la_CFLAGS += \ $(LOCALE_CPPFLAGS) endif libsipe_la_CFLAGS = $(libsipe_backend_la_CFLAGS) libsipe_la_LDFLAGS = \ -module -avoid-version -no-undefined \ $(ADDITIONAL_LDFLAGS) libsipe_la_LIBADD = \ ../core/libsipe_core.la \ ../core/libsipe_core_crypto.la \ ../core/libsipe_core_libxml2.la \ libsipe_backend.la \ $(DBUS_LIBS) \ $(LIBXML2_LIBS) \ $(NSS_LIBS) \ $(OPENSSL_LIBS) \ $(GLIB_LIBS) \ $(PURPLE_LIBS) if SIPE_OS_WIN32 libsipe_la_CFLAGS += -DHAVE_SSPI=1 libsipe_la_LIBADD += -lws2_32 -lsecur32 endif if SIP_SEC_GSSAPI libsipe_la_LIBADD += $(KRB5_LDFLAGS) endif check_PROGRAMS = tests_load tests_load_SOURCES = tests-load.c tests_load_CFLAGS = $(GMODULE_CFLAGS) tests_load_LDADD = $(GMODULE_LIBS) if !SIPE_OS_WIN32 if !SIP_SEC_GSSAPI_ONLY check_PROGRAMS += tests tests_SOURCES = tests.c tests_CFLAGS = $(libsipe_la_CFLAGS) tests_LDADD = \ ../core/libsipe_core_tests.la \ ../core/libsipe_core_crypto.la \ libsipe_backend.la \ $(NSS_LIBS) \ $(OPENSSL_LIBS) \ $(PURPLE_LIBS) endif endif # D-Bus functionality no longer available in 3.x.x API if !SIPE_PURPLE3 if SIPE_DBUS libsipe_backend_la_SOURCES += \ purple-dbus.c \ purple-dbus.h \ purple-dbus-bindings.c endif endif if SIPE_MIME_GMIME libsipe_la_LIBADD += \ ../core/libsipe_core_mime.la \ $(GMIME_LIBS) if !SIPE_OS_WIN32 if !SIP_SEC_GSSAPI_ONLY tests_LDADD += \ ../core/libsipe_core_mime.la \ $(GMIME_LIBS) endif endif else libsipe_backend_la_SOURCES += purple-mime.c endif if SIPE_WITH_VV noinst_LTLIBRARIES += libsipe_backend_vv.la libsipe_backend_vv_la_SOURCES = purple-media.c libsipe_backend_vv_la_CFLAGS = \ $(libsipe_backend_la_CFLAGS) \ $(NICE_CFLAGS) \ $(GSTREAMER_CFLAGS) \ $(FARSTREAM_CFLAGS) libsipe_la_LIBADD += \ libsipe_backend_vv.la \ $(NICE_LIBS) \ $(GSTREAMER_LIBS) \ $(FARSTREAM_LIBS) if SIPE_HAVE_APPSHARE_SERVER libsipe_la_LIBADD += \ $(FREERDP_SHADOW_LIBS) endif endif TESTS = $(check_PROGRAMS) # Remove any libsipe.so from the old incorrect installation location install-exec-local: rm -f $(DESTDIR)$(libdir)/pidgin/libsipe.so if SIPE_WITH_APPSTREAM pidginmetainfofiledir = $(datadir)/metainfo pidginmetainfofile_DATA = pidgin-sipe.metainfo.xml check: validate-metainfo .PHONY: validate-metainfo validate-metainfo: $(pidginmetainfofile_DATA) appstreamcli validate --pedantic --nonet $< || \ appstreamcli validate --pedantic $< || \ appstream-validate $< endif ================================================ FILE: src/purple/pidgin-sipe.metainfo.xml ================================================ net.sourceforge.sipe.purple pidgin.desktop SIPE Pidgin protocol plugin to connect to MS Office Communicator

SIPE is a third-party plugin for the Pidgin/Adium/Miranda/Telepathy multi-protocol instant messaging clients/frameworks. It implements the extended version of SIP/SIMPLE used by various products:

  • Skype for Business
  • Microsoft Office 365
  • Microsoft Business Productivity Online Suite (BPOS)
  • Microsoft Lync Server
  • Microsoft Office Communications Server (OCS 2007/2007 R2)
  • Microsoft Live Communications Server (LCS 2003/2005)

With this plugin you should be able to replace your Microsoft Office Communicator client with Pidgin/Adium/Miranda/Telepathy.

http://sipe.sourceforge.net/ https://sourceforge.net/p/sipe/bugs/ GFDL-1.3 GPL-2.0+
================================================ FILE: src/purple/purple-buddy.c ================================================ /** * @file purple-buddy.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "notify.h" #include "request.h" #include "version.h" #if PURPLE_VERSION_CHECK(3,0,0) #include "action.h" #include "account.h" #include "buddylist.h" #else #include "blist.h" #include "privacy.h" #define purple_account_privacy_check(a, n) purple_privacy_check(a, n) #define purple_account_privacy_deny_add(a, n, l) purple_privacy_deny_add(a, n, l) #define purple_account_privacy_deny_remove(a, n, l) purple_privacy_deny_remove(a, n, l) #define purple_action_menu_new(l, c, d, ch) purple_menu_action_new(l, c, d, ch) #define purple_blist_find_buddies(a, n) purple_find_buddies(a, n) #define purple_blist_find_buddy(a, n) purple_find_buddy(a, n) #define purple_blist_find_buddy_in_group(a, n, g) purple_find_buddy_in_group(a, n, g) #define purple_blist_find_group(n) purple_find_group(n) #define purple_blist_get_default_root() purple_blist_get_root() #define purple_buddy_set_local_alias(b, n) purple_blist_alias_buddy(b, n) #define purple_buddy_set_server_alias(b, n) purple_blist_server_alias_buddy(b, n) #define purple_buddy_set_name(b, n) purple_blist_rename_buddy(b, n) #define purple_group_set_name(g, n) purple_blist_rename_group(g, n) #define purple_notify_user_info_add_pair_html(i, k, v) purple_notify_user_info_add_pair(i, k, v) #define purple_protocol_got_user_status purple_prpl_got_user_status #define PurpleActionMenu PurpleMenuAction #define PURPLE_IS_BUDDY(b) PURPLE_BLIST_NODE_IS_BUDDY(b) #define PURPLE_IS_GROUP(b) PURPLE_BLIST_NODE_IS_GROUP(b) #endif #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-nls.h" #include "purple-private.h" static const struct { const gchar *property; /* property name to store in blist.xml */ const gchar *description; /* label for "Get Info" dialog */ } buddy_info_map[] = { /* SIPE_BUDDY_INFO_DISPLAY_NAME */ { "alias", N_("Display name") }, /* SIPE_BUDDY_INFO_JOB_TITLE */ { "title", N_("Job title") }, /* SIPE_BUDDY_INFO_CITY */ { "address-city", N_("City") }, /* SIPE_BUDDY_INFO_STATE */ { "address-state", N_("State") }, /* SIPE_BUDDY_INFO_OFFICE */ { "office", N_("Office") }, /* SIPE_BUDDY_INFO_DEPARTMENT */ { "department", NULL }, /* SIPE_BUDDY_INFO_COUNTRY */ { "address-country-code", N_("Country") }, /* SIPE_BUDDY_INFO_WORK_PHONE */ { "phone", N_("Business phone") }, /* SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY */ { "phone-display", NULL }, /* SIPE_BUDDY_INFO_COMPANY */ { "company", N_("Company") }, /* SIPE_BUDDY_INFO_EMAIL */ { "email", N_("Email address") }, /* SIPE_BUDDY_INFO_SITE */ { "site", N_("Site") }, /* SIPE_BUDDY_INFO_ZIPCODE */ { "address-zipcode", NULL }, /* SIPE_BUDDY_INFO_STREET */ { "address-street", NULL }, /* SIPE_BUDDY_INFO_MOBILE_PHONE */ { "phone-mobile", NULL }, /* SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY */ { "phone-mobile-display", NULL }, /* SIPE_BUDDY_INFO_HOME_PHONE */ { "phone-home", NULL }, /* SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY */ { "phone-home-display", NULL }, /* SIPE_BUDDY_INFO_OTHER_PHONE */ { "phone-other", NULL }, /* SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY */ { "phone-other-display", NULL }, /* SIPE_BUDDY_INFO_CUSTOM1_PHONE */ { "phone-custom1", NULL }, /* SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY */ { "phone-custom1-display", NULL }, /* SIPE_BUDDY_INFO_ALIAS */ { NULL, N_("Alias") }, /* SIPE_BUDDY_INFO_DEVICE */ { NULL, N_("Device") }, }; #define buddy_info_property(i) buddy_info_map[i].property #define buddy_info_description(i) gettext(buddy_info_map[i].description) sipe_backend_buddy sipe_backend_buddy_find(struct sipe_core_public *sipe_public, const gchar *buddy_name, const gchar *group_name) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleGroup *purple_group; if (group_name) { purple_group = purple_blist_find_group(group_name); if (!purple_group) return NULL; return purple_blist_find_buddy_in_group(purple_private->account, buddy_name, purple_group); } else { return purple_blist_find_buddy(purple_private->account, buddy_name); } } GSList* sipe_backend_buddy_find_all(struct sipe_core_public *sipe_public, const gchar *buddy_name, const gchar *group_name) { struct sipe_backend_private *purple_private = sipe_public->backend_private; if (group_name) { SIPE_DEBUG_ERROR_NOFORMAT("Finding all buddies in a group not supported on purple"); return NULL; } return purple_blist_find_buddies(purple_private->account, buddy_name); } gchar* sipe_backend_buddy_get_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { return g_strdup(purple_buddy_get_name((PurpleBuddy *) who)); } gchar* sipe_backend_buddy_get_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { return g_strdup(purple_buddy_get_alias(who)); } gchar* sipe_backend_buddy_get_server_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { return g_strdup(purple_buddy_get_server_alias(who)); } gchar *sipe_backend_buddy_get_local_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { return g_strdup( #if PURPLE_VERSION_CHECK(3,0,0) purple_buddy_get_local_alias(who) #else purple_buddy_get_local_buddy_alias(who) #endif ); } gchar* sipe_backend_buddy_get_group_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { return g_strdup(purple_group_get_name(purple_buddy_get_group((PurpleBuddy*)who))); } guint sipe_backend_buddy_get_status(struct sipe_core_public *sipe_public, const gchar *uri) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleBuddy *pbuddy = purple_blist_find_buddy(purple_private->account, uri); PurplePresence *presence = purple_buddy_get_presence(pbuddy); PurpleStatus *pstatus = purple_presence_get_active_status(presence); return(sipe_purple_token_to_activity(purple_status_get_id(pstatus))); } void sipe_backend_buddy_set_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who, const gchar *alias) { purple_buddy_set_local_alias(who, alias); } void sipe_backend_buddy_set_server_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who, const gchar *alias) { purple_buddy_set_server_alias(who, alias); } gchar* sipe_backend_buddy_get_string(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, sipe_backend_buddy buddy, const sipe_buddy_info_fields key) { PurpleBuddy *b = (PurpleBuddy*) buddy; return g_strdup(purple_blist_node_get_string(&b->node, buddy_info_property(key))); } void sipe_backend_buddy_set_string(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, sipe_backend_buddy buddy, const sipe_buddy_info_fields key, const gchar *val) { PurpleBuddy *b = (PurpleBuddy*) buddy; purple_blist_node_set_string(&b->node, buddy_info_property(key), val); } void sipe_backend_buddy_refresh_properties(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *uri) { /* nothing to do here: already taken care of by libpurple */ } void sipe_backend_buddy_list_processing_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { } void sipe_backend_buddy_list_processing_finish(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { } sipe_backend_buddy sipe_backend_buddy_add(struct sipe_core_public *sipe_public, const gchar *name, const gchar *alias, const gchar *groupname) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleBuddy *b; PurpleGroup *purple_group = purple_blist_find_group(groupname); if (!purple_group) return NULL; b = purple_buddy_new(purple_private->account, name, alias); purple_blist_add_buddy(b, NULL, purple_group, NULL); return b; } void sipe_backend_buddy_remove(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { purple_blist_remove_buddy(who); } struct authorization_cb_data { sipe_backend_buddy_request_authorization_cb auth_cb; sipe_backend_buddy_request_authorization_cb deny_cb; gpointer data; }; static void authorization_auth_cb( #if PURPLE_VERSION_CHECK(3,0,0) SIPE_UNUSED_PARAMETER const char *response, #endif void *user_data) { struct authorization_cb_data *cb_data = user_data; sipe_backend_buddy_request_authorization_cb callback = cb_data->auth_cb; void *data = cb_data->data; g_free(user_data); (*callback)(data); } static void authorization_deny_cb( #if PURPLE_VERSION_CHECK(3,0,0) SIPE_UNUSED_PARAMETER const char *response, #endif void *user_data) { struct authorization_cb_data *cb_data = user_data; sipe_backend_buddy_request_authorization_cb callback = cb_data->deny_cb; void *data = cb_data->data; g_free(user_data); (*callback)(data); } void sipe_backend_buddy_request_authorization(struct sipe_core_public *sipe_public, const gchar *who, const gchar *alias, gboolean on_list, sipe_backend_buddy_request_authorization_cb auth_cb, sipe_backend_buddy_request_authorization_cb deny_cb, gpointer data) { struct sipe_backend_private *purple_private = sipe_public->backend_private; struct authorization_cb_data *cb_data = g_new(struct authorization_cb_data, 1); cb_data->auth_cb = auth_cb; cb_data->deny_cb = deny_cb; cb_data->data = data; purple_account_request_authorization( purple_private->account, who, _("you"), /* id */ alias, NULL, /* message */ on_list, authorization_auth_cb, authorization_deny_cb, cb_data); } void sipe_backend_buddy_request_add(struct sipe_core_public *sipe_public, const gchar *who, const gchar *alias) { struct sipe_backend_private *purple_private = sipe_public->backend_private; purple_account_request_add(purple_private->account, who, _("you"), alias, NULL); } gboolean sipe_backend_buddy_is_blocked(struct sipe_core_public *sipe_public, const gchar *who) { struct sipe_backend_private *purple_private = sipe_public->backend_private; return(!purple_account_privacy_check(purple_private->account, who)); } void sipe_backend_buddy_set_blocked_status(struct sipe_core_public *sipe_public, const gchar *who, gboolean blocked) { struct sipe_backend_private *purple_private = sipe_public->backend_private; if (blocked) { purple_account_privacy_deny_add(purple_private->account, who, TRUE); } else { purple_account_privacy_deny_remove(purple_private->account, who, TRUE); } /* stupid workaround to make pidgin re-render screen to reflect our changes */ SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_buddy_set_blocked_status: forcefully refreshing screen."); sipe_core_buddy_got_status(sipe_public, who, sipe_backend_buddy_get_status(sipe_public, who), 0); } void sipe_backend_buddy_set_status(struct sipe_core_public *sipe_public, const gchar *who, guint activity, time_t last_active) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleBuddy *buddy = NULL; gchar *tmp = NULL; buddy = purple_blist_find_buddy(purple_private->account, who); if (buddy) { const PurpleStatusType *status_type = purple_status_type_find_with_id( purple_account_get_status_types(purple_private->account), sipe_purple_activity_to_token(activity)); tmp = sipe_core_buddy_status(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, purple_buddy_get_name(buddy), activity, purple_status_type_get_name(status_type)); } /* make sure to clear status message when there is none */ if (!tmp) tmp = g_strdup(""); purple_protocol_got_user_status(purple_private->account, who, sipe_purple_activity_to_token(activity), SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE, tmp, NULL); g_free(tmp); /* * One of the idiosyncrasies in the libpurple API: it is not * enough to set the buddy status to one of the idle ones. * You also must make another call to set an idle flag! */ if (buddy) { PurplePresence *presence = purple_buddy_get_presence(buddy); gboolean is_idle = (activity == SIPE_ACTIVITY_INACTIVE) || (activity == SIPE_ACTIVITY_AWAY) || (activity == SIPE_ACTIVITY_BRB) || (activity == SIPE_ACTIVITY_LUNCH); purple_presence_set_idle(presence, is_idle, is_idle ? last_active : 0); } } gboolean sipe_backend_uses_photo(void) { return TRUE; } void sipe_backend_buddy_set_photo(struct sipe_core_public *sipe_public, const gchar *who, gpointer photo_data, gsize data_len, const gchar *photo_hash) { PurpleAccount *account = sipe_public->backend_private->account; purple_buddy_icons_set_for_user(account, who, photo_data, data_len, photo_hash); } const gchar *sipe_backend_buddy_get_photo_hash(struct sipe_core_public *sipe_public, const gchar *who) { PurpleAccount *account = sipe_public->backend_private->account; const gchar *result = NULL; PurpleBuddyIcon *icon = purple_buddy_icons_find(account, who); if (icon) { result = purple_buddy_icon_get_checksum(icon); purple_buddy_icon_unref(icon); } return result; } gboolean sipe_backend_buddy_group_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const gchar *group_name) { PurpleGroup * purple_group = purple_blist_find_group(group_name); if (!purple_group) { purple_group = purple_group_new(group_name); purple_blist_add_group(purple_group, NULL); } return (purple_group != NULL); } gboolean sipe_backend_buddy_group_rename(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const gchar *old_name, const gchar *new_name) { PurpleGroup *purple_group = purple_blist_find_group(old_name); if (purple_group) purple_group_set_name(purple_group, new_name); return(purple_group != NULL); } void sipe_backend_buddy_group_remove(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const gchar *group_name) { PurpleGroup *purple_group = purple_blist_find_group(group_name); if (purple_group) purple_blist_remove_group(purple_group); } struct sipe_backend_buddy_info *sipe_backend_buddy_info_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { return((struct sipe_backend_buddy_info *)purple_notify_user_info_new()); } void sipe_backend_buddy_info_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_info *info, sipe_buddy_info_fields key, const gchar *value) { if (info) { purple_notify_user_info_add_pair_html((PurpleNotifyUserInfo *) info, buddy_info_description(key), value); } } void sipe_backend_buddy_info_break(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_info *info) { purple_notify_user_info_add_section_break((PurpleNotifyUserInfo *) info); } void sipe_backend_buddy_info_finalize(struct sipe_core_public *sipe_public, struct sipe_backend_buddy_info *info, const gchar *uri) { struct sipe_backend_private *purple_private = sipe_public->backend_private; /* show a buddy's user info in a nice dialog box */ purple_notify_userinfo(purple_private->gc, uri, /* buddy's URI */ (PurpleNotifyUserInfo *) info, NULL, /* callback called when dialog closed */ NULL); /* userdata for callback */ } void sipe_backend_buddy_tooltip_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_tooltip *tooltip, const gchar *description, const gchar *value) { purple_notify_user_info_add_pair_html((PurpleNotifyUserInfo *) tooltip, description, value); } void sipe_purple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group #if PURPLE_VERSION_CHECK(3,0,0) , SIPE_UNUSED_PARAMETER const gchar *message #endif ) { SIPE_DEBUG_INFO("sipe_purple_add_buddy[CB]: buddy:%s group:%s", buddy ? purple_buddy_get_name(buddy) : "", group ? purple_group_get_name(group) : ""); /* libpurple can call us with undefined buddy or group */ if (buddy && group) { /* * Buddy name must be lower case as we use * purple_normalize_nocase() to compare */ gchar *buddy_name = g_ascii_strdown(purple_buddy_get_name(buddy), -1); gchar *uri = sip_uri_if_valid(buddy_name); g_free(buddy_name); if (uri) { purple_buddy_set_name(buddy, uri); g_free(uri); sipe_core_buddy_add(PURPLE_GC_TO_SIPE_CORE_PUBLIC, purple_buddy_get_name(buddy), purple_group_get_name(group)); } else { SIPE_DEBUG_ERROR_NOFORMAT("sipe_purple_add_buddy[CB]: buddy name is invalid for URI"); purple_blist_remove_buddy(buddy); purple_notify_error(gc, NULL, _("User name should be a valid SIP URI\nExample: user@company.com"), NULL #if PURPLE_VERSION_CHECK(3,0,0) , NULL #endif ); } } } void sipe_purple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { SIPE_DEBUG_INFO("sipe_purple_remove_buddy[CB]: buddy: '%s' group: '%s'", buddy ? purple_buddy_get_name(buddy) : "", group ? purple_group_get_name(group) : ""); if (!buddy) return; sipe_core_buddy_remove(PURPLE_GC_TO_SIPE_CORE_PUBLIC, purple_buddy_get_name(buddy), group ? purple_group_get_name(group) : NULL); } void sipe_purple_group_buddy(PurpleConnection *gc, const char *who, const char *old_group_name, const char *new_group_name) { sipe_core_buddy_group(PURPLE_GC_TO_SIPE_CORE_PUBLIC, who, old_group_name, new_group_name); } /* Buddy Menu Handling */ static void sipe_purple_buddy_make_chat_leader_cb(PurpleBuddy *buddy, gpointer parameter) { SIPE_DEBUG_INFO("sipe_purple_buddy_make_chat_leader_cb: name '%s'", purple_buddy_get_name(buddy)); sipe_core_conf_make_leader(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, parameter, purple_buddy_get_name(buddy)); } static void sipe_purple_buddy_remove_from_chat_cb(PurpleBuddy *buddy, gpointer parameter) { SIPE_DEBUG_INFO("sipe_purple_buddy_remove_from_chat_cb: name '%s'", purple_buddy_get_name(buddy)); sipe_core_conf_remove_from(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, parameter, purple_buddy_get_name(buddy)); } static void sipe_purple_buddy_invite_to_chat_cb(PurpleBuddy *buddy, gpointer parameter) { SIPE_DEBUG_INFO("sipe_purple_buddy_invite_to_chat_cb: name '%s'", purple_buddy_get_name(buddy)); sipe_core_chat_invite(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, parameter, purple_buddy_get_name(buddy)); } static void sipe_purple_buddy_new_chat_cb(PurpleBuddy *buddy, SIPE_UNUSED_PARAMETER gpointer parameter) { SIPE_DEBUG_INFO("sipe_purple_buddy_new_chat_cb: name '%s'", purple_buddy_get_name(buddy)); sipe_core_buddy_new_chat(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, purple_buddy_get_name(buddy)); } static void sipe_purple_buddy_make_call_cb(PurpleBuddy *buddy, gpointer parameter) { SIPE_DEBUG_INFO("sipe_purple_buddy_make_call_cb: name '%s'", purple_buddy_get_name(buddy)); sipe_core_buddy_make_call(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, parameter); } static void sipe_purple_buddy_send_email_cb(PurpleBuddy *buddy, SIPE_UNUSED_PARAMETER gpointer parameter) { SIPE_DEBUG_INFO("sipe_purple_buddy_send_email_cb: name '%s'", purple_buddy_get_name(buddy)); sipe_core_buddy_send_email(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, purple_buddy_get_name(buddy)); } static void sipe_purple_buddy_access_level_help_cb(PurpleBuddy *buddy, SIPE_UNUSED_PARAMETER gpointer parameter) { /** * Translators: replace with URL to localized page * If it doesn't exist copy the original URL */ purple_notify_uri(purple_account_get_connection(purple_buddy_get_account(buddy)), _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels")); } static void sipe_purple_buddy_change_access_level_cb(PurpleBuddy *buddy, gpointer parameter) { sipe_core_change_access_level_from_container(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, parameter); } static void sipe_purple_ask_access_domain_cb(PurpleConnection *gc, PurpleRequestFields *fields) { const gchar *domain = purple_request_fields_get_string(fields, "access_domain"); guint index = #if PURPLE_VERSION_CHECK(3,0,0) GPOINTER_TO_UINT( #endif purple_request_fields_get_choice(fields, "container_id") #if PURPLE_VERSION_CHECK(3,0,0) ) #endif ; sipe_core_change_access_level_for_domain(PURPLE_GC_TO_SIPE_CORE_PUBLIC, domain, index); } static void sipe_purple_buddy_add_new_domain_cb(PurpleBuddy *buddy, SIPE_UNUSED_PARAMETER gpointer parameter) { PurpleAccount *account = purple_buddy_get_account(buddy); PurpleConnection *gc = purple_account_get_connection(account); PurpleRequestFields *fields; PurpleRequestFieldGroup *g; PurpleRequestField *f; fields = purple_request_fields_new(); g = purple_request_field_group_new(NULL); f = purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE); purple_request_field_set_required(f, TRUE); purple_request_field_group_add_field(g, f); f = purple_request_field_choice_new("container_id", _("Access level"), 0); #if PURPLE_VERSION_CHECK(3,0,0) purple_request_field_choice_add(f, _("Personal"), GUINT_TO_POINTER(0)); purple_request_field_choice_add(f, _("Team"), GUINT_TO_POINTER(1)); purple_request_field_choice_add(f, _("Company"), GUINT_TO_POINTER(2)); purple_request_field_choice_add(f, _("Public"), GUINT_TO_POINTER(3)); purple_request_field_choice_add(f, _("Blocked"), GUINT_TO_POINTER(4)); purple_request_field_choice_set_default_value(f, GUINT_TO_POINTER(3)); #else purple_request_field_choice_add(f, _("Personal")); /* index 0 */ purple_request_field_choice_add(f, _("Team")); purple_request_field_choice_add(f, _("Company")); purple_request_field_choice_add(f, _("Public")); purple_request_field_choice_add(f, _("Blocked")); /* index 4 */ purple_request_field_choice_set_default_value(f, 3); /* index */ #endif purple_request_field_set_required(f, TRUE); purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); purple_request_fields(gc, _("Add new domain"), _("Add new domain"), NULL, fields, _("Add"), G_CALLBACK(sipe_purple_ask_access_domain_cb), _("Cancel"), NULL, #if PURPLE_VERSION_CHECK(3,0,0) purple_request_cpar_from_account(account), #else account, NULL, NULL, #endif gc); } static void sipe_purple_buddy_share_desktop_cb(SIPE_UNUSED_PARAMETER PurpleBuddy *buddy, SIPE_UNUSED_PARAMETER gpointer parameter) { #ifdef HAVE_APPSHARE_SERVER sipe_core_appshare_share_desktop(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, purple_buddy_get_name(buddy)); #endif } static void sipe_purple_buddy_give_desktop_control_cb(SIPE_UNUSED_PARAMETER PurpleBuddy *buddy, SIPE_UNUSED_PARAMETER gpointer parameter) { #ifdef HAVE_APPSHARE_SERVER sipe_core_appshare_set_remote_control(parameter, TRUE); #endif } static void sipe_purple_buddy_take_desktop_control_cb(SIPE_UNUSED_PARAMETER PurpleBuddy *buddy, SIPE_UNUSED_PARAMETER gpointer parameter) { #ifdef HAVE_APPSHARE_SERVER sipe_core_appshare_set_remote_control(parameter, FALSE); #endif } typedef void (*buddy_menu_callback)(PurpleBuddy *buddy, gpointer parameter); static const buddy_menu_callback callback_map[SIPE_BUDDY_MENU_TYPES] = { /* SIPE_BUDDY_MENU_MAKE_CHAT_LEADER */ sipe_purple_buddy_make_chat_leader_cb, /* SIPE_BUDDY_MENU_REMOVE_FROM_CHAT */ sipe_purple_buddy_remove_from_chat_cb, /* SIPE_BUDDY_MENU_INVITE_TO_CHAT */ sipe_purple_buddy_invite_to_chat_cb, /* SIPE_BUDDY_MENU_NEW_CHAT */ sipe_purple_buddy_new_chat_cb, /* SIPE_BUDDY_MENU_MAKE_CALL */ sipe_purple_buddy_make_call_cb, /* SIPE_BUDDY_MENU_SEND_EMAIL */ sipe_purple_buddy_send_email_cb, /* SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP */ sipe_purple_buddy_access_level_help_cb, /* SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL */ sipe_purple_buddy_change_access_level_cb, /* SIPE_BUDDY_MENU_ADD_NEW_DOMAIN */ sipe_purple_buddy_add_new_domain_cb, /* SIPE_BUDDY_MENU_SHARE_DESKTOP */ sipe_purple_buddy_share_desktop_cb, /* SIPE_BUDDY_MENU_GIVE_DESKTOP_CONTROL*/ sipe_purple_buddy_give_desktop_control_cb, /* SIPE_BUDDY_MENU_TAKE_DESKTOP_CONTROL*/ sipe_purple_buddy_take_desktop_control_cb, }; struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { return(NULL); } struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_menu *menu, const gchar *label, enum sipe_buddy_menu_type type, gpointer parameter) { return((struct sipe_backend_buddy_menu *) g_list_prepend((GList *) menu, purple_action_menu_new(label, PURPLE_CALLBACK(callback_map[type]), parameter, NULL))); } struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_separator(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_menu *menu, const gchar *label) { return((struct sipe_backend_buddy_menu *) g_list_prepend((GList *) menu, purple_action_menu_new(label, NULL, NULL, NULL))); } struct sipe_backend_buddy_menu *sipe_backend_buddy_sub_menu_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_buddy_menu *menu, const gchar *label, struct sipe_backend_buddy_menu *sub) { return((struct sipe_backend_buddy_menu *) g_list_prepend((GList *) menu, purple_action_menu_new(label, NULL, NULL, g_list_reverse((GList *) sub)))); } static void sipe_purple_buddy_copy_to_cb(PurpleBlistNode *node, const gchar *group_name) { struct sipe_core_public *sipe_public; PurpleBuddy *buddy = (PurpleBuddy *)node; PurpleGroup *group; PurpleBuddy *clone; g_return_if_fail(PURPLE_IS_BUDDY(node)); sipe_public = PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC; group = purple_blist_find_group(group_name); SIPE_DEBUG_INFO("sipe_purple_buddy_copy_to_cb: copying %s to %s", purple_buddy_get_name(buddy), group_name); clone = purple_blist_find_buddy_in_group(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), group); if (!clone) { clone = sipe_backend_buddy_add(sipe_public, purple_buddy_get_name(buddy), #if PURPLE_VERSION_CHECK(3,0,0) purple_buddy_get_local_alias(buddy), #else buddy->alias, #endif purple_group_get_name(group)); if (clone) { const gchar *tmp; const gchar *key; PurpleStatus *status = purple_presence_get_active_status(purple_buddy_get_presence(buddy)); tmp = purple_buddy_get_server_alias(buddy); if (tmp) purple_buddy_set_server_alias(clone, tmp); key = buddy_info_property(SIPE_BUDDY_INFO_EMAIL); tmp = purple_blist_node_get_string(&buddy->node, key); if (tmp) purple_blist_node_set_string(&clone->node, key, tmp); tmp = purple_status_get_id(status); purple_presence_set_status_active(purple_buddy_get_presence(clone), tmp, TRUE); /* update UI */ purple_protocol_got_user_status(purple_buddy_get_account(clone), purple_buddy_get_name(clone), tmp, SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE, tmp, NULL); } } if (clone && group) sipe_core_buddy_add(sipe_public, purple_buddy_get_name(clone), purple_group_get_name(group)); } static GList *sipe_purple_copy_to_menu(GList *menu, PurpleBuddy *buddy) { GList *menu_groups = NULL; PurpleGroup *gr_parent = purple_buddy_get_group(buddy); PurpleBlistNode *g_node; for (g_node = purple_blist_get_default_root(); g_node; g_node = g_node->next) { PurpleGroup *group = (PurpleGroup *)g_node; PurpleActionMenu *act; if ((!PURPLE_IS_GROUP(g_node)) || (group == gr_parent) || purple_blist_find_buddy_in_group(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), group)) continue; act = purple_action_menu_new(purple_group_get_name(group), PURPLE_CALLBACK(sipe_purple_buddy_copy_to_cb), (gpointer) purple_group_get_name(group), NULL); menu_groups = g_list_prepend(menu_groups, act); } if (menu_groups) menu = g_list_prepend(menu, purple_action_menu_new(_("Copy to"), NULL, NULL, g_list_reverse(menu_groups))); return(menu); } GList *sipe_purple_buddy_menu(PurpleBuddy *buddy) { struct sipe_core_public *sipe_public = PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC; GList *menu = (GList *) sipe_core_buddy_create_menu(sipe_public, purple_buddy_get_name(buddy), NULL); menu = sipe_purple_copy_to_menu(menu, buddy); return(g_list_reverse(menu)); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-chat.c ================================================ /** * @file purple-chat.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * Copyright (C) 2009 pier11 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "conversation.h" #include "server.h" /* for ENOTCONN */ #ifdef _WIN32 #include "win32/win32dep.h" #else #include #endif #include "version.h" #if PURPLE_VERSION_CHECK(3,0,0) #include "action.h" #include "buddylist.h" #include "conversations.h" #define BACKEND_SESSION_TO_PURPLE_CONV_CHAT(s) ((PurpleChatConversation *) s) #define PURPLE_CONV_CHAT(c) c #define PURPLE_CONV_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_connection_get_protocol_data(purple_conversation_get_connection(conv))) #else #include "blist.h" #define purple_action_menu_new(l, c, d, ch) purple_menu_action_new(l, c, d, ch) #define purple_chat_conversation_add_user(c, n, m, f, b) purple_conv_chat_add_user(c, n, m, f, b) #define purple_chat_conversation_clear_users(c) purple_conv_chat_clear_users(c) #define purple_chat_conversation_get_id(c) purple_conv_chat_get_id(c) #define purple_chat_conversation_remove_user(c, n, s) purple_conv_chat_remove_user(c, n, s) #define purple_chat_conversation_set_nick(c, n) purple_conv_chat_set_nick(c, n) #define purple_chat_conversation_set_topic(c, n, s) purple_conv_chat_set_topic(c, n, s) #define purple_chat_get_components(chat) chat->components #define purple_conversations_find_chat(g, n) purple_find_chat(g, n) #define purple_conversations_get_chats purple_get_chats #define purple_conversation_get_connection(c) purple_conversation_get_gc(c) #define purple_serv_got_chat_in(c, i, w, f, m, t) serv_got_chat_in(c, i, w, f, m, t) #define purple_serv_got_joined_chat(c, i, n) serv_got_joined_chat(c, i, n) #define PurpleActionMenu PurpleMenuAction #define BACKEND_SESSION_TO_PURPLE_CONV_CHAT(s) (PURPLE_CONV_CHAT(((PurpleConversation *)s))) #define PURPLE_CHAT_USER_NONE PURPLE_CBFLAGS_NONE #define PURPLE_CONV_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) conv->account->gc->proto_data) #define PURPLE_CONVERSATION_UPDATE_TOPIC PURPLE_CONV_UPDATE_TOPIC #endif #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-nls.h" #define _PurpleMessageFlags PurpleMessageFlags #include "purple-private.h" /** * Mapping between chat sessions in SIPE core and libpurple backend * * PurpleAccount * This data structure is created when the user creates the account or at * startup. It lives as long as the account exists, i.e. until the user * deletes it or shutdown. * * Value does not change when connection is dropped & re-created. * HAS: gc (PurpleConnection *) * * PurpleConversation / PurpleConvChat (sub-type) * This data structure is created by serv_got_join_chat(). It lives as long * as the user doesn't leave the chat or until shutdown. * * Value does not change when connection is dropped & re-created. * HAS: account (PurpleAccount *) * HAS: chat ID (int), must be unique * HAS: name (char *), must be unique * HAS: data (GHashTable *) * * PurpleConnection * This data structure is created when the connection to the service is * set up. It lives as long as the connection stays open, the user disables * the account or until shutdown. * * Value *DOES NOT* survive a connection drop & re-creation. * ASSOCIATED TO: account * * SIPE -> libpurple API * add user: purple_conv_chat_add_user(conv, ...) * create: serv_got_joined_chat(gc, chat ID, name) * find user: purple_conv_chat_find_user(conv, ...) * message: serv_got_chat_in(gc, chat ID, ...) * remove user: purple_conv_chat_remove_user(conv, ...) * topic: purple_conv_chat_set_topic(conv, ...) * * libpurple -> SIPE API * join_chat(gc, params (GHashTable *)) * request to join a channel (again) [only Group Chat] * SIPE must call serv_got_joined_chat() on join response * * reject_chat(gc, params (GHashTable *)) NOT IMPLEMENTED * get_chat_name(params (GHashTable *)) NOT IMPLEMENTED * * chat_invite(gc, chat ID,...) * invite a user to a join a chat * * chat_leave(gc, chat ID) * request to leave a channel, also called on conversation destroy * SIPE must call serv_got_chat_left() immediately! * * chat_whisper(gc, chat ID, ...) NOT IMPLEMENTED * * chat_send(gc, chat ID, ...) * send a message to the channel * * set_chat_topic(gc, chat ID, ...) NOT IMPLEMENTED * set channel topic [@TODO: for Group Chat] * * * struct sipe_chat_session * Same life span as PurpleConversation * Pointer stored under key "sipe" in PurpleConversation->data * Contains information private to core to identify chat session on server * * If connection is closed and THEN the conversation, then libpurple will * not call chat_leave() and this will be a dangling data structure! Core * must take care to release them at unload. * * HAS: backend_session (gpointer) -> PurpleConversation * * struct sipe_backend_private * * HAS: rejoin_chats (GList *) * created on login() for existing chats * initiate re-join calls to core (sipe_backend_chat_rejoin_all) */ #define SIPE_PURPLE_KEY_CHAT_SESSION "sipe" struct sipe_chat_session *sipe_purple_chat_get_session(PurpleConversation *conv) { return( #if PURPLE_VERSION_CHECK(3,0,0) g_object_get_data(G_OBJECT(conv), #else purple_conversation_get_data(conv, #endif SIPE_PURPLE_KEY_CHAT_SESSION)); } static struct sipe_chat_session *sipe_purple_chat_find(PurpleConnection *gc, int id) { PurpleConversation *conv = (PurpleConversation *) purple_conversations_find_chat(gc, id); if (!conv) { SIPE_DEBUG_ERROR("sipe_purple_chat_find: can't find chat with ID %d?!?", id); return NULL; } return sipe_purple_chat_get_session(conv); } void sipe_purple_chat_setup_rejoin(struct sipe_backend_private *purple_private) { GList *entry = purple_conversations_get_chats(); while (entry) { PurpleConversation *conv = entry->data; if (purple_conversation_get_connection(conv) == purple_private->gc) purple_private->rejoin_chats = g_list_prepend(purple_private->rejoin_chats, sipe_purple_chat_get_session(conv)); entry = entry->next; } } void sipe_purple_chat_destroy_rejoin(struct sipe_backend_private *purple_private) { g_list_free(purple_private->rejoin_chats); purple_private->rejoin_chats = NULL; } void sipe_purple_chat_invite(PurpleConnection *gc, int id, SIPE_UNUSED_PARAMETER const char *message, const char *name) { struct sipe_chat_session *session = sipe_purple_chat_find(gc, id); if (!session) return; sipe_core_chat_invite(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session, name); } void sipe_purple_chat_leave(PurpleConnection *gc, int id) { struct sipe_chat_session *session = sipe_purple_chat_find(gc, id); if (!session) return; sipe_core_chat_leave(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session); } int sipe_purple_chat_send(PurpleConnection *gc, int id, #if PURPLE_VERSION_CHECK(3,0,0) PurpleMessage *msg) #else const char *what, SIPE_UNUSED_PARAMETER PurpleMessageFlags flags) #endif { struct sipe_chat_session *session = sipe_purple_chat_find(gc, id); if (!session) return -ENOTCONN; sipe_core_chat_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session, #if PURPLE_VERSION_CHECK(3,0,0) purple_message_get_contents(msg)); #else what); #endif return 1; } static void sipe_purple_chat_menu_unlock_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat, PurpleConversation *conv) { struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC; struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv); SIPE_DEBUG_INFO("sipe_purple_chat_menu_lock_cb: %p %p", conv, chat_session); sipe_core_chat_modify_lock(sipe_public, chat_session, FALSE); } static void sipe_purple_chat_menu_lock_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat, PurpleConversation *conv) { struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC; struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv); SIPE_DEBUG_INFO("sipe_purple_chat_menu_lock_cb: %p %p", conv, chat_session); sipe_core_chat_modify_lock(sipe_public, chat_session, TRUE); } #ifdef HAVE_VV static void join_conference_call(PurpleConversation *conv, gboolean with_video) { struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC; struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv); sipe_core_media_connect_conference(sipe_public, chat_session, with_video); } static void sipe_purple_chat_menu_join_call_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat, PurpleConversation *conv) { SIPE_DEBUG_INFO("sipe_purple_chat_join_call_cb: %p", conv); join_conference_call(conv, FALSE); } static void sipe_purple_chat_menu_join_video_call_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat, PurpleConversation *conv) { SIPE_DEBUG_INFO("sipe_purple_chat_join_video_call_cb: %p", conv); join_conference_call(conv, TRUE); } #ifdef HAVE_APPSHARE static void sipe_purple_chat_menu_show_presentation_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat, PurpleConversation *conv) { sipe_appshare_role role; role = sipe_core_conf_get_appshare_role(PURPLE_CONV_TO_SIPE_CORE_PUBLIC, sipe_purple_chat_get_session(conv)); if (role == SIPE_APPSHARE_ROLE_VIEWER) { return; } sipe_core_appshare_connect_conference(PURPLE_CONV_TO_SIPE_CORE_PUBLIC, sipe_purple_chat_get_session(conv), FALSE); } #ifdef HAVE_APPSHARE_SERVER static void sipe_purple_chat_menu_share_desktop_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat, PurpleConversation *conv) { sipe_core_conf_share_desktop(PURPLE_CONV_TO_SIPE_CORE_PUBLIC, sipe_purple_chat_get_session(conv)); } #endif #endif #endif // HAVE_VV static void sipe_purple_chat_menu_entry_info_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat, PurpleConversation *conv) { gchar *tmp = sipe_core_conf_entry_info(PURPLE_CONV_TO_SIPE_CORE_PUBLIC, sipe_purple_chat_get_session(conv)); purple_notify_formatted(NULL, NULL, "", NULL, tmp, NULL, NULL); g_free(tmp); } GList * sipe_purple_chat_menu(PurpleChat *chat) { PurpleConversation *conv = g_hash_table_lookup(purple_chat_get_components(chat), SIPE_PURPLE_COMPONENT_KEY_CONVERSATION); GList *menu = NULL; if (conv) { PurpleActionMenu *act = NULL; struct sipe_chat_session *chat_session; #ifdef HAVE_APPSHARE sipe_appshare_role role; #endif SIPE_DEBUG_INFO("sipe_purple_chat_menu: %p", conv); chat_session = sipe_purple_chat_get_session(conv); switch (sipe_core_chat_lock_status(PURPLE_CONV_TO_SIPE_CORE_PUBLIC, chat_session)) { case SIPE_CHAT_LOCK_STATUS_UNLOCKED: act = purple_action_menu_new(_("Lock"), PURPLE_CALLBACK(sipe_purple_chat_menu_lock_cb), conv, NULL); break; case SIPE_CHAT_LOCK_STATUS_LOCKED: act = purple_action_menu_new(_("Unlock"), PURPLE_CALLBACK(sipe_purple_chat_menu_unlock_cb), conv, NULL); break; default: /* Not allowed */ break; } if (act) menu = g_list_prepend(menu, act); switch (sipe_core_chat_type(chat_session)) { case SIPE_CHAT_TYPE_CONFERENCE: case SIPE_CHAT_TYPE_MULTIPARTY: #ifdef HAVE_VV if (!sipe_core_media_get_call(PURPLE_CONV_TO_SIPE_CORE_PUBLIC)) { act = purple_action_menu_new(_("Join conference video call"), PURPLE_CALLBACK(sipe_purple_chat_menu_join_video_call_cb), conv, NULL); menu = g_list_prepend(menu, act); act = purple_action_menu_new(_("Join conference call"), PURPLE_CALLBACK(sipe_purple_chat_menu_join_call_cb), conv, NULL); menu = g_list_prepend(menu, act); } #ifdef HAVE_APPSHARE role = sipe_core_conf_get_appshare_role(PURPLE_CONV_TO_SIPE_CORE_PUBLIC, chat_session); if (role == SIPE_APPSHARE_ROLE_NONE) { act = purple_action_menu_new(_("Show presentation"), PURPLE_CALLBACK(sipe_purple_chat_menu_show_presentation_cb), conv, NULL); menu = g_list_prepend(menu, act); } #ifdef HAVE_APPSHARE_SERVER if (role != SIPE_APPSHARE_ROLE_PRESENTER) { act = purple_action_menu_new(_("Share my desktop"), PURPLE_CALLBACK(sipe_purple_chat_menu_share_desktop_cb), conv, NULL); menu = g_list_prepend(menu, act); } #endif #endif #endif // HAVE_VV act = purple_action_menu_new(_("Meeting entry info"), PURPLE_CALLBACK(sipe_purple_chat_menu_entry_info_cb), conv, NULL); menu = g_list_append(menu, act); break; default: break; } } return menu; } void sipe_backend_chat_session_destroy(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *session) { /* Nothing to do here */ } void sipe_backend_chat_add(struct sipe_backend_chat_session *backend_session, const gchar *uri, gboolean is_new) { purple_chat_conversation_add_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session), uri, NULL, PURPLE_CHAT_USER_NONE, is_new); } void sipe_backend_chat_close(struct sipe_backend_chat_session *backend_session) { purple_chat_conversation_clear_users(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session)); } static int sipe_purple_chat_id(PurpleConnection *gc) { /** * A non-volatile ID counter. * Should survive connection drop & reconnect. */ static int chat_id = 0; /* Find next free ID */ do { if (++chat_id < 0) chat_id = 0; } while (purple_conversations_find_chat(gc, chat_id) != NULL) ; return chat_id; } struct sipe_backend_chat_session *sipe_backend_chat_create(struct sipe_core_public *sipe_public, struct sipe_chat_session *session, const gchar *title, const gchar *nick) { struct sipe_backend_private *purple_private = sipe_public->backend_private; #if PURPLE_VERSION_CHECK(3,0,0) PurpleChatConversation *conv; #else PurpleConversation *conv; #endif /* * Adium calls back into SIPE code during execution of the following * libpurple API. That code needs access to "session". As "conv" is * still being initialized we can't use sipe_purple_chat_get_session(). */ purple_private->adium_chat_session = session; conv = purple_serv_got_joined_chat(purple_private->gc, sipe_purple_chat_id(purple_private->gc), title); purple_private->adium_chat_session = NULL; #if PURPLE_VERSION_CHECK(3,0,0) g_object_set_data(G_OBJECT(conv), #else purple_conversation_set_data(conv, #endif SIPE_PURPLE_KEY_CHAT_SESSION, session); purple_chat_conversation_set_nick(PURPLE_CONV_CHAT(conv), nick); return((struct sipe_backend_chat_session *) conv); } gboolean sipe_backend_chat_find(struct sipe_backend_chat_session *backend_session, const gchar *uri) { #if PURPLE_VERSION_CHECK(3,0,0) return(purple_chat_conversation_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session), uri) != NULL); #else return purple_conv_chat_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session), uri); #endif } gboolean sipe_backend_chat_is_operator(struct sipe_backend_chat_session *backend_session, const gchar *uri) { #if PURPLE_VERSION_CHECK(3,0,0) return((purple_chat_user_get_flags( purple_chat_conversation_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session), uri)) & PURPLE_CHAT_USER_OP) == PURPLE_CHAT_USER_OP); #else return (purple_conv_chat_user_get_flags(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session), uri) & PURPLE_CBFLAGS_OP) == PURPLE_CBFLAGS_OP; #endif } void sipe_backend_chat_message(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *from, time_t when, const gchar *html) { struct sipe_backend_private *purple_private = sipe_public->backend_private; purple_serv_got_chat_in(purple_private->gc, purple_chat_conversation_get_id(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session)), from, PURPLE_MESSAGE_RECV, html, when ? when : time(NULL)); } void sipe_backend_chat_operator(struct sipe_backend_chat_session *backend_session, const gchar *uri) { #if PURPLE_VERSION_CHECK(3,0,0) purple_chat_user_set_flags( purple_chat_conversation_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session), uri), PURPLE_CHAT_USER_NONE | PURPLE_CHAT_USER_OP); #else purple_conv_chat_user_set_flags(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session), uri, PURPLE_CBFLAGS_NONE | PURPLE_CBFLAGS_OP); #endif } void sipe_backend_chat_rejoin(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *nick, const gchar *title) { struct sipe_backend_private *purple_private = sipe_public->backend_private; #if PURPLE_VERSION_CHECK(3,0,0) PurpleChatConversation *chat = BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session); PurpleChatConversation *new; #else PurpleConvChat *chat = BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session); PurpleConversation *new; #endif /** * As the chat is marked as "left", serv_got_joined_chat() will * do a "rejoin cleanup" and return the same conversation. */ new = purple_serv_got_joined_chat(purple_private->gc, purple_chat_conversation_get_id(chat), title); SIPE_DEBUG_INFO("sipe_backend_chat_rejoin: old %p (%p) == new %p (%p)", backend_session, chat, new, PURPLE_CONV_CHAT(new)); purple_chat_conversation_set_nick(chat, nick); } /** * Connection re-established: tell core what chats need to be rejoined */ void sipe_backend_chat_rejoin_all(struct sipe_core_public *sipe_public) { struct sipe_backend_private *purple_private = sipe_public->backend_private; GList *entry = purple_private->rejoin_chats; while (entry) { sipe_core_chat_rejoin(sipe_public, entry->data); entry = entry->next; } sipe_purple_chat_destroy_rejoin(purple_private); } void sipe_backend_chat_remove(struct sipe_backend_chat_session *backend_session, const gchar *uri) { purple_chat_conversation_remove_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session), uri, NULL /* reason */); } void sipe_backend_chat_show(struct sipe_backend_chat_session *backend_session) { /* Bring existing purple chat to the front */ /* @TODO: This seems to the trick, but is it the correct way? */ purple_conversation_update((PurpleConversation *) backend_session, PURPLE_CONVERSATION_UPDATE_TOPIC); } void sipe_backend_chat_topic(struct sipe_backend_chat_session *backend_session, const gchar *topic) { purple_chat_conversation_set_topic(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session), NULL, topic); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-connection.c ================================================ /** * @file purple-connection.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "glib.h" #include "connection.h" #include "sipe-backend.h" #include "sipe-core.h" #include "purple-private.h" #if PURPLE_VERSION_CHECK(3,0,0) #else #define PURPLE_CONNECTION_CONNECTED PURPLE_CONNECTED #define purple_account_is_disconnecting(a) a->disconnecting #define purple_connection_error(g, e, m) purple_connection_error_reason(g, e, m) #endif void sipe_backend_connection_completed(struct sipe_core_public *sipe_public) { purple_connection_set_state(sipe_public->backend_private->gc, PURPLE_CONNECTION_CONNECTED); } static const guint map[SIPE_CONNECTION_ERROR_LAST] = { PURPLE_CONNECTION_ERROR_NETWORK_ERROR, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, }; void sipe_backend_connection_error(struct sipe_core_public *sipe_public, sipe_connection_error error, const gchar *msg) { purple_connection_error(sipe_public->backend_private->gc, map[error], msg); } gboolean sipe_backend_connection_is_disconnecting(struct sipe_core_public *sipe_public) { return(purple_account_is_disconnecting(sipe_public->backend_private->account)); } gboolean sipe_backend_connection_is_valid(struct sipe_core_public *sipe_public) { return PURPLE_CONNECTION_IS_CONNECTED(sipe_public->backend_private->gc); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-dbus-bindings.c ================================================ /** * @file purple-dbus-bindings.c * * pidgin-sipe * * Copyright (C) 2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "purple-dbus.h" /* * The contents of this file need to be updated when any line which starts * with DBUS_EXPORT in purple-dbus.h gets added/removed/changed. * * You'll need access to the Pidgin source code to update this file: * * $ python /libpurple/dbus-analyze-functions.py \ * --export-only \ * src/purple/purple-dbus.h \ * >> src/purple/purple-dbus-bindings.c * * You'll have to edit the contents manually after running the above command. */ /* * The generated xxx_DBUS() functions need to be copied here */ #ifdef HAVE_VV static DBusMessage* sipe_call_phone_number_DBUS(DBusMessage *message_DBUS, DBusError *error_DBUS) { DBusMessage *reply_DBUS; dbus_int32_t account_ID; PurpleAccount *account; const char *phone_number; dbus_message_get_args(message_DBUS, error_DBUS, DBUS_TYPE_INT32, &account_ID, DBUS_TYPE_STRING, &phone_number, DBUS_TYPE_INVALID); CHECK_ERROR(error_DBUS); PURPLE_DBUS_ID_TO_POINTER(account, account_ID, PurpleAccount, error_DBUS); phone_number = (phone_number && phone_number[0]) ? phone_number : NULL; sipe_call_phone_number(account, phone_number); reply_DBUS = dbus_message_new_method_return (message_DBUS); dbus_message_append_args(reply_DBUS, DBUS_TYPE_INVALID); return reply_DBUS; } #endif static DBusMessage* sipe_join_conference_with_organizer_and_id_DBUS(DBusMessage *message_DBUS, DBusError *error_DBUS) { DBusMessage *reply_DBUS; dbus_int32_t account_ID; PurpleAccount *account; const char *organizer; const char *meeting_id; dbus_message_get_args(message_DBUS, error_DBUS, DBUS_TYPE_INT32, &account_ID, DBUS_TYPE_STRING, &organizer, DBUS_TYPE_STRING, &meeting_id, DBUS_TYPE_INVALID); CHECK_ERROR(error_DBUS); PURPLE_DBUS_ID_TO_POINTER(account, account_ID, PurpleAccount, error_DBUS); organizer = (organizer && organizer[0]) ? organizer : NULL; meeting_id = (meeting_id && meeting_id[0]) ? meeting_id : NULL; sipe_join_conference_with_organizer_and_id(account, organizer, meeting_id); reply_DBUS = dbus_message_new_method_return (message_DBUS); dbus_message_append_args(reply_DBUS, DBUS_TYPE_INVALID); return reply_DBUS; } static DBusMessage* sipe_join_conference_with_uri_DBUS(DBusMessage *message_DBUS, DBusError *error_DBUS) { DBusMessage *reply_DBUS; dbus_int32_t account_ID; PurpleAccount *account; const char *uri; dbus_message_get_args(message_DBUS, error_DBUS, DBUS_TYPE_INT32, &account_ID, DBUS_TYPE_STRING, &uri, DBUS_TYPE_INVALID); CHECK_ERROR(error_DBUS); PURPLE_DBUS_ID_TO_POINTER(account, account_ID, PurpleAccount, error_DBUS); uri = (uri && uri[0]) ? uri : NULL; sipe_join_conference_with_uri(account, uri); reply_DBUS = dbus_message_new_method_return (message_DBUS); dbus_message_append_args(reply_DBUS, DBUS_TYPE_INVALID); return reply_DBUS; } static DBusMessage* sipe_republish_calendar_DBUS(DBusMessage *message_DBUS, DBusError *error_DBUS) { DBusMessage *reply_DBUS; dbus_int32_t account_ID; PurpleAccount *account; dbus_message_get_args(message_DBUS, error_DBUS, DBUS_TYPE_INT32, &account_ID, DBUS_TYPE_INVALID); CHECK_ERROR(error_DBUS); PURPLE_DBUS_ID_TO_POINTER(account, account_ID, PurpleAccount, error_DBUS); sipe_republish_calendar(account); reply_DBUS = dbus_message_new_method_return (message_DBUS); dbus_message_append_args(reply_DBUS, DBUS_TYPE_INVALID); return reply_DBUS; } static DBusMessage* sipe_reset_status_DBUS(DBusMessage *message_DBUS, DBusError *error_DBUS) { DBusMessage *reply_DBUS; dbus_int32_t account_ID; PurpleAccount *account; dbus_message_get_args(message_DBUS, error_DBUS, DBUS_TYPE_INT32, &account_ID, DBUS_TYPE_INVALID); CHECK_ERROR(error_DBUS); PURPLE_DBUS_ID_TO_POINTER(account, account_ID, PurpleAccount, error_DBUS); sipe_reset_status(account); reply_DBUS = dbus_message_new_method_return (message_DBUS); dbus_message_append_args(reply_DBUS, DBUS_TYPE_INVALID); return reply_DBUS; } /* * The contents of bindings_DBUS[] need to be copied here */ PurpleDBusBinding sipe_purple_dbus_bindings[] = { #ifdef HAVE_VV {"SipeCallPhoneNumber", "in\0i\0account\0in\0s\0phone_number\0", sipe_call_phone_number_DBUS}, #endif {"SipeJoinConferenceWithOrganizerAndId", "in\0i\0account\0in\0s\0organizer\0in\0s\0meeting_id\0", sipe_join_conference_with_organizer_and_id_DBUS}, {"SipeJoinConferenceWithUri", "in\0i\0account\0in\0s\0uri\0", sipe_join_conference_with_uri_DBUS}, {"SipeRepublishCalendar", "in\0i\0account\0", sipe_republish_calendar_DBUS}, {"SipeResetStatus", "in\0i\0account\0", sipe_reset_status_DBUS}, {NULL, NULL, NULL} }; /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-dbus.c ================================================ /** * @file purple-dbus.c * * pidgin-sipe * * Copyright (C) 2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "account.h" #include "connection.h" #include "sipe-core.h" #include "purple-dbus.h" #include "purple-private.h" #if PURPLE_VERSION_CHECK(3,0,0) #else #define purple_account_is_disconnecting(a) a->disconnecting #endif /** * A call to our D-Bus interface is independent from the actual libpurple * state. Therefore we can't trust any of the incoming data. * * @param account (in) libpurple account (may be @c NULL) * * @return @c TRUE if it is safe to use PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC */ static gboolean account_is_valid(PurpleAccount *account) { gboolean valid = FALSE; if (account && !purple_account_is_disconnecting(account) && (sipe_strequal(purple_account_get_protocol_id(account), SIPE_PURPLE_PLUGIN_ID))) { PurpleConnection *gc = purple_account_get_connection(account); if (gc && PURPLE_CONNECTION_IS_CONNECTED(gc)) valid = TRUE; } return(valid); } #ifdef HAVE_VV void sipe_call_phone_number(PurpleAccount *account, const gchar *phone_number) { /* Make sure phone number is valid before calling to core */ if (account_is_valid(account) && phone_number) sipe_core_media_phone_call(PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC, phone_number); } #endif void sipe_join_conference_with_organizer_and_id(PurpleAccount *account, const gchar *organizer, const gchar *meeting_id) { /* Make sure organizer & ID are valid before calling to core */ if (account_is_valid(account) && organizer && meeting_id) sipe_core_conf_create(PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC, NULL, organizer, meeting_id); } void sipe_join_conference_with_uri(PurpleAccount *account, const gchar *uri) { /* Make sure URI is valid before calling to core */ if (account_is_valid(account) && uri) sipe_core_conf_create(PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC, uri, NULL, NULL); } void sipe_republish_calendar(PurpleAccount *account) { if (account_is_valid(account)) sipe_purple_republish_calendar(account); } void sipe_reset_status(PurpleAccount *account) { if (account_is_valid(account)) sipe_purple_reset_status(account); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-dbus.h ================================================ /** * @file purple-dbus.h * * pidgin-sipe * * Copyright (C) 2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Work around some versions of dbus-server.h that redefine DBUS_EXPORT * without checking that it is already defined. Include dbus/dbus.h first * to suppress the implicit inclusion through dbus-server.h. Then undefine * the macro to avoid the potential build failure. */ #include #ifdef DBUS_EXPORT #undef DBUS_EXPORT #endif #include "dbus-server.h" #include "account.h" extern PurpleDBusBinding sipe_purple_dbus_bindings[]; /** * SipeCallPhoneNumber - call phone number * * @param account (in) libpurple account * @param number (in) phone number string */ DBUS_EXPORT void sipe_call_phone_number(PurpleAccount *account, const gchar *phone_number); /** * SipeJoinConferenceWithOrganizerAndId - join conference using * organizer account name and meeting ID * * @param account (in) libpurple account * @param organizer (in) organizer account name * @param id (in) meeting ID string */ DBUS_EXPORT void sipe_join_conference_with_organizer_and_id(PurpleAccount *account, const gchar *organizer, const gchar *meeting_id); /** * SipeJoinConferenceWithUri - join conference using URI * * @param account (in) libpurple account * @param uri (in) URI string */ DBUS_EXPORT void sipe_join_conference_with_uri(PurpleAccount *account, const gchar *uri); /** * SipeRepublishCalendar */ DBUS_EXPORT void sipe_republish_calendar(PurpleAccount *account); /** * SipeResetStatus */ DBUS_EXPORT void sipe_reset_status(PurpleAccount *account); /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-debug.c ================================================ /** * @file purple-debug.c * * pidgin-sipe * * Copyright (C) 2010-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "glib.h" #include "debug.h" #include "version.h" #include "sipe-backend.h" #ifdef ADIUM /* * libpurple uses g_print() and PurpleDebugUiOps->debug() when * purple_debug_is_enabled() returns TRUE. Both are redirected * by Adium to AILog(). To avoid duplicated log lines Adium * therefore never calls purple_debug_set_enabled(TRUE). */ gboolean AIDebugLoggingIsEnabled(void); #define SIPE_PURPLE_DEBUG_IS_ENABLED AIDebugLoggingIsEnabled() #define SIPE_PURPLE_DEBUG_IS_UNSAFE AIDebugLoggingIsEnabled() #else /* * The same problem happens when a client uses PurpleDebugUiOps->debug() * to redirect it to stderr, e.g. bitlbee. Such a client will not call * purple_debug_set_enabled(TRUE). Check also the other flags that were * introduced in the 2.6.x API. */ #define SIPE_PURPLE_DEBUG_IS_ENABLED (purple_debug_is_enabled() || \ purple_debug_is_verbose() || \ purple_debug_is_unsafe()) #define SIPE_PURPLE_DEBUG_IS_UNSAFE purple_debug_is_unsafe() #endif void sipe_backend_debug_literal(sipe_debug_level level, const gchar *msg) { if ((level < SIPE_DEBUG_LEVEL_LOWEST) || SIPE_PURPLE_DEBUG_IS_ENABLED) { /* purple_debug doesn't have a vprintf-like API call :-( */ switch (level) { case SIPE_LOG_LEVEL_INFO: case SIPE_DEBUG_LEVEL_INFO: purple_debug_info("sipe", "%s\n", msg); break; case SIPE_LOG_LEVEL_WARNING: case SIPE_DEBUG_LEVEL_WARNING: purple_debug_warning("sipe", "%s\n", msg); break; case SIPE_LOG_LEVEL_ERROR: case SIPE_DEBUG_LEVEL_ERROR: purple_debug_error("sipe", "%s\n", msg); break; } } } void sipe_backend_debug(sipe_debug_level level, const gchar *format, ...) { va_list ap; va_start(ap, format); if ((level < SIPE_DEBUG_LEVEL_LOWEST) || SIPE_PURPLE_DEBUG_IS_ENABLED) { /* purple_debug doesn't have a vprintf-like API call :-( */ gchar *msg = g_strdup_vprintf(format, ap); sipe_backend_debug_literal(level, msg); g_free(msg); } va_end(ap); } gboolean sipe_backend_debug_enabled(void) { return SIPE_PURPLE_DEBUG_IS_UNSAFE; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-dnsquery.c ================================================ /** * @file purple-dnsquery.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "version.h" #if PURPLE_VERSION_CHECK(2,8,0) #include "account.h" #endif #if PURPLE_VERSION_CHECK(3,0,0) #include "protocols.h" #include #else #ifdef _WIN32 /* wrappers for write() & friends for socket handling */ #include "win32/win32dep.h" #include #else #include #include #include #include #endif #include "dnsquery.h" #include "dnssrv.h" #endif #include "sipe-backend.h" #include "sipe-core.h" #include "purple-private.h" struct sipe_dns_query { struct sipe_backend_private *purple_private; sipe_dns_resolved_cb callback; gpointer extradata; gpointer purple_query_data; gboolean is_valid; #if PURPLE_VERSION_CHECK(3,0,0) guint port; #else enum { A, SRV } type; #endif }; static void sipe_dns_query_free(struct sipe_dns_query *query) { #if PURPLE_VERSION_CHECK(3,0,0) g_object_unref(query->purple_query_data); #endif g_free(query); } #if PURPLE_VERSION_CHECK(3,0,0) static void dns_a_response(GObject *source, GAsyncResult *res, gpointer user_data) { struct sipe_dns_query *query = user_data; GList *hosts; GError *error = NULL; gchar *address_str = NULL; if (!query->is_valid) { /* Ignore spurious responses after disconnect */ return; } query->purple_private->dns_queries = g_slist_remove(query->purple_private->dns_queries, query); hosts = g_resolver_lookup_by_name_finish(G_RESOLVER(source), res, &error); if (!error && g_list_length(hosts) > 0) { address_str = g_inet_address_to_string(hosts->data); } query->callback(query->extradata, address_str, address_str ? query->port : 0); g_free(address_str); if (error) g_error_free(error); g_resolver_free_addresses(hosts); sipe_dns_query_free(query); } #else static void dns_a_response(GSList *hosts, struct sipe_dns_query *query, const char *error_message) { char ipstr[INET6_ADDRSTRLEN]; struct sockaddr *addr; const void *addrdata; int port; /* Ignore spurious responses after disconnect */ if (query->is_valid) { struct sipe_backend_private *purple_private = query->purple_private; purple_private->dns_queries = g_slist_remove(purple_private->dns_queries, query); if (error_message || !g_slist_next(hosts)) { query->callback(query->extradata, NULL, 0); g_slist_free(hosts); return; } addr = g_slist_next(hosts)->data; if (addr->sa_family == AF_INET6) { /* OS provides addr so it must be properly aligned */ struct sockaddr_in6 *sin6 = (void *) addr; addrdata = &sin6->sin6_addr; port = sin6->sin6_port; } else { /* OS provides addr so it must be properly aligned */ struct sockaddr_in *sin = (void *) addr; addrdata = &sin->sin_addr; port = sin->sin_port; } inet_ntop(addr->sa_family, addrdata, ipstr, sizeof (ipstr)); query->callback(query->extradata, ipstr, port); g_free(query); } for (; hosts; hosts = g_slist_delete_link(hosts, hosts)) { // Free the addrlen, no data in this link hosts = g_slist_delete_link(hosts, hosts); // Free the address g_free(hosts->data); } } #endif struct sipe_dns_query *sipe_backend_dns_query_a(struct sipe_core_public *sipe_public, const gchar *hostname, guint port, sipe_dns_resolved_cb callback, gpointer data) { struct sipe_dns_query *query = g_new(struct sipe_dns_query, 1); struct sipe_backend_private *purple_private = sipe_public->backend_private; #if PURPLE_VERSION_CHECK(3,0,0) GResolver *resolver = g_resolver_get_default(); #endif query->purple_private = purple_private; query->callback = callback; query->extradata = data; query->is_valid = TRUE; purple_private->dns_queries = g_slist_prepend(purple_private->dns_queries, query); #if PURPLE_VERSION_CHECK(3,0,0) query->port = port; query->purple_query_data = g_cancellable_new(); g_resolver_lookup_by_name_async(resolver, hostname, query->purple_query_data, dns_a_response, query); g_object_unref(resolver); #else query->type = A; query->purple_query_data = #if PURPLE_VERSION_CHECK(2,8,0) purple_dnsquery_a_account( purple_private->account, #else purple_dnsquery_a( #endif hostname, port, (PurpleDnsQueryConnectFunction) dns_a_response, query); #endif return query; } #if PURPLE_VERSION_CHECK(3,0,0) static void dns_srv_response(GObject *source, GAsyncResult *res, gpointer user_data) { struct sipe_dns_query *query = user_data; GError *error = NULL; GList *targets; if (!query->is_valid) { /* Ignore spurious responses after disconnect */ return; } query->purple_private->dns_queries = g_slist_remove(query->purple_private->dns_queries, query); targets = g_resolver_lookup_service_finish(G_RESOLVER(source), res, &error); if (error || g_list_length(targets) == 0) { query->callback(query->extradata, NULL, 0); } else { query->callback(query->extradata, g_srv_target_get_hostname(targets->data), g_srv_target_get_port(targets->data)); } if (error) g_error_free(error); g_resolver_free_targets(targets); sipe_dns_query_free(query); } #else static void dns_srv_response(PurpleSrvResponse *resp, int results, struct sipe_dns_query *query) { /* Ignore spurious responses after disconnect */ if (query->is_valid) { struct sipe_backend_private *purple_private = query->purple_private; purple_private->dns_queries = g_slist_remove(purple_private->dns_queries, query); if (results) query->callback(query->extradata, resp->hostname, resp->port); else query->callback(query->extradata, NULL, 0); g_free(query); } g_free(resp); } #endif struct sipe_dns_query *sipe_backend_dns_query_srv(struct sipe_core_public *sipe_public, const gchar *protocol, const gchar *transport, const gchar *domain, sipe_dns_resolved_cb callback, gpointer data) { struct sipe_dns_query *query = g_new(struct sipe_dns_query, 1); struct sipe_backend_private *purple_private = sipe_public->backend_private; #if PURPLE_VERSION_CHECK(3,0,0) GResolver *resolver = g_resolver_get_default(); #endif query->purple_private = purple_private; query->callback = callback; query->extradata = data; query->is_valid = TRUE; purple_private->dns_queries = g_slist_prepend(purple_private->dns_queries, query); #if PURPLE_VERSION_CHECK(3,0,0) query->purple_query_data = g_cancellable_new(); g_resolver_lookup_service_async(resolver, protocol, transport, domain, query->purple_query_data, dns_srv_response, query); g_object_unref(resolver); #else query->type = SRV; query->purple_query_data = #if PURPLE_VERSION_CHECK(2,8,0) purple_srv_resolve_account( purple_private->account, #else purple_srv_resolve( #endif protocol, transport, domain, (PurpleSrvCallback) dns_srv_response, query); #endif return query; } static gboolean dns_query_deferred_destroy(gpointer user_data) { /* * All pending events on query have been processed. * Now it is safe to destroy the data structure. */ SIPE_DEBUG_INFO("dns_query_deferred_destroy: %p", user_data); sipe_dns_query_free(user_data); return(FALSE); } void sipe_backend_dns_query_cancel(struct sipe_dns_query *query) { SIPE_DEBUG_INFO("sipe_backend_dns_query_cancel: %p", query); if (query->is_valid) { struct sipe_backend_private *purple_private = query->purple_private; purple_private->dns_queries = g_slist_remove(purple_private->dns_queries, query); #if PURPLE_VERSION_CHECK(3,0,0) g_cancellable_cancel(query->purple_query_data); #else switch (query->type) { case A: purple_dnsquery_destroy(query->purple_query_data); break; case SRV: #if PURPLE_VERSION_CHECK(2,8,0) purple_srv_txt_query_destroy(query->purple_query_data); #else purple_srv_cancel(query->purple_query_data); #endif break; } #endif /* defer deletion of query data structure to idle callback */ query->is_valid = FALSE; g_idle_add(dns_query_deferred_destroy, query); } } void sipe_purple_dns_query_cancel_all(struct sipe_backend_private *purple_private) { GSList *entry; SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_dns_query_cancel_all: entered"); while ((entry = purple_private->dns_queries) != NULL) sipe_backend_dns_query_cancel(entry->data); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-ft.c ================================================ /** * @file purple-ft.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * Copyright (C) 2010 Jakub Adam * Copyright (C) 2010 Tomáš Hrabčík * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "version.h" #if PURPLE_VERSION_CHECK(3,0,0) #include "protocol.h" #define PURPLE_XFER_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_connection_get_protocol_data(purple_account_get_connection(purple_xfer_get_account(xfer)))) #define PURPLE_XFER_TO_SIPE_FILE_TRANSFER (SIPE_PURPLE_XFER(xfer)->ft) #define SIPE_TYPE_PURPLE_XFER (sipe_purple_xfer_get_type()) G_DECLARE_FINAL_TYPE(SipePurpleXfer, sipe_purple_xfer, SIPE, PURPLE_XFER, PurpleXfer); struct _SipePurpleXfer { PurpleXfer parent; struct sipe_file_transfer *ft; }; #else #include "ft.h" #define g_timeout_add(t, f, d) purple_timeout_add(t, f, d) #define PurpleXferStatus PurpleXferStatusType #define PURPLE_XFER_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_account_get_connection(xfer->account)->proto_data) #define PURPLE_XFER_TO_SIPE_FILE_TRANSFER ((struct sipe_file_transfer *) purple_xfer_get_protocol_data(xfer)) #define PURPLE_XFER_TYPE_RECEIVE PURPLE_XFER_RECEIVE #define PURPLE_XFER_TYPE_SEND PURPLE_XFER_SEND #define purple_xfer_get_fd(xfer) xfer->fd #define purple_xfer_get_protocol_data(xfer) xfer->data #define purple_xfer_get_status(xfer) purple_xfer_get_status(xfer) #define purple_xfer_get_xfer_type(xfer) purple_xfer_get_type(xfer) #define purple_xfer_get_watcher(xfer) xfer->watcher #define purple_xfer_set_protocol_data(xfer, d) xfer->data = d #define purple_xfer_set_watcher(xfer, w) xfer->watcher = w #endif #ifdef _WIN32 /* wrappers for write() & friends for socket handling */ #include "win32/win32dep.h" #endif #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "purple-private.h" #define FT_TO_PURPLE_XFER ((PurpleXfer *) ft->backend_private) void sipe_backend_ft_error(struct sipe_file_transfer *ft, const char *errmsg) { PurpleXfer *xfer = FT_TO_PURPLE_XFER; purple_xfer_error(purple_xfer_get_xfer_type(xfer), purple_xfer_get_account(xfer), purple_xfer_get_remote_user(xfer), errmsg); } const gchar *sipe_backend_ft_get_error(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft) { return strerror(errno); } void sipe_backend_ft_deallocate(struct sipe_file_transfer *ft) { PurpleXfer *xfer = FT_TO_PURPLE_XFER; PurpleXferStatus status = purple_xfer_get_status(xfer); // If file transfer is not finished, cancel it if ( status != PURPLE_XFER_STATUS_DONE && status != PURPLE_XFER_STATUS_CANCEL_LOCAL && status != PURPLE_XFER_STATUS_CANCEL_REMOTE) { purple_xfer_cancel_remote(xfer); } } gssize sipe_backend_ft_read(struct sipe_file_transfer *ft, guchar *data, gsize size) { gssize bytes_read = read(purple_xfer_get_fd(FT_TO_PURPLE_XFER), data, size); if (bytes_read == 0) { /* Sender canceled transfer before it was finished */ return -2; } else if (bytes_read == -1) { if (errno == EAGAIN) return 0; else return -1; } return bytes_read; } gssize sipe_backend_ft_write(struct sipe_file_transfer *ft, const guchar *data, gsize size) { gssize bytes_written = write(purple_xfer_get_fd(FT_TO_PURPLE_XFER), data, size); if (bytes_written == -1) { if (errno == EAGAIN) return 0; else return -1; } return bytes_written; } static gboolean end_transfer_cb(gpointer data) { purple_xfer_end((PurpleXfer *)data); return FALSE; } void sipe_backend_ft_set_completed(struct sipe_file_transfer *ft) { purple_xfer_set_completed(FT_TO_PURPLE_XFER, TRUE); g_timeout_add(0, end_transfer_cb, FT_TO_PURPLE_XFER); } void sipe_backend_ft_cancel_local(struct sipe_file_transfer *ft) { purple_xfer_cancel_local(FT_TO_PURPLE_XFER); } void sipe_backend_ft_cancel_remote(struct sipe_file_transfer *ft) { purple_xfer_cancel_remote(FT_TO_PURPLE_XFER); } static void ft_free_xfer_struct(PurpleXfer *xfer) { if (purple_xfer_get_watcher(xfer)) { purple_input_remove(purple_xfer_get_watcher(xfer)); purple_xfer_set_watcher(xfer, 0); } #if PURPLE_VERSION_CHECK(3,0,0) SIPE_PURPLE_XFER(xfer)->ft = NULL; #else purple_xfer_set_protocol_data(xfer, NULL); #endif } static void ft_request_denied(PurpleXfer *xfer) { struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER; if (ft->ft_request_denied) { ft->ft_request_denied(ft); } ft_free_xfer_struct(xfer); } static void ft_cancelled(PurpleXfer *xfer) { struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER; if (ft->ft_cancelled && purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) { ft->ft_cancelled(ft); } ft_free_xfer_struct(xfer); } static void ft_init(PurpleXfer *xfer) { struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER; g_return_if_fail(ft->ft_init); ft->ft_init(ft, purple_xfer_get_filename(xfer), purple_xfer_get_size(xfer), purple_xfer_get_remote_user(xfer)); } static void ft_start(PurpleXfer *xfer) { struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER; if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) { /* Set socket to non-blocking mode */ int flags = fcntl(purple_xfer_get_fd(xfer), F_GETFL, 0); if (flags == -1) { flags = 0; } /* @TODO: ignoring potential error return - how to handle? */ (void) fcntl(purple_xfer_get_fd(xfer), F_SETFL, flags | O_NONBLOCK); } if (ft->ft_start) { ft->ft_start(ft, purple_xfer_get_size(xfer)); } } static void ft_end(PurpleXfer *xfer) { struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER; if (!ft->ft_end || ft->ft_end(ft)) { /* We're done with this transfer */ ft_free_xfer_struct(xfer); } else if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) { /* Remove incomplete file from failed transfer. */ unlink(purple_xfer_get_local_filename(xfer)); } } static gssize ft_read(guchar **buffer, #if PURPLE_VERSION_CHECK(3,0,0) size_t buffer_size, #endif PurpleXfer *xfer) { struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER; g_return_val_if_fail(ft->ft_read, 0); return ft->ft_read(ft, buffer, purple_xfer_get_bytes_remaining(xfer), #if PURPLE_VERSION_CHECK(3,0,0) buffer_size #else xfer->current_buffer_size #endif ); } static gssize ft_write(const guchar *buffer, size_t size, PurpleXfer *xfer) { struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER; gssize bytes_written = 0; g_return_val_if_fail(ft->ft_write, 0); bytes_written = ft->ft_write(ft, buffer, size); if ((purple_xfer_get_bytes_remaining(xfer) - bytes_written) == 0) purple_xfer_set_completed(xfer, TRUE); return bytes_written; } static PurpleXfer * create_xfer(PurpleAccount *account, PurpleXferType type, const char *who, struct sipe_file_transfer *ft) { #if PURPLE_VERSION_CHECK(3,0,0) SipePurpleXfer *xfer = g_object_new(SIPE_TYPE_PURPLE_XFER, "account", account, "type", type, "remote-user", who, NULL); xfer->ft = ft; ft->backend_private = (struct sipe_backend_file_transfer *)xfer; return PURPLE_XFER (xfer); #else PurpleXfer *xfer = purple_xfer_new(account, type, who); if (xfer) { ft->backend_private = (struct sipe_backend_file_transfer *)xfer; purple_xfer_set_protocol_data(xfer, ft); purple_xfer_set_init_fnc(xfer, ft_init); purple_xfer_set_request_denied_fnc(xfer, ft_request_denied); purple_xfer_set_cancel_send_fnc(xfer, ft_cancelled); purple_xfer_set_cancel_recv_fnc(xfer, ft_cancelled); purple_xfer_set_start_fnc(xfer, ft_start); purple_xfer_set_end_fnc(xfer, ft_end); } return xfer; #endif } void sipe_backend_ft_incoming(struct sipe_core_public *sipe_public, struct sipe_file_transfer *ft, const gchar *who, const gchar *file_name, gsize file_size) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleXfer *xfer = create_xfer(purple_private->account, PURPLE_XFER_TYPE_RECEIVE, who, ft); if (xfer) { purple_xfer_set_filename(xfer, file_name); purple_xfer_set_size(xfer, file_size); purple_xfer_request(xfer); } } void sipe_backend_ft_outgoing(struct sipe_core_public *sipe_public, struct sipe_file_transfer *ft, const gchar *who, const gchar *file_name) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleXfer *xfer = create_xfer(purple_private->account, PURPLE_XFER_TYPE_SEND, who, ft); if (xfer) { if (file_name != NULL) purple_xfer_request_accepted(xfer, file_name); else purple_xfer_request(xfer); } } static void connect_cb(gpointer data, gint fd, SIPE_UNUSED_PARAMETER const gchar *error_message) { struct sipe_file_transfer *ft = data; if (fd < 0) { purple_xfer_cancel_local(FT_TO_PURPLE_XFER); return; } purple_xfer_start(FT_TO_PURPLE_XFER, fd, NULL, 0); } void sipe_backend_ft_start(struct sipe_file_transfer *ft, struct sipe_backend_fd *fd, const char* ip, unsigned port) { #if !PURPLE_VERSION_CHECK(3,0,0) PurpleXferType type = purple_xfer_get_xfer_type(FT_TO_PURPLE_XFER); if (type == PURPLE_XFER_TYPE_SEND && ft->ft_write) { purple_xfer_set_write_fnc(FT_TO_PURPLE_XFER, ft_write); } else if (type == PURPLE_XFER_TYPE_RECEIVE && ft->ft_read) { purple_xfer_set_read_fnc(FT_TO_PURPLE_XFER, ft_read); } #endif if (ip && port && !sipe_backend_ft_is_incoming(ft)) { /* Purple accepts ip & port only for incoming file transfers. * If we want to send file with Sender-Connect = TRUE negotiated, * we have to open the connection ourselves and pass the file * descriptor to purple_xfer_start. */ purple_proxy_connect(NULL, purple_xfer_get_account(FT_TO_PURPLE_XFER), ip, port, connect_cb, ft); return; } purple_xfer_start(FT_TO_PURPLE_XFER, fd ? fd->fd : -1, ip, port); } void sipe_purple_ft_send_file( #if PURPLE_VERSION_CHECK(3,0,0) SIPE_UNUSED_PARAMETER PurpleProtocolXfer *xfer, #endif PurpleConnection *gc, const char *who, const char *file) { sipe_core_ft_create_outgoing(PURPLE_GC_TO_SIPE_CORE_PUBLIC, who, file); } gboolean sipe_backend_ft_is_incoming(struct sipe_file_transfer *ft) { return(purple_xfer_get_xfer_type(FT_TO_PURPLE_XFER) == PURPLE_XFER_TYPE_RECEIVE); } #if PURPLE_VERSION_CHECK(3,0,0) G_DEFINE_DYNAMIC_TYPE(SipePurpleXfer, sipe_purple_xfer, PURPLE_TYPE_XFER); static void sipe_purple_xfer_init(SIPE_UNUSED_PARAMETER SipePurpleXfer *xfer) {} static gssize sipe_purple_xfer_read(PurpleXfer *xfer, guchar **buffer, gsize size) { struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER; if (ft->ft_read) { return ft_read(buffer, size, xfer); } else { PurpleXferClass *xfer_class; xfer_class = PURPLE_XFER_CLASS(sipe_purple_xfer_parent_class); return xfer_class->read(xfer, buffer, size); } } static gssize sipe_purple_xfer_write(PurpleXfer *xfer, const guchar *buffer, gsize size) { struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER; if (ft->ft_write) { return ft_write(buffer, size, xfer); } else { PurpleXferClass *xfer_class; xfer_class = PURPLE_XFER_CLASS(sipe_purple_xfer_parent_class); return xfer_class->write(xfer, buffer, size); } } static void sipe_purple_xfer_class_init(SipePurpleXferClass *klass) { PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass); xfer_class->init = ft_init; xfer_class->request_denied = ft_request_denied; xfer_class->start = ft_start; xfer_class->end = ft_end; xfer_class->cancel_send = ft_cancelled; xfer_class->cancel_recv = ft_cancelled; xfer_class->read = sipe_purple_xfer_read; xfer_class->write = sipe_purple_xfer_write; } static void sipe_purple_xfer_class_finalize(SIPE_UNUSED_PARAMETER SipePurpleXferClass *klass) {} void sipe_purple_xfer_register(GTypeModule *module) { sipe_purple_xfer_register_type(module); } #endif /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-groupchat.c ================================================ /** * @file purple-groupchat.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "sipe-common.h" #include "conversation.h" #include "roomlist.h" #include "version.h" #if PURPLE_VERSION_CHECK(3,0,0) #include "conversations.h" #else #define purple_roomlist_get_account(r) r->account #endif #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-nls.h" #include "purple-private.h" GList *sipe_purple_chat_info(SIPE_UNUSED_PARAMETER PurpleConnection *gc) { GList *m = NULL; #if PURPLE_VERSION_CHECK(3,0,0) PurpleProtocolChatEntry *pce = g_new0(PurpleProtocolChatEntry, 1); #else struct proto_chat_entry *pce = g_new0(struct proto_chat_entry, 1); #endif pce->label = _("_URI:"); pce->identifier = "uri"; pce->required = TRUE; m = g_list_append(m, pce); return m; } /** * This callback is called for two reasons: * * a) generate the defaults for the "Add chat..." dialog initiated from the * roomlist (applies only to group chat) * b) generate the components for the creation of a PurpleChat object * */ GHashTable *sipe_purple_chat_info_defaults(PurpleConnection *gc, const char *chat_name) { GHashTable *defaults = g_hash_table_new(g_str_hash, g_str_equal); if (chat_name != NULL) { struct sipe_core_public *sipe_public = PURPLE_GC_TO_SIPE_CORE_PUBLIC; struct sipe_backend_private *purple_private = sipe_public->backend_private; GHashTable *uri_map = purple_private->roomlist_map; const gchar *uri = uri_map != NULL ? g_hash_table_lookup(uri_map, chat_name) : NULL; #if PURPLE_VERSION_CHECK(3,0,0) PurpleChatConversation *conv = purple_conversations_find_chat_with_account( #else PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, #endif chat_name, purple_private->account); /* Group Chat rooms have a valid URI */ if (uri) { g_hash_table_insert(defaults, (char *)"uri", (gpointer)uri); } /** * Remember the PurpleConversation * * libpurple API is so brain-dead that we don't receive this * information when it is known and we need it. Make our life * easier by remembering it here for later lookup.... */ if (conv) { g_hash_table_insert(defaults, (char *)SIPE_PURPLE_COMPONENT_KEY_CONVERSATION, conv); } } return defaults; } void sipe_purple_chat_join(PurpleConnection *gc, GHashTable *data) { struct sipe_core_public *sipe_public = PURPLE_GC_TO_SIPE_CORE_PUBLIC; const gchar *uri = g_hash_table_lookup(data, "uri"); if (uri) { SIPE_DEBUG_INFO("sipe_purple_chat_join: uri '%s'", uri); sipe_core_groupchat_join(sipe_public, uri); } } static void clear_roomlist(struct sipe_backend_private *purple_private) { #if PURPLE_VERSION_CHECK(3,0,0) g_object_unref(purple_private->roomlist); #else purple_roomlist_unref(purple_private->roomlist); #endif purple_private->roomlist = NULL; } PurpleRoomlist *sipe_purple_roomlist_get_list(PurpleConnection *gc) { struct sipe_core_public *sipe_public = PURPLE_GC_TO_SIPE_CORE_PUBLIC; struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleAccount *account = purple_private->account; PurpleRoomlist *roomlist; GList *fields = NULL; PurpleRoomlistField *f; SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_roomlist_get_list"); if (purple_private->roomlist) clear_roomlist(purple_private); if (purple_private->roomlist_map) g_hash_table_destroy(purple_private->roomlist_map); purple_private->roomlist = roomlist = purple_roomlist_new(account); purple_private->roomlist_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); /* The order needs to be kept in-sync with sipe_backend_groupchat_room_add() */ f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "uri", TRUE); fields = g_list_append(fields, f); f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT, _("Users"), "users", FALSE); fields = g_list_append(fields, f); f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_BOOL, _("Invite"), "invite", FALSE); fields = g_list_append(fields, f); f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_BOOL, _("Private"), "private", FALSE); fields = g_list_append(fields, f); f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_BOOL, _("Log"), "logged", FALSE); fields = g_list_append(fields, f); f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Description"), "description", FALSE); fields = g_list_append(fields, f); purple_roomlist_set_fields(roomlist, fields); purple_roomlist_set_in_progress(roomlist, TRUE); if (!sipe_core_groupchat_query_rooms(sipe_public)) { sipe_purple_roomlist_cancel(roomlist); roomlist = NULL; } return roomlist; } void sipe_purple_roomlist_cancel(PurpleRoomlist *roomlist) { PurpleAccount *account = purple_roomlist_get_account(roomlist); struct sipe_core_public *sipe_public = PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC; struct sipe_backend_private *purple_private = sipe_public->backend_private; SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_roomlist_cancel"); purple_roomlist_set_in_progress(roomlist, FALSE); if (purple_private->roomlist == roomlist) clear_roomlist(purple_private); } void sipe_backend_groupchat_room_add(struct sipe_core_public *sipe_public, const gchar *uri, const gchar *name, const gchar *description, guint users, guint32 flags) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleRoomlist *roomlist = purple_private->roomlist; if (roomlist) { PurpleRoomlistRoom *room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL); /* The order needs to be kept in-sync with sipe_roomlist_get_list() */ purple_roomlist_room_add_field(roomlist, room, uri); purple_roomlist_room_add_field(roomlist, room, GUINT_TO_POINTER(users)); purple_roomlist_room_add_field(roomlist, room, GUINT_TO_POINTER(flags & SIPE_GROUPCHAT_ROOM_INVITE)); purple_roomlist_room_add_field(roomlist, room, GUINT_TO_POINTER(flags & SIPE_GROUPCHAT_ROOM_PRIVATE)); purple_roomlist_room_add_field(roomlist, room, GUINT_TO_POINTER(flags & SIPE_GROUPCHAT_ROOM_LOGGED)); purple_roomlist_room_add_field(roomlist, room, description); /* libpurple API only gives us the channel name */ g_hash_table_insert(purple_private->roomlist_map, g_strdup(name), g_strdup(uri)); purple_roomlist_room_add(roomlist, room); } } void sipe_backend_groupchat_room_terminate(struct sipe_core_public *sipe_public) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleRoomlist *roomlist = purple_private->roomlist; if (roomlist) { purple_roomlist_set_in_progress(roomlist, FALSE); clear_roomlist(purple_private); } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-im.c ================================================ /** * @file purple-im.c * * pidgin-sipe * * Copyright (C) 2010-2013 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "server.h" #include "version.h" #if PURPLE_VERSION_CHECK(3,0,0) #include "conversations.h" #else #define purple_serv_got_im(c, w, m, f, t) serv_got_im(c, w, m, f, t) #endif #include "purple-private.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-nls.h" void sipe_backend_im_message(struct sipe_core_public *sipe_public, const gchar *from, const gchar *html) { struct sipe_backend_private *purple_private = sipe_public->backend_private; purple_serv_got_im(purple_private->gc, from, html, 0, time(NULL)); } void sipe_backend_im_topic(struct sipe_core_public *sipe_public, const gchar *with, const gchar *topic) { PurpleAccount *account = sipe_public->backend_private->account; PurpleConversation *conv; gchar *msg; /* * Ensure we have an open conversation with the buddy, otherwise * message would be lost. */ #if PURPLE_VERSION_CHECK(3,0,0) conv = (PurpleConversation *) purple_conversations_find_im_with_account( #else conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, #endif with, account); if (!conv) #if PURPLE_VERSION_CHECK(3,0,0) conv = (PurpleConversation *) purple_im_conversation_new( #else conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, #endif account, with); msg = g_strdup_printf(_("Conversation subject: %s"), topic); sipe_backend_notify_message_info(sipe_public, (struct sipe_backend_chat_session *)conv, with, msg); g_free(msg); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-markup.c ================================================ /** * @file purple-markup.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "util.h" #include "sipe-backend.h" gchar *sipe_backend_markup_css_property(const gchar *style, const gchar *option) { return purple_markup_get_css_property(style, option); } gchar *sipe_backend_markup_strip_html(const gchar *html) { return purple_markup_strip_html(html); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-media.c ================================================ /** * @file purple-media.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "glib.h" #include "glib/gstdio.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include "sipe-common.h" #include "mediamanager.h" #include "agent.h" #include "version.h" #ifdef _WIN32 /* wrappers for write() & friends for socket handling */ #include "win32/win32dep.h" #endif #if PURPLE_VERSION_CHECK(3,0,0) /* nothing here yet */ #else #define purple_config_dir purple_user_dir #endif #include "sipe-backend.h" #include "sipe-core.h" #include "purple-private.h" /* * GStreamer interfaces fail to compile on ARM architecture with -Wcast-align * * Diagnostic #pragma was added in GCC 4.2.0 */ #if defined(__GNUC__) #if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ >= 5) #if defined(__ARMEL__) || defined(__ARMEB__) || defined(__hppa__) || defined(__mips__) || defined(__sparc__) || (defined(__powerpc__) && defined(__NO_FPRS__)) #pragma GCC diagnostic ignored "-Wcast-align" #endif #endif #endif #include "media-gst.h" #include #include struct sipe_backend_media { PurpleMedia *m; /** * Number of media streams that were not yet locally accepted or rejected. */ guint unconfirmed_streams; }; struct sipe_backend_media_stream { gboolean local_on_hold; gboolean remote_on_hold; gboolean accepted; gboolean initialized_cb_was_fired; gulong gst_bus_cb_id; GObject *rtpsession; gulong on_sending_rtcp_cb_id; }; void sipe_backend_media_stream_free(struct sipe_backend_media_stream *stream) { if (stream->gst_bus_cb_id != 0) { GstElement *pipe; pipe = purple_media_manager_get_pipeline( purple_media_manager_get()); if (pipe) { GstBus *bus; bus = gst_element_get_bus(pipe); g_signal_handler_disconnect(bus, stream->gst_bus_cb_id); stream->gst_bus_cb_id = 0; gst_object_unref(bus); } } if (stream->rtpsession) { g_clear_object(&stream->rtpsession); } g_free(stream); } static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type); static PurpleMediaCandidateType sipe_candidate_type_to_purple(SipeCandidateType type); static SipeCandidateType purple_candidate_type_to_sipe(PurpleMediaCandidateType type); static PurpleMediaNetworkProtocol sipe_network_protocol_to_purple(SipeNetworkProtocol proto); static SipeNetworkProtocol purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto); static void maybe_signal_stream_initialized(struct sipe_media_call *call, gchar *sessionid) { if (call->stream_initialized_cb) { struct sipe_media_stream *stream; stream = sipe_core_media_get_stream_by_id(call, sessionid); if (stream && sipe_backend_stream_initialized(call, stream) && !stream->backend_private->initialized_cb_was_fired) { stream->backend_private->initialized_cb_was_fired = TRUE; call->stream_initialized_cb(call, stream); } } } static void on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *sessionid, SIPE_UNUSED_PARAMETER gchar *participant, struct sipe_media_call *call) { maybe_signal_stream_initialized(call, sessionid); } static void on_codecs_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *sessionid, struct sipe_media_call *call) { maybe_signal_stream_initialized(call, sessionid); } static void on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, PurpleMediaState state, gchar *sessionid, gchar *participant, struct sipe_media_call *call) { SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state, sessionid, participant); if (state == PURPLE_MEDIA_STATE_CONNECTED && sessionid && participant) { struct sipe_media_stream *stream; stream = sipe_core_media_get_stream_by_id(call, sessionid); if (stream && stream->backend_private->rtpsession && stream->backend_private->on_sending_rtcp_cb_id != 0) { struct sipe_backend_media_stream *backend_stream; SIPE_DEBUG_INFO_NOFORMAT("Peer started sending. Ceasing" " video source requests."); backend_stream = stream->backend_private; g_signal_handler_disconnect(backend_stream->rtpsession, backend_stream->on_sending_rtcp_cb_id); g_clear_object(&backend_stream->rtpsession); backend_stream->on_sending_rtcp_cb_id = 0; } } else if (state == PURPLE_MEDIA_STATE_END) { if (sessionid && participant) { struct sipe_media_stream *stream = sipe_core_media_get_stream_by_id(call, sessionid); if (stream) { sipe_core_media_stream_end(stream); } } else if (!sessionid && !participant && call->media_end_cb) { call->media_end_cb(call); } } } void capture_pipeline(const gchar *label) { PurpleMediaManager *manager = purple_media_manager_get(); GstElement *pipeline = purple_media_manager_get_pipeline(manager); GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, label); } static void on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *message, struct sipe_media_call *call) { capture_pipeline("ERROR"); if (call->error_cb) call->error_cb(call, message); } static void on_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, gchar *sessionid, gchar *participant, gboolean local, struct sipe_media_call *call) { if (type == PURPLE_MEDIA_INFO_ACCEPT) { if (call->call_accept_cb && !sessionid && !participant) call->call_accept_cb(call, local); else if (sessionid && participant) { struct sipe_media_stream *stream; stream = sipe_core_media_get_stream_by_id(call, sessionid); if (stream) { if (!stream->backend_private->accepted && local) --call->backend_private->unconfirmed_streams; stream->backend_private->accepted = TRUE; } } } else if (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD) { gboolean state = (type == PURPLE_MEDIA_INFO_HOLD); if (sessionid) { // Hold specific stream struct sipe_media_stream *stream; stream = sipe_core_media_get_stream_by_id(call, sessionid); if (stream) { if (local) stream->backend_private->local_on_hold = state; else stream->backend_private->remote_on_hold = state; } } else { // Hold all streams GList *session_ids = purple_media_get_session_ids(media); for (; session_ids; session_ids = session_ids->next) { struct sipe_media_stream *stream = sipe_core_media_get_stream_by_id(call, session_ids->data); if (stream) { if (local) stream->backend_private->local_on_hold = state; else stream->backend_private->remote_on_hold = state; } } g_list_free(session_ids); } if (call->call_hold_cb) call->call_hold_cb(call, local, state); } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) { if (!sessionid && !participant) { if (type == PURPLE_MEDIA_INFO_HANGUP && call->call_hangup_cb) call->call_hangup_cb(call, local); else if (type == PURPLE_MEDIA_INFO_REJECT && call->call_reject_cb && !local) call->call_reject_cb(call, local); } else if (sessionid && participant) { struct sipe_media_stream *stream; stream = sipe_core_media_get_stream_by_id(call, sessionid); #ifdef HAVE_XDATA purple_media_manager_set_application_data_callbacks( purple_media_manager_get(), media, sessionid, participant, NULL, NULL, NULL); #endif if (stream) { if (local && --call->backend_private->unconfirmed_streams == 0 && call->call_reject_cb) call->call_reject_cb(call, local); } } } else if (type == PURPLE_MEDIA_INFO_MUTE || type == PURPLE_MEDIA_INFO_UNMUTE) { struct sipe_media_stream *stream = sipe_core_media_get_stream_by_id(call, "audio"); if (stream && stream->mute_cb) { stream->mute_cb(stream, type == PURPLE_MEDIA_INFO_MUTE); } } } static void on_candidate_pair_established_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, const gchar *sessionid, SIPE_UNUSED_PARAMETER const gchar *participant, SIPE_UNUSED_PARAMETER PurpleMediaCandidate *local_candidate, SIPE_UNUSED_PARAMETER PurpleMediaCandidate *remote_candidate, struct sipe_media_call *call) { struct sipe_media_stream *stream = sipe_core_media_get_stream_by_id(call, sessionid); if (!stream) { return; } #ifdef HAVE_PURPLE_NEW_TCP_ENUMS if (purple_media_candidate_get_protocol(local_candidate) != PURPLE_MEDIA_NETWORK_PROTOCOL_UDP) { purple_media_set_send_rtcp_mux(media, sessionid, participant, TRUE); } #endif sipe_core_media_stream_candidate_pair_established(stream); } struct sipe_backend_media * sipe_backend_media_new(struct sipe_core_public *sipe_public, struct sipe_media_call *call, const gchar *participant, SipeMediaCallFlags flags) { struct sipe_backend_media *media = g_new0(struct sipe_backend_media, 1); struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleMediaManager *manager = purple_media_manager_get(); GstElement *pipeline; if (flags & SIPE_MEDIA_CALL_NO_UI) { #ifdef HAVE_XDATA media->m = purple_media_manager_create_private_media(manager, purple_private->account, "fsrtpconference", participant, flags & SIPE_MEDIA_CALL_INITIATOR); #else SIPE_DEBUG_ERROR_NOFORMAT("Purple doesn't support private media"); #endif } else { media->m = purple_media_manager_create_media(manager, purple_private->account, "fsrtpconference", participant, flags & SIPE_MEDIA_CALL_INITIATOR); } g_signal_connect(G_OBJECT(media->m), "candidates-prepared", G_CALLBACK(on_candidates_prepared_cb), call); g_signal_connect(G_OBJECT(media->m), "codecs-changed", G_CALLBACK(on_codecs_changed_cb), call); g_signal_connect(G_OBJECT(media->m), "stream-info", G_CALLBACK(on_stream_info_cb), call); g_signal_connect(G_OBJECT(media->m), "error", G_CALLBACK(on_error_cb), call); g_signal_connect(G_OBJECT(media->m), "state-changed", G_CALLBACK(on_state_changed_cb), call); g_signal_connect(G_OBJECT(media->m), "candidate-pair-established", G_CALLBACK(on_candidate_pair_established_cb), call); /* On error, the pipeline is no longer in PLAYING state and libpurple * will not switch it back to PLAYING, preventing any more calls until * application restart. We switch the state ourselves here to negate * effect of any error in previous call (if any). */ pipeline = purple_media_manager_get_pipeline(manager); gst_element_set_state(pipeline, GST_STATE_PLAYING); return media; } void sipe_backend_media_free(struct sipe_backend_media *media) { g_free(media); } void sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname) { if (media) { guint num_params = 3; GParameter *params = g_new0(GParameter, num_params); params[0].name = "sdes-cname"; g_value_init(¶ms[0].value, G_TYPE_STRING); g_value_set_string(¶ms[0].value, cname); params[1].name = "sdes-name"; g_value_init(¶ms[1].value, G_TYPE_STRING); params[2].name = "sdes-tool"; g_value_init(¶ms[2].value, G_TYPE_STRING); purple_media_set_params(media->m, num_params, params); g_value_unset(¶ms[0].value); g_free(params); } } #define FS_CODECS_CONF \ "# Automatically created by SIPE plugin\n" \ "[video/H264]\n" \ "farstream-send-profile=videoscale ! videoconvert ! fsvideoanyrate ! x264enc ! video/x-h264,profile=constrained-baseline ! rtph264pay\n" \ "\n" \ "[application/X-DATA]\n" \ "id=127\n" static void ensure_codecs_conf() { gchar *filename; const gchar *fs_codecs_conf = FS_CODECS_CONF; GError *error = NULL; filename = g_build_filename(purple_config_dir(), "fs-codec.conf", NULL); g_file_set_contents(filename, fs_codecs_conf, strlen(fs_codecs_conf), &error); if (error) { SIPE_DEBUG_ERROR("Couldn't create fs-codec.conf: %s", error->message); g_error_free(error); } g_free(filename); } static void append_relay(struct sipe_backend_media_relays *relay_info, const gchar *ip, guint port, const gchar *type, gchar *username, gchar *password) { GstStructure *gst_relay_info; gst_relay_info = gst_structure_new("relay-info", "ip", G_TYPE_STRING, ip, "port", G_TYPE_UINT, port, "relay-type", G_TYPE_STRING, type, "username", G_TYPE_STRING, username, "password", G_TYPE_STRING, password, NULL); if (gst_relay_info) { g_ptr_array_add((GPtrArray *)relay_info, gst_relay_info); } } struct sipe_backend_media_relays * sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password) { struct sipe_backend_media_relays *relay_info; relay_info = (struct sipe_backend_media_relays *) g_ptr_array_new_with_free_func((GDestroyNotify) gst_structure_free); for (; media_relays; media_relays = media_relays->next) {\ struct sipe_media_relay *relay = media_relays->data; /* Skip relays where IP could not be resolved. */ if (!relay->hostname) continue; if (relay->udp_port != 0) append_relay(relay_info, relay->hostname, relay->udp_port, "udp", username, password); #ifdef HAVE_PURPLE_NEW_TCP_ENUMS if (relay->tcp_port != 0) { const gchar *type = "tcp"; if (relay->tcp_port == 443) type = "tls"; append_relay(relay_info, relay->hostname, relay->tcp_port, type, username, password); } #endif } return relay_info; } void sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays) { g_ptr_array_unref((GPtrArray *)media_relays); } #ifdef HAVE_XDATA static void stream_readable_cb(SIPE_UNUSED_PARAMETER PurpleMediaManager *manager, SIPE_UNUSED_PARAMETER PurpleMedia *media, const gchar *session_id, SIPE_UNUSED_PARAMETER const gchar *participant, gpointer user_data) { struct sipe_media_call *call = (struct sipe_media_call *)user_data; struct sipe_media_stream *stream; SIPE_DEBUG_INFO("stream_readable_cb: %s is readable", session_id); stream = sipe_core_media_get_stream_by_id(call, session_id); if (stream) { sipe_core_media_stream_readable(stream); } } gssize sipe_backend_media_stream_read(struct sipe_media_stream *stream, guint8 *buffer, gsize len) { return purple_media_manager_receive_application_data( purple_media_manager_get(), stream->call->backend_private->m, stream->id, stream->call->with, buffer, len, FALSE); } gssize sipe_backend_media_stream_write(struct sipe_media_stream *stream, guint8 *buffer, gsize len) { return purple_media_manager_send_application_data( purple_media_manager_get(), stream->call->backend_private->m, stream->id, stream->call->with, buffer, len, FALSE); } static void stream_writable_cb(SIPE_UNUSED_PARAMETER PurpleMediaManager *manager, SIPE_UNUSED_PARAMETER PurpleMedia *media, const gchar *session_id, SIPE_UNUSED_PARAMETER const gchar *participant, gboolean writable, gpointer user_data) { struct sipe_media_call *call = (struct sipe_media_call *)user_data; struct sipe_media_stream *stream; stream = sipe_core_media_get_stream_by_id(call, session_id); if (!stream) { SIPE_DEBUG_ERROR("stream_writable_cb: stream %s not found!", session_id); return; } SIPE_DEBUG_INFO("stream_writable_cb: %s has become %swritable", session_id, writable ? "" : "not "); sipe_core_media_stream_writable(stream, writable); } #endif static gboolean write_ms_h264_video_source_request(GstRTCPBuffer *buffer, guint32 ssrc, guint8 payload_type, guint32 media_source_id) { GstRTCPPacket packet; guint8 *fci_data; if (!gst_rtcp_buffer_add_packet(buffer, GST_RTCP_TYPE_PSFB, &packet)) { return FALSE; } gst_rtcp_packet_fb_set_type(&packet, GST_RTCP_PSFB_TYPE_AFB); gst_rtcp_packet_fb_set_sender_ssrc(&packet, ssrc); gst_rtcp_packet_fb_set_media_ssrc(&packet, media_source_id); if (!gst_rtcp_packet_fb_set_fci_length(&packet, SIPE_MSRTP_VSR_FCI_WORDLEN)) { gst_rtcp_packet_remove(&packet); return FALSE; } fci_data = gst_rtcp_packet_fb_get_fci(&packet); sipe_core_msrtp_write_video_source_request(fci_data, payload_type, media_source_id); return TRUE; } static gboolean on_sending_rtcp_cb(SIPE_UNUSED_PARAMETER GObject *rtpsession, GstBuffer *buffer, SIPE_UNUSED_PARAMETER gboolean is_early, FsSession *fssession) { gboolean was_changed = FALSE; FsCodec *send_codec; g_object_get(fssession, "current-send-codec", &send_codec, NULL); if (!send_codec) { return FALSE; } if (sipe_strequal(send_codec->encoding_name, "H264")) { GstRTCPBuffer rtcp_buffer = GST_RTCP_BUFFER_INIT; guint32 ssrc; struct sipe_media_stream *stream; g_object_get(fssession, "ssrc", &ssrc, NULL); stream = g_object_get_data(G_OBJECT(fssession), "sipe-media-stream"); gst_rtcp_buffer_map(buffer, GST_MAP_READWRITE, &rtcp_buffer); was_changed = write_ms_h264_video_source_request(&rtcp_buffer, ssrc, send_codec->id, stream->media_source_id); gst_rtcp_buffer_unmap(&rtcp_buffer); } fs_codec_destroy(send_codec); return was_changed; } static GstPadProbeReturn h264_buffer_cb(SIPE_UNUSED_PARAMETER GstPad *pad, GstPadProbeInfo *info, SIPE_UNUSED_PARAMETER gpointer user_data) { GstBuffer *buffer; GstMemory *memory; GstMapInfo map; guint8 *data; guint8 nal_count = 0; gsize pacsi_len; buffer = gst_pad_probe_info_get_buffer(info); // Count NALs in the buffer gst_buffer_map(buffer, &map, GST_MAP_READ); data = map.data; while (data < map.data + map.size) { guint32 size = GST_READ_UINT32_BE(data); data += GST_READ_UINT32_BE(data) + sizeof (size); ++nal_count; } gst_buffer_unmap(buffer, &map); // Write PACSI (RFC6190 section 4.9) memory = gst_allocator_alloc(NULL, 100, NULL); gst_memory_map(memory, &map, GST_MAP_WRITE); pacsi_len = sipe_core_msrtp_write_video_scalability_info(map.data, nal_count); gst_memory_unmap(memory, &map); gst_memory_resize(memory, 0, pacsi_len); buffer = gst_buffer_make_writable(buffer); gst_buffer_insert_memory(buffer, 0, memory); GST_PAD_PROBE_INFO_DATA(info) = buffer; return GST_PAD_PROBE_OK; } static gint find_payloader(GValue *value, GstCaps *rtpcaps) { gint result = 1; GstElement *element; GstPad *sinkpad; GstCaps *caps; element = g_value_get_object(value); sinkpad = gst_element_get_static_pad(element, "sink"); caps = gst_pad_query_caps(sinkpad, NULL); /* Elements are iterated from the most downstream. We're looking for the * first that does NOT consume RTP frames. */ result = gst_caps_can_intersect(caps, rtpcaps); gst_caps_unref(caps); gst_object_unref(sinkpad); return result; } static void current_send_codec_changed_cb(FsSession *fssession, SIPE_UNUSED_PARAMETER GParamSpec *pspec, GstBin *fsconference) { FsCodec *send_codec; g_object_get(fssession, "current-send-codec", &send_codec, NULL); if (sipe_strequal(send_codec->encoding_name, "H264")) { guint session_id; gchar *sendbin_name; GstBin *sendbin; GstCaps *caps; GstIterator *it; GValue val = G_VALUE_INIT; g_object_get(fssession, "id", &session_id, NULL); sendbin_name = g_strdup_printf("send_%u_%u", session_id, send_codec->id); sendbin = GST_BIN(gst_bin_get_by_name(fsconference, sendbin_name)); g_free(sendbin_name); if (!sendbin) { SIPE_DEBUG_ERROR("Couldn't find Farstream send bin for " "session %d", session_id); return; } caps = gst_caps_new_empty_simple("application/x-rtp"); it = gst_bin_iterate_sorted(sendbin); if (gst_iterator_find_custom(it, (GCompareFunc)find_payloader, &val, caps)) { GstElement *payloader; GstPad *sinkpad; payloader = g_value_get_object(&val); sinkpad = gst_element_get_static_pad(payloader, "sink"); if (sinkpad) { gst_pad_add_probe(sinkpad, GST_PAD_PROBE_TYPE_BUFFER, h264_buffer_cb, NULL, NULL); gst_object_unref(sinkpad); } g_value_unset(&val); } gst_caps_unref(caps); gst_iterator_free(it); gst_object_unref(sendbin); } fs_codec_destroy(send_codec); } static gint find_sinkpad(GValue *value, GstPad *fssession_sinkpad) { GstElement *tee_srcpad = g_value_get_object(value); return !(GST_PAD_PEER(tee_srcpad) == fssession_sinkpad); } static void gst_bus_cb(GstBus *bus, GstMessage *msg, struct sipe_media_stream *stream) { PurpleMedia *m; const GstStructure *s; FsSession *fssession; GstElement *tee; GstPad *sinkpad; GstIterator *it; GValue val = G_VALUE_INIT; if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT) { return; } m = stream->call->backend_private->m; s = gst_message_get_structure(msg); if (!gst_structure_has_name(s, "farstream-codecs-changed")) { return; } fssession = g_value_get_object(gst_structure_get_value(s, "session")); g_return_if_fail(fssession); tee = purple_media_get_tee(m, stream->id, NULL); g_return_if_fail(tee); g_object_get(fssession, "sink-pad", &sinkpad, NULL); g_return_if_fail(sinkpad); /* Check whether this message is from the FsSession we're waiting for. * For this to be true, the tee we got from libpurple has to be linked * to "sink-pad" of the message's FsSession. */ it = gst_element_iterate_src_pads(tee); if (gst_iterator_find_custom(it, (GCompareFunc)find_sinkpad, &val, sinkpad)) { FsMediaType media_type; if (stream->ssrc_range) { g_object_set(fssession, "ssrc", stream->ssrc_range->begin, NULL); } g_object_set_data(G_OBJECT(fssession), "sipe-media-stream", stream); g_object_get(fssession, "media-type", &media_type, NULL); if (media_type == FS_MEDIA_TYPE_VIDEO) { GObject *rtpsession; GstBin *fsconference; g_object_get(fssession, "internal-session", &rtpsession, NULL); if (rtpsession) { stream->backend_private->rtpsession = gst_object_ref(rtpsession); stream->backend_private->on_sending_rtcp_cb_id = g_signal_connect(rtpsession, "on-sending-rtcp", G_CALLBACK(on_sending_rtcp_cb), fssession); g_object_unref (rtpsession); } g_object_get(fssession, "conference", &fsconference, NULL); g_signal_connect_object(fssession, "notify::current-send-codec", G_CALLBACK(current_send_codec_changed_cb), fsconference, 0); gst_object_unref(fsconference); } g_signal_handler_disconnect(bus, stream->backend_private->gst_bus_cb_id); stream->backend_private->gst_bus_cb_id = 0; g_value_unset (&val); } gst_iterator_free(it); gst_object_unref(sinkpad); } struct sipe_backend_media_stream * sipe_backend_media_add_stream(struct sipe_media_stream *stream, SipeMediaType type, SipeIceVersion ice_version, gboolean initiator, struct sipe_backend_media_relays *media_relays, guint min_port, guint max_port) { struct sipe_backend_media *media = stream->call->backend_private; struct sipe_backend_media_stream *backend_stream = NULL; GstElement *pipe; // Preallocate enough space for all potential parameters to fit. GParameter *params = g_new0(GParameter, 7); guint params_cnt = 0; const gchar *transmitter; #ifdef HAVE_XDATA PurpleMediaAppDataCallbacks callbacks = { stream_readable_cb, stream_writable_cb }; #endif if (ice_version != SIPE_ICE_NO_ICE) { transmitter = "nice"; params[params_cnt].name = "compatibility-mode"; g_value_init(¶ms[params_cnt].value, G_TYPE_UINT); g_value_set_uint(¶ms[params_cnt].value, ice_version == SIPE_ICE_DRAFT_6 ? NICE_COMPATIBILITY_OC2007 : NICE_COMPATIBILITY_OC2007R2); ++params_cnt; if (min_port != 0) { params[params_cnt].name = "min-port"; g_value_init(¶ms[params_cnt].value, G_TYPE_UINT); g_value_set_uint(¶ms[params_cnt].value, min_port); ++params_cnt; } if (max_port != 0) { params[params_cnt].name = "max-port"; g_value_init(¶ms[params_cnt].value, G_TYPE_UINT); g_value_set_uint(¶ms[params_cnt].value, max_port); ++params_cnt; } if (media_relays) { params[params_cnt].name = "relay-info"; g_value_init(¶ms[params_cnt].value, G_TYPE_PTR_ARRAY); g_value_set_boxed(¶ms[params_cnt].value, media_relays); ++params_cnt; } if (type == SIPE_MEDIA_APPLICATION) { params[params_cnt].name = "ice-udp"; g_value_init(¶ms[params_cnt].value, G_TYPE_BOOLEAN); g_value_set_boolean(¶ms[params_cnt].value, FALSE); ++params_cnt; params[params_cnt].name = "reliable"; g_value_init(¶ms[params_cnt].value, G_TYPE_BOOLEAN); g_value_set_boolean(¶ms[params_cnt].value, TRUE); ++params_cnt; } /* Don't use the globally defined stun server (it is used by * default in backend-fs2) because it generates srflx local * candidates that confuse SfB when they contribute to valid * pairs in controlled mode: * * ms-client-diagnostics: 21 * "Call failed to establish due to a media connectivity * failure where one endpoint is of unknown type" * * These candidates will be discovered anyway during the * conncheck as prflx, and they will be accepted and nominated * by SfB when having this type. */ params[params_cnt].name = "stun-ip"; g_value_init(¶ms[params_cnt].value, G_TYPE_STRING); g_value_set_string(¶ms[params_cnt].value, NULL); ++params_cnt; } else { // TODO: session naming here, Communicator needs audio/video transmitter = "rawudp"; //sessionid = "sipe-voice-rawudp"; } ensure_codecs_conf(); #ifdef HAVE_XDATA if (type == SIPE_MEDIA_APPLICATION) { purple_media_manager_set_application_data_callbacks( purple_media_manager_get(), media->m, stream->id, stream->call->with, &callbacks, stream->call, NULL); } #endif backend_stream = g_new0(struct sipe_backend_media_stream, 1); pipe = purple_media_manager_get_pipeline(purple_media_manager_get()); if (pipe) { GstBus *bus; bus = gst_element_get_bus(pipe); backend_stream->gst_bus_cb_id = g_signal_connect(bus, "message", G_CALLBACK(gst_bus_cb), stream); gst_object_unref(bus); } if (purple_media_add_stream(media->m, stream->id, stream->call->with, sipe_media_to_purple(type), initiator, transmitter, params_cnt, params)) { if (!initiator) ++media->unconfirmed_streams; } else { sipe_backend_media_stream_free(backend_stream); backend_stream = NULL; } g_free(params); return backend_stream; } void sipe_backend_media_stream_end(struct sipe_media_call *media, struct sipe_media_stream *stream) { purple_media_end(media->backend_private->m, stream->id, NULL); } void sipe_backend_media_add_remote_candidates(struct sipe_media_call *media, struct sipe_media_stream *stream, GList *candidates) { GList *udp_candidates = NULL; #ifndef HAVE_PURPLE_NEW_TCP_ENUMS /* Keep only UDP candidates in the list to set. */ while (candidates) { PurpleMediaCandidate *candidate = candidates->data; PurpleMediaNetworkProtocol proto; proto = purple_media_candidate_get_protocol(candidate); if (proto == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP) udp_candidates = g_list_append(udp_candidates, candidate); candidates = candidates->next; } candidates = udp_candidates; #endif purple_media_add_remote_candidates(media->backend_private->m, stream->id, media->with, candidates); g_list_free(udp_candidates); } gboolean sipe_backend_media_is_initiator(struct sipe_media_call *media, struct sipe_media_stream *stream) { return purple_media_is_initiator(media->backend_private->m, stream ? stream->id : NULL, stream ? media->with : NULL); } gboolean sipe_backend_media_accepted(struct sipe_backend_media *media) { return purple_media_accepted(media->m, NULL, NULL); } gboolean sipe_backend_stream_initialized(struct sipe_media_call *media, struct sipe_media_stream *stream) { g_return_val_if_fail(media, FALSE); g_return_val_if_fail(stream, FALSE); if (purple_media_candidates_prepared(media->backend_private->m, stream->id, media->with)) { GList *codecs; codecs = purple_media_get_codecs(media->backend_private->m, stream->id); if (codecs) { purple_media_codec_list_free(codecs); return TRUE; } } return FALSE; } static GList * duplicate_tcp_candidates(GList *candidates) { GList *i; GList *result = NULL; for (i = candidates; i; i = i->next) { PurpleMediaCandidate *candidate = i->data; PurpleMediaNetworkProtocol protocol = purple_media_candidate_get_protocol(candidate); guint component_id = purple_media_candidate_get_component_id(candidate); if (protocol != PURPLE_MEDIA_NETWORK_PROTOCOL_UDP) { PurpleMediaCandidate *c2; if (component_id != PURPLE_MEDIA_COMPONENT_RTP) { /* Ignore TCP candidates for other than * the first component. */ g_object_unref(candidate); continue; } c2 = purple_media_candidate_copy(candidate); g_object_set(c2, "component-id", PURPLE_MEDIA_COMPONENT_RTCP, NULL); result = g_list_append(result, c2); } result = g_list_append(result, candidate); } g_list_free(candidates); return result; } GList * sipe_backend_media_stream_get_active_local_candidates(struct sipe_media_stream *stream) { GList *candidates = purple_media_get_active_local_candidates( stream->call->backend_private->m, stream->id, stream->call->with); return duplicate_tcp_candidates(candidates); } GList * sipe_backend_media_stream_get_active_remote_candidates(struct sipe_media_stream *stream) { GList *candidates = purple_media_get_active_remote_candidates( stream->call->backend_private->m, stream->id, stream->call->with); return duplicate_tcp_candidates(candidates); } #ifdef HAVE_SRTP void sipe_backend_media_set_encryption_keys(struct sipe_media_call *media, struct sipe_media_stream *stream, const guchar *encryption_key, const guchar *decryption_key) { purple_media_set_encryption_parameters(media->backend_private->m, stream->id, "aes-128-icm", "hmac-sha1-80", (gchar *)encryption_key, SIPE_SRTP_KEY_LEN); purple_media_set_decryption_parameters(media->backend_private->m, stream->id, media->with, "aes-128-icm", "hmac-sha1-80", (gchar *)decryption_key, SIPE_SRTP_KEY_LEN); } #else void sipe_backend_media_set_encryption_keys(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER const guchar *encryption_key, SIPE_UNUSED_PARAMETER const guchar *decryption_key) {} #endif #if defined(HAVE_SRTP) && PURPLE_VERSION_CHECK(2,14,0) void sipe_backend_media_set_require_encryption(struct sipe_media_call *media, struct sipe_media_stream *stream, const gboolean require_encryption) { purple_media_set_require_encryption(media->backend_private->m, stream->id, media->with, require_encryption); } #else void sipe_backend_media_set_require_encryption(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER const gboolean require_encryption) {} #endif void sipe_backend_stream_hold(struct sipe_media_call *media, struct sipe_media_stream *stream, gboolean local) { purple_media_stream_info(media->backend_private->m, PURPLE_MEDIA_INFO_HOLD, stream->id, media->with, local); } void sipe_backend_stream_unhold(struct sipe_media_call *media, struct sipe_media_stream *stream, gboolean local) { purple_media_stream_info(media->backend_private->m, PURPLE_MEDIA_INFO_UNHOLD, stream->id, media->with, local); } gboolean sipe_backend_stream_is_held(struct sipe_media_stream *stream) { g_return_val_if_fail(stream, FALSE); return stream->backend_private->local_on_hold || stream->backend_private->remote_on_hold; } struct sipe_backend_codec * sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate, guint channels) { PurpleMediaCodec *codec; if (sipe_strcase_equal(name, "X-H264UC")) { name = "H264"; } codec = purple_media_codec_new(id, name, sipe_media_to_purple(type), clock_rate); g_object_set(codec, "channels", channels, NULL); return (struct sipe_backend_codec *)codec; } void sipe_backend_codec_free(struct sipe_backend_codec *codec) { if (codec) g_object_unref(codec); } int sipe_backend_codec_get_id(struct sipe_backend_codec *codec) { return purple_media_codec_get_id((PurpleMediaCodec *)codec); } gchar * sipe_backend_codec_get_name(struct sipe_backend_codec *codec) { /* Not explicitly documented, but return value must be g_free()'d */ return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec); } guint sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec) { return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec); } void sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec, const gchar *name, const gchar *value) { purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value); } GList * sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec) { return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec); } gboolean sipe_backend_set_remote_codecs(struct sipe_media_call *media, struct sipe_media_stream *stream, GList *codecs) { gboolean result; /* Lync offers multichannel audio as a codec with the same encoding name * as the mono variant, but a different payload type and an extra * encoding parameter: * * a=rtpmap:117 G722/8000/2 * a=rtpmap:9 G722/8000 * * Since avenc_g722 from gst-libav can encode only one audio channel, ignore * multichannel codecs we were offered by the remote host. */ GList *tmp = NULL; PurpleMediaSessionType type; for (; codecs; codecs = codecs->next) { PurpleMediaCodec *codec = codecs->data; g_object_get(codec, "media-type", &type, NULL); if (type == PURPLE_MEDIA_AUDIO && purple_media_codec_get_channels(codec) > 1) { continue; } tmp = g_list_append(tmp, codec); } result = purple_media_set_remote_codecs(media->backend_private->m, stream->id, media->with, tmp); g_list_free(tmp); return result; } GList* sipe_backend_get_local_codecs(struct sipe_media_call *media, struct sipe_media_stream *stream) { GList *codecs = purple_media_get_codecs(media->backend_private->m, stream->id); GList *i = codecs; while (i) { PurpleMediaCodec *codec = i->data; gchar *encoding_name = purple_media_codec_get_encoding_name(codec); if (sipe_strequal(encoding_name, "H264")) { /* * Sanitize H264 codec: * - the encoding name must be "X-H264UC" * - remove "sprop-parameter-sets" parameter which is * rejected by Lync * - add "packetization-mode" parameter if not already * present */ PurpleMediaCodec *new_codec; GList *it; new_codec = purple_media_codec_new( purple_media_codec_get_id(codec), "X-H264UC", PURPLE_MEDIA_VIDEO, purple_media_codec_get_clock_rate(codec)); g_object_set(new_codec, "channels", purple_media_codec_get_channels(codec), NULL); it = purple_media_codec_get_optional_parameters(codec); for (; it; it = g_list_next(it)) { PurpleKeyValuePair *pair = it->data; if (sipe_strequal(pair->key, "sprop-parameter-sets")) { continue; } purple_media_codec_add_optional_parameter(new_codec, pair->key, pair->value); } if (!purple_media_codec_get_optional_parameter(new_codec, "packetization-mode", NULL)) { purple_media_codec_add_optional_parameter(new_codec, "packetization-mode", "1;mst-mode=NI-TC"); } i->data = new_codec; g_object_unref(codec); } else i = i->next; g_free(encoding_name); } return codecs; } struct sipe_backend_candidate * sipe_backend_candidate_new(const gchar *foundation, SipeComponentType component, SipeCandidateType type, SipeNetworkProtocol proto, const gchar *ip, guint port, const gchar *username, const gchar *password) { PurpleMediaCandidate *c = purple_media_candidate_new( /* Libnice and Farsight rely on non-NULL foundation to * distinguish between candidates of a component. When NULL * foundation is passed (ie. ICE draft 6 does not use foudation), * use username instead. If no foundation is provided, Farsight * may signal an active candidate different from the one actually * in use. See Farsight's agent_new_selected_pair() in * fs-nice-stream-transmitter.h where first candidate in the * remote list is always selected when no foundation. */ foundation ? foundation : username, component, sipe_candidate_type_to_purple(type), sipe_network_protocol_to_purple(proto), ip, port); g_object_set(c, "username", username, "password", password, NULL); return (struct sipe_backend_candidate *)c; } void sipe_backend_candidate_free(struct sipe_backend_candidate *candidate) { if (candidate) g_object_unref(candidate); } gchar * sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate) { /* Not explicitly documented, but return value must be g_free()'d */ return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate); } gchar * sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate) { /* Not explicitly documented, but return value must be g_free()'d */ return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate); } gchar * sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate) { /* Not explicitly documented, but return value must be g_free()'d */ return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate); } gchar * sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate) { /* Not explicitly documented, but return value must be g_free()'d */ return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate); } guint sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate) { return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate); } gchar * sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate) { /* Not explicitly documented, but return value must be g_free()'d */ return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate); } guint sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate) { return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate); } guint32 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate) { return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate); } void sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority) { g_object_set(candidate, "priority", priority, NULL); } SipeComponentType sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate) { return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate); } SipeCandidateType sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate) { PurpleMediaCandidateType type = purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate); return purple_candidate_type_to_sipe(type); } SipeNetworkProtocol sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate) { PurpleMediaNetworkProtocol proto = purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate); return purple_network_protocol_to_sipe(proto); } /* * libnice can return a candidate list with duplicates. It is currently * unknown if this is a bug in libnice or a configuration error in Skype * for Business setups. * * While this is not a bug in SIPE, by removing these duplicates we make * sure that SIPE doesn't generate incorrect SDP messages. */ static GList * filter_duplicate_candidates(GList *candidates) { GHashTable *seen = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); GList *result = NULL; GList *it; for (it = candidates; it; it = it->next) { PurpleMediaCandidate *c = it->data; gchar *foundation = purple_media_candidate_get_foundation(c); gchar *ip = purple_media_candidate_get_ip(c); gchar *base_ip = purple_media_candidate_get_base_ip(c); gchar *id = g_strdup_printf("%s %d %d %d %s %d %d %s %d", foundation ? foundation : "-", purple_media_candidate_get_component_id(c), purple_media_candidate_get_protocol(c), purple_media_candidate_get_priority(c), ip ? ip : "-", purple_media_candidate_get_port(c), purple_media_candidate_get_candidate_type(c), base_ip ? base_ip : "-", purple_media_candidate_get_base_port(c) ); g_free(base_ip); g_free(ip); g_free(foundation); if (g_hash_table_lookup(seen, id)) { SIPE_DEBUG_INFO("filter_duplicate_candidates: dropping '%s'", id); g_free(id); g_object_unref(c); } else { g_hash_table_insert(seen, id, GUINT_TO_POINTER(TRUE)); result = g_list_append(result, c); } } g_hash_table_destroy(seen); g_list_free(candidates); return result; } static void remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key, gpointer value, gpointer user_data) { GList *entry = value; GList **candidates = user_data; g_object_unref(entry->data); *candidates = g_list_delete_link(*candidates, entry); } static GList * ensure_candidate_pairs(GList *candidates) { GHashTable *lone_cand_links; GList *i; lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); for (i = candidates; i; i = i->next) { PurpleMediaCandidate *c = i->data; gchar *foundation = purple_media_candidate_get_foundation(c); if (g_hash_table_lookup(lone_cand_links, foundation)) { g_hash_table_remove(lone_cand_links, foundation); g_free(foundation); } else { g_hash_table_insert(lone_cand_links, foundation, i); } } g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates); g_hash_table_destroy(lone_cand_links); return candidates; } GList * sipe_backend_get_local_candidates(struct sipe_media_call *media, struct sipe_media_stream *stream) { GList *candidates = purple_media_get_local_candidates(media->backend_private->m, stream->id, media->with); candidates = filter_duplicate_candidates(candidates); candidates = duplicate_tcp_candidates(candidates); /* * Sometimes purple will not return complete list of candidates, even * after "candidates-prepared" signal is emitted. This is a feature of * libnice, namely affecting candidates discovered via UPnP. Nice does * not wait until discovery is finished and can signal end of candidate * gathering before all responses from UPnP enabled gateways are received. * * Remove any incomplete RTP+RTCP candidate pairs from the list. */ candidates = ensure_candidate_pairs(candidates); return candidates; } void sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local) { if (media) purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT, NULL, NULL, local); } void sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local) { if (media) purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, local); } void sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local) { if (media) purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT, NULL, NULL, local); } static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type) { switch (type) { case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO; case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO; #ifdef HAVE_XDATA case SIPE_MEDIA_APPLICATION: return PURPLE_MEDIA_APPLICATION; #endif default: return PURPLE_MEDIA_NONE; } } static PurpleMediaCandidateType sipe_candidate_type_to_purple(SipeCandidateType type) { switch (type) { case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST; case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY; case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX; case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX; default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST; } } static SipeCandidateType purple_candidate_type_to_sipe(PurpleMediaCandidateType type) { switch (type) { case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST; case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY; case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX; case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX; default: return SIPE_CANDIDATE_TYPE_HOST; } } static PurpleMediaNetworkProtocol sipe_network_protocol_to_purple(SipeNetworkProtocol proto) { switch (proto) { #ifdef HAVE_PURPLE_NEW_TCP_ENUMS case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE; case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE; case SIPE_NETWORK_PROTOCOL_TCP_SO: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_SO; #else case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE: case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE: case SIPE_NETWORK_PROTOCOL_TCP_SO: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP; #endif default: case SIPE_NETWORK_PROTOCOL_UDP: return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP; } } static SipeNetworkProtocol purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto) { switch (proto) { #ifdef HAVE_PURPLE_NEW_TCP_ENUMS case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE: return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE; case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE: return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE; case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_SO: return SIPE_NETWORK_PROTOCOL_TCP_SO; #else case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP: return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE; #endif default: case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP: return SIPE_NETWORK_PROTOCOL_UDP; } } #ifdef HAVE_SRTP SipeEncryptionPolicy sipe_backend_media_get_encryption_policy(struct sipe_core_public *sipe_public) { PurpleAccount *account = sipe_public->backend_private->account; const char *policy = purple_account_get_string(account, "encryption-policy", "obey-server"); if (sipe_strequal(policy, "disabled")) { return SIPE_ENCRYPTION_POLICY_REJECTED; } else if (sipe_strequal(policy, "optional")) { return SIPE_ENCRYPTION_POLICY_OPTIONAL; } else if (sipe_strequal(policy, "required")) { return SIPE_ENCRYPTION_POLICY_REQUIRED; } else { return SIPE_ENCRYPTION_POLICY_OBEY_SERVER; } } #else SipeEncryptionPolicy sipe_backend_media_get_encryption_policy(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { return SIPE_ENCRYPTION_POLICY_REJECTED; } #endif /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-mime.c ================================================ /** * @file purple-mime.c * * pidgin-sipe * * Copyright (C) 2010-2015 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "glib.h" #include "mime.h" #include "sipe-mime.h" #include "sipe-core.h" void sipe_mime_init(void) { /* Nothing to do */ } void sipe_mime_shutdown(void) { /* Nothing to do */ } static GSList * mime_fields_to_nameval(PurpleMimePart* part) { GList *keys = purple_mime_part_get_fields(part); GSList *fields = NULL; while (keys) { const char *key = keys->data; const char *value = purple_mime_part_get_field(part, key); fields = sipe_utils_nameval_add(fields, key, value); keys = keys->next; } return fields; } void sipe_mime_parts_foreach(const gchar *type, const gchar *body, sipe_mime_parts_cb callback, gpointer user_data) { gchar *doc = g_strdup_printf("Content-Type: %s\r\n\r\n%s", type, body); PurpleMimeDocument *mime = purple_mime_document_parse(doc); if (mime) { GList* parts = purple_mime_document_get_parts(mime); while (parts) { const gchar *content_type = purple_mime_part_get_field(parts->data, "Content-Type"); if (content_type) { const gchar *content = NULL; guchar *content_decoded = NULL; gsize length = 0; GSList *fields = mime_fields_to_nameval(parts->data); purple_mime_part_get_data_decoded(parts->data, &content_decoded, &length); if (content_decoded) { content = (gchar *) content_decoded; } else { /* Unknown encoding in Content-Transfer-Encoding * field; revert to the plain content extraction. */ content = purple_mime_part_get_data(parts->data); length = purple_mime_part_get_length(parts->data); } (*callback)(user_data, fields, content, length); sipe_utils_nameval_free(fields); g_free(content_decoded); } parts = parts->next; } purple_mime_document_free(mime); } g_free(doc); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-network.c ================================================ /** * @file purple-network.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" /* coverity[hfa: FALSE] */ #endif #include #include "glib.h" #include "conversation.h" #include "network.h" #include "eventloop.h" #ifdef _WIN32 /* wrappers for write() & friends for socket handling */ #include "win32/win32dep.h" #include #else #include #include #include #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "sipe-common.h" #include "sipe-backend.h" #include "purple-private.h" struct sipe_backend_listendata { sipe_listen_start_cb listen_cb; sipe_client_connected_cb connect_cb; PurpleNetworkListenData *listener; int watcher; int listenfd; gpointer data; }; static void client_connected_cb(struct sipe_backend_listendata *ldata, gint listenfd, SIPE_UNUSED_PARAMETER PurpleInputCondition cond) { struct sockaddr_in saddr; socklen_t slen = sizeof (saddr); int fd = accept(listenfd, (struct sockaddr*)&saddr, &slen); purple_input_remove(ldata->watcher); ldata->watcher = 0; close(listenfd); ldata->listenfd = -1; if (fd >= 0) { if (ldata->connect_cb) { ldata->connect_cb(sipe_backend_fd_from_int(fd), ldata->data); } else { close(fd); } } g_free(ldata); } static void backend_listen_cb(int listenfd, struct sipe_backend_listendata *ldata) { ldata->listenfd = -1; ldata->listener = NULL; ldata->listenfd = listenfd; if (ldata->listen_cb) { /* * NOTE: getsockname() on Windows seems to be picky about the * buffer location. Use an allocated buffer instead of * one on the stack, */ union socket_info { struct sockaddr sa; /* to avoid casts */ struct sockaddr_in sa_in; /* IPv4 variant */ struct sockaddr_in6 sa_in6; /* IPv6 variant */ struct sockaddr_storage unused; /* for alignment */ } *si = g_new(union socket_info, 1); socklen_t si_len = sizeof(*si); guint port = htons(0); /* error fallback */ if (getsockname(listenfd, &si->sa, &si_len) == 0) { port = (si->sa.sa_family == AF_INET) ? si->sa_in.sin_port : (si->sa.sa_family == AF_INET6) ? si->sa_in6.sin6_port : port; } g_free(si); ldata->listen_cb(ntohs(port), ldata->data); } ldata->watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, (PurpleInputFunction)client_connected_cb, ldata); } struct sipe_backend_listendata * sipe_backend_network_listen_range(unsigned short port_min, unsigned short port_max, sipe_listen_start_cb listen_cb, sipe_client_connected_cb connect_cb, gpointer data) { struct sipe_backend_listendata *ldata; ldata = g_new0(struct sipe_backend_listendata, 1); ldata->listen_cb = listen_cb; ldata->connect_cb = connect_cb; ldata->data = data; ldata->listener = purple_network_listen_range(port_min, port_max, #if PURPLE_VERSION_CHECK(3,0,0) /* @TODO: does FT work with IPv6? */ AF_INET, #endif SOCK_STREAM, #if PURPLE_VERSION_CHECK(3,0,0) /* @TODO: should we allow external mapping? */ FALSE, #endif (PurpleNetworkListenCallback)backend_listen_cb, ldata); if (!ldata->listener) { g_free(ldata); return NULL; } return ldata; } void sipe_backend_network_listen_cancel(struct sipe_backend_listendata *ldata) { g_return_if_fail(ldata); if (ldata->listener) purple_network_listen_cancel(ldata->listener); if (ldata->listenfd) close(ldata->listenfd); g_free(ldata); } struct sipe_backend_fd * sipe_backend_fd_from_int(int fd) { struct sipe_backend_fd *sipe_fd = g_new(struct sipe_backend_fd, 1); sipe_fd->fd = fd; return sipe_fd; } gboolean sipe_backend_fd_is_valid(struct sipe_backend_fd *fd) { return fd && fd->fd >= 0; } void sipe_backend_fd_free(struct sipe_backend_fd *fd) { g_free(fd); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-notify.c ================================================ /** * @file purple-notify.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "conversation.h" #include "notify.h" #include "version.h" #if PURPLE_VERSION_CHECK(3,0,0) #include "conversations.h" #endif #include "sipe-backend.h" #include "sipe-core.h" #include "purple-private.h" static void notify_message(struct sipe_core_public *sipe_public, PurpleMessageFlags flags, struct sipe_backend_chat_session *backend_session, const gchar *who, const gchar *message) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleConversation *conv; if (backend_session) { conv = (PurpleConversation *) backend_session; } else { #if PURPLE_VERSION_CHECK(3,0,0) conv = (PurpleConversation *) purple_conversations_find_im_with_account( #else conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, #endif who, purple_private->account); } if (conv) { #if PURPLE_VERSION_CHECK(3,0,0) purple_conversation_write_system_message(conv, message, flags); #else purple_conversation_write(conv, NULL, message, flags, time(NULL)); #endif } } void sipe_backend_notify_message_error(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *who, const gchar *message) { notify_message(sipe_public, PURPLE_MESSAGE_ERROR, backend_session, who, message); } void sipe_backend_notify_message_info(struct sipe_core_public *sipe_public, struct sipe_backend_chat_session *backend_session, const gchar *who, const gchar *message) { notify_message(sipe_public, PURPLE_MESSAGE_SYSTEM, backend_session, who, message); } void sipe_backend_notify_error(struct sipe_core_public *sipe_public, const gchar *title, const gchar *msg) { struct sipe_backend_private *purple_private = sipe_public->backend_private; purple_notify_error(purple_private->gc, NULL, title, msg #if PURPLE_VERSION_CHECK(3,0,0) , NULL #endif ); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-plugin-common.c ================================================ /** * @file purple-plugin-common.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "sipe-common.h" #include "account.h" #include "core.h" #include "notify.h" #include "request.h" #include "version.h" #if PURPLE_VERSION_CHECK(3,0,0) #include "action.h" #define PURPLE_TYPE_STRING G_TYPE_STRING #define SIPE_PURPLE_ACTION_TO_CONNECTION action->connection #else #include "accountopt.h" #include "blist.h" #define g_source_remove(t) purple_timeout_remove(t) #define PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY #define PURPLE_CONNECTION_FLAG_FORMATTING_WBFO PURPLE_CONNECTION_FORMATTING_WBFO #define PURPLE_CONNECTION_FLAG_HTML PURPLE_CONNECTION_HTML #define PURPLE_CONNECTION_FLAG_NO_BGCOLOR PURPLE_CONNECTION_NO_BGCOLOR #define PURPLE_CONNECTION_FLAG_NO_FONTSIZE PURPLE_CONNECTION_NO_FONTSIZE #define PURPLE_CONNECTION_FLAG_NO_URLDESC PURPLE_CONNECTION_NO_URLDESC #define PURPLE_IS_BUDDY(n) PURPLE_BLIST_NODE_IS_BUDDY(n) #define PURPLE_IS_CHAT(n) PURPLE_BLIST_NODE_IS_CHAT(n) #define PURPLE_IM_TYPING PURPLE_TYPING #define PURPLE_IM_NOT_TYPING PURPLE_NOT_TYPING #define purple_account_option_string_set_masked(o, f) purple_account_option_set_masked(o, f) #define purple_connection_error(g, e, m) purple_connection_error_reason(g, e, m) #define purple_connection_get_flags(gc) 0 #define purple_connection_set_protocol_data(gc, p) gc->proto_data = p #define purple_connection_set_flags(gc, f) gc->flags |= f #define purple_protocol_action_new(l, c) purple_plugin_action_new(l, c) #define PurpleProtocolAction PurplePluginAction #define SIPE_PURPLE_ACTION_TO_CONNECTION action->context #endif #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-nls.h" #include "purple-private.h" /* * NOTE: this flag means two things: * * - is Single Sign-On supported, and * - is Kerberos supported */ #if defined(HAVE_GSSAPI_GSSAPI_H) || defined(HAVE_SSPI) #define PURPLE_SIPE_SSO_AND_KERBEROS 1 #else #define PURPLE_SIPE_SSO_AND_KERBEROS 0 #endif /* * SIPE core activity <-> Purple status mapping * * NOTE: this needs to be kept in sync with sipe_purple_status_types() */ static const gchar * const activity_to_purple_map[SIPE_ACTIVITY_NUM_TYPES] = { /* SIPE_ACTIVITY_UNSET */ "unset", /* == purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) */ /* SIPE_ACTIVITY_AVAILABLE */ "available", /* == purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) */ /* SIPE_ACTIVITY_ONLINE */ "online", /* SIPE_ACTIVITY_INACTIVE */ "idle", /* SIPE_ACTIVITY_BUSY */ "busy", /* SIPE_ACTIVITY_BUSYIDLE */ "busyidle", /* SIPE_ACTIVITY_DND */ "do-not-disturb", /* SIPE_ACTIVITY_BRB */ "be-right-back", /* SIPE_ACTIVITY_AWAY */ "away", /* == purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) */ /* SIPE_ACTIVITY_LUNCH */ "out-to-lunch", /* SIPE_ACTIVITY_INVISIBLE */ "invisible", /* == purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) */ /* SIPE_ACTIVITY_OFFLINE */ "offline", /* == purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) */ /* SIPE_ACTIVITY_ON_PHONE */ "on-the-phone", /* SIPE_ACTIVITY_IN_CONF */ "in-a-conference", /* SIPE_ACTIVITY_IN_MEETING */ "in-a-meeting", /* SIPE_ACTIVITY_OOF */ "out-of-office", /* SIPE_ACTIVITY_URGENT_ONLY */ "urgent-interruptions-only", /* SIPE_ACTIVITY_IN_PRES */ "presenting", /* SIPE_ACTIVIY_NUM_TYPES == 18 -> compare to sipe_purple_status_types() */ }; GHashTable *purple_token_map; static void sipe_purple_activity_init(void) { guint index; purple_token_map = g_hash_table_new(g_str_hash, g_str_equal); for (index = SIPE_ACTIVITY_UNSET; index < SIPE_ACTIVITY_NUM_TYPES; index++) { g_hash_table_insert(purple_token_map, (gchar *) activity_to_purple_map[index], GUINT_TO_POINTER(index)); } } static void sipe_purple_activity_shutdown(void) { g_hash_table_destroy(purple_token_map); } const gchar *sipe_purple_activity_to_token(guint type) { return(activity_to_purple_map[type]); } guint sipe_purple_token_to_activity(const gchar *token) { return(GPOINTER_TO_UINT(g_hash_table_lookup(purple_token_map, token))); } gchar *sipe_backend_version(void) { return(g_strdup_printf("Purple/%s", purple_core_get_version())); } const char *sipe_purple_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount *a, SIPE_UNUSED_PARAMETER PurpleBuddy *b) { return "sipe"; } gchar *sipe_purple_status_text(PurpleBuddy *buddy) { PurpleStatus *status = purple_presence_get_active_status(purple_buddy_get_presence(buddy)); return sipe_core_buddy_status(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, purple_buddy_get_name(buddy), sipe_purple_token_to_activity(purple_status_get_id(status)), purple_status_get_name(status)); } void sipe_purple_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, SIPE_UNUSED_PARAMETER gboolean full) { PurplePresence *presence = purple_buddy_get_presence(buddy); sipe_core_buddy_tooltip_info(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC, purple_buddy_get_name(buddy), purple_status_get_name(purple_presence_get_active_status(presence)), purple_presence_is_online(presence), (struct sipe_backend_buddy_tooltip *) user_info); } GList *sipe_purple_status_types(SIPE_UNUSED_PARAMETER PurpleAccount *acc) { PurpleStatusType *type; GList *types = NULL; /* Macro to reduce code repetition Translators: noun */ #define SIPE_ADD_STATUS(prim,activity,user) type = purple_status_type_new_with_attrs( \ prim, \ sipe_purple_activity_to_token(activity), \ sipe_core_activity_description(activity), \ TRUE, user, FALSE, \ SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \ NULL); \ types = g_list_append(types, type); /* * NOTE: needs to be kept in sync with activity_to_purple_map[], * i.e. for each SIPE_ACTIVITY_xxx value there must be an * entry on this list. * * NOTE: the following code is sorted by purple primitive type not * by SIPE_ACTIVITY_xxx value. */ /* 1: Unset - special case: no entry needed */ /* * Status list entries for primitive type AVAILABLE * * 2: Available */ SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE, SIPE_ACTIVITY_AVAILABLE, TRUE); /* 3: Online */ SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE, SIPE_ACTIVITY_ONLINE, FALSE); /* 4: Inactive (Idle) */ SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE, SIPE_ACTIVITY_INACTIVE, FALSE); /* * Status list entries for primitive type UNAVAILABLE * * 5: Busy */ SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE, SIPE_ACTIVITY_BUSY, TRUE); /* 6: Busy-Idle */ SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE, SIPE_ACTIVITY_BUSYIDLE, FALSE); /* 7: Do Not Disturb */ SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE, SIPE_ACTIVITY_DND, TRUE); /* 8: In a call */ SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE, SIPE_ACTIVITY_ON_PHONE, FALSE); /* 9: In a conference call */ SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE, SIPE_ACTIVITY_IN_CONF, FALSE); /* 10: In a meeting */ SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE, SIPE_ACTIVITY_IN_MEETING, FALSE); /* 11: Urgent interruptions only */ SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE, SIPE_ACTIVITY_URGENT_ONLY, FALSE); /* Presenting */ SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE, SIPE_ACTIVITY_IN_PRES, FALSE); /* * Status list entries for primitive type AWAY * * 12: Away - special case: needs to go first in the list as purple * picks the first status with primitive type AWAY for idle */ SIPE_ADD_STATUS(PURPLE_STATUS_AWAY, SIPE_ACTIVITY_AWAY, TRUE); /* 13: Be Right Back */ SIPE_ADD_STATUS(PURPLE_STATUS_AWAY, SIPE_ACTIVITY_BRB, TRUE); /* 14: Out to lunch */ SIPE_ADD_STATUS(PURPLE_STATUS_AWAY, SIPE_ACTIVITY_LUNCH, FALSE); /* * Status list entries for primitive type EXTENDED_AWAY * * 15: Out of office */ SIPE_ADD_STATUS(PURPLE_STATUS_EXTENDED_AWAY, SIPE_ACTIVITY_OOF, FALSE); /* * Status list entries for primitive type INVISIBLE * * 16: Appear Offline */ SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE, SIPE_ACTIVITY_INVISIBLE, TRUE); /* * Status list entries for primitive type OFFLINE * * NOTE: this is always the last entry. Compare the number * with the comment in activity_to_purple_map[]. * * 17: Offline - special case: no message text */ type = purple_status_type_new(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE); types = g_list_append(types, type); return types; } GList *sipe_purple_blist_node_menu(PurpleBlistNode *node) { if (PURPLE_IS_BUDDY(node)) { return sipe_purple_buddy_menu((PurpleBuddy *) node); } else if (PURPLE_IS_CHAT(node)) { return sipe_purple_chat_menu((PurpleChat *)node); } else { return NULL; } } static guint get_authentication_type(PurpleAccount *account) { const gchar *auth = purple_account_get_string(account, "authentication", "ntlm"); /* map option list to type - default is automatic */ guint authentication_type = SIPE_AUTHENTICATION_TYPE_AUTOMATIC; if (sipe_strequal(auth, "ntlm")) { authentication_type = SIPE_AUTHENTICATION_TYPE_NTLM; } else #if PURPLE_SIPE_SSO_AND_KERBEROS if (sipe_strequal(auth, "krb5")) { authentication_type = SIPE_AUTHENTICATION_TYPE_KERBEROS; } else #endif if (sipe_strequal(auth, "tls-dsk")) { authentication_type = SIPE_AUTHENTICATION_TYPE_TLS_DSK; } return(authentication_type); } static gboolean get_sso_flag(PurpleAccount *account) { #if PURPLE_SIPE_SSO_AND_KERBEROS /* * NOTE: the default must be *OFF*, i.e. it is up to the user to tell * SIPE that it is OK to use Single Sign-On or not. */ return(purple_account_get_bool(account, "sso", FALSE)); #else (void) account; /* keep compiler happy */ return(FALSE); #endif } static gboolean get_dont_publish_flag(PurpleAccount *account) { /* default is to publish calendar information */ return(purple_account_get_bool(account, "dont-publish", FALSE)); } static gboolean get_allow_web_photo_flag(PurpleAccount *account) { /* default is to not allow insecure download of buddy icons from web */ return purple_account_get_bool(account, "allow-web-photo", FALSE); } static void connect_to_core(PurpleConnection *gc, PurpleAccount *account, const gchar *password) { const gchar *username = purple_account_get_username(account); const gchar *email = purple_account_get_string(account, "email", NULL); const gchar *email_url = purple_account_get_string(account, "email_url", NULL); const gchar *transport = purple_account_get_string(account, "transport", "auto"); struct sipe_core_public *sipe_public; gchar **username_split; const gchar *errmsg; guint transport_type; struct sipe_backend_private *purple_private; /* username format: ,[] */ SIPE_DEBUG_INFO("sipe_purple_login: username '%s'", username); username_split = g_strsplit(username, ",", 2); sipe_public = sipe_core_allocate(username_split[0], get_sso_flag(account), username_split[1], password, email, email_url, &errmsg); g_strfreev(username_split); if (!sipe_public) { purple_connection_error(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, errmsg); return; } sipe_public->backend_private = purple_private = g_new0(struct sipe_backend_private, 1); purple_private->public = sipe_public; purple_private->gc = gc; purple_private->account = account; sipe_purple_chat_setup_rejoin(purple_private); SIPE_CORE_FLAG_UNSET(DONT_PUBLISH); if (get_dont_publish_flag(account)) SIPE_CORE_FLAG_SET(DONT_PUBLISH); SIPE_CORE_FLAG_UNSET(ALLOW_WEB_PHOTO); if (get_allow_web_photo_flag(account)) SIPE_CORE_FLAG_SET(ALLOW_WEB_PHOTO); purple_connection_set_protocol_data(gc, sipe_public); purple_connection_set_flags(gc, purple_connection_get_flags(gc) | PURPLE_CONNECTION_FLAG_HTML | PURPLE_CONNECTION_FLAG_FORMATTING_WBFO | PURPLE_CONNECTION_FLAG_NO_BGCOLOR | PURPLE_CONNECTION_FLAG_NO_FONTSIZE | PURPLE_CONNECTION_FLAG_NO_URLDESC | PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY); purple_connection_set_display_name(gc, sipe_public->sip_name); purple_connection_update_progress(gc, _("Connecting"), 1, 2); username_split = g_strsplit(purple_account_get_string(account, "server", ""), ":", 2); if (sipe_strequal(transport, "auto")) { transport_type = (username_split[0] == NULL) ? SIPE_TRANSPORT_AUTO : SIPE_TRANSPORT_TLS; } else if (sipe_strequal(transport, "tls")) { transport_type = SIPE_TRANSPORT_TLS; } else { transport_type = SIPE_TRANSPORT_TCP; } sipe_core_transport_sip_connect(sipe_public, transport_type, get_authentication_type(account), username_split[0], username_split[0] ? username_split[1] : NULL); g_strfreev(username_split); } static void password_required_cb(PurpleConnection *gc, SIPE_UNUSED_PARAMETER PurpleRequestFields *fields) { #if !PURPLE_VERSION_CHECK(3,0,0) if (!PURPLE_CONNECTION_IS_VALID(gc)) { return; } #endif purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Password required")); } static void password_ok_cb(PurpleConnection *gc, PurpleRequestFields *fields) { const gchar *password; #if !PURPLE_VERSION_CHECK(3,0,0) if (!PURPLE_CONNECTION_IS_VALID(gc)) { return; } #endif password = purple_request_fields_get_string(fields, "password"); if (password && strlen(password)) { PurpleAccount *account = purple_connection_get_account(gc); if (purple_request_fields_get_bool(fields, "remember")) purple_account_set_remember_password(account, TRUE); purple_account_set_password(account, password #if PURPLE_VERSION_CHECK(3,0,0) , NULL, NULL #endif ); /* Now we have a password and we can connect */ connect_to_core(gc, account, password); } else /* reject an empty password */ password_required_cb(gc, fields); } void sipe_purple_login(PurpleAccount *account) { PurpleConnection *gc = purple_account_get_connection(account); const gchar *password = purple_connection_get_password(gc); /* Password required? */ if (sipe_core_transport_sip_requires_password(get_authentication_type(account), get_sso_flag(account)) && (!password || !strlen(password))) /* No password set - request one from user */ purple_account_request_password(account, G_CALLBACK(password_ok_cb), G_CALLBACK(password_required_cb), gc); else /* No password required or saved password - connect now */ connect_to_core(gc, account, password); } void sipe_purple_close(PurpleConnection *gc) { struct sipe_core_public *sipe_public = PURPLE_GC_TO_SIPE_CORE_PUBLIC; if (sipe_public) { struct sipe_backend_private *purple_private = sipe_public->backend_private; sipe_core_deallocate(sipe_public); /* anything left after that must be in pending state... */ sipe_purple_dns_query_cancel_all(purple_private); sipe_purple_transport_close_all(purple_private); if (purple_private->roomlist_map) g_hash_table_destroy(purple_private->roomlist_map); sipe_purple_chat_destroy_rejoin(purple_private); if (purple_private->deferred_status_timeout) g_source_remove(purple_private->deferred_status_timeout); g_free(purple_private->deferred_status_note); g_free(purple_private); purple_connection_set_protocol_data(gc, NULL); } } unsigned int sipe_purple_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state) { gboolean typing = (state == PURPLE_IM_TYPING); /* only enable this debug output while testing SIPE_DEBUG_INFO("sipe_purple_send_typing: '%s' state %d", who, state); */ /* * libpurple calls this function with PURPLE_NOT_TYPING *after* * calling sipe_purple_send_im() with the message. This causes * SIPE core to send out two SIP messages to the same dialog in * short succession without waiting for the response to the first * one. Some servers then reject the first one with * * SIP/2.0 500 Stale CSeq Value * * which triggers a "message not delivered" error for the user. * * Work around this by filtering out PURPLE_NOT_TYPING events. */ if (state != PURPLE_IM_NOT_TYPING) sipe_core_user_feedback_typing(PURPLE_GC_TO_SIPE_CORE_PUBLIC, who, typing); /* tell libpurple to send typing indications every 4 seconds */ return(typing ? 4 : 0); } void sipe_purple_get_info(PurpleConnection *gc, const char *who) { sipe_core_buddy_get_info(PURPLE_GC_TO_SIPE_CORE_PUBLIC, who); } void sipe_purple_add_permit(PurpleConnection *gc, const char *name) { sipe_core_contact_allow_deny(PURPLE_GC_TO_SIPE_CORE_PUBLIC, name, TRUE); } void sipe_purple_add_deny(PurpleConnection *gc, const char *name) { sipe_core_contact_allow_deny(PURPLE_GC_TO_SIPE_CORE_PUBLIC, name, FALSE); } void sipe_purple_alias_buddy(PurpleConnection *gc, const char *name, const char *alias) { sipe_core_group_set_alias(PURPLE_GC_TO_SIPE_CORE_PUBLIC, name, alias); } void sipe_purple_group_rename(PurpleConnection *gc, const char *old_name, PurpleGroup *group, SIPE_UNUSED_PARAMETER GList *moved_buddies) { sipe_core_group_rename(PURPLE_GC_TO_SIPE_CORE_PUBLIC, old_name, purple_group_get_name(group)); } void sipe_purple_convo_closed(PurpleConnection *gc, const char *who) { sipe_core_im_close(PURPLE_GC_TO_SIPE_CORE_PUBLIC, who); } void sipe_purple_group_remove(PurpleConnection *gc, PurpleGroup *group) { sipe_core_group_remove(PURPLE_GC_TO_SIPE_CORE_PUBLIC, purple_group_get_name(group)); } GHashTable * sipe_purple_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount *account) { GHashTable *table; table = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(table, (char *)"login_label", (gpointer)_("user@company.com")); return table; } #ifdef HAVE_VV static void sipe_purple_sigusr1_handler(SIPE_UNUSED_PARAMETER int signum) { capture_pipeline("PURPLE_SIPE_PIPELINE"); } gboolean sipe_purple_initiate_media(PurpleAccount *account, const char *who, SIPE_UNUSED_PARAMETER PurpleMediaSessionType type) { sipe_core_media_initiate_call(PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC, who, (type & PURPLE_MEDIA_VIDEO)); return TRUE; } PurpleMediaCaps sipe_purple_get_media_caps(SIPE_UNUSED_PARAMETER PurpleAccount *account, SIPE_UNUSED_PARAMETER const char *who) { return PURPLE_MEDIA_CAPS_AUDIO | PURPLE_MEDIA_CAPS_AUDIO_VIDEO | PURPLE_MEDIA_CAPS_MODIFY_SESSION; } #endif /* PurplePluginInfo function calls & data structure */ gboolean sipe_purple_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin *plugin) { #ifdef HAVE_VV { struct sigaction action; memset(&action, 0, sizeof (action)); action.sa_handler = sipe_purple_sigusr1_handler; sigaction(SIGUSR1, &action, NULL); } #endif sipe_purple_activity_init(); return TRUE; } gboolean sipe_purple_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin *plugin) { #ifdef HAVE_VV struct sigaction action; memset(&action, 0, sizeof (action)); action.sa_handler = SIG_DFL; sigaction(SIGUSR1, &action, NULL); #endif sipe_purple_activity_shutdown(); return TRUE; } static void sipe_purple_show_about_plugin(PurpleProtocolAction *action) { gchar *tmp = sipe_core_about(); purple_notify_formatted(SIPE_PURPLE_ACTION_TO_CONNECTION, NULL, " ", NULL, tmp, NULL, NULL); g_free(tmp); } static void sipe_purple_join_conference_cb(PurpleConnection *gc, PurpleRequestFields *fields) { GList *entries = purple_request_field_group_get_fields(purple_request_fields_get_groups(fields)->data); if (entries) { const gchar *location = purple_request_fields_get_string(fields, "meetingLocation"); const gchar *organizer = purple_request_fields_get_string(fields, "meetingOrganizer"); const gchar *meeting_id = purple_request_fields_get_string(fields, "meetingID"); sipe_core_conf_create(PURPLE_GC_TO_SIPE_CORE_PUBLIC, location, organizer, meeting_id); } } #ifdef HAVE_VV static void sipe_purple_phone_call_cb(PurpleConnection *gc, PurpleRequestFields *fields) { GList *entries = purple_request_field_group_get_fields(purple_request_fields_get_groups(fields)->data); if (entries) sipe_core_media_phone_call(PURPLE_GC_TO_SIPE_CORE_PUBLIC, purple_request_fields_get_string(fields, "phoneNumber")); } static void sipe_purple_phone_call(PurpleProtocolAction *action) { PurpleConnection *gc = SIPE_PURPLE_ACTION_TO_CONNECTION; PurpleRequestFields *fields; PurpleRequestFieldGroup *group; PurpleRequestField *field; fields = purple_request_fields_new(); group = purple_request_field_group_new(NULL); purple_request_fields_add_group(fields, group); field = purple_request_field_string_new("phoneNumber", _("Phone number"), NULL, FALSE); purple_request_field_group_add_field(group, field); purple_request_fields(gc, _("Call a phone number"), _("Call a phone number"), NULL, fields, _("_Call"), G_CALLBACK(sipe_purple_phone_call_cb), _("_Cancel"), NULL, #if PURPLE_VERSION_CHECK(3,0,0) purple_request_cpar_from_connection(gc), #else purple_connection_get_account(gc), NULL, NULL, #endif gc); } static void sipe_purple_test_call(PurpleProtocolAction *action) { PurpleConnection *gc = SIPE_PURPLE_ACTION_TO_CONNECTION; sipe_core_media_test_call(PURPLE_GC_TO_SIPE_CORE_PUBLIC); } #endif static void sipe_purple_show_join_conference(PurpleProtocolAction *action) { PurpleConnection *gc = SIPE_PURPLE_ACTION_TO_CONNECTION; PurpleRequestFields *fields; PurpleRequestFieldGroup *group; PurpleRequestField *field; fields = purple_request_fields_new(); group = purple_request_field_group_new(NULL); purple_request_fields_add_group(fields, group); field = purple_request_field_string_new("meetingLocation", _("Meeting location"), NULL, FALSE); purple_request_field_group_add_field(group, field); field = purple_request_field_label_new("separator", _("Alternatively")); purple_request_field_group_add_field(group, field); field = purple_request_field_string_new("meetingOrganizer", _("Organizer email"), NULL, FALSE); purple_request_field_group_add_field(group, field); field = purple_request_field_string_new("meetingID", _("Meeting ID"), NULL, FALSE); purple_request_field_group_add_field(group, field); purple_request_fields(gc, _("Join conference"), _("Join scheduled conference"), _("Enter meeting location string you received in the invitation.\n" "\n" "Valid location will be something like\n" "meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n" "or\n" "https://meet.company.com/someone/abcdef1234"), fields, _("_Join"), G_CALLBACK(sipe_purple_join_conference_cb), _("_Cancel"), NULL, #if PURPLE_VERSION_CHECK(3,0,0) purple_request_cpar_from_connection(gc), #else purple_connection_get_account(gc), NULL, NULL, #endif gc); } void sipe_purple_republish_calendar(PurpleAccount *account) { struct sipe_core_public *sipe_public = PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC; if (get_dont_publish_flag(account)) { sipe_backend_notify_error(sipe_public, _("Publishing of calendar information has been disabled"), NULL); } else { sipe_core_update_calendar(sipe_public); } } static void sipe_purple_republish_calendar_action(PurpleProtocolAction *action) { PurpleConnection *gc = SIPE_PURPLE_ACTION_TO_CONNECTION; PurpleAccount *account = purple_connection_get_account(gc); sipe_purple_republish_calendar(account); } void sipe_purple_reset_status(PurpleAccount *account) { if (get_dont_publish_flag(account)) { sipe_backend_notify_error(PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC, _("Publishing of calendar information has been disabled"), NULL); } else { sipe_core_reset_status(PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC); } } static void sipe_purple_reset_status_action(PurpleProtocolAction *action) { PurpleConnection *gc = SIPE_PURPLE_ACTION_TO_CONNECTION; PurpleAccount *account = purple_connection_get_account(gc); sipe_purple_reset_status(account); } GList *sipe_purple_actions() { GList *menu = NULL; PurpleProtocolAction *act; act = purple_protocol_action_new(_("About SIPE plugin..."), sipe_purple_show_about_plugin); menu = g_list_prepend(menu, act); act = purple_protocol_action_new(_("Contact search..."), sipe_purple_show_find_contact); menu = g_list_prepend(menu, act); #ifdef HAVE_VV act = purple_protocol_action_new(_("Call a phone number..."), sipe_purple_phone_call); menu = g_list_prepend(menu, act); act = purple_protocol_action_new(_("Test call"), sipe_purple_test_call); menu = g_list_prepend(menu, act); #endif act = purple_protocol_action_new(_("Join scheduled conference..."), sipe_purple_show_join_conference); menu = g_list_prepend(menu, act); act = purple_protocol_action_new(_("Republish Calendar"), sipe_purple_republish_calendar_action); menu = g_list_prepend(menu, act); act = purple_protocol_action_new(_("Reset status"), sipe_purple_reset_status_action); menu = g_list_prepend(menu, act); return g_list_reverse(menu); } GList * sipe_purple_account_options() { PurpleAccountOption *option; GList *options = NULL; /** * When adding new string settings please make sure to keep these * in sync: * * api/sipe-backend.h * purple-settings.c:setting_name[] */ option = purple_account_option_string_new(_("Server[:Port]\n(leave empty for auto-discovery)"), "server", ""); options = g_list_append(options, option); option = purple_account_option_list_new(_("Connection type"), "transport", NULL); purple_account_option_add_list_item(option, _("Auto"), "auto"); purple_account_option_add_list_item(option, _("SSL/TLS"), "tls"); purple_account_option_add_list_item(option, _("TCP"), "tcp"); options = g_list_append(options, option); /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE); sipe_prpl_info.protocol_options = g_list_append(sipe_prpl_info.protocol_options, option);*/ option = purple_account_option_string_new(_("User Agent"), "useragent", ""); options = g_list_append(options, option); option = purple_account_option_list_new(_("Authentication scheme"), "authentication", NULL); purple_account_option_add_list_item(option, _("Auto"), "auto"); purple_account_option_add_list_item(option, _("NTLM"), "ntlm"); #if PURPLE_SIPE_SSO_AND_KERBEROS purple_account_option_add_list_item(option, _("Kerberos"), "krb5"); #endif purple_account_option_add_list_item(option, _("TLS-DSK"), "tls-dsk"); options = g_list_append(options, option); #if PURPLE_SIPE_SSO_AND_KERBEROS /* * When the user selects Single Sign-On then SIPE will ignore the * settings for "login name" and "password". Instead it will use the * default credentials provided by the OS. * * NOTE: the default must be *OFF*, i.e. it is up to the user to tell * SIPE that it is OK to use Single Sign-On or not. * * Configurations that are known to support Single Sign-On: * * - Windows, host joined to domain, SIPE with SSPI: NTLM * - Windows, host joined to domain, SIPE with SSPI: Kerberos * - SIPE with libkrb5, valid TGT in cache (kinit): Kerberos */ option = purple_account_option_bool_new(_("Use Single Sign-On"), "sso", FALSE); options = g_list_append(options, option); #endif /** Example (Exchange): https://server.company.com/EWS/Exchange.asmx * Example (Domino) : https://[domino_server]/[mail_database_name].nsf */ option = purple_account_option_bool_new(_("Don't publish my calendar information"), "dont-publish", FALSE); options = g_list_append(options, option); option = purple_account_option_bool_new(_("Show profile pictures from web\n(potentially dangerous)"), "allow-web-photo", FALSE); options = g_list_append(options, option); option = purple_account_option_string_new(_("Email services URL\n(leave empty for auto-discovery)"), "email_url", ""); options = g_list_append(options, option); option = purple_account_option_string_new(_("Email address\n(if different from Username)"), "email", ""); options = g_list_append(options, option); /** Example (Exchange): DOMAIN\user or user@company.com * Example (Domino) : email_address */ option = purple_account_option_string_new(_("Email login\n(if different from Login)"), "email_login", ""); options = g_list_append(options, option); option = purple_account_option_string_new(_("Email password\n(if different from Password)"), "email_password", ""); purple_account_option_string_set_masked(option, TRUE); options = g_list_append(options, option); /** Example (federated domain): company.com (i.e. ocschat@company.com) * Example (non-default user): user@company.com */ option = purple_account_option_string_new(_("Group Chat Proxy\n company.com or user@company.com\n(leave empty to determine from Username)"), "groupchat_user", ""); options = g_list_append(options, option); #ifdef HAVE_APPSHARE option = purple_account_option_string_new(_("Remote desktop client"), "rdp_client", ""); options = g_list_append(options, option); #endif #ifdef HAVE_SRTP option = purple_account_option_list_new(_("Media encryption"), "encryption-policy", NULL); purple_account_option_add_list_item(option, _("Obey server policy"), "obey-server"); purple_account_option_add_list_item(option, _("Always"), "required"); purple_account_option_add_list_item(option, _("Optional"), "optional"); purple_account_option_add_list_item(option, _("Disabled"), "disabled"); options = g_list_append(options, option); #endif return options; } gpointer sipe_purple_user_split() { PurpleAccountUserSplit *split = purple_account_user_split_new( _("Login\n user or DOMAIN\\user or\n user@company.com"), /* * pidgin works fine with NULL as default * finch returns string "(null)" for NULL */ "", ','); purple_account_user_split_set_reverse(split, FALSE); return split; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-plugin.c ================================================ /** * @file purple-plugin.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "sipe-common.h" #include "sipe-backend.h" /* Flag needed for correct version of PURPLE_INIT_PLUGIN() */ #ifndef PURPLE_PLUGINS #define PURPLE_PLUGINS #endif /* for LOCALEDIR * as it's determined on runtime, as Pidgin installation can be anywhere. */ #ifdef _WIN32 #include "win32/win32dep.h" #endif #include "accountopt.h" #include "prpl.h" #include "sipe-core.h" #define _PurpleMessageFlags PurpleMessageFlags #include "purple-private.h" #ifdef HAVE_DBUS #include "purple-dbus.h" #endif #if !PURPLE_VERSION_CHECK(2,7,0) #error purple >= 2.7.0 is required to build SIPE #endif static int sipe_purple_send_im(PurpleConnection *gc, const char *who, const char *what, SIPE_UNUSED_PARAMETER PurpleMessageFlags flags) { sipe_core_im_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC, who, what); return 1; } /* * Simplistic source upward compatibility path for newer libpurple APIs * * Usually we compile with -Werror=missing-field-initializers if GCC supports * it. But that means that the compilation of this structure can fail if the * newer API has added additional plugin callbacks. For the benefit of the * user we downgrade it to a warning here. * * Diagnostic #pragma was added in GCC 4.2.0 * Diagnostic push/pop was added in GCC 4.6.0 */ #ifdef __GNUC__ #if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ >= 5) #if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ >= 5) #pragma GCC diagnostic push #endif #pragma GCC diagnostic warning "-Wmissing-field-initializers" #endif #endif static PurplePluginProtocolInfo sipe_prpl_info = { SIPE_PURPLE_PROTOCOL_OPTIONS, NULL, /* user_splits */ NULL, /* protocol_options */ NO_BUDDY_ICONS, /* icon_spec */ sipe_purple_list_icon, /* list_icon */ NULL, /* list_emblems */ sipe_purple_status_text, /* status_text */ sipe_purple_tooltip_text, /* tooltip_text */ // add custom info to contact tooltip sipe_purple_status_types, /* away_states */ sipe_purple_blist_node_menu, /* blist_node_menu */ sipe_purple_chat_info, /* chat_info */ sipe_purple_chat_info_defaults, /* chat_info_defaults */ sipe_purple_login, /* login */ sipe_purple_close, /* close */ sipe_purple_send_im, /* send_im */ NULL, /* set_info */ // TODO maybe sipe_purple_send_typing, /* send_typing */ sipe_purple_get_info, /* get_info */ sipe_purple_set_status, /* set_status */ sipe_purple_set_idle, /* set_idle */ NULL, /* change_passwd */ sipe_purple_add_buddy, /* add_buddy */ NULL, /* add_buddies */ sipe_purple_remove_buddy, /* remove_buddy */ NULL, /* remove_buddies */ sipe_purple_add_permit, /* add_permit */ sipe_purple_add_deny, /* add_deny */ sipe_purple_add_deny, /* rem_permit */ sipe_purple_add_permit, /* rem_deny */ NULL, /* set_permit_deny */ sipe_purple_chat_join, /* join_chat */ NULL, /* reject_chat */ NULL, /* get_chat_name */ sipe_purple_chat_invite, /* chat_invite */ sipe_purple_chat_leave, /* chat_leave */ NULL, /* chat_whisper */ sipe_purple_chat_send, /* chat_send */ NULL, /* keepalive */ NULL, /* register_user */ NULL, /* get_cb_info */ // deprecated NULL, /* get_cb_away */ // deprecated sipe_purple_alias_buddy, /* alias_buddy */ sipe_purple_group_buddy, /* group_buddy */ sipe_purple_group_rename, /* rename_group */ NULL, /* buddy_free */ sipe_purple_convo_closed, /* convo_closed */ purple_normalize_nocase, /* normalize */ NULL, /* set_buddy_icon */ sipe_purple_group_remove, /* remove_group */ NULL, /* get_cb_real_name */ // TODO? NULL, /* set_chat_topic */ NULL, /* find_blist_chat */ sipe_purple_roomlist_get_list, /* roomlist_get_list */ sipe_purple_roomlist_cancel, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ NULL, /* can_receive_file */ sipe_purple_ft_send_file, /* send_file */ NULL, /* new_xfer */ NULL, /* offline_message */ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ NULL, /* unregister_user */ NULL, /* send_attention */ NULL, /* get_attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ sipe_purple_get_account_text_table, /* get_account_text_table */ #ifdef HAVE_VV sipe_purple_initiate_media, /* initiate_media */ sipe_purple_get_media_caps, /* get_media_caps */ #else NULL, /* initiate_media */ NULL, /* get_media_caps */ #endif NULL, /* get_moods */ NULL, /* set_public_alias */ NULL, /* get_public_alias */ #if PURPLE_VERSION_CHECK(2,8,0) NULL, /* add_buddy_with_invite */ NULL, /* add_buddies_with_invite */ #endif #if PURPLE_VERSION_CHECK(2,14,0) NULL, /* get_cb_alias */ #endif }; #ifdef __GNUC__ #if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ >= 5) #pragma GCC diagnostic pop #endif #endif /* Original GCC error checking restored from here on... (see above) */ /* PurplePluginInfo function calls & data structure */ static void sipe_purple_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin *plugin) { GList *entry; sipe_core_destroy(); entry = sipe_prpl_info.protocol_options; while (entry) { purple_account_option_destroy(entry->data); entry = g_list_delete_link(entry, entry); } sipe_prpl_info.protocol_options = NULL; entry = sipe_prpl_info.user_splits; while (entry) { purple_account_user_split_destroy(entry->data); entry = g_list_delete_link(entry, entry); } sipe_prpl_info.user_splits = NULL; } static gboolean plugin_load(PurplePlugin *plugin) { #ifdef HAVE_DBUS if (purple_dbus_get_init_error() == NULL) { SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_plugin_load: registering D-Bus bindings"); purple_dbus_register_bindings(plugin, sipe_purple_dbus_bindings); } #endif return sipe_purple_plugin_load(plugin); } static GList *purple_actions(SIPE_UNUSED_PARAMETER PurplePlugin *plugin, SIPE_UNUSED_PARAMETER gpointer context) { return sipe_purple_actions(); } static PurplePluginInfo sipe_purple_info = { PURPLE_PLUGIN_MAGIC, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_PLUGIN_PROTOCOL, /**< type */ NULL, /**< ui_requirement */ 0, /**< flags */ NULL, /**< dependencies */ PURPLE_PRIORITY_DEFAULT, /**< priority */ SIPE_PURPLE_PLUGIN_ID, /**< id */ SIPE_PURPLE_PLUGIN_NAME, /**< name */ PACKAGE_VERSION, /**< version */ SIPE_PURPLE_PLUGIN_SUMMARY, /**< summary */ SIPE_PURPLE_PLUGIN_DESCRIPTION, /**< description */ SIPE_PURPLE_PLUGIN_AUTHORS, /**< authors */ PACKAGE_URL, /**< homepage */ plugin_load, /**< load */ sipe_purple_plugin_unload, /**< unload */ sipe_purple_plugin_destroy, /**< destroy */ NULL, /**< ui_info */ &sipe_prpl_info, /**< extra_info */ NULL, purple_actions, NULL, NULL, NULL, NULL }; static void sipe_purple_init_plugin(PurplePlugin *plugin) { /* This needs to be called first */ sipe_core_init(LOCALEDIR); purple_plugin_register(plugin); sipe_prpl_info.user_splits = g_list_append(sipe_prpl_info.user_splits, sipe_purple_user_split()); sipe_prpl_info.protocol_options = sipe_purple_account_options(); } /* This macro makes the code a purple plugin */ PURPLE_INIT_PLUGIN(sipe, sipe_purple_init_plugin, sipe_purple_info); /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-plugin3.c ================================================ /** * @file purple-plugin3.c * * pidgin-sipe * * Copyright (C) 2015-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "protocol.h" #include "purple-private.h" #include "sipe-common.h" #include "sipe-core.h" #define SIPE_TYPE_PROTOCOL (sipe_protocol_get_type()) typedef struct _SipeProtocol { PurpleProtocol parent; } SipeProtocol; typedef struct _SipeProtocolClass { PurpleProtocolClass parent_class; } SipeProtocolClass; G_MODULE_EXPORT GType sipe_protocol_get_type(void); static void sipe_protocol_class_init(SipeProtocolClass *klass) { PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass); protocol_class->login = sipe_purple_login; protocol_class->close = sipe_purple_close; protocol_class->status_types = sipe_purple_status_types; protocol_class->list_icon = sipe_purple_list_icon; } static void sipe_protocol_class_finalize(SIPE_UNUSED_PARAMETER SipeProtocolClass *klass) { } static void sipe_protocol_init(SipeProtocol *self) { PurpleProtocol *protocol = PURPLE_PROTOCOL(self); sipe_core_init(LOCALEDIR); protocol->id = SIPE_PURPLE_PLUGIN_ID; protocol->name = SIPE_PURPLE_PLUGIN_NAME; protocol->options = SIPE_PURPLE_PROTOCOL_OPTIONS; protocol->user_splits = g_list_append(NULL, sipe_purple_user_split()); protocol->account_options = sipe_purple_account_options(); } static GList * get_actions(SIPE_UNUSED_PARAMETER PurpleConnection *gc) { return sipe_purple_actions(); } static void sipe_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface, SIPE_UNUSED_PARAMETER void *unused) { client_iface->get_actions = get_actions; client_iface->status_text = sipe_purple_status_text; client_iface->tooltip_text = sipe_purple_tooltip_text; client_iface->blist_node_menu = sipe_purple_blist_node_menu; client_iface->convo_closed = sipe_purple_convo_closed; client_iface->normalize = purple_normalize_nocase; client_iface->get_account_text_table = sipe_purple_get_account_text_table; } static void sipe_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface, SIPE_UNUSED_PARAMETER void *unused) { server_iface->get_info = sipe_purple_get_info; server_iface->set_status = sipe_purple_set_status; server_iface->set_idle = sipe_purple_set_idle; server_iface->add_buddy = sipe_purple_add_buddy; server_iface->remove_buddy = sipe_purple_remove_buddy; server_iface->alias_buddy = sipe_purple_alias_buddy; server_iface->group_buddy = sipe_purple_group_buddy; server_iface->rename_group = sipe_purple_group_rename; server_iface->remove_group = sipe_purple_group_remove; } static int send_im(PurpleConnection *gc, PurpleMessage *msg) { sipe_core_im_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC, purple_message_get_recipient(msg), purple_message_get_contents(msg)); return 1; } static void sipe_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface, SIPE_UNUSED_PARAMETER void *unused) { im_iface->send = send_im; im_iface->send_typing = sipe_purple_send_typing; } static void sipe_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface, SIPE_UNUSED_PARAMETER void *unused) { chat_iface->info = sipe_purple_chat_info; chat_iface->info_defaults = sipe_purple_chat_info_defaults; chat_iface->join = sipe_purple_chat_join; chat_iface->invite = sipe_purple_chat_invite; chat_iface->leave = sipe_purple_chat_leave; chat_iface->send = sipe_purple_chat_send; } static void sipe_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface, SIPE_UNUSED_PARAMETER void *unused) { privacy_iface->add_permit = sipe_purple_add_permit; privacy_iface->add_deny = sipe_purple_add_deny; privacy_iface->rem_permit = sipe_purple_add_deny; privacy_iface->rem_deny = sipe_purple_add_permit; } static void sipe_protocol_xfer_iface_init(PurpleProtocolXferInterface *xfer_iface, SIPE_UNUSED_PARAMETER void *unused) { xfer_iface->send_file = sipe_purple_ft_send_file; } static void sipe_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *roomlist_iface, SIPE_UNUSED_PARAMETER void *unused) { roomlist_iface->get_list = sipe_purple_roomlist_get_list; roomlist_iface->cancel = sipe_purple_roomlist_cancel; } #ifdef HAVE_VV static void sipe_protocol_media_iface_init(PurpleProtocolMediaInterface *media_iface, SIPE_UNUSED_PARAMETER void *unused) { media_iface->initiate_session = sipe_purple_initiate_media; media_iface->get_caps = sipe_purple_get_media_caps; } #endif /* HAVE_VV */ G_DEFINE_DYNAMIC_TYPE_EXTENDED( SipeProtocol, sipe_protocol, PURPLE_TYPE_PROTOCOL, 0, G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT, sipe_protocol_client_iface_init) G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER, sipe_protocol_server_iface_init) G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM, sipe_protocol_im_iface_init) G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT, sipe_protocol_chat_iface_init) G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY, sipe_protocol_privacy_iface_init) G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER, sipe_protocol_xfer_iface_init) G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST, sipe_protocol_roomlist_iface_init) #ifdef HAVE_VV G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_MEDIA, sipe_protocol_media_iface_init) #endif /* HAVE_VV */ ) static PurplePluginInfo * plugin_query(SIPE_UNUSED_PARAMETER GError **error) { gchar ** authors = g_strsplit(SIPE_PURPLE_PLUGIN_AUTHORS, ", ", -1); PurplePluginInfo *info = purple_plugin_info_new( "id", SIPE_PURPLE_PLUGIN_ID, "name", SIPE_PURPLE_PLUGIN_NAME, "version", PACKAGE_VERSION, "category", "Protocol", "summary", SIPE_PURPLE_PLUGIN_SUMMARY, "description", SIPE_PURPLE_PLUGIN_DESCRIPTION, "authors", authors, "website", PACKAGE_URL, "abi-version", PURPLE_ABI_VERSION, "flags", PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, NULL); g_strfreev(authors); return info; } static PurpleProtocol *sipe_protocol = NULL; static gboolean plugin_load(PurplePlugin *plugin, GError **error) { sipe_protocol_register_type(G_TYPE_MODULE(plugin)); sipe_purple_xfer_register(G_TYPE_MODULE(plugin)); sipe_protocol = purple_protocols_add(SIPE_TYPE_PROTOCOL, error); if (!sipe_protocol) { return FALSE; } if (!sipe_purple_plugin_load(plugin)) { return FALSE; } return TRUE; } static gboolean plugin_unload(PurplePlugin *plugin, GError **error) { sipe_purple_plugin_unload(plugin); if (!purple_protocols_remove(sipe_protocol, error)) { return FALSE; } return TRUE; } PURPLE_PLUGIN_INIT(sipe, plugin_query, plugin_load, plugin_unload); /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-private.h ================================================ /** * @file purple-private.h * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "version.h" #define SIPE_PURPLE_PLUGIN_ID "prpl-sipe" #define SIPE_PURPLE_PLUGIN_NAME "Office Communicator" #define SIPE_PURPLE_PLUGIN_SUMMARY \ "Microsoft Office Communicator Protocol Plugin" #define SIPE_PURPLE_PLUGIN_DESCRIPTION \ "A plugin for the extended SIP/SIMPLE protocol used by " \ "Microsoft Live/Office Communications/Lync Server (LCS2005/OCS2007+)" #define SIPE_PURPLE_PLUGIN_AUTHORS \ "Stefan Becker , " \ "Jakub Adam , " \ "Anibal Avelar (retired), " \ "pier11 (retired), " \ "Gabriel Burt (retired)" #define SIPE_PURPLE_PROTOCOL_OPTIONS OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL #if PURPLE_VERSION_CHECK(3,0,0) #include "conversationtypes.h" /* PurpleIMTypingState */ #include "plugins.h" /* PurplePlugin */ /* Forward declarations */ struct _PurpleProtocolAction; struct _PurpleProtocolXfer; #else #include "conversation.h" /* PurpleTypingState */ #include "plugin.h" /* PurplePlugin */ #define PurpleIMTypingState PurpleTypingState #define _PurpleProtocolAction _PurplePluginAction /* Forward declarations */ struct _PurplePluginAction; #endif /* Forward declarations */ struct sipe_chat_session; struct sipe_core_public; struct _PurpleAccount; struct _PurpleBlistNode; struct _PurpleBuddy; struct _PurpleChat; struct _PurpleConnection; struct _PurpleConversation; struct _PurpleGroup; struct _PurpleMessage; struct _PurpleNotifyUserInfo; struct _PurpleRoomlist; struct _PurpleStatus; struct _PurpleXfer; #ifndef _PurpleMessageFlags #define _PurpleMessageFlags int #endif struct sipe_backend_private { struct sipe_core_public *public; struct _PurpleConnection *gc; struct _PurpleAccount *account; struct _PurpleRoomlist *roomlist; /* see sipe_backend_chat_create() */ struct sipe_chat_session *adium_chat_session; GHashTable *roomlist_map; /* name -> uri */ GList *rejoin_chats; GSList *transports; GSList *dns_queries; /* work around broken libpurple idle notification */ gchar *deferred_status_note; guint deferred_status_activity; guint deferred_status_timeout; /* flags */ gboolean status_changed_by_core; /* status changed by core */ gboolean user_is_not_idle; /* user came back online */ }; struct sipe_backend_fd { int fd; }; /* Status attributes */ #define SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE "message" const gchar *sipe_purple_activity_to_token(guint type); guint sipe_purple_token_to_activity(const gchar *token); /* DNS queries */ void sipe_purple_dns_query_cancel_all(struct sipe_backend_private *purple_private); /** * Initiates outgoing file transfer, sending @c file to remote peer identified * by @c who. * * @param gc a PurpleConnection * @param who string identifying receiver of the file * @param file local file system path of the file to send */ void sipe_purple_ft_send_file( #if PURPLE_VERSION_CHECK(3,0,0) struct _PurpleProtocolXfer *xfer, #endif struct _PurpleConnection *gc, const char *who, const char *file); void sipe_purple_xfer_register(GTypeModule *module); /* libpurple chat callbacks */ #define SIPE_PURPLE_COMPONENT_KEY_CONVERSATION "_conv" struct sipe_chat_session *sipe_purple_chat_get_session(struct _PurpleConversation *conv); void sipe_purple_chat_setup_rejoin(struct sipe_backend_private *purple_private); void sipe_purple_chat_destroy_rejoin(struct sipe_backend_private *purple_private); void sipe_purple_chat_invite(struct _PurpleConnection *gc, int id, const char *message, const char *name); void sipe_purple_chat_leave(struct _PurpleConnection *gc, int id); int sipe_purple_chat_send(struct _PurpleConnection *gc, int id, #if PURPLE_VERSION_CHECK(3,0,0) struct _PurpleMessage *msg); #else const char *what, _PurpleMessageFlags flags); #endif GList *sipe_purple_chat_menu(struct _PurpleChat *chat); /* libpurple chat room callbacks */ GList *sipe_purple_chat_info(struct _PurpleConnection *gc); GHashTable *sipe_purple_chat_info_defaults(struct _PurpleConnection *gc, const char *chat_name); void sipe_purple_chat_join(struct _PurpleConnection *gc, GHashTable *data); struct _PurpleRoomlist *sipe_purple_roomlist_get_list(struct _PurpleConnection *gc); void sipe_purple_roomlist_cancel(struct _PurpleRoomlist *list); /* libpurple buddy callbacks */ #ifdef PURPLE_VERSION_CHECK void sipe_purple_add_buddy(struct _PurpleConnection *gc, struct _PurpleBuddy *buddy, struct _PurpleGroup *group #if PURPLE_VERSION_CHECK(3,0,0) , const gchar *message #endif ); #endif void sipe_purple_remove_buddy(struct _PurpleConnection *gc, struct _PurpleBuddy *buddy, struct _PurpleGroup *group); void sipe_purple_group_buddy(struct _PurpleConnection *gc, const char *who, const char *old_group_name, const char *new_group_name); GList *sipe_purple_buddy_menu(struct _PurpleBuddy *buddy); /* libpurple search callbacks */ void sipe_purple_show_find_contact(struct _PurpleProtocolAction *action); /* libpurple status callbacks */ void sipe_purple_set_status(struct _PurpleAccount *account, struct _PurpleStatus *status); void sipe_purple_set_idle(struct _PurpleConnection *gc, int interval); /* media */ void capture_pipeline(const gchar *label); /* transport */ void sipe_purple_transport_close_all(struct sipe_backend_private *purple_private); /* Convenience macros */ #define PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_connection_get_protocol_data(purple_account_get_connection(account))) #define PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_connection_get_protocol_data(purple_account_get_connection(purple_buddy_get_account(buddy)))) #define PURPLE_GC_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_connection_get_protocol_data(gc)) /* Protocol common functions */ gboolean sipe_purple_plugin_load(PurplePlugin *plugin); gboolean sipe_purple_plugin_unload(PurplePlugin *plugin); gpointer sipe_purple_user_split(void); GList *sipe_purple_account_options(void); void sipe_purple_republish_calendar(struct _PurpleAccount *account); void sipe_purple_reset_status(struct _PurpleAccount *account); GList *sipe_purple_actions(void); gchar *sipe_purple_status_text(struct _PurpleBuddy *buddy); void sipe_purple_tooltip_text(struct _PurpleBuddy *buddy, struct _PurpleNotifyUserInfo *user_info, gboolean full); GList *sipe_purple_blist_node_menu(struct _PurpleBlistNode *node); void sipe_purple_convo_closed(struct _PurpleConnection *gc, const char *who); GHashTable *sipe_purple_get_account_text_table(struct _PurpleAccount *account); void sipe_purple_login(struct _PurpleAccount *account); void sipe_purple_close(struct _PurpleConnection *gc); GList *sipe_purple_status_types(struct _PurpleAccount *account); const char *sipe_purple_list_icon(struct _PurpleAccount *account, struct _PurpleBuddy *buddy); void sipe_purple_get_info(struct _PurpleConnection *gc, const char *who); void sipe_purple_alias_buddy(struct _PurpleConnection *gc, const char *name, const char *alias); void sipe_purple_group_rename(struct _PurpleConnection *gc, const char *old_name, struct _PurpleGroup *group, GList *moved_buddies); void sipe_purple_group_remove(struct _PurpleConnection *gc, struct _PurpleGroup *group); unsigned int sipe_purple_send_typing(struct _PurpleConnection *gc, const char *who, PurpleIMTypingState state); void sipe_purple_add_permit(struct _PurpleConnection *gc, const char *name); void sipe_purple_add_deny(struct _PurpleConnection *gc, const char *name); gboolean sipe_purple_initiate_media(struct _PurpleAccount *account, const char *who, PurpleMediaSessionType type); PurpleMediaCaps sipe_purple_get_media_caps(struct _PurpleAccount *account, const char *who); /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-schedule.c ================================================ /** * @file sipe-schedule.c * * pidgin-sipe * * Copyright (C) 2010-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "version.h" #if PURPLE_VERSION_CHECK(3,0,0) #else #include "eventloop.h" #define g_timeout_add(t, f, d) purple_timeout_add(t, f, d) #define g_timeout_add_seconds(t, f, d) purple_timeout_add_seconds(t, f, d) #define g_source_remove(t) purple_timeout_remove(t) #endif #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" struct purple_schedule { gpointer core_data; guint timeout_handler; }; static gboolean purple_timeout_execute(gpointer data) { gpointer core_data = ((struct purple_schedule *) data)->core_data; g_free(data); sipe_core_schedule_execute(core_data); return(FALSE); } gpointer sipe_backend_schedule_seconds(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, guint timeout, gpointer data) { struct purple_schedule *schedule = g_malloc(sizeof(struct purple_schedule)); schedule->core_data = data; schedule->timeout_handler = g_timeout_add_seconds(timeout, purple_timeout_execute, schedule); return(schedule); } gpointer sipe_backend_schedule_mseconds(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, guint timeout, gpointer data) { struct purple_schedule *schedule = g_malloc(sizeof(struct purple_schedule)); schedule->core_data = data; schedule->timeout_handler = g_timeout_add(timeout, purple_timeout_execute, schedule); return(schedule); } void sipe_backend_schedule_cancel(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, gpointer data) { struct purple_schedule *schedule = data; g_source_remove(schedule->timeout_handler); g_free(schedule); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-search.c ================================================ /** * @file purple-search.c * * pidgin-sipe * * Copyright (C) 2011-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "notify.h" #include "request.h" #include "version.h" #if PURPLE_VERSION_CHECK(3,0,0) #include "action.h" #include "conversations.h" #endif #include "sipe-common.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-nls.h" #include "purple-private.h" void sipe_backend_search_failed(struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_search_token *token, const gchar *msg) { sipe_backend_notify_error(sipe_public, msg, NULL); } struct sipe_backend_search_results *sipe_backend_search_results_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_search_token *token) { PurpleNotifySearchResults *results = purple_notify_searchresults_new(); if (results) { PurpleNotifySearchColumn *column; column = purple_notify_searchresults_column_new(_("User name")); purple_notify_searchresults_column_add(results, column); column = purple_notify_searchresults_column_new(_("Name")); purple_notify_searchresults_column_add(results, column); column = purple_notify_searchresults_column_new(_("Company")); purple_notify_searchresults_column_add(results, column); column = purple_notify_searchresults_column_new(_("Country")); purple_notify_searchresults_column_add(results, column); column = purple_notify_searchresults_column_new(_("Email")); purple_notify_searchresults_column_add(results, column); } return((struct sipe_backend_search_results *)results); } void sipe_backend_search_results_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_search_results *results, const gchar *uri, const gchar *name, const gchar *company, const gchar *country, const gchar *email) { GList *row = NULL; row = g_list_append(row, g_strdup(uri)); row = g_list_append(row, g_strdup(name)); row = g_list_append(row, g_strdup(company)); row = g_list_append(row, g_strdup(country)); row = g_list_append(row, g_strdup(email)); purple_notify_searchresults_row_add((PurpleNotifySearchResults *) results, row); } static void searchresults_im_buddy(PurpleConnection *gc, GList *row, SIPE_UNUSED_PARAMETER void *user_data) { PurpleAccount *acct = purple_connection_get_account(gc); gchar *id = sip_uri_from_name(g_list_nth_data(row, 0)); #if PURPLE_VERSION_CHECK(3,0,0) PurpleIMConversation *conv = purple_conversations_find_im_with_account(id, acct); if (conv == NULL) conv = purple_im_conversation_new(acct, id); #else PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, id, acct); if (conv == NULL) conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, id); #endif g_free(id); purple_conversation_present((PurpleConversation *) conv); } static void searchresults_add_buddy(PurpleConnection *gc, GList *row, SIPE_UNUSED_PARAMETER void *user_data) { purple_blist_request_add_buddy(purple_connection_get_account(gc), g_list_nth_data(row, 0), _("Other Contacts"), NULL); } void sipe_backend_search_results_finalize(struct sipe_core_public *sipe_public, struct sipe_backend_search_results *results, const gchar *description, SIPE_UNUSED_PARAMETER gboolean more) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleNotifySearchResults *r = (PurpleNotifySearchResults *) results; purple_notify_searchresults_button_add(r, PURPLE_NOTIFY_BUTTON_IM, searchresults_im_buddy); purple_notify_searchresults_button_add(r, PURPLE_NOTIFY_BUTTON_ADD, searchresults_add_buddy); purple_notify_searchresults(purple_private->gc, NULL, NULL, description, r, NULL, NULL); } static void sipe_purple_find_contact_cb(PurpleConnection *gc, PurpleRequestFields *fields) { GList *entries = purple_request_field_group_get_fields(purple_request_fields_get_groups(fields)->data); const gchar *given_name = NULL; const gchar *surname = NULL; const gchar *email = NULL; const gchar *sipid = NULL; const gchar *company = NULL; const gchar *country = NULL; while (entries) { PurpleRequestField *field = entries->data; const char *id = purple_request_field_get_id(field); const char *value = purple_request_field_string_get_value(field); SIPE_DEBUG_INFO("sipe_purple_find_contact_cb: %s = '%s'", id, value ? value : ""); if (value && strlen(value)) { if (strcmp(id, "given") == 0) { given_name = value; } else if (strcmp(id, "surname") == 0) { surname = value; } else if (strcmp(id, "email") == 0) { email = value; } else if (strcmp(id, "sipid") == 0) { sipid = value; } else if (strcmp(id, "company") == 0) { company = value; } else if (strcmp(id, "country") == 0) { country = value; } } entries = g_list_next(entries); }; sipe_core_buddy_search(PURPLE_GC_TO_SIPE_CORE_PUBLIC, NULL, given_name, surname, email, sipid, company, country); } #if PURPLE_VERSION_CHECK(3,0,0) void sipe_purple_show_find_contact(PurpleProtocolAction *action) { PurpleConnection *gc = action->connection; #else void sipe_purple_show_find_contact(PurplePluginAction *action) { PurpleConnection *gc = (PurpleConnection *) action->context; #endif PurpleRequestFields *fields; PurpleRequestFieldGroup *group; PurpleRequestField *field; fields = purple_request_fields_new(); group = purple_request_field_group_new(NULL); purple_request_fields_add_group(fields, group); field = purple_request_field_string_new("given", _("First name"), NULL, FALSE); purple_request_field_group_add_field(group, field); field = purple_request_field_string_new("surname", _("Last name"), NULL, FALSE); purple_request_field_group_add_field(group, field); field = purple_request_field_string_new("email", _("Email"), NULL, FALSE); purple_request_field_group_add_field(group, field); field = purple_request_field_string_new("sipid", _("SIP ID"), NULL, FALSE); purple_request_field_group_add_field(group, field); field = purple_request_field_string_new("company", _("Company"), NULL, FALSE); purple_request_field_group_add_field(group, field); field = purple_request_field_string_new("country", _("Country"), NULL, FALSE); purple_request_field_group_add_field(group, field); purple_request_fields(gc, _("Search"), _("Search for a contact"), _("Enter the information for the person you wish to find. Empty fields will be ignored."), fields, _("_Search"), G_CALLBACK(sipe_purple_find_contact_cb), _("_Cancel"), NULL, #if PURPLE_VERSION_CHECK(3,0,0) purple_request_cpar_from_connection(gc), #else purple_connection_get_account(gc), NULL, NULL, #endif gc); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-setting.c ================================================ /** * @file purple-setting.c * * pidgin-sipe * * Copyright (C) 2010-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "account.h" #include "connection.h" #include "sipe-backend.h" #include "sipe-core.h" #include "purple-private.h" /** * Map sipe_setting values to purple account setting keys * * This needs to be kept in sync with * * api/sipe-backend.h * purple-plugin-common.c:sipe_purple_account_options() */ static const gchar * const setting_name[SIPE_SETTING_LAST] = { "email_url", /* SIPE_SETTING_EMAIL_URL */ "email_login", /* SIPE_SETTING_EMAIL_LOGIN */ "email_password", /* SIPE_SETTING_EMAIL_PASSWORD */ "groupchat_user", /* SIPE_SETTING_GROUPCHAT_USER */ "rdp_client", /* SIPE_SETTING_RDP_CLIENT */ "useragent" /* SIPE_SETTING_USER_AGENT */ }; const gchar *sipe_backend_setting(struct sipe_core_public *sipe_public, sipe_setting type) { return(purple_account_get_string(purple_connection_get_account(sipe_public->backend_private->gc), setting_name[type], NULL)); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-status.c ================================================ /** * @file purple-status.c * * pidgin-sipe * * Copyright (C) 2011-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "account.h" #include "savedstatuses.h" #include "version.h" #if !PURPLE_VERSION_CHECK(3,0,0) #include "eventloop.h" #define g_timeout_add_seconds(t, f, d) purple_timeout_add_seconds(t, f, d) #define g_source_remove(t) purple_timeout_remove(t) #endif #include "sipe-backend.h" #include "sipe-core.h" #include "purple-private.h" guint sipe_backend_status(struct sipe_core_public *sipe_public) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleStatus *status = purple_account_get_active_status(purple_private->account); if (!status) return(SIPE_ACTIVITY_UNSET); return(sipe_purple_token_to_activity(purple_status_get_id(status))); } gboolean sipe_backend_status_changed(struct sipe_core_public *sipe_public, guint activity, const gchar *message) { gboolean result = FALSE; if ((activity == SIPE_ACTIVITY_AWAY) && purple_savedstatus_is_idleaway()) { SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_status_changed: user is already idle-away"); } else { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleStatus *status = purple_account_get_active_status(purple_private->account); const gchar *status_id = sipe_purple_activity_to_token(activity); result = !(g_str_equal(status_id, purple_status_get_id(status)) && sipe_strequal(message, purple_status_get_attr_string(status, SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE))); } return(result); } /** * This method motivates Purple's Host (e.g. Pidgin) to update its UI * by using standard Purple's means of signals and saved statuses. * * Thus all UI elements get updated: Status Button with Note, docklet. * This is ablolutely important as both our status and note can come * inbound (roaming) or be updated programmatically (e.g. based on our * calendar data). */ void sipe_backend_status_and_note(struct sipe_core_public *sipe_public, guint activity, const gchar *message) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleAccount *account = purple_private->account; const gchar *status_id = sipe_purple_activity_to_token(activity); PurpleSavedStatus *saved_status; const PurpleStatusType *acct_status_type = purple_status_type_find_with_id(purple_account_get_status_types(account), status_id); PurpleStatusPrimitive primitive = purple_status_type_get_primitive(acct_status_type); /* code adapted from: pidgin/gtkstatusbox.c */ saved_status = purple_savedstatus_find_transient_by_type_and_message(primitive, message); if (saved_status) { purple_savedstatus_set_substatus(saved_status, account, acct_status_type, message); } else { /* This type+message is unique then create a new transient saved status */ GList *entry; GList *active_accts = purple_accounts_get_all_active(); SIPE_DEBUG_INFO("sipe_backend_status_and_note: creating new saved status %s '%s'", status_id, message ? message : "(null)"); saved_status = purple_savedstatus_new(NULL, primitive); purple_savedstatus_set_message(saved_status, message); for (entry = active_accts; entry != NULL; entry = entry->next) purple_savedstatus_set_substatus(saved_status, (PurpleAccount *) entry->data, acct_status_type, message); g_list_free(active_accts); } /* Set the status for each account */ purple_private->status_changed_by_core = TRUE; purple_savedstatus_activate(saved_status); } /** * Work around broken libpurple idle notification * * (1) user changes the status * sipe_purple_set_status() * -> user changed state * * (2) client detects that user is idle * sipe_purple_set_status() [sometimes omitted?!?!?] * sipe_purple_set_idle( != 0 ) * -> machine changed state * * (3) client detects that user is no longer idle * sipe_purple_set_idle(0) * sipe_purple_set_status() * -> user changed state * * (4) core sends a status change * sipe_backend_status_and_note() * purple_savedstatus_activate() * sipe_purple_set_status() * -> status change must be ignored * * Cases (1) and (2) can only be differentiated by deferring the update. */ static void sipe_purple_status_deferred_update(struct sipe_backend_private *purple_private, gboolean changed_by_user) { gchar *note = purple_private->deferred_status_note; purple_private->deferred_status_note = NULL; purple_private->deferred_status_timeout = 0; sipe_core_status_set(purple_private->public, changed_by_user, purple_private->deferred_status_activity, note); g_free(note); } static gboolean sipe_purple_status_timeout(gpointer data) { /* timeout expired -> no idle indication -> state changed by user */ sipe_purple_status_deferred_update(data, TRUE); return(FALSE); } void sipe_purple_set_status(PurpleAccount *account, PurpleStatus *status) { if (purple_account_get_connection(account) && purple_status_is_active(status)) { struct sipe_core_public *sipe_public = PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC; struct sipe_backend_private *purple_private = sipe_public->backend_private; const gchar *status_id = purple_status_get_id(status); guint activity = sipe_purple_token_to_activity(status_id); const gchar *note = purple_status_get_attr_string(status, SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE); SIPE_DEBUG_INFO("sipe_purple_set_status[CB]: '%s'", status_id); if (purple_private->status_changed_by_core) { SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_set_status[CB]: triggered by core - ignoring"); } else if (purple_private->user_is_not_idle) { sipe_core_status_set(sipe_public, TRUE, activity, note); } else { if (purple_private->deferred_status_timeout) g_source_remove(purple_private->deferred_status_timeout); g_free(purple_private->deferred_status_note); SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_set_status[CB]: defer status update"); purple_private->deferred_status_note = g_strdup(note); purple_private->deferred_status_activity = activity; purple_private->deferred_status_timeout = g_timeout_add_seconds(1, sipe_purple_status_timeout, purple_private); } /* reset flags */ purple_private->status_changed_by_core = FALSE; purple_private->user_is_not_idle = FALSE; } } void sipe_purple_set_idle(PurpleConnection *gc, int interval) { if (gc) { struct sipe_core_public *sipe_public = PURPLE_GC_TO_SIPE_CORE_PUBLIC; struct sipe_backend_private *purple_private = sipe_public->backend_private; purple_private->user_is_not_idle = interval == 0; SIPE_DEBUG_INFO("sipe_purple_set_idle[CB]: user is %sidle", purple_private->user_is_not_idle ? "not " : ""); if (!purple_private->user_is_not_idle) { /* timeout not expired -> state changed by machine */ if (purple_private->deferred_status_timeout) g_source_remove(purple_private->deferred_status_timeout); sipe_purple_status_deferred_update(purple_private, FALSE); } } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-transport.c ================================================ /** * @file purple-transport.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "sipe-common.h" #include "connection.h" #include "eventloop.h" #include "network.h" #include "proxy.h" #include "sslconn.h" #include "version.h" #if PURPLE_VERSION_CHECK(3,0,0) #include "circularbuffer.h" #else #include "circbuffer.h" #define PurpleCircularBuffer PurpleCircBuffer #define purple_circular_buffer_append(b, s, n) purple_circ_buffer_append(b, s, n) #define purple_circular_buffer_get_max_read(b) purple_circ_buffer_get_max_read(b) #define purple_circular_buffer_get_output(b) b->outptr #define purple_circular_buffer_mark_read(b, s) purple_circ_buffer_mark_read(b, s) #define purple_circular_buffer_new(s) purple_circ_buffer_new(s) #endif #ifdef _WIN32 /* wrappers for write() & friends for socket handling */ #include "win32/win32dep.h" #else #include #include #include #include #endif #include "purple-private.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-nls.h" struct sipe_transport_purple { /* public part shared with core */ struct sipe_transport_connection public; /* purple private part */ struct sipe_backend_private *purple_private; transport_connected_cb *connected; transport_input_cb *input; transport_error_cb *error; PurpleSslConnection *gsc; PurpleProxyConnectData *proxy; PurpleCircularBuffer *transmit_buffer; guint transmit_handler; guint receive_handler; int socket; gboolean is_valid; gchar ip_address[INET6_ADDRSTRLEN]; /* OK for IPv4 too */ }; #define PURPLE_TRANSPORT ((struct sipe_transport_purple *) conn) #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport) #define BUFFER_SIZE_INCREMENT 4096 #define FLUSH_MAX_RETRIES 5 /***************************************************************************** * * Common transport handling * *****************************************************************************/ static void transport_common_input(struct sipe_transport_purple *transport) { struct sipe_transport_connection *conn = SIPE_TRANSPORT_CONNECTION; gssize readlen, len; gboolean firstread = TRUE; /* Read all available data from the connection */ do { /* Increase input buffer size as needed */ if (conn->buffer_length < conn->buffer_used + BUFFER_SIZE_INCREMENT) { conn->buffer_length += BUFFER_SIZE_INCREMENT; conn->buffer = g_realloc(conn->buffer, conn->buffer_length); SIPE_DEBUG_INFO("transport_input_common: new buffer length %" G_GSIZE_FORMAT, conn->buffer_length); } /* Try to read as much as there is space left in the buffer */ /* minus 1 for the string terminator */ readlen = conn->buffer_length - conn->buffer_used - 1; len = transport->gsc ? (gssize) purple_ssl_read(transport->gsc, conn->buffer + conn->buffer_used, readlen) : read(transport->socket, conn->buffer + conn->buffer_used, readlen); if (len < 0 && errno == EAGAIN) { /* * Work around rare SSL read deadlock situation * * When we went around the loop then the previous call * to purple_ssl_read() filled the buffer exactly. If * it also happened to read all pending bytes then it * seems that the next call returns len < 0 with EAGAIN * instead of the expected len == 0. */ if (transport->gsc && !firstread) { SIPE_DEBUG_INFO("transport_input_common: SSL read deadlock detected - assuming message is %" G_GSIZE_FORMAT " bytes long", conn->buffer_used); break; } /* Try again later */ return; } else if (len < 0) { SIPE_DEBUG_ERROR("Read error: %s (%d)", strerror(errno), errno); transport->error(SIPE_TRANSPORT_CONNECTION, _("Read error")); return; } else if (firstread && (len == 0)) { SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected"); transport->error(SIPE_TRANSPORT_CONNECTION, _("Server has disconnected")); return; } conn->buffer_used += len; firstread = FALSE; /* Equivalence indicates that there is possibly more data to read */ } while (len == readlen); conn->buffer[conn->buffer_used] = '\0'; transport->input(conn); } static void transport_ssl_input(gpointer data, SIPE_UNUSED_PARAMETER PurpleSslConnection *gsc, SIPE_UNUSED_PARAMETER PurpleInputCondition cond) { struct sipe_transport_purple *transport = data; /* Ignore spurious "SSL input" events after disconnect */ if (transport->is_valid) transport_common_input(transport); } static void transport_tcp_input(gpointer data, SIPE_UNUSED_PARAMETER gint source, SIPE_UNUSED_PARAMETER PurpleInputCondition cond) { struct sipe_transport_purple *transport = data; /* Ignore spurious "TCP input" events after disconnect */ if (transport->is_valid) transport_common_input(transport); } static void transport_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection *gsc, PurpleSslErrorType error, gpointer data) { struct sipe_transport_purple *transport = data; /* Ignore spurious "SSL connect failure" events after disconnect */ if (transport->is_valid) { transport->socket = -1; transport->gsc = NULL; transport->error(SIPE_TRANSPORT_CONNECTION, purple_ssl_strerror(error)); sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION); } } static void transport_get_socket_info(struct sipe_transport_purple *transport) { /* * NOTE: getsockname() on Windows seems to be picky about the buffer * location. Use an allocated buffer instead of one on the stack, */ union socket_info { struct sockaddr sa; /* to avoid casts */ struct sockaddr_in sa_in; /* IPv4 variant */ struct sockaddr_in6 sa_in6; /* IPv6 variant */ struct sockaddr_storage unused; /* for alignment */ } *si = g_new(union socket_info, 1); socklen_t si_len = sizeof(*si); const void *addr; guint port; /* * libpurple only returns IPv4 addresses * * purple_network_get_my_ip(transport->socket); * * libpurple returns port 0 on Windows for IPv6 sockets * * purple_network_get_port_from_fd(transport->socket); * * Replace them with our own code. */ if (getsockname(transport->socket, &si->sa, &si_len) < 0) { SIPE_DEBUG_ERROR("transport_get_socket_info: %s (%d)", strerror(errno), errno); /* make sure socket address family is initialized */ si->sa.sa_family = AF_UNSPEC; } switch (si->sa.sa_family) { case AF_INET: port = si->sa_in.sin_port; addr = &si->sa_in.sin_addr; break; case AF_INET6: port = si->sa_in6.sin6_port; addr = &si->sa_in6.sin6_addr; break; default: port = htons(0); /* error fallback */ addr = NULL; break; } transport->public.client_port = ntohs(port); if ((addr == NULL) || (inet_ntop(si->sa.sa_family, addr, transport->ip_address, sizeof(transport->ip_address)) == NULL)) { /* error fallback */ strcpy(transport->ip_address, "0.0.0.0"); } g_free(si); SIPE_DEBUG_INFO("transport_get_socket_info: %s:%d(%p)", transport->ip_address, transport->public.client_port, transport); } static void transport_common_connected(struct sipe_transport_purple *transport, int fd) { /* Ignore spurious "connected" events after disconnect */ if (transport->is_valid) { transport->proxy = NULL; if (fd < 0) { transport->error(SIPE_TRANSPORT_CONNECTION, _("Could not connect")); sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION); return; } transport->socket = fd; transport_get_socket_info(transport); if (transport->gsc) { purple_ssl_input_add(transport->gsc, transport_ssl_input, transport); } else { transport->receive_handler = purple_input_add(fd, PURPLE_INPUT_READ, transport_tcp_input, transport); } transport->connected(SIPE_TRANSPORT_CONNECTION); } } static void transport_ssl_connected(gpointer data, PurpleSslConnection *gsc, SIPE_UNUSED_PARAMETER PurpleInputCondition cond) { transport_common_connected(data, gsc->fd); } static void transport_tcp_connected(gpointer data, gint source, SIPE_UNUSED_PARAMETER const gchar *error_message) { transport_common_connected(data, source); } struct sipe_transport_connection * sipe_backend_transport_connect(struct sipe_core_public *sipe_public, const sipe_connect_setup *setup) { struct sipe_transport_purple *transport = g_new0(struct sipe_transport_purple, 1); struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleConnection *gc = purple_private->gc; PurpleAccount *account = purple_connection_get_account(gc); SIPE_DEBUG_INFO("transport_connect - hostname: %s port: %d", setup->server_name, setup->server_port); transport->public.type = setup->type; transport->public.user_data = setup->user_data; transport->purple_private = purple_private; transport->connected = setup->connected; transport->input = setup->input; transport->error = setup->error; transport->transmit_buffer = purple_circular_buffer_new(0); transport->is_valid = TRUE; purple_private->transports = g_slist_prepend(purple_private->transports, transport); if (setup->type == SIPE_TRANSPORT_TLS) { /* SSL case */ SIPE_DEBUG_INFO_NOFORMAT("using SSL"); if ((transport->gsc = purple_ssl_connect(account, setup->server_name, setup->server_port, transport_ssl_connected, transport_ssl_connect_failure, transport)) == NULL) { setup->error(SIPE_TRANSPORT_CONNECTION, _("Could not create SSL context")); sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION); return(NULL); } } else if (setup->type == SIPE_TRANSPORT_TCP) { /* TCP case */ SIPE_DEBUG_INFO_NOFORMAT("using TCP"); /* * NOTE: during shutdown libpurple calls * * purple_proxy_connect_cancel_with_handle(gc); * * before our cleanup code. Therefore we can't use "gc" as * handle. We are not using it for anything thus NULL is fine. */ if ((transport->proxy = purple_proxy_connect(NULL, account, setup->server_name, setup->server_port, transport_tcp_connected, transport)) == NULL) { setup->error(SIPE_TRANSPORT_CONNECTION, _("Could not create socket")); sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION); return(NULL); } } else { setup->error(SIPE_TRANSPORT_CONNECTION, "This should not happen..."); sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION); return(NULL); } return(SIPE_TRANSPORT_CONNECTION); } static gboolean transport_deferred_destroy(gpointer user_data) { /* * All pending events on transport have been processed. * Now it is safe to destroy the data structure. */ SIPE_DEBUG_INFO("transport_deferred_destroy: %p", user_data); g_free(user_data); return(FALSE); } void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn) { struct sipe_transport_purple *transport = PURPLE_TRANSPORT; struct sipe_backend_private *purple_private; if (!transport || !transport->is_valid) return; purple_private = transport->purple_private; purple_private->transports = g_slist_remove(purple_private->transports, transport); if (transport->gsc) { purple_ssl_close(transport->gsc); } else if (transport->socket > 0) { close(transport->socket); } if (transport->proxy) purple_proxy_connect_cancel(transport->proxy); if (transport->transmit_handler) purple_input_remove(transport->transmit_handler); if (transport->receive_handler) purple_input_remove(transport->receive_handler); if (transport->transmit_buffer) #if PURPLE_VERSION_CHECK(3,0,0) g_object_unref(transport->transmit_buffer); #else purple_circ_buffer_destroy(transport->transmit_buffer); #endif g_free(transport->public.buffer); /* defer deletion of transport data structure to idle callback */ transport->is_valid = FALSE; g_idle_add(transport_deferred_destroy, transport); } gchar *sipe_backend_transport_ip_address(struct sipe_transport_connection *conn) { return(g_strdup(PURPLE_TRANSPORT->ip_address)); } void sipe_purple_transport_close_all(struct sipe_backend_private *purple_private) { GSList *entry; SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_transport_close_all: entered"); while ((entry = purple_private->transports) != NULL) sipe_backend_transport_disconnect(entry->data); } /* returns a negative number on write error */ static gssize transport_write(struct sipe_transport_purple *transport) { gsize max_write; max_write = purple_circular_buffer_get_max_read(transport->transmit_buffer); if (max_write > 0) { gssize written = transport->gsc ? (gssize) purple_ssl_write(transport->gsc, purple_circular_buffer_get_output(transport->transmit_buffer), max_write) : write(transport->socket, purple_circular_buffer_get_output(transport->transmit_buffer), max_write); if (written <= 0) { if (written == 0 || errno != EAGAIN) { SIPE_DEBUG_ERROR("Write error: %s (%d)", strerror(errno), errno); transport->error(SIPE_TRANSPORT_CONNECTION, _("Write error")); } } else { purple_circular_buffer_mark_read(transport->transmit_buffer, written); } return written; } else { /* buffer is empty -> stop sending */ purple_input_remove(transport->transmit_handler); transport->transmit_handler = 0; } return 0; } static void transport_canwrite_cb(gpointer data, SIPE_UNUSED_PARAMETER gint source, SIPE_UNUSED_PARAMETER PurpleInputCondition cond) { struct sipe_transport_purple *transport = data; /* Ignore spurious "can write" events after disconnect */ if (transport->is_valid) transport_write(data); } void sipe_backend_transport_message(struct sipe_transport_connection *conn, const gchar *buffer) { struct sipe_transport_purple *transport = PURPLE_TRANSPORT; /* add packet to circular buffer */ purple_circular_buffer_append(transport->transmit_buffer, buffer, strlen(buffer)); /* initiate transmission */ if (!transport->transmit_handler) { transport->transmit_handler = purple_input_add(transport->socket, PURPLE_INPUT_WRITE, transport_canwrite_cb, transport); } } void sipe_backend_transport_flush(struct sipe_transport_connection *conn) { struct sipe_transport_purple *transport = PURPLE_TRANSPORT; gssize written; int retries = 0; while ((written = transport_write(transport))) { if (written < 0) { if (errno == EAGAIN && retries++ < FLUSH_MAX_RETRIES) { continue; } break; } retries = 0; } if (written != 0) { /* We couldn't send the whole buffer. Transport is probably * broken. */ SIPE_DEBUG_INFO("sipe_backend_transport_flush: leaving " "%" G_GSSIZE_FORMAT " unsent bytes in buffer.", purple_circular_buffer_get_max_read( transport->transmit_buffer)); } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/purple-user.c ================================================ /** * @file purple-user.c * * pidgin-sipe * * Copyright (C) 2010-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "server.h" #include "request.h" #include "purple-private.h" #if PURPLE_VERSION_CHECK(3,0,0) #else #define purple_serv_got_typing(c, n, t, s) serv_got_typing(c, n, t, s) #define purple_serv_got_typing_stopped(c, n) serv_got_typing_stopped(c, n) #define PURPLE_IM_TYPING PURPLE_TYPING #endif #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "sipe-nls.h" #define SIPE_TYPING_RECV_TIMEOUT 6 void sipe_backend_user_feedback_typing(struct sipe_core_public *sipe_public, const gchar *from) { struct sipe_backend_private *purple_private = sipe_public->backend_private; purple_serv_got_typing(purple_private->gc, from, SIPE_TYPING_RECV_TIMEOUT, PURPLE_IM_TYPING); } void sipe_backend_user_feedback_typing_stop(struct sipe_core_public *sipe_public, const gchar *from) { struct sipe_backend_private *purple_private = sipe_public->backend_private; purple_serv_got_typing_stopped(purple_private->gc, from); } static void accept_cb(gpointer key, SIPE_UNUSED_PARAMETER int choice) { sipe_core_user_ask_cb(key, TRUE); } static void decline_cb(gpointer key, SIPE_UNUSED_PARAMETER int choice) { sipe_core_user_ask_cb(key, FALSE); } void sipe_backend_user_ask(struct sipe_core_public *sipe_public, const gchar *message, const gchar *accept_label, const gchar *decline_label, gpointer key) { struct sipe_backend_private *purple_private = sipe_public->backend_private; purple_request_action(key, "Office Communicator", message, NULL, 0, #if PURPLE_VERSION_CHECK(3,0,0) purple_request_cpar_from_account(purple_private->account), #else purple_private->account, NULL, NULL, #endif key, decline_label ? 2 : 1, accept_label, (PurpleRequestActionCb) accept_cb, decline_label, (PurpleRequestActionCb) decline_cb); } void sipe_backend_user_close_ask(gpointer key) { purple_request_close_with_handle(key); } static void ask_choice_accept_cb(gpointer key, PurpleRequestFields *fields) { guint choice_id = GPOINTER_TO_INT(purple_request_fields_get_choice(fields, "choice")); sipe_core_user_ask_choice_cb(key, choice_id); } static void ask_choice_cancel_cb(gpointer key) { sipe_core_user_ask_choice_cb(key, SIPE_CHOICE_CANCELLED); } void sipe_backend_user_ask_choice(struct sipe_core_public *sipe_public, const gchar *message, GSList *choices, gpointer key) { struct sipe_backend_private *purple_private = sipe_public->backend_private; PurpleRequestFields *fields = purple_request_fields_new(); PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL); PurpleRequestField *field = purple_request_field_choice_new("choice", message, 0); guint i; for (i = 0; i != g_slist_length(choices); ++i) { purple_request_field_choice_add(field, g_slist_nth_data(choices, i) #if PURPLE_VERSION_CHECK(3,0,0) , GUINT_TO_POINTER(i) #endif ); } purple_request_field_group_add_field(group, field); purple_request_fields_add_group(fields, group); purple_request_fields(key, "Microsoft Lync", NULL, NULL, fields, _("OK"), (GCallback)ask_choice_accept_cb, _("Cancel"), (GCallback)ask_choice_cancel_cb, #if PURPLE_VERSION_CHECK(3,0,0) purple_request_cpar_from_account(purple_private->account), #else purple_private->account, NULL, NULL, #endif key); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/tests-load.c ================================================ /** * @file tests-load.c * * pidgin-sipe * * Copyright (C) 2010 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include /* test that libsipe.so loads succesfully */ int main(int argc, char *argv[]) { int rc = 1; (void) argc; (void) argv; /* well if this doesn't work, what's the use of a plugin? */ if (g_module_supported()) { gchar *name = g_module_build_path(".libs", "sipe"); GModule *module = g_module_open(name, G_MODULE_BIND_LOCAL); if (module) { g_module_close(module); /* all OK */ printf("plugin loaded OK\n"); rc = 0; } else { fprintf(stderr, "plugin loaded error: %s\n", g_module_error()); } g_free(name); } return(rc); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/purple/tests.c ================================================ /** * @file tests.c * * pidgin-sipe * * Copyright (C) 2011 SIPE Project * Copyright (C) 2010 pier11 * Copyright (C) 2008 Novell, Inc. * * Implemented with reference to the follow documentation: * - http://davenport.sourceforge.net/ntlm.html * - MS-NLMP: http://msdn.microsoft.com/en-us/library/cc207842.aspx * - MS-SIP : http://msdn.microsoft.com/en-us/library/cc246115.aspx * * Please use "make tests" to build & run them! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "version.h" #if !PURPLE_VERSION_CHECK(3,0,0) #include "cipher.h" #endif #include "debug.h" #include "signals.h" #include "sipe-common.h" #include "sipe-core.h" /* stub for purple-user.c */ void sipe_core_user_ask_cb(SIPE_UNUSED_PARAMETER gpointer key, SIPE_UNUSED_PARAMETER gboolean accepted) { } gboolean sip_sec_ntlm_tests(void); int main() { /* Initialization that libpurple/core.c would normally do */ purple_signals_init(); purple_debug_init(); purple_debug_set_enabled(TRUE); #if !PURPLE_VERSION_CHECK(3,0,0) purple_ciphers_init(); #endif /* Run tests */ return(sip_sec_ntlm_tests() ? 0 : 1); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/Makefile.am ================================================ SUBDIRS = data MAINTAINERCLEANFILES = \ Makefile.in libexec_PROGRAMS = telepathy-sipe telepathy_sipe_SOURCES = \ telepathy-buddy.c \ telepathy-connection.c \ telepathy-debug.c \ telepathy-dnsquery.c \ telepathy-main.c \ telepathy-private.h \ telepathy-protocol.c \ telepathy-schedule.c \ telepathy-search.c \ telepathy-status.c \ telepathy-stubs.c \ telepathy-tls.c \ telepathy-transport.c AM_CFLAGS = $(st) telepathy_sipe_CFLAGS = \ $(DEBUG_CFLAGS) \ $(QUALITY_CFLAGS) \ $(LOCALE_CPPFLAGS) \ $(TELEPATHY_GLIB_CFLAGS) \ $(DBUS_GLIB_CFLAGS) \ $(GIO_CFLAGS) \ $(GOBJECT_CFLAGS) \ $(GLIB_CFLAGS) \ -I$(srcdir)/../api telepathy_sipe_LDADD = \ ../core/libsipe_core.la \ ../core/libsipe_core_crypto.la \ ../core/libsipe_core_libxml2.la \ ../core/libsipe_core_mime.la \ $(GMIME_LIBS) \ $(LIBXML2_LIBS) \ $(NSS_LIBS) \ $(OPENSSL_LIBS) \ $(TELEPATHY_GLIB_LIBS) \ $(DBUS_GLIB_LIBS) \ $(GIO_LIBS) \ $(GOBJECT_LIBS) \ $(GLIB_LIBS) if SIPE_HAVE_APPSHARE_SERVER telepathy_sipe_LDADD += \ $(FREERDP_SHADOW_LIBS) endif ================================================ FILE: src/telepathy/data/Makefile.am ================================================ MAINTAINERCLEANFILES = \ Makefile.in # D-Bus service file servicedir = $(datadir)/dbus-1/services service_in_files = org.freedesktop.Telepathy.ConnectionManager.sipe.service.in service_DATA = $(service_in_files:%.service.in=%.service) profiledir = $(datadir)/telepathy/profiles profile_DATA = sipe.profile EXTRA_DIST = $(service_in_files) sipe.profile CLEANFILES = $(service_DATA) # Rule to make the service file with libexecdir expanded $(service_DATA): $(service_in_files) Makefile $(AM_V_GEN)sed -e "s|\@libexecdir\@|$(libexecdir)|" $< >$@ ================================================ FILE: src/telepathy/data/org.freedesktop.Telepathy.ConnectionManager.sipe.service.in ================================================ [D-BUS Service] Name=org.freedesktop.Telepathy.ConnectionManager.sipe Exec=@libexecdir@/telepathy-sipe ================================================ FILE: src/telepathy/data/sipe.profile ================================================ Office Communicator ================================================ FILE: src/telepathy/telepathy-buddy.c ================================================ /** * @file telepathy-buddy.c * * pidgin-sipe * * Copyright (C) 2012-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "telepathy-private.h" #define SIPE_INFO_FIELD_MAX (SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY + 1) struct telepathy_buddy { const gchar *uri; /* borrowed from contact_list->buddies key */ GHashTable *groups; /* key: group name, value: buddy_entry */ /* keys are borrowed from contact_list->groups */ TpHandle handle; /* includes alias as stored on the server */ gchar *info[SIPE_INFO_FIELD_MAX]; gchar *hash; /* photo hash */ guint activity; }; struct telepathy_buddy_entry { struct telepathy_buddy *buddy; /* pointer to parent */ const gchar *group; /* borrowed from contact_list->groups key */ }; G_BEGIN_DECLS /* * Contact List class - data structures */ typedef struct _SipeContactListClass { TpBaseContactListClass parent_class; } SipeContactListClass; typedef struct _SipeContactList { TpBaseContactList parent; TpBaseConnection *connection; TpHandleRepoIface *contact_repo; TpHandleSet *contacts; GHashTable *buddies; /* key: SIP URI, value: buddy */ GHashTable *buddy_handles; /* key: TpHandle, value: buddy */ GHashTable *groups; /* key: group name, value: buddy */ gboolean initial_received; } SipeContactList; /* * Contact List class - type macros */ static GType sipe_contact_list_get_type(void) G_GNUC_CONST; #define SIPE_TYPE_CONTACT_LIST \ (sipe_contact_list_get_type()) #define SIPE_CONTACT_LIST(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONTACT_LIST, \ SipeContactList)) G_END_DECLS /* * Contact List class - type definition */ static void contact_group_list_iface_init(TpContactGroupListInterface *); G_DEFINE_TYPE_WITH_CODE(SipeContactList, sipe_contact_list, TP_TYPE_BASE_CONTACT_LIST, G_IMPLEMENT_INTERFACE (TP_TYPE_CONTACT_GROUP_LIST, contact_group_list_iface_init); ) /* * Contact List class - instance methods */ static TpHandleSet *dup_contacts(TpBaseContactList *contact_list) { SipeContactList *self = SIPE_CONTACT_LIST(contact_list); return(tp_handle_set_copy(self->contacts)); } static void dup_states(SIPE_UNUSED_PARAMETER TpBaseContactList *contact_list, SIPE_UNUSED_PARAMETER TpHandle contact, TpSubscriptionState *subscribe, TpSubscriptionState *publish, gchar **publish_request) { /* @TODO */ SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_states - NOT IMPLEMENTED"); if (subscribe) *subscribe = TP_SUBSCRIPTION_STATE_YES; if (publish) *publish = TP_SUBSCRIPTION_STATE_YES; if (publish_request) *publish_request = g_strdup(""); } static void sipe_contact_list_constructed(GObject *object) { SipeContactList *self = SIPE_CONTACT_LIST(object); void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_contact_list_parent_class)->constructed; if (chain_up) chain_up(object); g_object_get(self, "connection", &self->connection, NULL); self->contact_repo = tp_base_connection_get_handles(self->connection, TP_HANDLE_TYPE_CONTACT); self->contacts = tp_handle_set_new(self->contact_repo); } static void sipe_contact_list_dispose(GObject *object) { SipeContactList *self = SIPE_CONTACT_LIST(object); void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_contact_list_parent_class)->dispose; SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dispose"); tp_clear_pointer(&self->contacts, tp_handle_set_destroy); tp_clear_object(&self->connection); /* NOTE: the order is important due to borrowing of keys! */ tp_clear_pointer(&self->buddy_handles, g_hash_table_unref); tp_clear_pointer(&self->buddies, g_hash_table_unref); tp_clear_pointer(&self->groups, g_hash_table_unref); if (chain_up) chain_up(object); } /* * Contact List class - type implementation */ static void sipe_contact_list_class_init(SipeContactListClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); TpBaseContactListClass *base_class = TP_BASE_CONTACT_LIST_CLASS(klass); SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::class_init"); object_class->constructed = sipe_contact_list_constructed; object_class->dispose = sipe_contact_list_dispose; base_class->dup_contacts = dup_contacts; base_class->dup_states = dup_states; } static void buddy_free(gpointer data); static void sipe_contact_list_init(SIPE_UNUSED_PARAMETER SipeContactList *self) { SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::init"); self->buddies = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, buddy_free); self->buddy_handles = g_hash_table_new(g_direct_hash, g_direct_equal); self->groups = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); self->initial_received = FALSE; } /* * Contact List class - interface implementation * * Contact groups */ static GStrv dup_groups(TpBaseContactList *contact_list) { SipeContactList *self = SIPE_CONTACT_LIST(contact_list); GPtrArray *groups = g_ptr_array_sized_new( g_hash_table_size(self->groups) + 1); GHashTableIter iter; gpointer name; SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_groups called"); g_hash_table_iter_init(&iter, self->groups); while (g_hash_table_iter_next(&iter, &name, NULL)) g_ptr_array_add(groups, g_strdup(name)); g_ptr_array_add(groups, NULL); return((GStrv) g_ptr_array_free(groups, FALSE)); } static TpHandleSet *dup_group_members(TpBaseContactList *contact_list, const gchar *group_name) { SipeContactList *self = SIPE_CONTACT_LIST(contact_list); TpHandleSet *members = tp_handle_set_new(self->contact_repo); GHashTableIter iter; struct telepathy_buddy *buddy; SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_group_members called"); g_hash_table_iter_init(&iter, self->buddies); while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy)) if (g_hash_table_lookup(buddy->groups, group_name)) tp_handle_set_add(members, buddy->handle); return(members); } static GStrv dup_contact_groups(TpBaseContactList *contact_list, TpHandle contact) { SipeContactList *self = SIPE_CONTACT_LIST(contact_list); GPtrArray *groups = g_ptr_array_sized_new( g_hash_table_size(self->groups) + 1); struct telepathy_buddy *buddy = g_hash_table_lookup(self->buddy_handles, GUINT_TO_POINTER(contact)); SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_contact_groups called"); if (buddy) { GHashTableIter iter; const gchar *group_name; g_hash_table_iter_init(&iter, buddy->groups); while (g_hash_table_iter_next(&iter, (gpointer) &group_name, NULL)) g_ptr_array_add(groups, g_strdup(group_name)); } g_ptr_array_add(groups, NULL); return((GStrv) g_ptr_array_free(groups, FALSE)); } static void contact_group_list_iface_init(TpContactGroupListInterface *iface) { #define IMPLEMENT(x) iface->x = x IMPLEMENT(dup_groups); IMPLEMENT(dup_group_members); IMPLEMENT(dup_contact_groups); #undef IMPLEMENT } /* create new contact list object */ SipeContactList *sipe_telepathy_contact_list_new(TpBaseConnection *connection) { return(g_object_new(SIPE_TYPE_CONTACT_LIST, "connection", connection, NULL)); } /* get & set alias for a contact */ const gchar *sipe_telepathy_buddy_get_alias(SipeContactList *contact_list, TpHandle contact) { struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles, GUINT_TO_POINTER(contact)); if (!buddy) return(NULL); return(buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME]); } static void update_alias(struct telepathy_buddy *buddy, const gchar *alias) { if (buddy) { g_free(buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME]); buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME] = g_strdup(alias); } } void sipe_telepathy_buddy_set_alias(SipeContactList *contact_list, const guint contact, const gchar *alias) { struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles, GUINT_TO_POINTER(contact)); update_alias(buddy, alias); /* tell core about the alias change */ if (buddy) { struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(G_OBJECT(contact_list->connection)); sipe_core_group_set_alias(telepathy_private->public, buddy->uri, alias); } } /* get photo hash for a contact */ const gchar *sipe_telepathy_buddy_get_hash(struct _SipeContactList *contact_list, const guint contact) { struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles, GUINT_TO_POINTER(contact)); if (!buddy) return(NULL); return(buddy->hash); } /* get presence status for a contact */ guint sipe_telepathy_buddy_get_presence(SipeContactList *contact_list, const TpHandle contact) { struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles, GUINT_TO_POINTER(contact)); if (!buddy) return(SIPE_ACTIVITY_UNSET); return(buddy->activity); } /* @TODO: are other MIME types supported by OCS? */ static const char * mimetypes[] = { "image/jpeg", NULL }; /* @TODO: are these correct or even needed? */ #define AVATAR_MIN_PX 16 #define AVATAR_MAX_PX 256 #define AVATAR_MAX_BYTES 32768 static void get_avatar_requirements(TpSvcConnectionInterfaceAvatars *iface, DBusGMethodInvocation *context) { TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(TP_BASE_CONNECTION(iface), context); tp_svc_connection_interface_avatars_return_from_get_avatar_requirements( context, mimetypes, AVATAR_MIN_PX, AVATAR_MIN_PX, AVATAR_MAX_PX, AVATAR_MAX_PX, AVATAR_MAX_BYTES); } void sipe_telepathy_avatars_iface_init(gpointer g_iface, SIPE_UNUSED_PARAMETER gpointer iface_data) { TpSvcConnectionInterfaceAvatarsClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x( \ klass, x) IMPLEMENT(get_avatar_requirements); /* Information is provided by server: can't implement IMPLEMENT(get_avatar_tokens); IMPLEMENT(get_known_avatar_tokens); IMPLEMENT(request_avatar); IMPLEMENT(request_avatars); IMPLEMENT(set_avatar); IMPLEMENT(clear_avatar); */ #undef IMPLEMENT } static const gchar *const sipe_to_vcard_field[SIPE_INFO_FIELD_MAX] = { /* SIPE_BUDDY_INFO_DISPLAY_NAME */ "fn", /* SIPE_BUDDY_INFO_JOB_TITLE */ "title", /* SIPE_BUDDY_INFO_CITY */ NULL, /* SIPE_BUDDY_INFO_STATE */ NULL, /* SIPE_BUDDY_INFO_OFFICE */ NULL, /* SIPE_BUDDY_INFO_DEPARTMENT */ NULL, /* SIPE_BUDDY_INFO_COUNTRY */ NULL, /* SIPE_BUDDY_INFO_WORK_PHONE */ "tel", /* SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY */ NULL, /* SIPE_BUDDY_INFO_COMPANY */ "org", /* SIPE_BUDDY_INFO_EMAIL */ "email", /* SIPE_BUDDY_INFO_SITE */ NULL, /* SIPE_BUDDY_INFO_ZIPCODE */ NULL, /* SIPE_BUDDY_INFO_STREET */ NULL, /* SIPE_BUDDY_INFO_MOBILE_PHONE */ NULL, /* SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY */ NULL, /* SIPE_BUDDY_INFO_HOME_PHONE */ NULL, /* SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY */ NULL, /* SIPE_BUDDY_INFO_OTHER_PHONE */ NULL, /* SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY */ NULL, /* SIPE_BUDDY_INFO_CUSTOM1_PHONE */ NULL, /* SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY */ NULL, }; static GPtrArray *convert_contact_info(struct telepathy_buddy *buddy) { GPtrArray *info = NULL; if (buddy) { guint i; info = dbus_g_type_specialized_construct( TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST); for (i = 0; i < SIPE_INFO_FIELD_MAX; i++) { const gchar *name = sipe_to_vcard_field[i]; const gchar *value = buddy->info[i]; if (name && value) { const gchar *const field_values[2] = { value, NULL }; SIPE_DEBUG_INFO("SipeContactInfo::convert_contact_info: %s: (%2d)%s = '%s'", buddy->uri, i, name, value); g_ptr_array_add(info, tp_value_array_build(3, G_TYPE_STRING, name, G_TYPE_STRV, NULL, G_TYPE_STRV, field_values, G_TYPE_INVALID)); } } } return(info); } static void get_contact_info(TpSvcConnectionInterfaceContactInfo *iface, const GArray *contacts, DBusGMethodInvocation *context) { struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(G_OBJECT(iface)); GHashTable *buddies = telepathy_private->contact_list->buddy_handles; TpBaseConnection *base = TP_BASE_CONNECTION(iface); TpHandleRepoIface *repo = tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; GHashTable *infos; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context); SIPE_DEBUG_INFO_NOFORMAT("SipeContactInfo::get_contact_info called"); if (!tp_handles_are_valid(repo, contacts, FALSE, &error)) { dbus_g_method_return_error(context, error); g_error_free(error); return; } infos = dbus_g_type_specialized_construct(TP_HASH_TYPE_CONTACT_INFO_MAP); for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index(contacts, TpHandle, i); struct telepathy_buddy *buddy = g_hash_table_lookup(buddies, GUINT_TO_POINTER(contact)); GPtrArray *info = convert_contact_info(buddy); if (info) g_hash_table_insert(infos, GUINT_TO_POINTER(contact), info); } tp_svc_connection_interface_contact_info_return_from_get_contact_info(context, infos); g_boxed_free(TP_HASH_TYPE_CONTACT_INFO_MAP, infos); } static void request_contact_info(TpSvcConnectionInterfaceContactInfo *iface, guint contact, DBusGMethodInvocation *context) { struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(G_OBJECT(iface)); struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddy_handles, GUINT_TO_POINTER(contact)); TpBaseConnection *base = TP_BASE_CONNECTION(iface); TpHandleRepoIface *repo = tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; GPtrArray *info; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context); SIPE_DEBUG_INFO_NOFORMAT("SipeContactInfo::request_contact_info called"); if (!tp_handle_is_valid(repo, contact, &error)) { dbus_g_method_return_error(context, error); g_error_free(error); return; } info = convert_contact_info(buddy); if (!info) { dbus_g_method_return_error(context, error); if (error) g_error_free(error); return; } tp_svc_connection_interface_contact_info_return_from_request_contact_info(context, info); g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info); } void sipe_telepathy_contact_info_iface_init(gpointer g_iface, SIPE_UNUSED_PARAMETER gpointer iface_data) { TpSvcConnectionInterfaceContactInfoClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_contact_info_implement_##x( \ klass, x) IMPLEMENT(get_contact_info); /* Information is provided by the server: can't implement IMPLEMENT(refresh_contact_info); */ IMPLEMENT(request_contact_info); /* Information is provided by the server: can't implement IMPLEMENT(set_contact_info); */ #undef IMPLEMENT } GPtrArray *sipe_telepathy_contact_info_fields(void) { GPtrArray *fields = dbus_g_type_specialized_construct(TP_ARRAY_TYPE_FIELD_SPECS); guint i; SIPE_DEBUG_INFO_NOFORMAT("SipeContactInfo::contact_info_fields called"); for (i = 0; i <= SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY; i++) { const gchar *vcard_name = sipe_to_vcard_field[i]; GValueArray *va; /* unsupported field */ if (!vcard_name) continue; va = tp_value_array_build(4, G_TYPE_STRING, vcard_name, G_TYPE_STRV, NULL, G_TYPE_UINT, 0, /* tp_flags */ G_TYPE_UINT, 1, /* max_times */ G_TYPE_INVALID); g_ptr_array_add (fields, va); } return(fields); } /* TpDBusPropertiesMixinPropImpl is a broken typedef */ gpointer sipe_telepathy_contact_info_props(void) { static TpDBusPropertiesMixinPropImpl props[] = { { .name = "ContactInfoFlags", .getter_data = GUINT_TO_POINTER(0), /* @TODO .getter_data = GUINT_TO_POINTER(TP_CONTACT_INFO_FLAG_CAN_SET), */ .setter_data = NULL, }, { .name = "SupportedFields", .getter_data = NULL, .setter_data = NULL, }, { .name = NULL } }; return(props); } /* * Backend adaptor functions */ sipe_backend_buddy sipe_backend_buddy_find(struct sipe_core_public *sipe_public, const gchar *buddy_name, const gchar *group_name) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies, buddy_name); if (!buddy) return(NULL); if (group_name) { return(g_hash_table_lookup(buddy->groups, group_name)); } else { /* just return the first entry */ GHashTableIter iter; gpointer value = NULL; g_hash_table_iter_init(&iter, buddy->groups); /* make Coverity happy: as buddy != NULL this can't fail */ (void) g_hash_table_iter_next(&iter, NULL, &value); return(value); } } static GSList *buddy_add_all(struct telepathy_buddy *buddy, GSList *list) { GHashTableIter iter; struct telepathy_buddy_entry *buddy_entry; if (!buddy) return(list); g_hash_table_iter_init(&iter, buddy->groups); while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy_entry)) list = g_slist_prepend(list, buddy_entry); return(list); } GSList *sipe_backend_buddy_find_all(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const gchar *buddy_name, const gchar *group_name) { GSList *result = NULL; /* NOTE: group_name != NULL not implemented in purple either */ if (!group_name) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; GHashTable *buddies = telepathy_private->contact_list->buddies; if (buddy_name) { result = buddy_add_all(g_hash_table_lookup(buddies, buddy_name), result); } else { GHashTableIter biter; struct telepathy_buddy *buddy; g_hash_table_iter_init(&biter, telepathy_private->contact_list->buddies); while (g_hash_table_iter_next(&biter, NULL, (gpointer) &buddy)) result = buddy_add_all(buddy, result); } } return(result); } gchar *sipe_backend_buddy_get_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->uri)); } gchar *sipe_backend_buddy_get_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME])); } gchar *sipe_backend_buddy_get_server_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { /* server alias is the same as alias */ return(sipe_backend_buddy_get_alias(sipe_public, who)); } gchar *sipe_backend_buddy_get_local_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { /* server alias is the same as alias */ return(sipe_backend_buddy_get_alias(sipe_public, who)); } gchar *sipe_backend_buddy_get_group_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { return(g_strdup(((struct telepathy_buddy_entry *) who)->group)); } gchar *sipe_backend_buddy_get_string(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, sipe_backend_buddy who, const sipe_buddy_info_fields key) { struct telepathy_buddy_entry *buddy_entry = who; struct telepathy_buddy *buddy = buddy_entry->buddy; if (key >= SIPE_INFO_FIELD_MAX) return(NULL); return(g_strdup(buddy->info[key])); } void sipe_backend_buddy_set_string(struct sipe_core_public *sipe_public, sipe_backend_buddy who, const sipe_buddy_info_fields key, const gchar *val) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; SipeContactList *contact_list = telepathy_private->contact_list; struct telepathy_buddy_entry *buddy_entry = who; struct telepathy_buddy *buddy = buddy_entry->buddy; if (key >= SIPE_INFO_FIELD_MAX) return; SIPE_DEBUG_INFO("sipe_backend_buddy_set_string: %s replacing info %d: %s -> %s", buddy->uri, key, buddy->info[key] ? buddy->info[key]: "", val); g_free(buddy->info[key]); buddy->info[key] = g_strdup(val); if (contact_list->initial_received) { /* @TODO: emit signal? */ } } void sipe_backend_buddy_refresh_properties(struct sipe_core_public *sipe_public, const gchar *uri) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies, uri); GPtrArray *info = convert_contact_info(buddy); if (info) { tp_svc_connection_interface_contact_info_emit_contact_info_changed(telepathy_private->connection, buddy->handle, info); g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info); } } guint sipe_backend_buddy_get_status(struct sipe_core_public *sipe_public, const gchar *uri) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies, uri); if (!buddy) return(SIPE_ACTIVITY_UNSET); return(buddy->activity); } void sipe_backend_buddy_set_alias(struct sipe_core_public *sipe_public, const sipe_backend_buddy who, const gchar *alias) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; SipeContactList *contact_list = telepathy_private->contact_list; struct telepathy_buddy_entry *buddy_entry = who; struct telepathy_buddy *buddy = buddy_entry->buddy; update_alias(buddy, alias); if (contact_list->initial_received) { SIPE_DEBUG_INFO("sipe_backend_buddy_set_alias: %s changed to '%s'", buddy->uri, alias); sipe_telepathy_connection_alias_updated(contact_list->connection, buddy->handle, alias); } } void sipe_backend_buddy_set_server_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const sipe_backend_buddy who, SIPE_UNUSED_PARAMETER const gchar *alias) { /* server alias is the same as alias. Ignore this */ } void sipe_backend_buddy_list_processing_finish(struct sipe_core_public *sipe_public) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; SipeContactList *contact_list = telepathy_private->contact_list; if (!contact_list->initial_received) { /* we can only call this once */ contact_list->initial_received = TRUE; SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_buddy_list_processing_finish called"); tp_base_contact_list_set_list_received(TP_BASE_CONTACT_LIST(contact_list)); } } static void buddy_free(gpointer data) { struct telepathy_buddy *buddy = data; guint i; g_hash_table_destroy(buddy->groups); for (i = 0; i < SIPE_INFO_FIELD_MAX; i++) g_free(buddy->info[i]); g_free(buddy->hash); g_free(buddy); } sipe_backend_buddy sipe_backend_buddy_add(struct sipe_core_public *sipe_public, const gchar *name, const gchar *alias, const gchar *group_name) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; SipeContactList *contact_list = telepathy_private->contact_list; const gchar *group = g_hash_table_lookup(contact_list->groups, group_name); struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies, name); struct telepathy_buddy_entry *buddy_entry; if (!group) return(NULL); if (!buddy) { buddy = g_new0(struct telepathy_buddy, 1); buddy->uri = g_strdup(name); /* reused as key */ buddy->groups = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME] = g_strdup(alias); buddy->hash = NULL; buddy->activity = SIPE_ACTIVITY_OFFLINE; buddy->handle = tp_handle_ensure(contact_list->contact_repo, buddy->uri, NULL, NULL); tp_handle_set_add(contact_list->contacts, buddy->handle); g_hash_table_insert(contact_list->buddies, (gchar *) buddy->uri, /* owned by hash table */ buddy); g_hash_table_insert(contact_list->buddy_handles, GUINT_TO_POINTER(buddy->handle), buddy); } buddy_entry = g_hash_table_lookup(buddy->groups, group); if (!buddy_entry) { buddy_entry = g_new0(struct telepathy_buddy_entry, 1); buddy_entry->buddy = buddy; buddy_entry->group = group; g_hash_table_insert(buddy->groups, (gchar *) group, /* key is borrowed */ buddy_entry); } if (contact_list->initial_received) { /* @TODO: emit signal? */ } return(buddy_entry); } void sipe_backend_buddy_remove(struct sipe_core_public *sipe_public, const sipe_backend_buddy who) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; SipeContactList *contact_list = telepathy_private->contact_list; struct telepathy_buddy_entry *remove_entry = who; struct telepathy_buddy *buddy = remove_entry->buddy; g_hash_table_remove(buddy->groups, remove_entry->group); /* remove_entry is invalid */ if (g_hash_table_size(buddy->groups) == 0) { /* removed from last group -> drop this buddy */ tp_handle_set_remove(contact_list->contacts, buddy->handle); g_hash_table_remove(contact_list->buddy_handles, GUINT_TO_POINTER(buddy->handle)); g_hash_table_remove(contact_list->buddies, buddy->uri); } if (contact_list->initial_received) { /* @TODO: emit signal? */ } } void sipe_backend_buddy_set_status(struct sipe_core_public *sipe_public, const gchar *uri, guint activity, SIPE_UNUSED_PARAMETER time_t last_active) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; SipeContactList *contact_list = telepathy_private->contact_list; struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies, uri); TpPresenceStatus *status; if (!buddy) return; buddy->activity = activity; SIPE_DEBUG_INFO("sipe_backend_buddy_set_status: %s to %d", uri, activity); /* emit status update signal */ status = tp_presence_status_new(activity, NULL); tp_presence_mixin_emit_one_presence_update(G_OBJECT(telepathy_private->connection), buddy->handle, status); tp_presence_status_free(status); } gboolean sipe_backend_uses_photo(void) { return(TRUE); } static void buddy_photo_updated(struct sipe_backend_private *telepathy_private, struct telepathy_buddy *buddy, const gchar *photo, gsize photo_len) { GArray *array = g_array_new(FALSE, FALSE, sizeof(gchar)); SIPE_DEBUG_INFO("buddy_photo_updated: %s (%" G_GSIZE_FORMAT ")", buddy->uri, photo_len); g_array_append_vals(array, photo, photo_len); tp_svc_connection_interface_avatars_emit_avatar_updated(telepathy_private->connection, buddy->handle, buddy->hash); tp_svc_connection_interface_avatars_emit_avatar_retrieved(telepathy_private->connection, buddy->handle, buddy->hash, array, /* @TODO: is this correct? */ "image/jpeg"); g_array_unref(array); } void sipe_backend_buddy_set_photo(struct sipe_core_public *sipe_public, const gchar *uri, gpointer image_data, gsize image_len, const gchar *photo_hash) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies, uri); if (buddy) { gchar *hash_file = g_build_filename(telepathy_private->cache_dir, uri, NULL); /* does this buddy already have a photo? -> delete it */ if (buddy->hash) { char *photo_file = g_build_filename(telepathy_private->cache_dir, buddy->hash, NULL); (void) g_remove(photo_file); g_free(photo_file); g_free(buddy->hash); buddy->hash = NULL; } /* update hash file */ if (g_file_set_contents(hash_file, photo_hash, strlen(photo_hash), NULL)) { gchar *photo_file = g_build_filename(telepathy_private->cache_dir, photo_hash, NULL); buddy->hash = g_strdup(photo_hash); g_file_set_contents(photo_file, image_data, image_len, NULL); buddy_photo_updated(telepathy_private, buddy, image_data, image_len); g_free(photo_file); } g_free(hash_file); } g_free(image_data); } const gchar *sipe_backend_buddy_get_photo_hash(struct sipe_core_public *sipe_public, const gchar *uri) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies, uri); if (!buddy) return(NULL); if (!buddy->hash) { gchar *hash_file = g_build_filename(telepathy_private->cache_dir, uri, NULL); /* returned memory is owned & freed by buddy */ if (g_file_get_contents(hash_file, &buddy->hash, NULL, NULL)) { gchar *photo_file = g_build_filename(telepathy_private->cache_dir, buddy->hash, NULL); gchar *image_data = NULL; gsize image_len; if (g_file_get_contents(photo_file, &image_data, &image_len, NULL)) buddy_photo_updated(telepathy_private, buddy, image_data, image_len); g_free(image_data); g_free(photo_file); } g_free(hash_file); } return(buddy->hash); } gboolean sipe_backend_buddy_group_add(struct sipe_core_public *sipe_public, const gchar *group_name) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; SipeContactList *contact_list = telepathy_private->contact_list; gchar *group = g_hash_table_lookup(contact_list->groups, group_name); if (!group) { group = g_strdup(group_name); g_hash_table_insert(contact_list->groups, group, group); tp_base_contact_list_groups_created(TP_BASE_CONTACT_LIST(contact_list), &group_name, 1); } return(group != NULL); } void sipe_backend_buddy_group_remove(struct sipe_core_public *sipe_public, const gchar *group_name) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; SipeContactList *contact_list = telepathy_private->contact_list; g_hash_table_remove(contact_list->groups, group_name); if (contact_list->initial_received) { /* @TODO: emit signal? */ } } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-connection.c ================================================ /** * @file telepathy-connection.c * * pidgin-sipe * * Copyright (C) 2012-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "telepathy-private.h" G_BEGIN_DECLS /* * Connection class - data structures */ typedef struct _SipeConnectionClass { TpBaseConnectionClass parent_class; TpDBusPropertiesMixinClass properties_mixin; TpContactsMixinClass contacts_mixin; TpPresenceMixinClass presence_mixin; } SipeConnectionClass; typedef struct _SipeConnection { TpBaseConnection parent; TpContactsMixinClass contacts_mixin; TpPresenceMixin presence_mixin; /* channel managers */ TpSimplePasswordManager *password_manager; struct _SipeContactList *contact_list; struct _SipeTLSManager *tls_manager; struct sipe_backend_private private; gchar *account; gchar *login; gchar *password; gchar *server; gchar *port; guint transport; guint authentication_type; gchar *user_agent; gchar *authentication; gboolean sso; gboolean dont_publish; gboolean allow_web_photo; gboolean is_disconnecting; GPtrArray *contact_info_fields; } SipeConnection; #define SIPE_PUBLIC_TO_CONNECTION sipe_public->backend_private->connection /* * Connection class - type macros */ static GType sipe_connection_get_type(void) G_GNUC_CONST; #define SIPE_TYPE_CONNECTION \ (sipe_connection_get_type()) #define SIPE_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONNECTION, \ SipeConnection)) G_END_DECLS /* * Connection class - type definition */ static void init_aliasing (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE(SipeConnection, sipe_connection, TP_TYPE_BASE_CONNECTION, G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING, init_aliasing); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS, sipe_telepathy_avatars_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS, tp_contacts_mixin_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_GROUPS, tp_base_contact_list_mixin_groups_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO, sipe_telepathy_contact_info_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_LIST, tp_base_contact_list_mixin_list_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE, tp_presence_mixin_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE, tp_presence_mixin_simple_presence_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init); ) /* * Connection class - instance methods */ static gchar *normalize_contact(SIPE_UNUSED_PARAMETER TpHandleRepoIface *repo, const gchar *id, SIPE_UNUSED_PARAMETER gpointer context, GError **error) { return(sipe_telepathy_protocol_normalize_contact(NULL, id, error)); } static void create_handle_repos(SIPE_UNUSED_PARAMETER TpBaseConnection *conn, TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]) { repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new(TP_HANDLE_TYPE_CONTACT, normalize_contact, NULL); } static gboolean connect_to_core(SipeConnection *self, GError **error) { struct sipe_core_public *sipe_public; const gchar *errmsg; sipe_public = sipe_core_allocate(self->account, self->sso, self->login, self->password, NULL, /* @TODO: email */ NULL, /* @TODO: email_url */ &errmsg); SIPE_DEBUG_INFO("connect_to_core: created %p", sipe_public); if (sipe_public) { struct sipe_backend_private *telepathy_private = &self->private; /* initialize backend private data */ sipe_public->backend_private = telepathy_private; telepathy_private->public = sipe_public; telepathy_private->contact_list = self->contact_list; telepathy_private->connection = self; telepathy_private->activity = SIPE_ACTIVITY_UNSET; telepathy_private->cache_dir = g_build_path(G_DIR_SEPARATOR_S, g_get_user_cache_dir(), "telepathy", "sipe", self->account, NULL); telepathy_private->message = NULL; telepathy_private->tls_manager = self->tls_manager; telepathy_private->transport = NULL; /* make sure cache directory exists */ if (!g_file_test(telepathy_private->cache_dir, G_FILE_TEST_IS_DIR) && (g_mkdir_with_parents(telepathy_private->cache_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0)) SIPE_DEBUG_INFO("connect_to_core: created cache directory %s", telepathy_private->cache_dir); SIPE_CORE_FLAG_UNSET(DONT_PUBLISH); if (self->dont_publish) SIPE_CORE_FLAG_SET(DONT_PUBLISH); SIPE_CORE_FLAG_UNSET(ALLOW_WEB_PHOTO); if (self->allow_web_photo) SIPE_CORE_FLAG_SET(ALLOW_WEB_PHOTO); sipe_core_transport_sip_connect(sipe_public, self->transport, self->authentication_type, self->server, self->port); return(TRUE); } else { g_set_error_literal(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, errmsg); return(FALSE); } } static void password_manager_cb(GObject *source, GAsyncResult *result, gpointer data) { SipeConnection *self = data; TpBaseConnection *base = TP_BASE_CONNECTION(self); GError *error = NULL; const GString *password = tp_simple_password_manager_prompt_finish( TP_SIMPLE_PASSWORD_MANAGER(source), result, &error); if (password == NULL) { SIPE_DEBUG_ERROR("password_manager_cb: failed: %s", error ? error->message : "UNKNOWN"); if (base->status != TP_CONNECTION_STATUS_DISCONNECTED) { tp_base_connection_disconnect_with_dbus_error(base, error ? tp_error_get_dbus_name(error->code) : "", NULL, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); } if (error) g_error_free(error); } else { g_free(self->password); self->password = g_strdup(password->str); if (!connect_to_core(self, &error)) { if (base->status != TP_CONNECTION_STATUS_DISCONNECTED) { tp_base_connection_disconnect_with_dbus_error(base, tp_error_get_dbus_name(error->code), NULL, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); } g_error_free(error); } } } static gboolean start_connecting(TpBaseConnection *base, GError **error) { SipeConnection *self = SIPE_CONNECTION(base); gboolean rc = TRUE; gchar *uri = sipe_telepathy_protocol_normalize_contact(NULL, self->account, error); SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::start_connecting"); /* set up mandatory self-handle */ if (uri) { base->self_handle = tp_handle_ensure(tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT), uri, NULL, error); g_free(uri); if (!base->self_handle) { SIPE_DEBUG_ERROR("SipeConnection::start_connecting: self handle creation failed: %s", (*error)->message); return(FALSE); } } else { SIPE_DEBUG_ERROR("SipeConnection::start_connecting: %s", (*error)->message); return(FALSE); } tp_base_connection_change_status(base, TP_CONNECTION_STATUS_CONNECTING, TP_CONNECTION_STATUS_REASON_REQUESTED); /* map option list to flags - default is automatic */ self->authentication_type = SIPE_AUTHENTICATION_TYPE_AUTOMATIC; if (sipe_strequal(self->authentication, "ntlm")) { SIPE_DEBUG_INFO_NOFORMAT("start_connecting: NTLM selected"); self->authentication_type = SIPE_AUTHENTICATION_TYPE_NTLM; } else #ifdef HAVE_GSSAPI_GSSAPI_H if (sipe_strequal(self->authentication, "krb5")) { SIPE_DEBUG_INFO_NOFORMAT("start_connecting: KRB5 selected"); self->authentication_type = SIPE_AUTHENTICATION_TYPE_KERBEROS; } else #endif if (sipe_strequal(self->authentication, "tls-dsk")) { SIPE_DEBUG_INFO_NOFORMAT("start_connecting: TLS-DSK selected"); self->authentication_type = SIPE_AUTHENTICATION_TYPE_TLS_DSK; } /* Only ask for a password when required */ if (!sipe_core_transport_sip_requires_password(self->authentication_type, self->sso) || (self->password && strlen(self->password))) rc = connect_to_core(self, error); else { SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::start_connecting: requesting password from user"); tp_simple_password_manager_prompt_async(self->password_manager, password_manager_cb, self); } return(rc); } static gboolean disconnect_from_core(gpointer data) { TpBaseConnection *base = data; SipeConnection *self = SIPE_CONNECTION(base); struct sipe_backend_private *telepathy_private = &self->private; struct sipe_core_public *sipe_public = telepathy_private->public; SIPE_DEBUG_INFO("disconnect_from_core: %p", sipe_public); if (sipe_public) sipe_core_deallocate(sipe_public); telepathy_private->public = NULL; telepathy_private->transport = NULL; g_free(telepathy_private->message); telepathy_private->message = NULL; g_free(telepathy_private->cache_dir); telepathy_private->cache_dir = NULL; SIPE_DEBUG_INFO_NOFORMAT("disconnect_from_core: core deallocated"); /* now it is OK to destroy the connection object */ tp_base_connection_finish_shutdown(base); return(FALSE); } static void shut_down(TpBaseConnection *base) { SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::shut_down"); /* this can be called synchronously, defer destruction */ g_idle_add(disconnect_from_core, base); } static GPtrArray *create_channel_managers(TpBaseConnection *base) { SipeConnection *self = SIPE_CONNECTION(base); GPtrArray *channel_managers = g_ptr_array_new(); SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::create_channel_managers"); self->contact_list = sipe_telepathy_contact_list_new(base); g_ptr_array_add(channel_managers, self->contact_list); self->password_manager = tp_simple_password_manager_new(base); g_ptr_array_add(channel_managers, self->password_manager); g_ptr_array_add(channel_managers, sipe_telepathy_search_new(base)); self->tls_manager = sipe_telepathy_tls_new(base); g_ptr_array_add(channel_managers, self->tls_manager); return(channel_managers); } static void aliasing_fill_contact_attributes(GObject *object, const GArray *contacts, GHashTable *attributes) { SipeConnection *self = SIPE_CONNECTION(object); guint i; for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index(contacts, guint, i); tp_contacts_mixin_set_contact_attribute(attributes, contact, TP_TOKEN_CONNECTION_INTERFACE_ALIASING_ALIAS, tp_g_value_slice_new_string( sipe_telepathy_buddy_get_alias(self->contact_list, contact))); } } static void avatars_fill_contact_attributes(GObject *object, const GArray *contacts, GHashTable *attributes) { SipeConnection *self = SIPE_CONNECTION(object); guint i; for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index(contacts, guint, i); const gchar *hash = sipe_telepathy_buddy_get_hash(self->contact_list, contact); if (!hash) hash = ""; tp_contacts_mixin_set_contact_attribute(attributes, contact, TP_IFACE_CONNECTION_INTERFACE_AVATARS"/token", tp_g_value_slice_new_string(hash)); } } static void contact_info_properties_getter(GObject *object, SIPE_UNUSED_PARAMETER GQuark interface, GQuark name, GValue *value, gpointer getter_data) { GQuark fields = g_quark_from_static_string("SupportedFields"); if (name == fields) g_value_set_boxed(value, SIPE_CONNECTION(object)->contact_info_fields); else g_value_set_uint(value, GPOINTER_TO_UINT(getter_data)); } static void sipe_connection_constructed(GObject *object) { SipeConnection *self = SIPE_CONNECTION(object); TpBaseConnection *base = TP_BASE_CONNECTION(object); void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_connection_parent_class)->constructed; if (chain_up) chain_up(object); tp_contacts_mixin_init(object, G_STRUCT_OFFSET(SipeConnection, contacts_mixin)); tp_base_connection_register_with_contacts_mixin(base); tp_base_contact_list_mixin_register_with_contacts_mixin(base); tp_contacts_mixin_add_contact_attributes_iface(object, TP_IFACE_CONNECTION_INTERFACE_ALIASING, aliasing_fill_contact_attributes); tp_contacts_mixin_add_contact_attributes_iface(object, TP_IFACE_CONNECTION_INTERFACE_AVATARS, avatars_fill_contact_attributes); tp_presence_mixin_init(object, G_STRUCT_OFFSET(SipeConnection, presence_mixin)); tp_presence_mixin_simple_presence_register_with_contacts_mixin(object); self->contact_info_fields = sipe_telepathy_contact_info_fields(); } static void sipe_connection_finalize(GObject *object) { SipeConnection *self = SIPE_CONNECTION(object); SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::finalize"); tp_contacts_mixin_finalize(object); tp_presence_mixin_finalize(object); g_boxed_free(TP_ARRAY_TYPE_FIELD_SPECS, self->contact_info_fields); g_free(self->authentication); g_free(self->user_agent); g_free(self->port); g_free(self->server); g_free(self->password); g_free(self->login); g_free(self->account); G_OBJECT_CLASS(sipe_connection_parent_class)->finalize(object); } /* * Connection class - type implementation */ static const gchar *interfaces_always_present[] = { /* @TODO */ TP_IFACE_CONNECTION_INTERFACE_ALIASING, TP_IFACE_CONNECTION_INTERFACE_AVATARS, TP_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS, TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, TP_IFACE_CONNECTION_INTERFACE_CONTACT_LIST, TP_IFACE_CONNECTION_INTERFACE_CONTACTS, TP_IFACE_CONNECTION_INTERFACE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_REQUESTS, TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, NULL }; static void sipe_connection_class_init(SipeConnectionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); TpBaseConnectionClass *base_class = TP_BASE_CONNECTION_CLASS(klass); static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { /* 0 */ .name = TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, .getter = contact_info_properties_getter, .setter = NULL, }, { /* LAST! */ .name = NULL, } }; /* initalize non-constant fields */ prop_interfaces[0].props = sipe_telepathy_contact_info_props(); SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::class_init"); object_class->constructed = sipe_connection_constructed; object_class->finalize = sipe_connection_finalize; base_class->create_handle_repos = create_handle_repos; base_class->start_connecting = start_connecting; base_class->shut_down = shut_down; base_class->create_channel_managers = create_channel_managers; base_class->interfaces_always_present = interfaces_always_present; klass->properties_mixin.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init(object_class, G_STRUCT_OFFSET(SipeConnectionClass, properties_mixin)); tp_contacts_mixin_class_init(object_class, G_STRUCT_OFFSET(SipeConnectionClass, contacts_mixin)); sipe_telepathy_status_init(object_class, G_STRUCT_OFFSET(SipeConnectionClass, presence_mixin)); tp_presence_mixin_simple_presence_init_dbus_properties(object_class); tp_base_contact_list_mixin_class_init(base_class); } static void sipe_connection_init(SIPE_UNUSED_PARAMETER SipeConnection *self) { SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::init"); } /* * Connection class - interface implementation * * Contact aliases */ static void get_alias_flags(TpSvcConnectionInterfaceAliasing *aliasing, DBusGMethodInvocation *context) { TpBaseConnection *base = TP_BASE_CONNECTION(aliasing); TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context); SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::get_alias_flags called"); tp_svc_connection_interface_aliasing_return_from_get_alias_flags(context, TP_CONNECTION_ALIAS_FLAG_USER_SET); } static void get_aliases(TpSvcConnectionInterfaceAliasing *aliasing, const GArray *contacts, DBusGMethodInvocation *context) { SipeConnection *self = SIPE_CONNECTION(aliasing); TpBaseConnection *base = TP_BASE_CONNECTION(aliasing); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; GHashTable *result; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context); SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::get_aliases called"); if (!tp_handles_are_valid(contact_repo, contacts, FALSE, &error)) { dbus_g_method_return_error(context, error); g_error_free(error); return; } result = g_hash_table_new(g_direct_hash, g_direct_equal); for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index(contacts, TpHandle, i); const gchar *alias = sipe_telepathy_buddy_get_alias(self->contact_list, contact); g_hash_table_insert(result, GUINT_TO_POINTER(contact), (gchar *) alias); } tp_svc_connection_interface_aliasing_return_from_get_aliases(context, result); g_hash_table_unref(result); } static void request_aliases(TpSvcConnectionInterfaceAliasing *aliasing, const GArray *contacts, DBusGMethodInvocation *context) { SipeConnection *self = SIPE_CONNECTION(aliasing); TpBaseConnection *base = TP_BASE_CONNECTION(aliasing); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; GPtrArray *result; gchar **strings; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context); SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::request_aliases called"); if (!tp_handles_are_valid(contact_repo, contacts, FALSE, &error)) { dbus_g_method_return_error(context, error); g_error_free(error); return; } result = g_ptr_array_sized_new(contacts->len + 1); for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index(contacts, TpHandle, i); const gchar *alias = sipe_telepathy_buddy_get_alias(self->contact_list, contact); g_ptr_array_add(result, (gchar *) alias); } g_ptr_array_add(result, NULL); strings = (gchar **) g_ptr_array_free(result, FALSE); tp_svc_connection_interface_aliasing_return_from_request_aliases(context, (const gchar **) strings); g_free(strings); } static void set_aliases(TpSvcConnectionInterfaceAliasing *aliasing, GHashTable *aliases, DBusGMethodInvocation *context) { SipeConnection *self = SIPE_CONNECTION(aliasing); TpBaseConnection *base = TP_BASE_CONNECTION(aliasing); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT); GHashTableIter iter; gpointer key, value; SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::set_aliases called"); g_hash_table_iter_init(&iter, aliases); while (g_hash_table_iter_next(&iter, &key, NULL)) { GError *error = NULL; if (!tp_handle_is_valid(contact_repo, GPOINTER_TO_UINT(key), &error)) { dbus_g_method_return_error(context, error); g_error_free(error); return; } } g_hash_table_iter_init(&iter, aliases); while (g_hash_table_iter_next(&iter, &key, &value)) { sipe_telepathy_buddy_set_alias(self->contact_list, GPOINTER_TO_UINT(key), value); } tp_svc_connection_interface_aliasing_return_from_set_aliases(context); } static void init_aliasing(gpointer iface, SIPE_UNUSED_PARAMETER gpointer iface_data) { TpSvcConnectionInterfaceAliasingClass *klass = iface; SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::init_aliasing called"); tp_svc_connection_interface_aliasing_implement_get_alias_flags(klass, get_alias_flags); tp_svc_connection_interface_aliasing_implement_request_aliases(klass, request_aliases); tp_svc_connection_interface_aliasing_implement_get_aliases(klass, get_aliases); tp_svc_connection_interface_aliasing_implement_set_aliases(klass, set_aliases); } /* create new connection object */ TpBaseConnection *sipe_telepathy_connection_new(TpBaseProtocol *protocol, GHashTable *params, SIPE_UNUSED_PARAMETER GError **error) { SipeConnection *conn = g_object_new(SIPE_TYPE_CONNECTION, "protocol", tp_base_protocol_get_name(protocol), NULL); const gchar *value; guint port; gboolean boolean_value; gboolean valid; SIPE_DEBUG_INFO_NOFORMAT("sipe_telepathy_connection_new"); /* initialize private fields */ conn->is_disconnecting = FALSE; /* account is required field */ conn->account = g_strdup(tp_asv_get_string(params, "account")); /* if login is not specified, account value will be used in connect_to_core */ value = tp_asv_get_string(params, "login"); if (value && strlen(value)) conn->login = g_strdup(value); else conn->login = NULL; /* password */ value = tp_asv_get_string(params, "password"); if (value && strlen(value)) conn->password = g_strdup(value); else conn->password = NULL; /* server name */ value = tp_asv_get_string(params, "server"); if (value && strlen(value)) conn->server = g_strdup(value); else conn->server = NULL; /* server port: core expects a string */ port = tp_asv_get_uint32(params, "port", &valid); if (valid) conn->port = g_strdup_printf("%d", port); else conn->port = NULL; /* transport type */ value = tp_asv_get_string(params, "transport"); if (sipe_strequal(value, "auto")) { conn->transport = conn->server ? SIPE_TRANSPORT_TLS : SIPE_TRANSPORT_AUTO; } else if (sipe_strequal(value, "tls")) { conn->transport = SIPE_TRANSPORT_TLS; } else { conn->transport = SIPE_TRANSPORT_TCP; } /* User-Agent: override */ value = tp_asv_get_string(params, "useragent"); if (value && strlen(value)) conn->user_agent = g_strdup(value); else conn->user_agent = NULL; /* authentication type */ value = tp_asv_get_string(params, "authentication"); if (value && strlen(value) && strcmp(value, "ntlm")) conn->authentication = g_strdup(value); else conn->authentication = NULL; /* NTLM is default */ /* Single Sign-On */ boolean_value = tp_asv_get_boolean(params, "single-sign-on", &valid); if (valid) conn->sso = boolean_value; else conn->sso = FALSE; /* Don't publish my calendar information */ boolean_value = tp_asv_get_boolean(params, "don't-publish-calendar", &valid); if (valid) conn->dont_publish = boolean_value; else conn->dont_publish = FALSE; /* Allow insecure download of buddy icons from web */ boolean_value = tp_asv_get_boolean(params, "allow-web-photo", &valid); if (valid) conn->allow_web_photo = boolean_value; else conn->allow_web_photo = FALSE; return(TP_BASE_CONNECTION(conn)); } void sipe_telepathy_connection_alias_updated(TpBaseConnection *connection, guint contact, const gchar *alias) { GPtrArray *aliases = g_ptr_array_sized_new(1); GValueArray *pair = g_value_array_new(2); g_value_array_append(pair, NULL); g_value_array_append(pair, NULL); g_value_init(pair->values + 0, G_TYPE_UINT); g_value_init(pair->values + 1, G_TYPE_STRING); g_value_set_uint(pair->values + 0, contact); g_value_set_string(pair->values + 1, alias); g_ptr_array_add(aliases, pair); tp_svc_connection_interface_aliasing_emit_aliases_changed(SIPE_CONNECTION(connection), aliases); g_ptr_array_unref(aliases); g_value_array_free(pair); } struct sipe_backend_private *sipe_telepathy_connection_private(GObject *object) { SipeConnection *self = SIPE_CONNECTION(object); /* connected to core already? */ if (self->private.public) return(&self->private); else return(NULL); } /* * Backend adaptor functions */ void sipe_backend_connection_completed(struct sipe_core_public *sipe_public) { SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION; TpBaseConnection *base = TP_BASE_CONNECTION(self); /* we are only allowed to do this once */ if (base->status != TP_CONNECTION_STATUS_CONNECTED) tp_base_connection_change_status(base, TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED); } void sipe_backend_connection_error(struct sipe_core_public *sipe_public, sipe_connection_error error, const gchar *msg) { SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION; TpBaseConnection *base = TP_BASE_CONNECTION(self); GHashTable *details = tp_asv_new("server-message", G_TYPE_STRING, msg, NULL); TpConnectionStatusReason reason; const gchar *name; self->is_disconnecting = TRUE; switch (error) { case SIPE_CONNECTION_ERROR_NETWORK: reason = TP_CONNECTION_STATUS_REASON_NETWORK_ERROR; if (base->status == TP_CONNECTION_STATUS_CONNECTING) name = TP_ERROR_STR_CONNECTION_FAILED; else name = TP_ERROR_STR_CONNECTION_LOST; break; case SIPE_CONNECTION_ERROR_INVALID_USERNAME: case SIPE_CONNECTION_ERROR_INVALID_SETTINGS: case SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED: case SIPE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE: /* copied from haze code. I agree there should be better ones */ reason = TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED; name = TP_ERROR_STR_AUTHENTICATION_FAILED; break; default: reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED; name = TP_ERROR_STR_DISCONNECTED; break; } SIPE_DEBUG_ERROR("sipe_backend_connection_error: %s (%s)", name, msg); tp_base_connection_disconnect_with_dbus_error(base, name, details, reason); g_hash_table_unref(details); } gboolean sipe_backend_connection_is_disconnecting(struct sipe_core_public *sipe_public) { SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION; /* disconnect was requested or transport was already disconnected */ return(self->is_disconnecting || self->private.transport == NULL); } gboolean sipe_backend_connection_is_valid(struct sipe_core_public *sipe_public) { return(!sipe_backend_connection_is_disconnecting(sipe_public)); } const gchar *sipe_backend_setting(struct sipe_core_public *sipe_public, sipe_setting type) { SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION; const gchar *value; switch (type) { case SIPE_SETTING_USER_AGENT: value = self->user_agent; break; default: /* @TODO: update when settings are implemented */ value = NULL; break; } return(value); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-debug.c ================================================ /** * @file telepathy-debug.c * * pidgin-sipe * * Copyright (C) 2012-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ****************************************************************************** * * How to collect debugging information * * Run the connection manager from the command line like this: * * $ G_MESSAGES_DEBUG="all" SIPE_PERSIST=1 \ * SIPE_DEBUG=[space separated keyword list \ * [SIPE_UNSAFE_DEBUG=1] [SIPE_TIMING=1] [SIPE_LOGFILE="..."] \ * telepathy-sipe * * G_MESSAGES_DEBUG=all: make debug & informational messages visible * * SIPE_PERSISTS=1 : keep the CM running permanently, * [otherwise the one installed in the system will * be started automatically by D-Bus when needed] * * SIPE_DEBUG=... : * all - enable all sipe & telepathy-glib messages * sipe - enable only sipe messages * "sipe ..." - enable sipe and some telepathy-glib messages * * SIPE_UNSAFE_DEBUG=1 : enable unsafe debugging output, i.e. include the * content of protocol messages which may reveal * secret information, like passwords. * [usually required to be able to debug issues] * * SIPE_TIMING=1 : enable time stamps * [recommended for any usable log file] * * SIPE_LOGFILE="..." : redirect output to this file * [prepend file name with "+" to enable append mode] * ****************************************************************************** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-backend.h" #include "telepathy-private.h" #define SIPE_TELEPATHY_DEBUG 1 static TpDebugSender *debug; static guint flags = 0; static gboolean unsafe = FALSE; void sipe_telepathy_debug_init(void) { static const GDebugKey keys[] = { /* This simulates pidgin's --debug flag, i.e. we only see * output from SIPE if this is set. * * @TODO: we could make this more finely grained, i.e. * which levels should be visible */ { "sipe", SIPE_TELEPATHY_DEBUG }, }; const gchar *env_flags = g_getenv("SIPE_DEBUG"); /* Telepathy debugger */ debug = tp_debug_sender_dup(); /* divert g_log_default_handler() output to a logfile */ tp_debug_divert_messages(g_getenv("SIPE_LOGFILE")); /* sipe & telepathy-glib debugging flags */ if (env_flags) flags |= g_parse_debug_string(env_flags, keys, 1); tp_debug_set_flags(env_flags); /* enable unsafe debug output */ if (g_getenv("SIPE_UNSAFE_DEBUG")) unsafe = TRUE; /* add time stamps to debug output */ if (g_getenv("SIPE_TIMING")) g_log_set_default_handler(tp_debug_timestamped_log_handler, NULL); /* enable test mode */ if (g_getenv("SIPE_PERSIST")) tp_debug_set_persistent(TRUE); } void sipe_telepathy_debug_finalize(void) { g_object_unref(debug); } static const GLogLevelFlags debug_level_mapping[] = { G_LOG_LEVEL_DEBUG, /* SIPE_LOG_LEVEL_INFO */ G_LOG_LEVEL_WARNING, /* SIPE_LOG_LEVEL_WARNING */ G_LOG_LEVEL_CRITICAL, /* SIPE_LOG_LEVEL_ERROR */ G_LOG_LEVEL_DEBUG, /* SIPE_DEBUG_LEVEL_INFO */ G_LOG_LEVEL_WARNING, /* SIPE_DEBUG_LEVEL_WARNING */ G_LOG_LEVEL_CRITICAL, /* SIPE_DEBUG_LEVEL_ERROR */ }; void sipe_backend_debug_literal(sipe_debug_level level, const gchar *msg) { if ((level < SIPE_DEBUG_LEVEL_LOWEST) || (flags & SIPE_TELEPATHY_DEBUG)) { GLogLevelFlags g_level = debug_level_mapping[level]; g_log(SIPE_TELEPATHY_DOMAIN, g_level, "%s", msg); tp_debug_sender_add_message(debug, NULL, SIPE_TELEPATHY_DOMAIN, g_level, msg); } } void sipe_backend_debug(sipe_debug_level level, const gchar *format, ...) { va_list ap; va_start(ap, format); if ((level < SIPE_DEBUG_LEVEL_LOWEST) || (flags & SIPE_TELEPATHY_DEBUG)) { gchar *msg = g_strdup_vprintf(format, ap); sipe_backend_debug_literal(level, msg); g_free(msg); } va_end(ap); } gboolean sipe_backend_debug_enabled(void) { return((flags & SIPE_TELEPATHY_DEBUG) && unsafe); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-dnsquery.c ================================================ /** * @file telepathy-dnsquery.c * * pidgin-sipe * * Copyright (C) 2012-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "sipe-backend.h" #include "sipe-common.h" struct sipe_dns_query { sipe_dns_resolved_cb callback; gpointer extradata; guint port; GCancellable *cancel; }; static void dns_srv_response(GObject *resolver, GAsyncResult *result, gpointer data) { GError *error = NULL; GList *targets = g_resolver_lookup_service_finish(G_RESOLVER(resolver), result, &error); struct sipe_dns_query *query = data; if (targets) { GSrvTarget *target = targets->data; query->callback(query->extradata, g_srv_target_get_hostname(target), g_srv_target_get_port(target)); g_resolver_free_targets(targets); } else { SIPE_DEBUG_INFO("dns_srv_response: failed: %s", error ? error->message : "UNKNOWN"); if (error) g_error_free(error); if (query->callback) query->callback(query->extradata, NULL, 0); } g_object_unref(query->cancel); g_free(query); } struct sipe_dns_query *sipe_backend_dns_query_srv(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const gchar *protocol, const gchar *transport, const gchar *domain, sipe_dns_resolved_cb callback, gpointer data) { struct sipe_dns_query *query = g_new0(struct sipe_dns_query, 1); GResolver *resolver = g_resolver_get_default(); SIPE_DEBUG_INFO("sipe_backend_dns_query_srv: %s/%s/%s", protocol, transport, domain); query->callback = callback; query->extradata = data; query->cancel = g_cancellable_new(); g_resolver_lookup_service_async(resolver, protocol, transport, domain, query->cancel, dns_srv_response, query); g_object_unref(resolver); return(query); } static void dns_a_response(GObject *resolver, GAsyncResult *result, gpointer data) { GError *error = NULL; GList *addresses = g_resolver_lookup_by_name_finish(G_RESOLVER(resolver), result, &error); struct sipe_dns_query *query = data; if (addresses) { GInetAddress *address = addresses->data; gchar *ipstr = g_inet_address_to_string(address); query->callback(query->extradata, ipstr, query->port); g_free(ipstr); g_resolver_free_addresses(addresses); } else { SIPE_DEBUG_INFO("dns_a_response: failed: %s", error ? error->message : "UNKNOWN"); if (error) g_error_free(error); if (query->callback) query->callback(query->extradata, NULL, 0); } g_object_unref(query->cancel); g_free(query); } struct sipe_dns_query *sipe_backend_dns_query_a(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, const gchar *hostname, guint port, sipe_dns_resolved_cb callback, gpointer data) { struct sipe_dns_query *query = g_new0(struct sipe_dns_query, 1); GResolver *resolver = g_resolver_get_default(); SIPE_DEBUG_INFO("sipe_backend_dns_query_a: %s", hostname); query->callback = callback; query->extradata = data; query->port = port; query->cancel = g_cancellable_new(); g_resolver_lookup_by_name_async(resolver, hostname, query->cancel, dns_a_response, query); g_object_unref(resolver); return(query); } void sipe_backend_dns_query_cancel(struct sipe_dns_query *query) { /* callback is invalid now, do no longer call! */ query->callback = NULL; g_cancellable_cancel(query->cancel); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-main.c ================================================ /** * @file telepathy-main.c * * pidgin-sipe * * Copyright (C) 2012-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "telepathy-private.h" #if TP_VERSION_MIN_REQUIRED < TP_VERSION_0_24 #error telepathy-glib >= 0.24.0 is required to build SIPE #endif G_BEGIN_DECLS /* * Connection manager class - data structures */ typedef struct _SipeConnectionManagerClass { TpBaseConnectionManagerClass parent_class; } SipeConnectionManagerClass; typedef struct _SipeConnectionManager { TpBaseConnectionManager parent; } SipeConnectionManager; /* * Connection manager class - type macros */ static GType sipe_connection_manager_get_type(void) G_GNUC_CONST; #define SIPE_TYPE_CONNECTION_MANAGER \ (sipe_connection_manager_get_type()) #define SIPE_CONNECTION_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONNECTION_MANAGER, \ SipeConnectionManager)) G_END_DECLS /* * Connection manager class - type definition */ G_DEFINE_TYPE(SipeConnectionManager, sipe_connection_manager, TP_TYPE_BASE_CONNECTION_MANAGER) /* * Connection manager class - instance methods */ static void sipe_connection_manager_constructed(GObject *object) { SipeConnectionManager *self = SIPE_CONNECTION_MANAGER(object); TpBaseConnectionManager *base = (TpBaseConnectionManager *) self; SIPE_DEBUG_INFO_NOFORMAT("SipeConnectionManager::constructed"); /* always chain up to the parent constructor first */ G_OBJECT_CLASS(sipe_connection_manager_parent_class)->constructed(object); sipe_telepathy_protocol_init(base); } /* * Connection manager class - type implementation */ static void sipe_connection_manager_class_init(SipeConnectionManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); TpBaseConnectionManagerClass *base_class = (TpBaseConnectionManagerClass *)klass; SIPE_DEBUG_INFO_NOFORMAT("SipeConnectionManager::class_init"); object_class->constructed = sipe_connection_manager_constructed; base_class->new_connection = NULL; base_class->cm_dbus_name = SIPE_TELEPATHY_DOMAIN; base_class->protocol_params = NULL; } static void sipe_connection_manager_init(SIPE_UNUSED_PARAMETER SipeConnectionManager *self) { SIPE_DEBUG_INFO_NOFORMAT("SipeConnectionManager::init"); } /* * Entry point */ static TpBaseConnectionManager *construct_cm(void) { return((TpBaseConnectionManager *) g_object_new(SIPE_TYPE_CONNECTION_MANAGER, NULL)); } int main(int argc, char *argv[]) { int rc; g_type_init(); sipe_telepathy_debug_init(); sipe_core_init(LOCALEDIR); SIPE_DEBUG_INFO("main: initializing - version %s", PACKAGE_VERSION); rc = tp_run_connection_manager(SIPE_TELEPATHY_DOMAIN, PACKAGE_VERSION, construct_cm, argc, argv); sipe_core_destroy(); sipe_telepathy_debug_finalize(); return(rc); } gchar *sipe_backend_version(void) { /* * @TODO: this is the version of telepathy-glib we have compiled this * code against, not the version of "telepathy" which is * currently running. How to get this? Is it even possible? * * requires telepathy-glib >= 0.19 return(g_strdup_printf("telepathy-glib/%d.%d.%d", TP_MAJOR_VERSION, TP_MINOR_VERSION, TP_MICRO_VERSION)); */ return(g_strdup("Telepathy")); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-private.h ================================================ /** * @file telepathy-private.h * * pidgin-sipe * * Copyright (C) 2012-2016 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Forward declarations */ struct _GObject; struct _GObjectClass; struct _GTlsCertificate; struct _SipeConnection; struct _SipeContactList; struct _SipeTLSManager; struct _TpBaseConnection; struct _TpBaseConnectionManager; struct _TpBaseProtocol; struct sipe_tls_info; struct sipe_transport_telepathy; /* constants */ #define SIPE_TELEPATHY_DOMAIN "sipe" struct sipe_backend_private { struct sipe_core_public *public; /* buddies */ struct _SipeContactList *contact_list; /* connection */ struct _SipeConnection *connection; /* photo */ gchar *cache_dir; /* status */ guint activity; gchar *message; /* TLS certificate verification */ struct _SipeTLSManager *tls_manager; /* transport */ struct sipe_transport_telepathy *transport; }; /* buddy */ struct _SipeContactList *sipe_telepathy_contact_list_new(struct _TpBaseConnection *connection); const gchar *sipe_telepathy_buddy_get_alias(struct _SipeContactList *contact_list, const guint contact); void sipe_telepathy_buddy_set_alias(struct _SipeContactList *contact_list, const guint contact, const gchar *alias); const gchar *sipe_telepathy_buddy_get_hash(struct _SipeContactList *contact_list, const guint contact); guint sipe_telepathy_buddy_get_presence(struct _SipeContactList *contact_list, guint contact); void sipe_telepathy_avatars_iface_init(gpointer g_iface, gpointer iface_data); void sipe_telepathy_contact_info_iface_init(gpointer g_iface, gpointer iface_data); GPtrArray *sipe_telepathy_contact_info_fields(void); /* TpDBusPropertiesMixinPropImpl is a broken typedef */ gpointer sipe_telepathy_contact_info_props(void); /* connection */ struct _TpBaseConnection *sipe_telepathy_connection_new(struct _TpBaseProtocol *protocol, GHashTable *params, GError **error); void sipe_telepathy_connection_alias_updated(struct _TpBaseConnection *connection, guint contact, const gchar *alias); struct sipe_backend_private *sipe_telepathy_connection_private(GObject *object); /* debugging */ void sipe_telepathy_debug_init(void); void sipe_telepathy_debug_finalize(void); /* protocol */ void sipe_telepathy_protocol_init(struct _TpBaseConnectionManager *cm); gchar *sipe_telepathy_protocol_normalize_contact(struct _TpBaseProtocol *self, const gchar *contact, GError **error); /* contact search */ GType sipe_search_manager_get_type(void); #define SIPE_TYPE_SEARCH_MANAGER (sipe_search_manager_get_type()) struct _GObject *sipe_telepathy_search_new(struct _TpBaseConnection *connection); /* status */ void sipe_telepathy_status_init(struct _GObjectClass *object_class, gsize struct_offset); /* TLS certificate verification */ struct _SipeTLSManager *sipe_telepathy_tls_new(struct _TpBaseConnection *connection); struct sipe_tls_info *sipe_telepathy_tls_info_new(const gchar *hostname, struct _GTlsCertificate *certificate); void sipe_telepathy_tls_info_free(struct sipe_tls_info *tls_info); void sipe_telepathy_tls_verify_async(struct _GObject *connection, struct sipe_tls_info *tls_info, GAsyncReadyCallback callback, gpointer user_data); /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-protocol.c ================================================ /** * @file telepathy-protocol.c * * pidgin-sipe * * Copyright (C) 2012-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "sipe-nls.h" #include "telepathy-private.h" G_BEGIN_DECLS /* * Protocol class - data structures */ typedef struct _SipeProtocolClass { TpBaseProtocolClass parent_class; } SipeProtocolClass; typedef struct _SipeProtocol { TpBaseProtocol parent; } SipeProtocol; /* * Protocol class - type macros */ static GType sipe_protocol_get_type(void) G_GNUC_CONST; #define SIPE_TYPE_PROTOCOL \ (sipe_protocol_get_type()) #define SIPE_PROTOCOL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_PROTOCOL, \ SipeProtocol)) G_END_DECLS /* * Protocol class - type definition */ G_DEFINE_TYPE(SipeProtocol, sipe_protocol, TP_TYPE_BASE_PROTOCOL) /* * Protocol class - instance methods */ /* * @TODO: parameter filtering doesn't seem to work: these functions aren't * called at all - why? */ static gboolean parameter_filter_account(SIPE_UNUSED_PARAMETER const TpCMParamSpec *paramspec, GValue *value, GError **error) { const gchar *str = g_value_get_string(value); if ((str == NULL) || (strchr(str, '@') == NULL)) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_HANDLE, _("User name should be a valid SIP URI\nExample: user@company.com")); return(FALSE); } return(TRUE); } static const TpCMParamSpec *get_parameters(SIPE_UNUSED_PARAMETER TpBaseProtocol *self) { /* ISO C99 Designated Initializers silences -Wmissing-field-initializers */ #define SIPE_PROTOCOL_PARAMETER(_name, _dtype, _gtype, _flags, _default, _filter) \ { \ .name = (_name), \ .dtype = (_dtype), \ .gtype = (_gtype), \ .flags = (_flags), \ .def = (_default), \ .offset = 0, \ .filter = (_filter), \ .filter_data = NULL, \ .setter_data = NULL, \ } static const TpCMParamSpec sipe_parameters[] = { SIPE_PROTOCOL_PARAMETER("account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_REQUIRED, NULL, parameter_filter_account), SIPE_PROTOCOL_PARAMETER("login", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, NULL /* can be empty */), SIPE_PROTOCOL_PARAMETER("password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_SECRET, NULL, NULL /* can be empty */), SIPE_PROTOCOL_PARAMETER("server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, NULL /* can be empty */), SIPE_PROTOCOL_PARAMETER("port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(0), NULL), /* @TODO: this should be combo auto/ssl/tcp */ SIPE_PROTOCOL_PARAMETER("transport", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "auto", tp_cm_param_filter_string_nonempty), SIPE_PROTOCOL_PARAMETER("useragent", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, NULL /* can be empty */), /* @TODO: this should be combo auto/ntlm/krb5/tls-dsk */ SIPE_PROTOCOL_PARAMETER("authentication", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "auto", tp_cm_param_filter_string_nonempty), SIPE_PROTOCOL_PARAMETER("single-sign-on", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), NULL), SIPE_PROTOCOL_PARAMETER("don't-publish-calendar", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), NULL), SIPE_PROTOCOL_PARAMETER("allow-web-photo", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), NULL), SIPE_PROTOCOL_PARAMETER(NULL, NULL, 0, 0, NULL, NULL) }; return(sipe_parameters); } /* non-static, because it is re-used by connection object */ gchar *sipe_telepathy_protocol_normalize_contact(SIPE_UNUSED_PARAMETER TpBaseProtocol *self, const gchar *contact, GError **error) { gchar *uri = sip_uri_if_valid(contact); SIPE_DEBUG_INFO_NOFORMAT("SipeProtocol::normalize_contact"); if (!uri) g_set_error(error, TP_ERROR, TP_ERROR_INVALID_HANDLE, _("User name should be a valid SIP URI\nExample: user@company.com")); return(uri); } static gchar *identify_account(SIPE_UNUSED_PARAMETER TpBaseProtocol *self, GHashTable *asv, SIPE_UNUSED_PARAMETER GError **error) { SIPE_DEBUG_INFO_NOFORMAT("SipeProtocol::identify_account"); return(g_strdup(tp_asv_get_string(asv, "account"))); } static GStrv get_interfaces(SIPE_UNUSED_PARAMETER TpBaseProtocol *base) { SIPE_DEBUG_INFO_NOFORMAT("SipeProtocol::get_interfaces"); return(g_new0(gchar *, 1)); } static void get_connection_details(SIPE_UNUSED_PARAMETER TpBaseProtocol *self, GStrv *connection_interfaces, GType **channel_managers, gchar **icon_name, gchar **english_name, gchar **vcard_field) { SIPE_DEBUG_INFO_NOFORMAT("SipeProtocol::get_connection_details"); if (connection_interfaces) { static const gchar * const interfaces[] = { /* @TODO */ NULL }; *connection_interfaces = g_strdupv((GStrv) interfaces); } if (channel_managers) { GType types[] = { /* @TODO */ TP_TYPE_SIMPLE_PASSWORD_MANAGER, SIPE_TYPE_SEARCH_MANAGER, G_TYPE_INVALID }; *channel_managers = g_memdup(types, sizeof(types)); } if (icon_name) *icon_name = g_strdup("im-" SIPE_TELEPATHY_DOMAIN); if (english_name) *english_name = g_strdup("Office Communicator"); if (vcard_field) *vcard_field = g_strdup("x-" SIPE_TELEPATHY_DOMAIN); } static GStrv dup_authentication_types(SIPE_UNUSED_PARAMETER TpBaseProtocol *self) { static const gchar * const types[] = { /* @TODO */ NULL }; SIPE_DEBUG_INFO_NOFORMAT("SipeProtocol::dup_authentication_types"); return(g_strdupv((GStrv) types)); } /* * Protocol class - type implementation */ static void sipe_protocol_class_init(SipeProtocolClass *klass) { TpBaseProtocolClass *base_class = TP_BASE_PROTOCOL_CLASS(klass); SIPE_DEBUG_INFO_NOFORMAT("SipeProtocol::class_init"); base_class->get_parameters = get_parameters; base_class->new_connection = sipe_telepathy_connection_new; base_class->normalize_contact = sipe_telepathy_protocol_normalize_contact; base_class->identify_account = identify_account; base_class->get_interfaces = get_interfaces; base_class->get_connection_details = get_connection_details; base_class->dup_authentication_types = dup_authentication_types; } static void sipe_protocol_init(SIPE_UNUSED_PARAMETER SipeProtocol *self) { SIPE_DEBUG_INFO_NOFORMAT("SipeProtocol::init"); } /* add protocol to connection manager */ void sipe_telepathy_protocol_init(TpBaseConnectionManager *cm) { TpBaseProtocol *protocol = g_object_new(SIPE_TYPE_PROTOCOL, "name", SIPE_TELEPATHY_DOMAIN, NULL); tp_base_connection_manager_add_protocol(cm, protocol); g_object_unref(protocol); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-schedule.c ================================================ /** * @file telepathy-schedule.c * * pidgin-sipe * * Copyright (C) 2012 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" static gboolean timeout_execute(gpointer data) { sipe_core_schedule_execute(data); return(FALSE); } gpointer sipe_backend_schedule_seconds(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, guint timeout, gpointer data) { return(GUINT_TO_POINTER(g_timeout_add_seconds(timeout, timeout_execute, data))); } gpointer sipe_backend_schedule_mseconds(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, guint timeout, gpointer data) { return(GUINT_TO_POINTER(g_timeout_add(timeout, timeout_execute, data))); } void sipe_backend_schedule_cancel(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, gpointer data) { g_source_remove(GPOINTER_TO_UINT(data)); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-search.c ================================================ /** * @file telepathy-search.c * * pidgin-sipe * * Copyright (C) 2012-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "telepathy-private.h" /* vCard/Telepathy search field names */ #define SIPE_TELEPATHY_SEARCH_KEY_FIRST "x-n-given" #define SIPE_TELEPATHY_SEARCH_KEY_LAST "x-n-family" #define SIPE_TELEPATHY_SEARCH_KEY_EMAIL "email" #define SIPE_TELEPATHY_SEARCH_KEY_COMPANY "x-org-name" #define SIPE_TELEPATHY_SEARCH_KEY_COUNTRY "x-adr-country" #define SIPE_TELEPATHY_SEARCH_KEY_FULLNAME "fn" #define SIPE_TELEPATHY_SEARCH_KEY_BLOB "" /* one big search box */ G_BEGIN_DECLS /* * Search Manager class - data structures */ typedef struct _SipeSearchManagerClass { GObjectClass parent_class; } SipeSearchManagerClass; typedef struct _SipeSearchManager { GObject parent; GObject *connection; GHashTable *channels; } SipeSearchManager; /* * Search Manager class - type macros */ /* telepathy-private.h: #define SIPE_TYPE_SEARCH_MANAGER ... */ #define SIPE_SEARCH_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_SEARCH_MANAGER, \ SipeSearchManager)) /* * Search Channel class - data structures */ typedef struct _SipeSearchChannelClass { TpBaseChannelClass parent_class; } SipeSearchChannelClass; typedef struct _SipeSearchChannel { TpBaseChannel parent; GObject *connection; GHashTable *results; TpChannelContactSearchState state; } SipeSearchChannel; /* * Search Channel class - type macros */ static GType sipe_search_channel_get_type(void) G_GNUC_CONST; #define SIPE_TYPE_SEARCH_CHANNEL \ (sipe_search_channel_get_type()) #define SIPE_SEARCH_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_SEARCH_CHANNEL, \ SipeSearchChannel)) G_END_DECLS /* * Search Manager class - type definition */ static void channel_manager_iface_init(gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE(SipeSearchManager, sipe_search_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); ) /* * Search Manager class - type definition */ static void contact_search_iface_init(gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE(SipeSearchChannel, sipe_search_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_TYPE_CONTACT_SEARCH, contact_search_iface_init); ) /* * Search Manager class - instance methods */ static void sipe_search_manager_constructed(GObject *object) { SipeSearchManager *self = SIPE_SEARCH_MANAGER(object); void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_search_manager_parent_class)->constructed; if (chain_up) chain_up(object); self->channels = g_hash_table_new(g_direct_hash, g_direct_equal); } static void sipe_search_manager_dispose(GObject *object) { SipeSearchManager *self = SIPE_SEARCH_MANAGER(object); void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_search_manager_parent_class)->constructed; tp_clear_pointer(&self->channels, g_hash_table_unref); tp_clear_object(&self->connection); if (chain_up) chain_up(object); } /* * Search Manager class - type implementation */ static void sipe_search_manager_class_init(SipeSearchManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); SIPE_DEBUG_INFO_NOFORMAT("SipeSearchManager::class_init"); object_class->constructed = sipe_search_manager_constructed; object_class->dispose = sipe_search_manager_dispose; } static void sipe_search_manager_init(SIPE_UNUSED_PARAMETER SipeSearchManager *self) { SIPE_DEBUG_INFO_NOFORMAT("SipeSearchManager::init"); } /* * Search Manager class - interface implementation * * Channel Manager */ static void foreach_channel(TpChannelManager *manager, TpExportableChannelFunc func, gpointer user_data) { SipeSearchManager *self = SIPE_SEARCH_MANAGER(manager); GHashTableIter iter; gpointer chan; SIPE_DEBUG_INFO_NOFORMAT("SipeSearchManager::foreach_channel"); g_hash_table_iter_init(&iter, self->channels); while (g_hash_table_iter_next(&iter, &chan, NULL)) func(chan, user_data); } static void type_foreach_channel_class(GType type, TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { static const gchar *const no_props[] = { NULL }; GHashTable *table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); SIPE_DEBUG_INFO_NOFORMAT("SipeSearchManager::type_foreach_channel_class"); g_hash_table_insert(table, TP_IFACE_CHANNEL ".ChannelType", tp_g_value_slice_new_string(TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH)); func(type, table, no_props, user_data); g_hash_table_unref(table); } static void search_channel_closed_cb(SipeSearchChannel *channel, SipeSearchManager *self) { SIPE_DEBUG_INFO("SipeSearchManager::search_channel_close_cb: %p", channel); tp_channel_manager_emit_channel_closed_for_object(self, (TpExportableChannel *) channel); g_hash_table_remove(self->channels, channel); } static GObject *search_channel_new(GObject *connection); static gboolean create_channel(TpChannelManager *manager, gpointer request_token, GHashTable *request_properties) { SipeSearchManager *self = SIPE_SEARCH_MANAGER(manager); GObject *channel; GSList *request_tokens; SIPE_DEBUG_INFO_NOFORMAT("SipeSearchManager::create_channel"); if (tp_strdiff(tp_asv_get_string(request_properties, TP_IFACE_CHANNEL ".ChannelType"), TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH)) return(FALSE); /* create new search channel */ channel = search_channel_new(self->connection); g_hash_table_insert(self->channels, channel, NULL); g_signal_connect(channel, "closed", (GCallback) search_channel_closed_cb, self); /* publish new channel */ request_tokens = g_slist_prepend(NULL, request_token); tp_channel_manager_emit_new_channel(self, TP_EXPORTABLE_CHANNEL(channel), request_tokens); g_slist_free(request_tokens); return(TRUE); } static void channel_manager_iface_init(gpointer g_iface, SIPE_UNUSED_PARAMETER gpointer iface_data) { TpChannelManagerIface *iface = g_iface; #define IMPLEMENT(x, y) iface->x = y IMPLEMENT(foreach_channel, foreach_channel); IMPLEMENT(type_foreach_channel_class, type_foreach_channel_class); IMPLEMENT(create_channel, create_channel); IMPLEMENT(request_channel, create_channel); /* Ensuring these channels doesn't really make much sense. */ IMPLEMENT(ensure_channel, NULL); #undef IMPLEMENT } /* create new search manager object */ GObject *sipe_telepathy_search_new(TpBaseConnection *connection) { SipeSearchManager *self = g_object_new(SIPE_TYPE_SEARCH_MANAGER, NULL); self->connection = g_object_ref(G_OBJECT(connection)); return(G_OBJECT(self)); } /* * Search Channel class - instance methods */ enum { CHANNEL_PROP_SEARCH_KEYS = 1, CHANNEL_LAST_PROP }; static void get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case CHANNEL_PROP_SEARCH_KEYS: { /* vCard/Telepathy search field names */ static const gchar *search_keys[] = { SIPE_TELEPATHY_SEARCH_KEY_FIRST, SIPE_TELEPATHY_SEARCH_KEY_LAST, SIPE_TELEPATHY_SEARCH_KEY_EMAIL, SIPE_TELEPATHY_SEARCH_KEY_COMPANY, SIPE_TELEPATHY_SEARCH_KEY_COUNTRY, SIPE_TELEPATHY_SEARCH_KEY_FULLNAME, SIPE_TELEPATHY_SEARCH_KEY_BLOB, NULL }; g_value_set_boxed(value, search_keys); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void fill_immutable_properties(TpBaseChannel *channel, GHashTable *properties) { TP_BASE_CHANNEL_CLASS(sipe_search_channel_parent_class)->fill_immutable_properties(channel, properties); tp_dbus_properties_mixin_fill_properties_hash(G_OBJECT(channel), properties, TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH, "AvailableSearchKeys", NULL); } static gchar *get_object_path_suffix(TpBaseChannel *base) { return(g_strdup_printf ("SearchChannel_%p", base)); } static GPtrArray *get_interfaces(TpBaseChannel *self) { GPtrArray *interfaces = TP_BASE_CHANNEL_CLASS(sipe_search_channel_parent_class)->get_interfaces(self); return(interfaces); } static void sipe_search_channel_constructed(GObject *object) { SipeSearchChannel *self = SIPE_SEARCH_CHANNEL(object); void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_search_channel_parent_class)->constructed; if (chain_up) chain_up(object); self->results = NULL; } static void sipe_search_channel_finalize(GObject *object) { SipeSearchChannel *self = SIPE_SEARCH_CHANNEL(object); SIPE_DEBUG_INFO_NOFORMAT("SipeSearchChannel::finalize"); if (self->results) g_hash_table_unref(self->results); G_OBJECT_CLASS(sipe_search_channel_parent_class)->finalize(object); } /* * Search Channel class - type implementation */ static void sipe_search_channel_class_init(SipeSearchChannelClass *klass) { static TpDBusPropertiesMixinPropImpl props[] = { { .name = "AvailableSearchKeys", .getter_data = "available-search-keys", .setter_data = NULL }, { .name = NULL } }; GObjectClass *object_class = G_OBJECT_CLASS(klass); TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS(klass); GParamSpec *ps; SIPE_DEBUG_INFO_NOFORMAT("SipeSearchChannel::class_init"); object_class->constructed = sipe_search_channel_constructed; object_class->finalize = sipe_search_channel_finalize; object_class->get_property = get_property; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH; base_class->target_handle_type = TP_HANDLE_TYPE_NONE; base_class->fill_immutable_properties = fill_immutable_properties; base_class->get_object_path_suffix = get_object_path_suffix; base_class->interfaces = NULL; base_class->get_interfaces = get_interfaces; base_class->close = tp_base_channel_destroyed; ps = g_param_spec_boxed("available-search-keys", "Available search keys", "The set of search keys supported by this channel", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property(object_class, CHANNEL_PROP_SEARCH_KEYS, ps); tp_dbus_properties_mixin_implement_interface(object_class, TP_IFACE_QUARK_CHANNEL_TYPE_CONTACT_SEARCH, tp_dbus_properties_mixin_getter_gobject_properties, NULL, props); } static void sipe_search_channel_init(SIPE_UNUSED_PARAMETER SipeSearchChannel *self) { SIPE_DEBUG_INFO_NOFORMAT("SipeSearchChannel::init"); } /* * Search Channel class - interface implementation * * Contact search */ static void search_channel_state(SipeSearchChannel *self, TpChannelContactSearchState new_state, const gchar *msg) { GHashTable *details = tp_asv_new(NULL, NULL); if (msg) tp_asv_set_string(details, "debug-message", msg); tp_svc_channel_type_contact_search_emit_search_state_changed(self, new_state, msg ? msg : "", details); g_hash_table_unref(details); self->state = new_state; } static void search_channel_search(TpSvcChannelTypeContactSearch *channel, GHashTable *terms, DBusGMethodInvocation *context) { SipeSearchChannel *self = SIPE_SEARCH_CHANNEL(channel); SIPE_DEBUG_INFO_NOFORMAT("SipeSearchChannel::search"); if (self->state == TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED) { const gchar *first = g_hash_table_lookup(terms, SIPE_TELEPATHY_SEARCH_KEY_FIRST); const gchar *last = g_hash_table_lookup(terms, SIPE_TELEPATHY_SEARCH_KEY_LAST); const gchar *email = g_hash_table_lookup(terms, SIPE_TELEPATHY_SEARCH_KEY_EMAIL); const gchar *company = g_hash_table_lookup(terms, SIPE_TELEPATHY_SEARCH_KEY_COMPANY); const gchar *country = g_hash_table_lookup(terms, SIPE_TELEPATHY_SEARCH_KEY_COUNTRY); struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(self->connection); gchar **split = NULL; /* did the requester honor our "AvailableSearchKeys"? */ if (!(first || last || email || company || country)) { const gchar *alternative = g_hash_table_lookup(terms, SIPE_TELEPATHY_SEARCH_KEY_FULLNAME); /* No. Did he give a full name instead? */ if (alternative) { SIPE_DEBUG_INFO("SipeSearchChannel::search: full name given: '%s'", alternative); /* assume: * - one word -> first name * - two words -> first & last name */ split = g_strsplit(alternative, " ", 3); if (split[0]) { first = split[0]; if (split[1]) last = split[1]; } /* No. Did he give a "on big search box" instead? */ } else if ((alternative = g_hash_table_lookup(terms, SIPE_TELEPATHY_SEARCH_KEY_BLOB)) != NULL) { SIPE_DEBUG_INFO("SipeSearchChannel::search: one big search box given: '%s'", alternative); /* assume: * - one word with '@' -> email * - one word -> first name * - two words -> first & last name */ split = g_strsplit(alternative, " ", 3); if (split[0]) { if (strchr(split[0], '@')) { email = split[0]; } else { first = split[0]; if (split[1]) last = split[1]; } } } else SIPE_DEBUG_ERROR_NOFORMAT("SipeSearchChannel::search: no valid terms found"); } sipe_core_buddy_search(telepathy_private->public, (struct sipe_backend_search_token *) self, first, last, email, NULL, company, country); g_strfreev(split); /* only switch to "in progress" if the above didn't fail */ if (self->state == TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED) search_channel_state(self, TP_CHANNEL_CONTACT_SEARCH_STATE_IN_PROGRESS, NULL); tp_svc_channel_type_contact_search_return_from_search(context); } else { GError *error = g_error_new(TP_ERROR, TP_ERROR_NOT_AVAILABLE, "invalid search state"); dbus_g_method_return_error(context, error); g_error_free(error); } } static void contact_search_iface_init(gpointer g_iface, SIPE_UNUSED_PARAMETER gpointer iface_data) { TpSvcChannelTypeContactSearchClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_channel_type_contact_search_implement_##x( \ klass, search_channel_##x) IMPLEMENT(search); /* we don't support stopping a search */ #undef IMPLEMENT } /* create new search channel object */ static GObject *search_channel_new(GObject *connection) { /* property "connection" required by TpBaseChannel */ SipeSearchChannel *self = g_object_new(SIPE_TYPE_SEARCH_CHANNEL, "connection", connection, NULL); self->connection = g_object_ref(connection); self->state = TP_CHANNEL_CONTACT_SEARCH_STATE_NOT_STARTED; tp_base_channel_register(TP_BASE_CHANNEL(self)); return(G_OBJECT(self)); } /* * Backend adaptor functions */ void sipe_backend_search_failed(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_search_token *token, const gchar *msg) { SIPE_DEBUG_INFO("sipe_backend_search_failed: %s", msg); search_channel_state(SIPE_SEARCH_CHANNEL(token), TP_CHANNEL_CONTACT_SEARCH_STATE_FAILED, msg); } static void free_info(GPtrArray *info) { g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info); } struct sipe_backend_search_results *sipe_backend_search_results_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_search_token *token) { SipeSearchChannel *self = SIPE_SEARCH_CHANNEL(token); self->results = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) free_info); return((struct sipe_backend_search_results *) self); } /* adds: the Contact_Info_Field (field_name, [], values) */ static void add_search_result(GPtrArray *info, const gchar *field_name, const gchar *field_value) { if (field_value) { static const gchar **empty = { NULL }; GValueArray *field = g_value_array_new(3); const gchar *components[] = { field_value, NULL }; GValue *value; SIPE_DEBUG_INFO("add_search_result: %s = '%s'", field_name, field_value); g_value_array_append(field, NULL); value = g_value_array_get_nth(field, 0); g_value_init(value, G_TYPE_STRING); g_value_set_static_string(value, field_name); g_value_array_append(field, NULL); value = g_value_array_get_nth(field, 1); g_value_init(value, G_TYPE_STRV); g_value_set_static_boxed(value, empty); g_value_array_append(field, NULL); value = g_value_array_get_nth(field, 2); g_value_init(value, G_TYPE_STRV); g_value_set_boxed(value, components); g_ptr_array_add(info, field); } } void sipe_backend_search_results_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_search_results *results, const gchar *uri, const gchar *name, const gchar *company, const gchar *country, const gchar *email) { SipeSearchChannel *self = SIPE_SEARCH_CHANNEL(results); GPtrArray *info = g_ptr_array_new(); add_search_result(info, SIPE_TELEPATHY_SEARCH_KEY_FULLNAME, name); add_search_result(info, SIPE_TELEPATHY_SEARCH_KEY_COMPANY, company); add_search_result(info, SIPE_TELEPATHY_SEARCH_KEY_COUNTRY, country); add_search_result(info, SIPE_TELEPATHY_SEARCH_KEY_EMAIL, email); g_hash_table_insert(self->results, g_strdup(uri), info); } void sipe_backend_search_results_finalize(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, struct sipe_backend_search_results *results, SIPE_UNUSED_PARAMETER const gchar *description, SIPE_UNUSED_PARAMETER gboolean more) { SipeSearchChannel *self = SIPE_SEARCH_CHANNEL(results); tp_svc_channel_type_contact_search_emit_search_result_received(self, self->results); search_channel_state(self, TP_CHANNEL_CONTACT_SEARCH_STATE_COMPLETED, NULL); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-status.c ================================================ /** * @file telepathy-status.c * * pidgin-sipe * * Copyright (C) 2012-2019 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "telepathy-private.h" static const TpPresenceStatusOptionalArgumentSpec args[] = { { .name = "message", .dtype = "s" }, { .name = NULL, .dtype = NULL } }; /* Sipe core activity <-> Telepathy status mapping */ #define SIPE_TELEPATHY_STATUS(_name, _type, _self, _args) \ { \ .name = (_name), \ .presence_type = (_type), \ .self = (_self), \ .optional_arguments = (_args), \ } #define SIPE_TELEPATHY_STATUS_NONE(_name, _type, _self) \ SIPE_TELEPATHY_STATUS(_name, _type, _self, NULL) #define SIPE_TELEPATHY_STATUS_MESSAGE(_name, _type, _self) \ SIPE_TELEPATHY_STATUS(_name, _type, _self, args) static const TpPresenceStatusSpec statuses[SIPE_ACTIVITY_NUM_TYPES + 1] = { /* SIPE_ACTIVITY_UNSET */ SIPE_TELEPATHY_STATUS_NONE( "unset", TP_CONNECTION_PRESENCE_TYPE_UNSET, FALSE), /* SIPE_ACTIVITY_AVAILABLE */ SIPE_TELEPATHY_STATUS_MESSAGE("available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE), /* SIPE_ACTIVITY_ONLINE */ SIPE_TELEPATHY_STATUS_MESSAGE("online", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE), /* SIPE_ACTIVITY_INACTIVE */ SIPE_TELEPATHY_STATUS_MESSAGE("idle", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE), /* SIPE_ACTIVITY_BUSY */ SIPE_TELEPATHY_STATUS_MESSAGE("busy", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), /* SIPE_ACTIVITY_BUSYIDLE */ SIPE_TELEPATHY_STATUS_MESSAGE("busyidle", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), /* SIPE_ACTIVITY_DND */ SIPE_TELEPATHY_STATUS_MESSAGE("do-not-disturb", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), /* SIPE_ACTIVITY_BRB */ SIPE_TELEPATHY_STATUS_MESSAGE("be-right-back", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE), /* SIPE_ACTIVITY_AWAY */ SIPE_TELEPATHY_STATUS_MESSAGE("away", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE), /* SIPE_ACTIVITY_LUNCH */ SIPE_TELEPATHY_STATUS_MESSAGE("out-to-lunch", TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY, TRUE), /* SIPE_ACTIVITY_INVISIBLE */ SIPE_TELEPATHY_STATUS_NONE( "invisible", TP_CONNECTION_PRESENCE_TYPE_HIDDEN, TRUE), /* SIPE_ACTIVITY_OFFLINE */ SIPE_TELEPATHY_STATUS_NONE( "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE), /* SIPE_ACTIVITY_ON_PHONE */ SIPE_TELEPATHY_STATUS_MESSAGE("on-the-phone", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), /* SIPE_ACTIVITY_IN_CONF */ SIPE_TELEPATHY_STATUS_MESSAGE("in-a-conference", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), /* SIPE_ACTIVITY_IN_MEETING */ SIPE_TELEPATHY_STATUS_MESSAGE("in-a-meeting", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), /* SIPE_ACTIVITY_OOF */ SIPE_TELEPATHY_STATUS_MESSAGE("out-of-office", TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY, TRUE), /* SIPE_ACTIVITY_URGENT_ONLY */ SIPE_TELEPATHY_STATUS_MESSAGE("urgent-interruptions-only", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE), /* end-of-array indicator */ SIPE_TELEPATHY_STATUS_NONE( NULL, 0, FALSE) }; static gboolean status_available(SIPE_UNUSED_PARAMETER GObject *object, guint index) { /* * @TODO: what is this function supposed to do? * - TRUE: index is one of the "user is available" statuses? * - TRUE: index is a valid status? */ return(statuses[index].name != NULL); } static GHashTable *get_contact_statuses(GObject *object, const GArray *contacts, SIPE_UNUSED_PARAMETER GError **error) { struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(object); TpBaseConnection *base = TP_BASE_CONNECTION(object); GHashTable *status_table = g_hash_table_new(g_direct_hash, g_direct_equal); guint i; for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index(contacts, guint, i); guint activity; GHashTable *parameters; /* we get our own status from the connection, and everyone * else's status from the contact lists */ if (contact == tp_base_connection_get_self_handle(base)) { activity = telepathy_private->activity; } else { /* @TODO */ activity = sipe_telepathy_buddy_get_presence(telepathy_private->contact_list, contact); } parameters = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); g_hash_table_insert(status_table, GUINT_TO_POINTER(contact), tp_presence_status_new(activity, parameters)); g_hash_table_unref(parameters); } return(status_table); } static void update_status(struct sipe_backend_private *telepathy_private, guint activity, const gchar *message, const TpPresenceStatus *status, gboolean outgoing) { GObject *connection = G_OBJECT(telepathy_private->connection); GHashTable *presences; /* update internal status */ telepathy_private->activity = activity; g_free(telepathy_private->message); telepathy_private->message = NULL; if (message) telepathy_private->message = g_strdup(message); /* outgoing status update */ if (outgoing) sipe_core_status_set(telepathy_private->public, TRUE, activity, message); /* emit status update signal */ presences = g_hash_table_new(g_direct_hash, g_direct_equal); g_hash_table_insert(presences, GUINT_TO_POINTER(tp_base_connection_get_self_handle(TP_BASE_CONNECTION(connection))), (gpointer) status); tp_presence_mixin_emit_presence_update(connection, presences); g_hash_table_unref(presences); } static gboolean set_own_status(GObject *object, const TpPresenceStatus *status, SIPE_UNUSED_PARAMETER GError **error) { struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(object); guint activity = SIPE_ACTIVITY_AVAILABLE; const gchar *message = NULL; if (!telepathy_private) return(FALSE); if (status) { activity = status->index; if (status->optional_arguments) message = tp_asv_get_string(status->optional_arguments, "message"); } SIPE_DEBUG_INFO("set_own_status: %d '%s'", activity, message ? message : "(none)"); update_status(telepathy_private, activity, message, status, TRUE); return(TRUE); } void sipe_telepathy_status_init(GObjectClass *object_class, gsize struct_offset) { tp_presence_mixin_class_init(object_class, struct_offset, status_available, get_contact_statuses, set_own_status, statuses); } /* * Backend adaptor functions */ guint sipe_backend_status(struct sipe_core_public *sipe_public) { return(sipe_public->backend_private->activity); } gboolean sipe_backend_status_changed(struct sipe_core_public *sipe_public, guint activity, const gchar *message) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; if ((activity == telepathy_private->activity) && sipe_strequal(message, telepathy_private->message)) return(FALSE); return(TRUE); } /* * This is used by: * * - incoming status updates (roaming) * - induced status updates (calendar) */ void sipe_backend_status_and_note(struct sipe_core_public *sipe_public, guint activity, const gchar *message) { struct sipe_backend_private *telepathy_private = sipe_public->backend_private; GHashTable *optional = NULL; TpPresenceStatus *status; if (message) optional = tp_asv_new("message", G_TYPE_STRING, message, NULL); status = tp_presence_status_new(activity, optional); if (optional) g_hash_table_unref(optional); update_status(telepathy_private, activity, message, status, FALSE); tp_presence_status_free(status); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-stubs.c ================================================ /** * @file telepathy-stubs.c * * pidgin-sipe * * Copyright (C) 2012-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Stubs for all unimplemented backend functions, because * * - feature is not yet implemented, or * - feature can't be implemented for telepathy backend * * Ordering copied from sipe-backend.h */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "sipe-backend.h" #include "sipe-common.h" /** BUDDIES ******************************************************************/ void sipe_backend_buddy_list_processing_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) {} void sipe_backend_buddy_request_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *who, SIPE_UNUSED_PARAMETER const gchar *alias) {} void sipe_backend_buddy_request_authorization(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *who, SIPE_UNUSED_PARAMETER const gchar *alias, SIPE_UNUSED_PARAMETER gboolean on_list, SIPE_UNUSED_PARAMETER sipe_backend_buddy_request_authorization_cb auth_cb, SIPE_UNUSED_PARAMETER sipe_backend_buddy_request_authorization_cb deny_cb, SIPE_UNUSED_PARAMETER gpointer data) {} gboolean sipe_backend_buddy_is_blocked(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *who) { return(FALSE); } void sipe_backend_buddy_set_blocked_status(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *who, SIPE_UNUSED_PARAMETER gboolean blocked) {} gboolean sipe_backend_buddy_group_rename(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *old_name, SIPE_UNUSED_PARAMETER const gchar *new_name) { return(FALSE); } struct sipe_backend_buddy_info *sipe_backend_buddy_info_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { return(NULL); } void sipe_backend_buddy_info_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_buddy_info *info, SIPE_UNUSED_PARAMETER sipe_buddy_info_fields key, SIPE_UNUSED_PARAMETER const gchar *value) {} void sipe_backend_buddy_info_break(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_buddy_info *info) {} void sipe_backend_buddy_info_finalize(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_buddy_info *info, SIPE_UNUSED_PARAMETER const gchar *uri) {} void sipe_backend_buddy_tooltip_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_buddy_tooltip *tooltip, SIPE_UNUSED_PARAMETER const gchar *description, SIPE_UNUSED_PARAMETER const gchar *value) {} struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_start(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { return(NULL); } struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_buddy_menu *menu, SIPE_UNUSED_PARAMETER const gchar *label, SIPE_UNUSED_PARAMETER enum sipe_buddy_menu_type type, SIPE_UNUSED_PARAMETER gpointer parameter) { return(NULL); } struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_separator(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_buddy_menu *menu, SIPE_UNUSED_PARAMETER const gchar *label) { return(NULL); } struct sipe_backend_buddy_menu *sipe_backend_buddy_sub_menu_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_buddy_menu *menu, SIPE_UNUSED_PARAMETER const gchar *label, SIPE_UNUSED_PARAMETER struct sipe_backend_buddy_menu *sub) { return(NULL); } /** CHAT *********************************************************************/ void sipe_backend_chat_session_destroy(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *session) {} void sipe_backend_chat_add(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session, SIPE_UNUSED_PARAMETER const gchar *uri, SIPE_UNUSED_PARAMETER gboolean is_new) {} void sipe_backend_chat_close(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session) {} struct sipe_backend_chat_session *sipe_backend_chat_create(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_chat_session *session, SIPE_UNUSED_PARAMETER const gchar *title, SIPE_UNUSED_PARAMETER const gchar *nick) { return(NULL); } gboolean sipe_backend_chat_find(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session, SIPE_UNUSED_PARAMETER const gchar *uri) { return(FALSE); } gboolean sipe_backend_chat_is_operator(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session, SIPE_UNUSED_PARAMETER const gchar *uri) { return(FALSE); } void sipe_backend_chat_message(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session, SIPE_UNUSED_PARAMETER const gchar *from, SIPE_UNUSED_PARAMETER time_t when, SIPE_UNUSED_PARAMETER const gchar *html) {} void sipe_backend_chat_operator(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session, SIPE_UNUSED_PARAMETER const gchar *uri) {} void sipe_backend_chat_rejoin(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session, SIPE_UNUSED_PARAMETER const gchar *nick, SIPE_UNUSED_PARAMETER const gchar *title) {} void sipe_backend_chat_rejoin_all(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) {} void sipe_backend_chat_remove(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session, SIPE_UNUSED_PARAMETER const gchar *uri) {} void sipe_backend_chat_show(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session) {} void sipe_backend_chat_topic(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session, SIPE_UNUSED_PARAMETER const gchar *topic) {} /** FILE TRANSFER ************************************************************/ void sipe_backend_ft_error(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft, SIPE_UNUSED_PARAMETER const gchar *errmsg) {} const gchar *sipe_backend_ft_get_error(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft) { return(""); } void sipe_backend_ft_deallocate(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft) {} gssize sipe_backend_ft_read(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft, SIPE_UNUSED_PARAMETER guchar *data, SIPE_UNUSED_PARAMETER gsize size) { return(-1); } gssize sipe_backend_ft_write(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft, SIPE_UNUSED_PARAMETER const guchar *data, SIPE_UNUSED_PARAMETER gsize size) { return(-1); } void sipe_backend_ft_set_completed(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft) {} void sipe_backend_ft_cancel_local(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft) {} void sipe_backend_ft_cancel_remote(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft) {} void sipe_backend_ft_incoming(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft, SIPE_UNUSED_PARAMETER const gchar *who, SIPE_UNUSED_PARAMETER const gchar *file_name, SIPE_UNUSED_PARAMETER gsize file_size) {} void sipe_backend_ft_outgoing(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft, SIPE_UNUSED_PARAMETER const gchar *who, SIPE_UNUSED_PARAMETER const gchar *file_name) {} void sipe_backend_ft_start(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft, SIPE_UNUSED_PARAMETER struct sipe_backend_fd *fd, SIPE_UNUSED_PARAMETER const char* ip, SIPE_UNUSED_PARAMETER unsigned port) {} gboolean sipe_backend_ft_is_incoming(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft) { return(FALSE); } /** GROUP CHAT ***************************************************************/ void sipe_backend_groupchat_room_add(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *uri, SIPE_UNUSED_PARAMETER const gchar *name, SIPE_UNUSED_PARAMETER const gchar *description, SIPE_UNUSED_PARAMETER guint users, SIPE_UNUSED_PARAMETER guint32 flags) {} void sipe_backend_groupchat_room_terminate(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) {} /** IM ***********************************************************************/ void sipe_backend_im_message(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *from, SIPE_UNUSED_PARAMETER const gchar *html) {} void sipe_backend_im_topic(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *with, SIPE_UNUSED_PARAMETER const gchar *topic) {} /** MARKUP *******************************************************************/ gchar *sipe_backend_markup_css_property(SIPE_UNUSED_PARAMETER SIPE_UNUSED_PARAMETER const gchar *style, SIPE_UNUSED_PARAMETER const gchar *option) { return(g_strdup("")); } gchar *sipe_backend_markup_strip_html(SIPE_UNUSED_PARAMETER SIPE_UNUSED_PARAMETER const gchar *html) { return(g_strdup("")); } /** MEDIA ********************************************************************/ #ifdef HAVE_VV struct sipe_backend_media *sipe_backend_media_new(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_media_call *call, SIPE_UNUSED_PARAMETER const gchar *participant, SIPE_UNUSED_PARAMETER SipeMediaCallFlags flags) { return(NULL); } void sipe_backend_media_free(SIPE_UNUSED_PARAMETER struct sipe_backend_media *media) {} void sipe_backend_media_set_cname(SIPE_UNUSED_PARAMETER struct sipe_backend_media *media, SIPE_UNUSED_PARAMETER gchar *cname) {} struct sipe_backend_media_relays * sipe_backend_media_relays_convert(SIPE_UNUSED_PARAMETER GSList *media_relays, SIPE_UNUSED_PARAMETER gchar *username, SIPE_UNUSED_PARAMETER gchar *password) { return(NULL); } void sipe_backend_media_relays_free(SIPE_UNUSED_PARAMETER struct sipe_backend_media_relays *media_relays) {} struct sipe_backend_media_stream *sipe_backend_media_add_stream(SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER SipeMediaType type, SIPE_UNUSED_PARAMETER SipeIceVersion ice_version, SIPE_UNUSED_PARAMETER gboolean initiator, SIPE_UNUSED_PARAMETER struct sipe_backend_media_relays *media_relays, SIPE_UNUSED_PARAMETER guint min_port, SIPE_UNUSED_PARAMETER guint max_port) { return(NULL); } void sipe_backend_media_add_remote_candidates(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER GList *candidates) {} gboolean sipe_backend_media_is_initiator(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream) { return(FALSE); } gboolean sipe_backend_media_accepted(SIPE_UNUSED_PARAMETER struct sipe_backend_media *media) { return(FALSE); } gboolean sipe_backend_stream_initialized(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream) { return(FALSE); } GList *sipe_backend_media_stream_get_active_local_candidates(SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream) { return(NULL); } GList *sipe_backend_media_stream_get_active_remote_candidates(SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream) { return(NULL); } void sipe_backend_media_set_encryption_keys(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER const guchar *encryption_key, SIPE_UNUSED_PARAMETER const guchar *decryption_key) {} void sipe_backend_media_set_require_encryption(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER const gboolean require_encryption) {} void sipe_backend_stream_hold(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER gboolean local) {} void sipe_backend_stream_unhold(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER gboolean local) {} gboolean sipe_backend_stream_is_held(SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream) { return(FALSE); } void sipe_backend_media_stream_end(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream) {} void sipe_backend_media_stream_free(SIPE_UNUSED_PARAMETER struct sipe_backend_media_stream *stream) {} struct sipe_backend_codec *sipe_backend_codec_new(SIPE_UNUSED_PARAMETER int id, SIPE_UNUSED_PARAMETER const char *name, SIPE_UNUSED_PARAMETER SipeMediaType type, SIPE_UNUSED_PARAMETER guint clock_rate, SIPE_UNUSED_PARAMETER guint channels) { return(NULL); } void sipe_backend_codec_free(SIPE_UNUSED_PARAMETER struct sipe_backend_codec *codec) {} int sipe_backend_codec_get_id(SIPE_UNUSED_PARAMETER struct sipe_backend_codec *codec) { return(0); } gchar *sipe_backend_codec_get_name(SIPE_UNUSED_PARAMETER struct sipe_backend_codec *codec) { return(g_strdup("")); } guint sipe_backend_codec_get_clock_rate(SIPE_UNUSED_PARAMETER struct sipe_backend_codec *codec) { return(0); } void sipe_backend_codec_add_optional_parameter(SIPE_UNUSED_PARAMETER struct sipe_backend_codec *codec, SIPE_UNUSED_PARAMETER const gchar *name, SIPE_UNUSED_PARAMETER const gchar *value) {} GList *sipe_backend_codec_get_optional_parameters(SIPE_UNUSED_PARAMETER struct sipe_backend_codec *codec) { return(NULL); } gboolean sipe_backend_set_remote_codecs(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER GList *codecs) { return(FALSE); } GList* sipe_backend_get_local_codecs(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream) { return(NULL); } struct sipe_backend_candidate * sipe_backend_candidate_new(SIPE_UNUSED_PARAMETER const gchar *foundation, SIPE_UNUSED_PARAMETER SipeComponentType component, SIPE_UNUSED_PARAMETER SipeCandidateType type, SIPE_UNUSED_PARAMETER SipeNetworkProtocol proto, SIPE_UNUSED_PARAMETER const gchar *ip, SIPE_UNUSED_PARAMETER guint port, SIPE_UNUSED_PARAMETER const gchar *username, SIPE_UNUSED_PARAMETER const gchar *password) { return(NULL); } void sipe_backend_candidate_free(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) {} gchar *sipe_backend_candidate_get_username(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(g_strdup("")); } gchar *sipe_backend_candidate_get_password(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(g_strdup("")); } gchar *sipe_backend_candidate_get_foundation(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(g_strdup("")); } gchar *sipe_backend_candidate_get_ip(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(g_strdup("127.0.0.1")); } guint sipe_backend_candidate_get_port(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(0); } gchar *sipe_backend_candidate_get_base_ip(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(g_strdup("127.0.0.1")); } guint sipe_backend_candidate_get_base_port(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(0); } guint32 sipe_backend_candidate_get_priority(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(0); } void sipe_backend_candidate_set_priority(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate, SIPE_UNUSED_PARAMETER guint32 priority) {} SipeComponentType sipe_backend_candidate_get_component_type(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(SIPE_COMPONENT_NONE); } SipeCandidateType sipe_backend_candidate_get_type(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(SIPE_CANDIDATE_TYPE_ANY); } SipeNetworkProtocol sipe_backend_candidate_get_protocol(SIPE_UNUSED_PARAMETER struct sipe_backend_candidate *candidate) { return(SIPE_NETWORK_PROTOCOL_TCP_ACTIVE); } GList* sipe_backend_get_local_candidates(SIPE_UNUSED_PARAMETER struct sipe_media_call *media, SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream) { return(NULL); } void sipe_backend_media_accept(SIPE_UNUSED_PARAMETER struct sipe_backend_media *media, SIPE_UNUSED_PARAMETER gboolean local) {} void sipe_backend_media_hangup(SIPE_UNUSED_PARAMETER struct sipe_backend_media *media, SIPE_UNUSED_PARAMETER gboolean local) {} void sipe_backend_media_reject(SIPE_UNUSED_PARAMETER struct sipe_backend_media *media, SIPE_UNUSED_PARAMETER gboolean local) {} SipeEncryptionPolicy sipe_backend_media_get_encryption_policy(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public) { return(SIPE_ENCRYPTION_POLICY_REJECTED); } gssize sipe_backend_media_stream_read(SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER guint8 *buffer, SIPE_UNUSED_PARAMETER gsize len) { return(-1); } gssize sipe_backend_media_stream_write(SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream, SIPE_UNUSED_PARAMETER guint8 *buffer, SIPE_UNUSED_PARAMETER gsize len) { return(-1); } #endif /** NETWORK ******************************************************************/ struct sipe_backend_listendata *sipe_backend_network_listen_range(SIPE_UNUSED_PARAMETER unsigned short port_min, SIPE_UNUSED_PARAMETER unsigned short port_max, SIPE_UNUSED_PARAMETER sipe_listen_start_cb listen_cb, SIPE_UNUSED_PARAMETER sipe_client_connected_cb connect_cb, SIPE_UNUSED_PARAMETER gpointer data) { return(NULL); } void sipe_backend_network_listen_cancel(SIPE_UNUSED_PARAMETER struct sipe_backend_listendata *ldata) {} struct sipe_backend_fd *sipe_backend_fd_from_int(SIPE_UNUSED_PARAMETER int fd) { return (NULL); } gboolean sipe_backend_fd_is_valid(SIPE_UNUSED_PARAMETER struct sipe_backend_fd *fd) { return(FALSE); } void sipe_backend_fd_free(SIPE_UNUSED_PARAMETER struct sipe_backend_fd *fd) {} /** NOTIFICATIONS *************************************************************/ void sipe_backend_notify_message_error(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session, SIPE_UNUSED_PARAMETER const gchar *who, SIPE_UNUSED_PARAMETER const gchar *message) {} void sipe_backend_notify_message_info(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *backend_session, SIPE_UNUSED_PARAMETER const gchar *who, SIPE_UNUSED_PARAMETER const gchar *message) {} void sipe_backend_notify_error(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *title, SIPE_UNUSED_PARAMETER const gchar *msg) {} /** USER *********************************************************************/ void sipe_backend_user_feedback_typing(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *from) {} void sipe_backend_user_feedback_typing_stop(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *from) {} void sipe_backend_user_ask(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *message, SIPE_UNUSED_PARAMETER const gchar *accept_label, SIPE_UNUSED_PARAMETER const gchar *decline_label, SIPE_UNUSED_PARAMETER gpointer key) {} void sipe_backend_user_ask_choice(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public, SIPE_UNUSED_PARAMETER const gchar *message, SIPE_UNUSED_PARAMETER GSList *choices, SIPE_UNUSED_PARAMETER gpointer key) {} void sipe_backend_user_close_ask(SIPE_UNUSED_PARAMETER gpointer key) {} /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-tls.c ================================================ /** * @file telepathy-tls.c * * pidgin-sipe * * Copyright (C) 2013-2018 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * TLS certificate accept/reject user interaction */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "sipe-backend.h" #include "sipe-common.h" #include "telepathy-private.h" /* TLS information required for user interaction */ struct _SipeTLSCertificate; struct sipe_tls_info { gchar *hostname; gchar *cert_path; GPtrArray *cert_data; GStrv reference_identities; struct _SipeTLSCertificate *certificate; }; /* Certificate states */ #define SIPE_TLS_CERTIFICATE_PENDING 0 #define SIPE_TLS_CERTIFICATE_REJECTED 1 #define SIPE_TLS_CERTIFICATE_ACCEPTED 2 G_BEGIN_DECLS /* * TLS Manager class - data structures */ typedef struct _SipeTLSManagerClass { GObjectClass parent_class; } SipeTLSManagerClass; typedef struct _SipeTLSManager { GObject parent; GObject *connection; GSList *channels; } SipeTLSManager; /* * TLS Manager class - type macros */ static GType sipe_tls_manager_get_type(void); #define SIPE_TYPE_TLS_MANAGER \ (sipe_tls_manager_get_type()) #define SIPE_TLS_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_TLS_MANAGER, \ SipeTLSManager)) /* * TLS Channel class - data structures */ typedef struct _SipeTLSChannelClass { TpBaseChannelClass parent_class; } SipeTLSChannelClass; typedef struct _SipeTLSChannel { TpBaseChannel parent; const struct sipe_tls_info *tls_info; GSimpleAsyncResult *result; } SipeTLSChannel; /* * TLS Channel class - type macros */ static GType sipe_tls_channel_get_type(void) G_GNUC_CONST; #define SIPE_TYPE_TLS_CHANNEL \ (sipe_tls_channel_get_type()) #define SIPE_TLS_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_TLS_CHANNEL, \ SipeTLSChannel)) /* * TLS Certificate class - data structures */ typedef struct _SipeTLSCertificateClass { GObjectClass parent_class; TpDBusPropertiesMixinClass dbus_props_class; } SipeTLSCertificateClass; typedef struct _SipeTLSCertificate { GObject parent; const struct sipe_tls_info *tls_info; guint state; } SipeTLSCertificate; /* * TLS Certificate class - type macros */ static GType sipe_tls_certificate_get_type(void) G_GNUC_CONST; #define SIPE_TYPE_TLS_CERTIFICATE \ (sipe_tls_certificate_get_type()) #define SIPE_TLS_CERTIFICATE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_TLS_CERTIFICATE, \ SipeTLSCertificate)) G_END_DECLS /* * TLS Manager class - type definition */ static void channel_manager_iface_init(gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE(SipeTLSManager, sipe_tls_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init); ) /* * TLS Channel class - type definition */ G_DEFINE_TYPE_WITH_CODE(SipeTLSChannel, sipe_tls_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_TYPE_SERVER_TLS_CONNECTION, NULL); ) /* * TLS Certificate class - type definition */ static void tls_certificate_iface_init(gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (SipeTLSCertificate, sipe_tls_certificate, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_AUTHENTICATION_TLS_CERTIFICATE, tls_certificate_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init); ) /* * TLS Manager class - instance methods */ static void sipe_tls_manager_constructed(GObject *object) { SipeTLSManager *self = SIPE_TLS_MANAGER(object); void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_tls_manager_parent_class)->constructed; if (chain_up) chain_up(object); self->channels = NULL; } static void sipe_tls_manager_dispose(GObject *object) { SipeTLSManager *self = SIPE_TLS_MANAGER(object); void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_tls_manager_parent_class)->constructed; tp_clear_object(&self->connection); if (chain_up) chain_up(object); } static void sipe_tls_manager_finalize(GObject *object) { SipeTLSManager *self = SIPE_TLS_MANAGER(object); void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_tls_manager_parent_class)->constructed; GSList *entry = self->channels; /* close channels */ while (entry) { GSList *next = entry->next; /* removes entry from list */ tp_base_channel_close(entry->data); entry = next; } tp_clear_object(&self->connection); if (chain_up) chain_up(object); } /* * TLS Manager class - type implementation */ static void sipe_tls_manager_class_init(SipeTLSManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); SIPE_DEBUG_INFO_NOFORMAT("SipeTLSManager::class_init"); object_class->constructed = sipe_tls_manager_constructed; object_class->dispose = sipe_tls_manager_dispose; object_class->finalize = sipe_tls_manager_finalize; } static void sipe_tls_manager_init(SIPE_UNUSED_PARAMETER SipeTLSManager *self) { SIPE_DEBUG_INFO_NOFORMAT("SipeTLSManager::init"); } /* * TLS Manager class - interface implementation * * Channel Manager */ static void foreach_channel(TpChannelManager *manager, TpExportableChannelFunc func, gpointer user_data) { SipeTLSManager *self = SIPE_TLS_MANAGER(manager); GSList *entry; SIPE_DEBUG_INFO_NOFORMAT("SipeTLSManager::foreach_channel"); for (entry = self->channels; entry; entry = entry->next) func(entry->data, user_data); } static void channel_manager_iface_init(gpointer g_iface, SIPE_UNUSED_PARAMETER gpointer iface_data) { TpChannelManagerIface *iface = g_iface; #define IMPLEMENT(x, y) iface->x = y IMPLEMENT(foreach_channel, foreach_channel); /* These channels are not requestable. */ IMPLEMENT(type_foreach_channel_class, NULL); IMPLEMENT(create_channel, NULL); IMPLEMENT(request_channel, NULL); IMPLEMENT(ensure_channel, NULL); #undef IMPLEMENT } /* create new TLS manager object */ SipeTLSManager *sipe_telepathy_tls_new(TpBaseConnection *connection) { SipeTLSManager *self = g_object_new(SIPE_TYPE_TLS_MANAGER, NULL); self->connection = g_object_ref(G_OBJECT(connection)); return(self); } static void channel_closed_cb(SipeTLSChannel *channel, SipeTLSManager *self) { SIPE_DEBUG_INFO("channel_closed_cb: %p", channel); self->channels = g_slist_remove(self->channels, channel); tp_channel_manager_emit_channel_closed_for_object(self, TP_EXPORTABLE_CHANNEL(channel)); g_object_unref(channel); } static void manager_new_channel(SipeTLSManager *self, SipeTLSChannel *channel) { self->channels = g_slist_prepend(self->channels, g_object_ref(channel)); g_signal_connect(channel, "closed", G_CALLBACK(channel_closed_cb), self); /* emit NewChannel on the ChannelManager iface */ tp_channel_manager_emit_new_channel(self, TP_EXPORTABLE_CHANNEL(channel), NULL); } /* * TLS Channel class - instance methods */ enum { CHANNEL_PROP_SERVER_CERTIFICATE = 1, CHANNEL_PROP_HOSTNAME, CHANNEL_PROP_REFERENCE_IDENTITIES, CHANNEL_LAST_PROP }; static void channel_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SipeTLSChannel *self = SIPE_TLS_CHANNEL(object); switch (property_id) { case CHANNEL_PROP_SERVER_CERTIFICATE: g_value_set_boxed(value, self->tls_info->cert_path); break; case CHANNEL_PROP_HOSTNAME: g_value_set_string(value, self->tls_info->hostname); break; case CHANNEL_PROP_REFERENCE_IDENTITIES: g_value_set_boxed(value, self->tls_info->reference_identities); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void channel_fill_immutable_properties(TpBaseChannel *channel, GHashTable *properties) { TP_BASE_CHANNEL_CLASS(sipe_tls_channel_parent_class)->fill_immutable_properties(channel, properties); tp_dbus_properties_mixin_fill_properties_hash(G_OBJECT(channel), properties, TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, "ServerCertificate", TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, "Hostname", TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, "ReferenceIdentities", NULL); } static gchar *channel_get_object_path_suffix(TpBaseChannel *base) { return(g_strdup_printf("TLSChannel_%p", base)); } static void sipe_tls_channel_constructed(GObject *object) { void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_tls_channel_parent_class)->constructed; if (chain_up) chain_up(object); } static void sipe_tls_channel_finalize(GObject *object) { SipeTLSChannel *self = SIPE_TLS_CHANNEL(object); SIPE_DEBUG_INFO_NOFORMAT("SipeTLSChannel::finalize"); if (self->result) { g_simple_async_result_set_error(self->result, TP_ERROR, TP_ERROR_CANCELLED, "The TLS channel is being destroyed"); g_simple_async_result_complete_in_idle(self->result); g_clear_object(&self->result); } G_OBJECT_CLASS(sipe_tls_channel_parent_class)->finalize(object); } /* * TLS Channel class - type implementation */ static void sipe_tls_channel_class_init(SipeTLSChannelClass *klass) { static TpDBusPropertiesMixinPropImpl props[] = { { .name = "ServerCertificate", .getter_data = "server-certificate", .setter_data = NULL }, { .name = "Hostname", .getter_data = "hostname", .setter_data = NULL }, { .name = "ReferenceIdentities", .getter_data = "reference-identities", .setter_data = NULL }, { .name = NULL } }; GObjectClass *object_class = G_OBJECT_CLASS(klass); TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS(klass); GParamSpec *ps; SIPE_DEBUG_INFO_NOFORMAT("SipeTLSChannel::class_init"); object_class->constructed = sipe_tls_channel_constructed; object_class->finalize = sipe_tls_channel_finalize; object_class->get_property = channel_get_property; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION; base_class->target_handle_type = TP_HANDLE_TYPE_NONE; base_class->fill_immutable_properties = channel_fill_immutable_properties; base_class->get_object_path_suffix = channel_get_object_path_suffix; base_class->interfaces = NULL; base_class->close = tp_base_channel_destroyed; ps = g_param_spec_boxed("server-certificate", "Server certificate path", "The object path of the server certificate.", DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property(object_class, CHANNEL_PROP_SERVER_CERTIFICATE, ps); ps = g_param_spec_string("hostname", "The hostname to be verified", "The hostname which should be certified by the server certificate.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property(object_class, CHANNEL_PROP_HOSTNAME, ps); ps = g_param_spec_boxed("reference-identities", "The various identities to check the certificate against", "The server certificate identity should match one of these identities.", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property(object_class, CHANNEL_PROP_REFERENCE_IDENTITIES, ps); tp_dbus_properties_mixin_implement_interface(object_class, TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION, tp_dbus_properties_mixin_getter_gobject_properties, NULL, props); } static void sipe_tls_channel_init(SIPE_UNUSED_PARAMETER SipeTLSChannel *self) { SIPE_DEBUG_INFO_NOFORMAT("SipeTLSChannel::init"); } static void certificate_accepted_cb(SIPE_UNUSED_PARAMETER SipeTLSCertificate *certificate, SipeTLSChannel *self) { g_simple_async_result_complete(self->result); g_clear_object(&self->result); tp_base_channel_close(TP_BASE_CHANNEL(self)); } static void certificate_rejected_cb(SIPE_UNUSED_PARAMETER SipeTLSCertificate *certificate, SIPE_UNUSED_PARAMETER GPtrArray *rejections, SipeTLSChannel *self) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string("server-tls-error"); g_simple_async_result_set_error(self->result, quark, 0, "TLS certificate rejected"); g_simple_async_result_complete(self->result); g_clear_object(&self->result); tp_base_channel_close(TP_BASE_CHANNEL(self)); } static void channel_new_certificate(GObject *connection, struct sipe_tls_info *tls_info, SipeTLSChannel *self, GAsyncReadyCallback callback, gpointer user_data) { struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(connection); self->tls_info = tls_info; self->result = g_simple_async_result_new(G_OBJECT(self), callback, user_data, channel_new_certificate); g_signal_connect(tls_info->certificate, "accepted", G_CALLBACK(certificate_accepted_cb), self); g_signal_connect(tls_info->certificate, "rejected", G_CALLBACK(certificate_rejected_cb), self); manager_new_channel(telepathy_private->tls_manager, self); } /* * TLS Certificate class - instance methods */ enum { CERTIFICATE_PROP_OBJECT_PATH = 1, CERTIFICATE_PROP_STATE, CERTIFICATE_PROP_TYPE, CERTIFICATE_PROP_CHAIN_DATA, CERTIFICATE_LAST_PROP }; static void certificate_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SipeTLSCertificate *self = SIPE_TLS_CERTIFICATE(object); switch (property_id) { case CERTIFICATE_PROP_OBJECT_PATH: g_value_set_string(value, self->tls_info->cert_path); break; case CERTIFICATE_PROP_STATE: g_value_set_uint(value, self->state); break; case CERTIFICATE_PROP_TYPE: g_value_set_string(value, "x509"); break; case CERTIFICATE_PROP_CHAIN_DATA: g_value_set_boxed(value, self->tls_info->cert_data); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void sipe_tls_certificate_constructed(GObject *object) { SipeTLSCertificate *self = SIPE_TLS_CERTIFICATE(object); void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_tls_certificate_parent_class)->constructed; if (chain_up) chain_up(object); self->state = SIPE_TLS_CERTIFICATE_PENDING; } static void sipe_tls_certificate_finalize(GObject *object) { SIPE_DEBUG_INFO_NOFORMAT("SipeTLSCertificate::finalize"); G_OBJECT_CLASS(sipe_tls_certificate_parent_class)->finalize(object); } /* * TLS Certificate class - type implementation */ static void sipe_tls_certificate_class_init(SipeTLSCertificateClass *klass) { static TpDBusPropertiesMixinPropImpl props[] = { { .name = "State", .getter_data = "state", .setter_data = NULL }, { .name = "CertificateType", .getter_data = "certificate-type", .setter_data = NULL }, { .name = "CertificateChainData", .getter_data = "certificate-chain-data", .setter_data = NULL }, { .name = NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { .name = TP_IFACE_AUTHENTICATION_TLS_CERTIFICATE, .getter = tp_dbus_properties_mixin_getter_gobject_properties, .setter = NULL, .props = props }, { .name = NULL } }; GObjectClass *object_class = G_OBJECT_CLASS(klass); GParamSpec *ps; SIPE_DEBUG_INFO_NOFORMAT("SipeTLSCertificate::class_init"); klass->dbus_props_class.interfaces = prop_interfaces; object_class->constructed = sipe_tls_certificate_constructed; object_class->finalize = sipe_tls_certificate_finalize; object_class->get_property = certificate_get_property; ps = 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_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property(object_class, CERTIFICATE_PROP_OBJECT_PATH, ps); ps = g_param_spec_uint("state", "State of this certificate", "The state of this TLS certificate.", SIPE_TLS_CERTIFICATE_PENDING, SIPE_TLS_CERTIFICATE_ACCEPTED, SIPE_TLS_CERTIFICATE_PENDING, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property(object_class, CERTIFICATE_PROP_STATE, ps); ps = g_param_spec_string("certificate-type", "The certificate type", "The type of this certificate.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property(object_class, CERTIFICATE_PROP_TYPE, ps); ps = g_param_spec_boxed("certificate-chain-data", "The certificate chain data", "The raw DER-encoded trust chain of this certificate.", TP_ARRAY_TYPE_UCHAR_ARRAY_LIST, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property(object_class, CERTIFICATE_PROP_CHAIN_DATA, ps); tp_dbus_properties_mixin_class_init(object_class, G_STRUCT_OFFSET(SipeTLSCertificateClass, dbus_props_class)); } static void sipe_tls_certificate_init(SIPE_UNUSED_PARAMETER SipeTLSCertificate *self) { SIPE_DEBUG_INFO_NOFORMAT("SipeTLSCertificate::init"); } /* * TLS Certificate class - interface implementation */ static void tls_certificate_accept(TpSvcAuthenticationTLSCertificate *certificate, DBusGMethodInvocation *context) { SipeTLSCertificate *self = SIPE_TLS_CERTIFICATE(certificate); SIPE_DEBUG_INFO_NOFORMAT("SipeTLSCertificate::accept"); if (self->state != SIPE_TLS_CERTIFICATE_PENDING) { GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Calling Accept() on a certificate with state != PENDING " "doesn't make sense." }; dbus_g_method_return_error(context, &error); return; } self->state = SIPE_TLS_CERTIFICATE_ACCEPTED; tp_svc_authentication_tls_certificate_emit_accepted(self); tp_svc_authentication_tls_certificate_return_from_accept(context); } static void tls_certificate_reject(TpSvcAuthenticationTLSCertificate *certificate, const GPtrArray *rejections, DBusGMethodInvocation *context) { SipeTLSCertificate *self = SIPE_TLS_CERTIFICATE(certificate); SIPE_DEBUG_INFO_NOFORMAT("SipeTLSCertificate::reject"); if (self->state != SIPE_TLS_CERTIFICATE_PENDING) { GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Calling Reject() on a certificate with state != PENDING " "doesn't make sense." }; dbus_g_method_return_error(context, &error); return; } self->state = SIPE_TLS_CERTIFICATE_REJECTED; tp_svc_authentication_tls_certificate_emit_rejected(self, rejections); tp_svc_authentication_tls_certificate_return_from_reject(context); } static void tls_certificate_iface_init(gpointer g_iface, SIPE_UNUSED_PARAMETER gpointer iface_data) { TpSvcAuthenticationTLSCertificateClass *klass = g_iface; #define IMPLEMENT(x) \ tp_svc_authentication_tls_certificate_implement_##x( \ klass, tls_certificate_##x) IMPLEMENT(accept); IMPLEMENT(reject); #undef IMPLEMENT } static void append_certificate_der(GPtrArray *certificates, GByteArray *der) { GArray *array = g_array_sized_new(FALSE, FALSE, sizeof(guchar), der->len); array = g_array_append_vals(array, der->data, der->len); g_byte_array_unref(der); g_ptr_array_add(certificates, array); } struct sipe_tls_info *sipe_telepathy_tls_info_new(const gchar *hostname, GTlsCertificate *certificate) { struct sipe_tls_info *tls_info = NULL; GByteArray *der = NULL; g_object_get(certificate, "certificate", &der, NULL); if (der) { GPtrArray *identities = g_ptr_array_new(); tls_info = g_new0(struct sipe_tls_info, 1); tls_info->hostname = g_strdup(hostname); /* build GStrv of identies */ g_ptr_array_add(identities, g_strdup(hostname)); g_ptr_array_add(identities, NULL); tls_info->reference_identities = (GStrv) g_ptr_array_free(identities, FALSE); tls_info->cert_data = g_ptr_array_new_full(1, (GDestroyNotify) g_array_unref); /* unrefs "der" */ append_certificate_der(tls_info->cert_data, der); /* will be unref'd in loop */ g_object_ref(certificate); while (certificate) { GTlsCertificate *issuer = NULL; g_object_get(certificate, "issuer", &issuer, NULL); g_object_unref(certificate); /* add issuer certificate */ if (issuer) { g_object_get(certificate, "certificate", &der, NULL); /* unrefs "der" */ if (der) append_certificate_der(tls_info->cert_data, der); } /* walk up the chain */ certificate = issuer; } } return(tls_info); } void sipe_telepathy_tls_info_free(struct sipe_tls_info *tls_info) { g_object_unref(tls_info->certificate); g_free(tls_info->hostname); g_free(tls_info->cert_path); g_ptr_array_unref(tls_info->cert_data); g_strfreev(tls_info->reference_identities); g_free(tls_info); } /* create new tls certificate object */ void sipe_telepathy_tls_verify_async(GObject *connection, struct sipe_tls_info *tls_info, GAsyncReadyCallback callback, gpointer user_data) { /* property "connection" required by TpBaseChannel */ SipeTLSChannel *channel = g_object_new(SIPE_TYPE_TLS_CHANNEL, "connection", connection, NULL); TpBaseChannel *base = TP_BASE_CHANNEL(channel); SipeTLSCertificate *certificate = g_object_new(SIPE_TYPE_TLS_CERTIFICATE, NULL); TpDBusDaemon *daemon = tp_dbus_daemon_dup(NULL); tls_info->certificate = certificate; certificate->tls_info = tls_info; tp_base_channel_register(base); tls_info->cert_path = g_strdup_printf("%s/TLSCertificateObject", tp_base_channel_get_object_path(base)); /* register the certificate on the bus */ tp_dbus_daemon_register_object(daemon, tls_info->cert_path, certificate); g_object_unref(daemon); channel_new_certificate(connection, tls_info, channel, callback, user_data); } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */ ================================================ FILE: src/telepathy/telepathy-transport.c ================================================ /** * @file telepathy-transport.c * * pidgin-sipe * * Copyright (C) 2012-2017 SIPE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sipe-backend.h" #include "sipe-common.h" #include "sipe-core.h" #include "sipe-nls.h" #include "telepathy-private.h" struct sipe_transport_telepathy { /* public part shared with core */ struct sipe_transport_connection public; /* telepathy private part */ transport_connected_cb *connected; transport_input_cb *input; transport_error_cb *error; gchar *hostname; struct sipe_tls_info *tls_info; struct sipe_backend_private *private; GCancellable *cancel; GSocketConnection *socket; GInputStream *istream; GOutputStream *ostream; GSList *buffers; /* != NULL -> write operation in progress */ guint port; gboolean do_flush; }; #define TELEPATHY_TRANSPORT ((struct sipe_transport_telepathy *) conn) #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport) #define BUFFER_SIZE_INCREMENT 4096 static void read_completed(GObject *stream, GAsyncResult *result, gpointer data) { struct sipe_transport_telepathy *transport = data; struct sipe_transport_connection *conn = SIPE_TRANSPORT_CONNECTION; do { if (conn->buffer_length < conn->buffer_used + BUFFER_SIZE_INCREMENT) { conn->buffer_length += BUFFER_SIZE_INCREMENT; conn->buffer = g_realloc(conn->buffer, conn->buffer_length); SIPE_DEBUG_INFO("read_completed: new buffer length %" G_GSIZE_FORMAT, conn->buffer_length); } /* callback result is valid */ if (result) { GError *error = NULL; gssize len = g_input_stream_read_finish(G_INPUT_STREAM(stream), result, &error); if (len < 0) { const gchar *msg = error ? error->message : "UNKNOWN"; SIPE_DEBUG_ERROR("read_completed: error: %s", msg); if (transport->error) transport->error(conn, msg); if (error) g_error_free(error); return; } else if (len == 0) { SIPE_DEBUG_ERROR_NOFORMAT("read_completed: server has disconnected"); transport->error(conn, _("Server has disconnected")); return; } else if (transport->do_flush) { /* read completed while disconnected transport is flushing */ SIPE_DEBUG_INFO_NOFORMAT("read_completed: ignored during flushing"); return; } else if (g_cancellable_is_cancelled(transport->cancel)) { /* read completed when transport was disconnected */ SIPE_DEBUG_INFO_NOFORMAT("read_completed: cancelled"); return; } /* Forward data to core */ conn->buffer_used += len; conn->buffer[conn->buffer_used] = '\0'; transport->input(conn); /* we processed the result */ result = NULL; } /* buffer too short? */ } while (conn->buffer_length - conn->buffer_used - 1 == 0); /* setup next read */ g_input_stream_read_async(G_INPUT_STREAM(stream), conn->buffer + conn->buffer_used, conn->buffer_length - conn->buffer_used - 1, G_PRIORITY_DEFAULT, transport->cancel, read_completed, transport); } static gboolean internal_connect(gpointer data); static void certificate_result(SIPE_UNUSED_PARAMETER GObject *unused, GAsyncResult *result, gpointer data) { struct sipe_transport_telepathy *transport = data; GError *error = NULL; g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result), &error); if (error) { SIPE_DEBUG_INFO("certificate_result: %s", error->message); if (transport->error) transport->error(SIPE_TRANSPORT_CONNECTION, error->message); g_error_free(error); } else { SIPE_DEBUG_INFO("certificate_result: trigger reconnect %p", transport); g_idle_add(internal_connect, transport); } } static void socket_connected(GObject *client, GAsyncResult *result, gpointer data) { struct sipe_transport_telepathy *transport = data; GError *error = NULL; transport->socket = g_socket_client_connect_finish(G_SOCKET_CLIENT(client), result, &error); if (transport->socket == NULL) { if (transport->tls_info) { SIPE_DEBUG_INFO_NOFORMAT("socket_connected: need to wait for user interaction"); sipe_telepathy_tls_verify_async(G_OBJECT(transport->private->connection), transport->tls_info, certificate_result, transport); } else { const gchar *msg = error ? error->message : "UNKNOWN"; SIPE_DEBUG_ERROR("socket_connected: failed: %s", msg); if (transport->error) transport->error(SIPE_TRANSPORT_CONNECTION, msg); if (error) g_error_free(error); } } else if (g_cancellable_is_cancelled(transport->cancel)) { /* connect already succeeded when transport was disconnected */ g_object_unref(transport->socket); transport->socket = NULL; SIPE_DEBUG_INFO_NOFORMAT("socket_connected: succeeded, but cancelled"); } else { GSocketAddress *saddr = g_socket_connection_get_local_address(transport->socket, &error); if (saddr) { SIPE_DEBUG_INFO_NOFORMAT("socket_connected: success"); transport->public.client_port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(saddr)); g_object_unref(saddr); transport->istream = g_io_stream_get_input_stream(G_IO_STREAM(transport->socket)); transport->ostream = g_io_stream_get_output_stream(G_IO_STREAM(transport->socket)); /* the first connection is always to the server */ if (transport->private->transport == NULL) transport->private->transport = transport; /* this sets up the async read handler */ read_completed(G_OBJECT(transport->istream), NULL, transport); transport->connected(SIPE_TRANSPORT_CONNECTION); } else { g_object_unref(transport->socket); transport->socket = NULL; SIPE_DEBUG_ERROR("socket_connected: failed: %s", error->message); transport->error(SIPE_TRANSPORT_CONNECTION, error->message); g_error_free(error); } } } static gboolean accept_certificate_signal(SIPE_UNUSED_PARAMETER GTlsConnection *tls, GTlsCertificate *peer_cert, SIPE_UNUSED_PARAMETER GTlsCertificateFlags errors, gpointer user_data) { struct sipe_transport_telepathy *transport = user_data; SIPE_DEBUG_INFO("accept_certificate_signal: %p", transport); /* second connection attempt after feedback from user? */ if (transport->tls_info) { /* user accepted certificate */ sipe_telepathy_tls_info_free(transport->tls_info); transport->tls_info = NULL; return(TRUE); } else { /* retry after user accepted certificate */ transport->tls_info = sipe_telepathy_tls_info_new(transport->hostname, peer_cert); return(FALSE); } } static void tls_handshake_starts(SIPE_UNUSED_PARAMETER GSocketClient *client, GSocketClientEvent event, SIPE_UNUSED_PARAMETER GSocketConnectable *connectable, GIOStream *connection, gpointer user_data) { if (event == G_SOCKET_CLIENT_TLS_HANDSHAKING) { SIPE_DEBUG_INFO("tls_handshake_starts: %p", connection); g_signal_connect(connection, /* is a GTlsConnection */ "accept-certificate", G_CALLBACK(accept_certificate_signal), user_data); } } static gboolean internal_connect(gpointer data) { struct sipe_transport_telepathy *transport = data; GSocketClient *client = g_socket_client_new(); SIPE_DEBUG_INFO("internal_connect - hostname: %s port: %d", transport->hostname, transport->port); /* request TLS connection */ if (transport->public.type == SIPE_TRANSPORT_TLS) { SIPE_DEBUG_INFO_NOFORMAT("using TLS"); g_socket_client_set_tls(client, TRUE); g_signal_connect(client, "event", G_CALLBACK(tls_handshake_starts), transport); } else SIPE_DEBUG_INFO_NOFORMAT("using TCP"); g_socket_client_connect_async(client, g_network_address_new(transport->hostname, transport->port), transport->cancel, socket_connected, transport); g_object_unref(client); return(FALSE); } struct sipe_transport_connection *sipe_backend_transport_connect(struct sipe_core_public *sipe_public, const sipe_connect_setup *setup) { struct sipe_transport_telepathy *transport = g_new0(struct sipe_transport_telepathy, 1); transport->public.type = setup->type; transport->public.user_data = setup->user_data; transport->connected = setup->connected; transport->input = setup->input; transport->error = setup->error; transport->hostname = g_strdup(setup->server_name); transport->tls_info = NULL; transport->private = sipe_public->backend_private; transport->cancel = g_cancellable_new(); transport->buffers = NULL; transport->port = setup->server_port; transport->do_flush = FALSE; if ((setup->type == SIPE_TRANSPORT_TLS) || (setup->type == SIPE_TRANSPORT_TCP)) { internal_connect(transport); return(SIPE_TRANSPORT_CONNECTION); } else { setup->error(SIPE_TRANSPORT_CONNECTION, "This should not happen..."); sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION); return(NULL); } } static gboolean free_transport(gpointer data) { struct sipe_transport_telepathy *transport = data; GSList *entry; SIPE_DEBUG_INFO("free_transport %p", transport); if (transport->tls_info) sipe_telepathy_tls_info_free(transport->tls_info); g_free(transport->hostname); /* free unflushed buffers */ for (entry = transport->buffers; entry; entry = entry->next) g_free(entry->data); g_slist_free(transport->buffers); if (transport->cancel) g_object_unref(transport->cancel); g_free(transport); return(FALSE); } static void close_completed(GObject *stream, GAsyncResult *result, gpointer data) { struct sipe_transport_telepathy *transport = data; SIPE_DEBUG_INFO("close_completed: transport %p", data); g_io_stream_close_finish(G_IO_STREAM(stream), result, NULL); g_idle_add(free_transport, transport); } static void do_close(struct sipe_transport_telepathy *transport) { SIPE_DEBUG_INFO("do_close: %p", transport); /* cancel outstanding asynchronous operations */ transport->do_flush = FALSE; g_cancellable_cancel(transport->cancel); g_io_stream_close_async(G_IO_STREAM(transport->socket), G_PRIORITY_DEFAULT, NULL, close_completed, transport); } void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn) { struct sipe_transport_telepathy *transport = TELEPATHY_TRANSPORT; if (!transport) return; SIPE_DEBUG_INFO("sipe_backend_transport_disconnect: %p", transport); /* error callback is invalid now, do no longer call! */ transport->error = NULL; /* dropping connection to the server? */ if (transport->private->transport == transport) transport->private->transport = NULL; /* already connected? */ if (transport->socket) { /* flush required? */ if (transport->do_flush && transport->buffers) SIPE_DEBUG_INFO("sipe_backend_transport_disconnect: %p needs flushing", transport); else do_close(transport); } else { /* cancel outstanding connect operation */ if (transport->cancel) g_cancellable_cancel(transport->cancel); /* queue transport to be deleted */ g_idle_add(free_transport, transport); } } gchar *sipe_backend_transport_ip_address(struct sipe_transport_connection *conn) { struct sipe_transport_telepathy *transport = TELEPATHY_TRANSPORT; gchar *ipstr = NULL; if (transport && transport->socket) { GSocketAddress *saddr = g_socket_connection_get_local_address(transport->socket, NULL); if (saddr) { GInetAddress *iaddr = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(saddr)); if (iaddr) { ipstr = g_inet_address_to_string(iaddr); SIPE_DEBUG_INFO("sipe_backend_transport_ip_address: %s", ipstr); } g_object_unref(saddr); } } return(ipstr ? ipstr : g_strdup("0.0.0.0")); } static void do_write(struct sipe_transport_telepathy *transport); static void write_completed(GObject *stream, GAsyncResult *result, gpointer data) { struct sipe_transport_telepathy *transport = data; gchar *buffer; GError *error = NULL; gssize written = g_output_stream_write_finish(G_OUTPUT_STREAM(stream), result, &error); /* free the buffer that has just been written. */ buffer = transport->buffers->data; transport->buffers = g_slist_remove(transport->buffers, buffer); g_free(buffer); if ((written < 0) || error) { const gchar *msg = error ? error->message : "UNKNOWN"; SIPE_DEBUG_ERROR("write_completed: error: %s", msg); if (transport->error) transport->error(SIPE_TRANSPORT_CONNECTION, msg); if (error) g_error_free(error); /* error during flush: give up and close transport */ if (transport->do_flush) do_close(transport); } else if (g_cancellable_is_cancelled(transport->cancel)) { /* write completed when transport was disconnected */ SIPE_DEBUG_INFO_NOFORMAT("write_completed: cancelled"); } else { /* more to write? */ if (transport->buffers) { do_write(transport); /* flush completed? */ } else if (transport->do_flush) { do_close(transport); } } } static void do_write(struct sipe_transport_telepathy *transport) { g_output_stream_write_async(transport->ostream, transport->buffers->data, strlen(transport->buffers->data), G_PRIORITY_DEFAULT, transport->cancel, write_completed, transport); } void sipe_backend_transport_message(struct sipe_transport_connection *conn, const gchar *buffer) { struct sipe_transport_telepathy *transport = TELEPATHY_TRANSPORT; gboolean can_write = (transport->buffers == NULL); transport->buffers = g_slist_append(transport->buffers, g_strdup(buffer)); if (can_write) do_write(transport); } void sipe_backend_transport_flush(struct sipe_transport_connection *conn) { struct sipe_transport_telepathy *transport = TELEPATHY_TRANSPORT; transport->do_flush = TRUE; } /* Local Variables: mode: c c-file-style: "bsd" indent-tabs-mode: t tab-width: 8 End: */