Install mcabber headers
* Change mcabber headers naming scheme
* Move 'src/' -> 'mcabber/'
* Add missing include <mcabber/config.h>'s
* Create and install clean config.h version in 'include/'
* Move "dirty" config.h version to 'mcabber/'
* Add $(top_srcdir) to compiler include path
* Update modules HOWTO
--- a/mcabber/INSTALL Tue Feb 02 21:27:26 2010 +0100
+++ b/mcabber/INSTALL Mon Jan 18 15:36:19 2010 +0200
@@ -4,7 +4,7 @@
... and if you want to install the software:
$ make install
(If you don't want to install it, the "mcabber" binary lies in
-the src/ directory after the build procedure)
+the mcabber/ directory after the build procedure)
You will need the Loudmouth library, version >= 1.4.3 is recommended.
--- a/mcabber/Makefile.am Tue Feb 02 21:27:26 2010 +0100
+++ b/mcabber/Makefile.am Mon Jan 18 15:36:19 2010 +0200
@@ -1,2 +1,2 @@
-SUBDIRS = src doc
+SUBDIRS = mcabber doc
ACLOCAL_AMFLAGS = -I macros
--- a/mcabber/configure.ac Tue Feb 02 21:27:26 2010 +0100
+++ b/mcabber/configure.ac Mon Jan 18 15:36:19 2010 +0200
@@ -4,8 +4,9 @@
AC_PREREQ(2.59)
AC_INIT([mcabber],[0.10.0-dev],[mcabber@lilotux.net])
AM_INIT_AUTOMAKE
-AC_CONFIG_SRCDIR([src])
-AM_CONFIG_HEADER(config.h)
+AC_CONFIG_SRCDIR([mcabber])
+AM_CONFIG_HEADER(mcabber/config.h)
+#AC_CONFIG_HEADER(include/config.h)
#AC_PROG_LIBTOOL
AC_PROG_RANLIB
@@ -253,11 +254,12 @@
fi
AM_CONDITIONAL([OTR], [test x$libotr_found = xyes])
+AM_CONDITIONAL([INSTALL_HEADERS], [test x$enable_modules = xyes])
# We need _GNU_SOURCE for strptime() and strcasestr()
CFLAGS="$CFLAGS -D_GNU_SOURCE"
-AC_CONFIG_FILES([src/Makefile
+AC_CONFIG_FILES([mcabber/Makefile
doc/Makefile
doc/guide/Makefile
doc/help/Makefile
--- a/mcabber/hgcset.sh Tue Feb 02 21:27:26 2010 +0100
+++ b/mcabber/hgcset.sh Mon Jan 18 15:36:19 2010 +0200
@@ -1,7 +1,7 @@
#! /bin/sh
if [ ! -f logprint.h ]; then
- echo "You are not in the src directory" >&2
+ echo "You are not in the mcabber directory" >&2
exit 1
fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/include/config.h.in Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,55 @@
+#ifndef __MCABBER_CONFIG_H__
+#define __MCABBER_CONFIG_H__ 1
+
+/* ... */
+#undef MODULES_ENABLE
+
+/* ... */
+#undef HAVE_LIBOTR
+
+/* ... */
+#undef HAVE_GPGME
+
+/* ... */
+#undef HAVE_NCURSESW_NCURSES_H
+
+/* ... */
+#undef HAVE_NCURSES_NCURSES_H
+
+/* ... */
+#undef WITH_ENCHANT
+
+/* ... */
+#undef WITH_ASPELL
+
+/* ... */
+#undef JEP0022
+
+/* ... */
+#undef JEP0085
+
+/* ... */
+#undef HAVE_UNICODE
+
+/* ... */
+#undef HAVE_WCHAR_H
+
+/* ... */
+#undef HAVE_WCTYPE_H
+
+/* ... */
+#undef HAVE_WCHAR_H
+
+/* ... */
+#undef HAVE_ISWBLANK
+
+/* ... */
+#undef HAVE_STRCASESTR
+
+/* ... */
+#undef DATA_DIR
+
+/* ... */
+#undef PKGLIB_DIR
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/COPYING Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,344 @@
+Specific permission is granted for the GPLed code in this distribution
+to be linked to OpenSSL without invoking GPL clause 2(b).
+----------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+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.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/Makefile.am Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,56 @@
+bin_PROGRAMS = mcabber
+mcabber_SOURCES = main.c main.h roster.c roster.h events.c events.h \
+ commands.c commands.h compl.c compl.h \
+ hbuf.c hbuf.h screen.c screen.h logprint.h \
+ settings.c settings.h hooks.c hooks.h utf8.c utf8.h \
+ histolog.c histolog.h utils.c utils.h pgp.c pgp.h \
+ xmpp.c xmpp.h xmpp_helper.c xmpp_helper.h xmpp_defines.h \
+ xmpp_iq.c xmpp_iq.h xmpp_iqrequest.c xmpp_iqrequest.h \
+ xmpp_muc.c xmpp_muc.h xmpp_s10n.c xmpp_s10n.h \
+ caps.c caps.h fifo.c fifo.h help.c help.h
+
+if OTR
+mcabber_SOURCES += otr.c otr.h nohtml.c nohtml.h
+endif
+
+LDADD = $(GLIB_LIBS) $(LOUDMOUTH_LIBS) $(GPGME_LIBS) $(LIBOTR_LIBS) \
+ $(ENCHANT_LIBS)
+
+AM_CPPFLAGS = -I$(top_srcdir) $(GLIB_CFLAGS) $(LOUDMOUTH_CFLAGS) \
+ $(GPGME_CFLAGS) $(LIBOTR_CFLAGS) \
+ $(ENCHANT_CFLAGS)
+
+CLEANFILES = hgcset.h
+
+if HGCSET
+BUILT_SOURCES = hgcset.h
+
+hgcset.h:
+ ../hgcset.sh
+
+.PHONY: hgcset.h
+endif
+
+if INSTALL_HEADERS
+mcabberinclude_HEADERS = main.h roster.h events.h \
+ commands.h compl.h \
+ hbuf.h screen.h logprint.h \
+ settings.h hooks.h utf8.c utf8.h \
+ histolog.h utils.h pgp.h \
+ xmpp.h xmpp_helper.h xmpp_defines.h \
+ xmpp_iq.h xmpp_iqrequest.h \
+ xmpp_muc.h xmpp_s10n.h \
+ caps.h fifo.h help.h $(top_srcdir)/include/config.h
+
+if OTR
+mcabberinclude_HEADERS += otr.h nohtml.h
+endif
+
+if HGCSET
+mcabberinclude_HEADERS += hgcset.h
+endif
+
+mcabberincludedir = $(includedir)/mcabber
+endif
+
+#SUBDIRS =
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/caps.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,178 @@
+/*
+ * caps.c -- Entity Capabilities Cache for mcabber
+ *
+ * Copyright (C) 2008 Frank Zschockelt <mcabber@freakysoft.de>
+ *
+ * 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>
+
+typedef struct {
+ char *category;
+ char *name;
+ char *type;
+ GHashTable *features;
+} caps;
+
+static GHashTable *caps_cache = NULL;
+
+void caps_destroy(gpointer data)
+{
+ caps *c = data;
+ g_free(c->category);
+ g_free(c->name);
+ g_free(c->type);
+ g_hash_table_destroy(c->features);
+ g_free(c);
+}
+
+void caps_init(void)
+{
+ if (!caps_cache)
+ caps_cache = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, caps_destroy);
+}
+
+void caps_free(void)
+{
+ if (caps_cache) {
+ g_hash_table_destroy(caps_cache);
+ caps_cache = NULL;
+ }
+}
+
+void caps_add(char *hash)
+{
+ if (!hash)
+ return;
+ caps *c = g_new0(caps, 1);
+ c->features = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ g_hash_table_insert(caps_cache, g_strdup(hash), c);
+}
+
+int caps_has_hash(const char *hash)
+{
+ return (hash != NULL && (g_hash_table_lookup(caps_cache, hash) != NULL));
+}
+
+void caps_set_identity(char *hash,
+ const char *category,
+ const char *name,
+ const char *type)
+{
+ caps *c;
+ if (!hash)
+ return;
+
+ c = g_hash_table_lookup(caps_cache, hash);
+ if (c) {
+ c->category = g_strdup(category);
+ c->name = g_strdup(name);
+ c->type = g_strdup(type);
+ }
+}
+
+void caps_add_feature(char *hash, const char *feature)
+{
+ caps *c;
+ if (!hash)
+ return;
+ c = g_hash_table_lookup(caps_cache, hash);
+ if (c) {
+ char *f = g_strdup(feature);
+ g_hash_table_replace(c->features, f, f);
+ }
+}
+
+int caps_has_feature(char *hash, char *feature)
+{
+ caps *c;
+ if (!hash)
+ return 0;
+ c = g_hash_table_lookup(caps_cache, hash);
+ if (c)
+ return (g_hash_table_lookup(c->features, feature) != NULL);
+ return 0;
+}
+
+static GFunc _foreach_function;
+
+void _caps_foreach_helper(gpointer key, gpointer value, gpointer user_data)
+{
+ // GFunc func = (GFunc)user_data;
+ _foreach_function(value, user_data);
+}
+
+void caps_foreach_feature(const char *hash, GFunc func, gpointer user_data)
+{
+ caps *c;
+ if (!hash)
+ return;
+ c = g_hash_table_lookup(caps_cache, hash);
+ if (!c)
+ return;
+ _foreach_function = func;
+ g_hash_table_foreach(c->features, _caps_foreach_helper, user_data);
+}
+
+gint _strcmp_sort(gconstpointer a, gconstpointer b)
+{
+ return g_strcmp0(a, b);
+}
+
+//generates the sha1 hash for the special capability "" and returns it
+const char *caps_generate(void)
+{
+ char *identity;
+ GList *features;
+ GChecksum *sha1;
+ guint8 digest[20];
+ gsize digest_size = 20;
+ gchar *hash, *old_hash = NULL;
+ caps *old_caps;
+ caps *c = g_hash_table_lookup(caps_cache, "");
+
+ g_hash_table_steal(caps_cache, "");
+ sha1 = g_checksum_new(G_CHECKSUM_SHA1);
+ identity = g_strdup_printf("%s/%s//%s<", c->category, c->type, c->name);
+ g_checksum_update(sha1, (guchar*)identity, -1);
+ g_free(identity);
+
+ features = g_hash_table_get_values(c->features);
+ features = g_list_sort(features, _strcmp_sort);
+ {
+ GList *feature;
+ for (feature=features; feature; feature=feature->next) {
+ g_checksum_update(sha1, feature->data, -1);
+ g_checksum_update(sha1, (guchar *)"<", -1);
+ }
+ }
+ g_list_free(features);
+
+ g_checksum_get_digest(sha1, digest, &digest_size);
+ hash = g_base64_encode(digest, digest_size);
+ g_checksum_free(sha1);
+ g_hash_table_lookup_extended(caps_cache, hash,
+ (gpointer *)&old_hash, (gpointer *)&old_caps);
+ g_hash_table_insert(caps_cache, hash, c);
+ if (old_hash)
+ return old_hash;
+ else
+ return hash;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/caps.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,22 @@
+#ifndef __MCABBER_CAPS_H__
+#define __MCABBER_CAPS_H__ 1
+
+#include <glib.h>
+
+void caps_init(void);
+void caps_free(void);
+void caps_add(char *hash);
+int caps_has_hash(const char *hash);
+void caps_set_identity(char *hash,
+ const char *category,
+ const char *name,
+ const char *type);
+void caps_add_feature(char *hash, const char *feature);
+int caps_has_feature(char *hash, char *feature);
+void caps_foreach_feature(const char *hash, GFunc func, gpointer user_data);
+
+char *caps_generate(void);
+
+#endif /* __MCABBER_CAPS_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/commands.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,3828 @@
+/*
+ * commands.c -- user commands handling
+ *
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "commands.h"
+#include "help.h"
+#include "roster.h"
+#include "screen.h"
+#include "compl.h"
+#include "hooks.h"
+#include "hbuf.h"
+#include "utils.h"
+#include "settings.h"
+#include "events.h"
+#include "otr.h"
+#include "utf8.h"
+#include "xmpp.h"
+#include "main.h"
+
+#define IMSTATUS_AWAY "away"
+#define IMSTATUS_ONLINE "online"
+#define IMSTATUS_OFFLINE "offline"
+#define IMSTATUS_FREE4CHAT "free"
+#define IMSTATUS_INVISIBLE "invisible"
+#define IMSTATUS_AVAILABLE "avail"
+#define IMSTATUS_NOTAVAILABLE "notavail"
+#define IMSTATUS_DONOTDISTURB "dnd"
+
+// Return value container for the following functions
+static int retval_for_cmds;
+
+// Commands callbacks
+static void do_roster(char *arg);
+static void do_status(char *arg);
+static void do_status_to(char *arg);
+static void do_add(char *arg);
+static void do_del(char *arg);
+static void do_group(char *arg);
+static void do_say(char *arg);
+static void do_msay(char *arg);
+static void do_say_to(char *arg);
+static void do_buffer(char *arg);
+static void do_clear(char *arg);
+static void do_info(char *arg);
+static void do_rename(char *arg);
+static void do_move(char *arg);
+static void do_set(char *arg);
+static void do_alias(char *arg);
+static void do_bind(char *arg);
+static void do_connect(char *arg);
+static void do_disconnect(char *arg);
+static void do_rawxml(char *arg);
+static void do_room(char *arg);
+static void do_authorization(char *arg);
+static void do_version(char *arg);
+static void do_request(char *arg);
+static void do_event(char *arg);
+static void do_help(char *arg);
+static void do_pgp(char *arg);
+static void do_iline(char *arg);
+static void do_screen_refresh(char *arg);
+static void do_chat_disable(char *arg);
+static void do_source(char *arg);
+static void do_color(char *arg);
+static void do_otr(char *arg);
+static void do_otrpolicy(char *arg);
+static void do_echo(char *arg);
+
+static void do_say_internal(char *arg, int parse_flags);
+
+// Global variable for the commands list
+static GSList *Commands;
+
+#ifdef MODULES_ENABLE
+#include <gmodule.h>
+
+static void do_load(char *arg);
+static void do_unload(char *arg);
+
+typedef struct {
+ char *name;
+ GModule *module;
+} loaded_module_t;
+
+GSList *loaded_modules = NULL;
+
+gpointer cmd_del(const char *name)
+{
+ GSList *sl_cmd;
+ for (sl_cmd = Commands; sl_cmd; sl_cmd = sl_cmd->next) {
+ cmd *command = (cmd *) sl_cmd->data;
+ if (!strcmp (command->name, name)) {
+ gpointer userdata = command->userdata;
+ Commands = g_slist_delete_link(Commands, sl_cmd);
+ compl_del_category_word(COMPL_CMD, command->name);
+ g_free(command);
+ return userdata;
+ }
+ }
+ return NULL;
+}
+
+// cmd_add()
+// Adds a command to the commands list and to the CMD completion list
+void cmd_add(const char *name, const char *help, guint flags_row1,
+ guint flags_row2, void (*f)(char*), gpointer userdata)
+#define cmd_add(A, B, C, D, E) cmd_add (A, B, C, D, E, NULL);
+#else
+static void cmd_add(const char *name, const char *help,
+ guint flags_row1, guint flags_row2, void (*f)(char*))
+#endif
+{
+ cmd *n_cmd = g_new0(cmd, 1);
+ strncpy(n_cmd->name, name, 32-1);
+ n_cmd->help = help;
+ n_cmd->completion_flags[0] = flags_row1;
+ n_cmd->completion_flags[1] = flags_row2;
+ n_cmd->func = f;
+#ifdef MODULES_ENABLE
+ n_cmd->userdata = userdata;
+#endif
+ Commands = g_slist_prepend(Commands, n_cmd);
+ // Add to completion CMD category
+ compl_add_category_word(COMPL_CMD, name);
+}
+
+// cmd_init()
+// Commands table initialization
+// !!!
+// After changing commands names and it arguments names here, you must change
+// ones in init_bindings()!
+//
+void cmd_init(void)
+{
+ cmd_add("add", "Add a jabber user", COMPL_JID, 0, &do_add);
+ cmd_add("alias", "Add an alias", 0, 0, &do_alias);
+ cmd_add("authorization", "Manage subscription authorizations",
+ COMPL_AUTH, COMPL_JID, &do_authorization);
+ cmd_add("bind", "Add an key binding", 0, 0, &do_bind);
+ cmd_add("buffer", "Manipulate current buddy's buffer (chat window)",
+ COMPL_BUFFER, 0, &do_buffer);
+ cmd_add("chat_disable", "Disable chat mode", 0, 0, &do_chat_disable);
+ cmd_add("clear", "Clear the dialog window", 0, 0, &do_clear);
+ cmd_add("color", "Set coloring options", COMPL_COLOR, 0, &do_color);
+ cmd_add("connect", "Connect to the server", 0, 0, &do_connect);
+ cmd_add("del", "Delete the current buddy", 0, 0, &do_del);
+ cmd_add("disconnect", "Disconnect from server", 0, 0, &do_disconnect);
+ cmd_add("echo", "Display a string in the log window", 0, 0, &do_echo);
+ cmd_add("event", "Process an event", COMPL_EVENTSID, COMPL_EVENTS, &do_event);
+ cmd_add("group", "Change group display settings",
+ COMPL_GROUP, COMPL_GROUPNAME, &do_group);
+ cmd_add("help", "Display some help", COMPL_CMD, 0, &do_help);
+ cmd_add("iline", "Manipulate input buffer", 0, 0, &do_iline);
+ cmd_add("info", "Show basic info on current buddy", 0, 0, &do_info);
+ cmd_add("move", "Move the current buddy to another group", COMPL_GROUPNAME,
+ 0, &do_move);
+ cmd_add("msay", "Send a multi-lines message to the selected buddy",
+ COMPL_MULTILINE, 0, &do_msay);
+ cmd_add("otr", "Manage OTR settings", COMPL_OTR, COMPL_JID, &do_otr);
+ cmd_add("otrpolicy", "Manage OTR policies", COMPL_JID, COMPL_OTRPOLICY,
+ &do_otrpolicy);
+ cmd_add("pgp", "Manage PGP settings", COMPL_PGP, COMPL_JID, &do_pgp);
+ cmd_add("quit", "Exit the software", 0, 0, NULL);
+ cmd_add("rawxml", "Send a raw XML string", 0, 0, &do_rawxml);
+ cmd_add("rename", "Rename the current buddy", 0, 0, &do_rename);
+ cmd_add("request", "Send a Jabber IQ request", COMPL_REQUEST, COMPL_JID,
+ &do_request);
+ cmd_add("room", "MUC actions command", COMPL_ROOM, 0, &do_room);
+ cmd_add("roster", "Manipulate the roster/buddylist", COMPL_ROSTER, 0,
+ &do_roster);
+ cmd_add("say", "Say something to the selected buddy", 0, 0, &do_say);
+ cmd_add("say_to", "Say something to a specific buddy", COMPL_JID, 0,
+ &do_say_to);
+ cmd_add("screen_refresh", "Redraw mcabber screen", 0, 0, &do_screen_refresh);
+ //cmd_add("search");
+ cmd_add("set", "Set/query an option value", 0, 0, &do_set);
+ cmd_add("source", "Read a configuration file", 0, 0, &do_source);
+ cmd_add("status", "Show or set your status", COMPL_STATUS, 0, &do_status);
+ cmd_add("status_to", "Show or set your status for one recipient",
+ COMPL_JID, COMPL_STATUS, &do_status_to);
+ cmd_add("version", "Show mcabber version", 0, 0, &do_version);
+#ifdef MODULES_ENABLE
+ cmd_add("load", "Load module", 0, 0, &do_load);
+ cmd_add("unload", "Unload module", 0, 0, &do_unload);
+#endif
+
+ // Status category
+ compl_add_category_word(COMPL_STATUS, "online");
+ compl_add_category_word(COMPL_STATUS, "avail");
+ compl_add_category_word(COMPL_STATUS, "invisible");
+ compl_add_category_word(COMPL_STATUS, "free");
+ compl_add_category_word(COMPL_STATUS, "dnd");
+ compl_add_category_word(COMPL_STATUS, "notavail");
+ compl_add_category_word(COMPL_STATUS, "away");
+ compl_add_category_word(COMPL_STATUS, "offline");
+ compl_add_category_word(COMPL_STATUS, "message");
+
+ // Roster category
+ compl_add_category_word(COMPL_ROSTER, "bottom");
+ compl_add_category_word(COMPL_ROSTER, "top");
+ compl_add_category_word(COMPL_ROSTER, "up");
+ compl_add_category_word(COMPL_ROSTER, "down");
+ compl_add_category_word(COMPL_ROSTER, "group_prev");
+ compl_add_category_word(COMPL_ROSTER, "group_next");
+ compl_add_category_word(COMPL_ROSTER, "hide");
+ compl_add_category_word(COMPL_ROSTER, "show");
+ compl_add_category_word(COMPL_ROSTER, "toggle");
+ compl_add_category_word(COMPL_ROSTER, "display");
+ compl_add_category_word(COMPL_ROSTER, "hide_offline");
+ compl_add_category_word(COMPL_ROSTER, "show_offline");
+ compl_add_category_word(COMPL_ROSTER, "toggle_offline");
+ compl_add_category_word(COMPL_ROSTER, "item_lock");
+ compl_add_category_word(COMPL_ROSTER, "item_unlock");
+ compl_add_category_word(COMPL_ROSTER, "item_toggle_lock");
+ compl_add_category_word(COMPL_ROSTER, "alternate");
+ compl_add_category_word(COMPL_ROSTER, "search");
+ compl_add_category_word(COMPL_ROSTER, "unread_first");
+ compl_add_category_word(COMPL_ROSTER, "unread_next");
+ compl_add_category_word(COMPL_ROSTER, "note");
+
+ // Buffer category
+ compl_add_category_word(COMPL_BUFFER, "clear");
+ compl_add_category_word(COMPL_BUFFER, "bottom");
+ compl_add_category_word(COMPL_BUFFER, "top");
+ compl_add_category_word(COMPL_BUFFER, "up");
+ compl_add_category_word(COMPL_BUFFER, "down");
+ compl_add_category_word(COMPL_BUFFER, "search_backward");
+ compl_add_category_word(COMPL_BUFFER, "search_forward");
+ compl_add_category_word(COMPL_BUFFER, "date");
+ compl_add_category_word(COMPL_BUFFER, "%");
+ compl_add_category_word(COMPL_BUFFER, "purge");
+ compl_add_category_word(COMPL_BUFFER, "close");
+ compl_add_category_word(COMPL_BUFFER, "close_all");
+ compl_add_category_word(COMPL_BUFFER, "scroll_lock");
+ compl_add_category_word(COMPL_BUFFER, "scroll_unlock");
+ compl_add_category_word(COMPL_BUFFER, "scroll_toggle");
+ compl_add_category_word(COMPL_BUFFER, "list");
+ compl_add_category_word(COMPL_BUFFER, "save");
+
+ // Group category
+ compl_add_category_word(COMPL_GROUP, "fold");
+ compl_add_category_word(COMPL_GROUP, "unfold");
+ compl_add_category_word(COMPL_GROUP, "toggle");
+
+ // Multi-line (msay) category
+ compl_add_category_word(COMPL_MULTILINE, "abort");
+ compl_add_category_word(COMPL_MULTILINE, "begin");
+ compl_add_category_word(COMPL_MULTILINE, "send");
+ compl_add_category_word(COMPL_MULTILINE, "send_to");
+ compl_add_category_word(COMPL_MULTILINE, "toggle");
+ compl_add_category_word(COMPL_MULTILINE, "toggle_verbatim");
+ compl_add_category_word(COMPL_MULTILINE, "verbatim");
+
+ // Room category
+ compl_add_category_word(COMPL_ROOM, "affil");
+ compl_add_category_word(COMPL_ROOM, "ban");
+ compl_add_category_word(COMPL_ROOM, "bookmark");
+ compl_add_category_word(COMPL_ROOM, "destroy");
+ compl_add_category_word(COMPL_ROOM, "invite");
+ compl_add_category_word(COMPL_ROOM, "join");
+ compl_add_category_word(COMPL_ROOM, "kick");
+ compl_add_category_word(COMPL_ROOM, "leave");
+ compl_add_category_word(COMPL_ROOM, "names");
+ compl_add_category_word(COMPL_ROOM, "nick");
+ compl_add_category_word(COMPL_ROOM, "privmsg");
+ compl_add_category_word(COMPL_ROOM, "remove");
+ compl_add_category_word(COMPL_ROOM, "role");
+ compl_add_category_word(COMPL_ROOM, "setopt");
+ compl_add_category_word(COMPL_ROOM, "topic");
+ compl_add_category_word(COMPL_ROOM, "unban");
+ compl_add_category_word(COMPL_ROOM, "unlock");
+ compl_add_category_word(COMPL_ROOM, "whois");
+
+ // Authorization category
+ compl_add_category_word(COMPL_AUTH, "allow");
+ compl_add_category_word(COMPL_AUTH, "cancel");
+ compl_add_category_word(COMPL_AUTH, "request");
+ compl_add_category_word(COMPL_AUTH, "request_unsubscribe");
+
+ // Request (query) category
+ compl_add_category_word(COMPL_REQUEST, "last");
+ compl_add_category_word(COMPL_REQUEST, "time");
+ compl_add_category_word(COMPL_REQUEST, "vcard");
+ compl_add_category_word(COMPL_REQUEST, "version");
+
+ // Events category
+ compl_add_category_word(COMPL_EVENTS, "accept");
+ compl_add_category_word(COMPL_EVENTS, "ignore");
+ compl_add_category_word(COMPL_EVENTS, "reject");
+
+ // PGP category
+ compl_add_category_word(COMPL_PGP, "disable");
+ compl_add_category_word(COMPL_PGP, "enable");
+ compl_add_category_word(COMPL_PGP, "force");
+ compl_add_category_word(COMPL_PGP, "info");
+ compl_add_category_word(COMPL_PGP, "setkey");
+
+ // OTR category
+ compl_add_category_word(COMPL_OTR, "start");
+ compl_add_category_word(COMPL_OTR, "stop");
+ compl_add_category_word(COMPL_OTR, "fingerprint");
+ compl_add_category_word(COMPL_OTR, "smpq");
+ compl_add_category_word(COMPL_OTR, "smpr");
+ compl_add_category_word(COMPL_OTR, "smpa");
+ compl_add_category_word(COMPL_OTR, "info");
+ compl_add_category_word(COMPL_OTR, "key");
+
+ // OTR Policy category
+ compl_add_category_word(COMPL_OTRPOLICY, "plain");
+ compl_add_category_word(COMPL_OTRPOLICY, "manual");
+ compl_add_category_word(COMPL_OTRPOLICY, "opportunistic");
+ compl_add_category_word(COMPL_OTRPOLICY, "always");
+
+ // Color category
+ compl_add_category_word(COMPL_COLOR, "roster");
+ compl_add_category_word(COMPL_COLOR, "muc");
+ compl_add_category_word(COMPL_COLOR, "mucnick");
+}
+
+#ifdef MODULES_ENABLE
+void cmd_deinit ()
+{
+ GSList *el = loaded_modules;
+ while (el) {
+ loaded_module_t *module = el->data;
+ if (!g_module_close ((GModule *) module->module))
+ scr_LogPrint (LPRINT_LOGNORM, "* Module unloading failed: %s",
+ g_module_error ());
+ g_free (module->name);
+ g_free (module);
+ el = g_slist_next (el);
+ }
+ g_slist_free (loaded_modules);
+}
+#endif
+
+// expandalias(line)
+// If there is one, expand the alias in line and returns a new allocated line
+// If no alias is found, returns line
+// Note: if the returned pointer is different from line, the caller should
+// g_free() the pointer after use
+char *expandalias(const char *line)
+{
+ const char *p1, *p2;
+ char *word;
+ const gchar *value;
+ char *newline = (char*)line;
+
+ // Ignore leading COMMAND_CHAR
+ for (p1 = line ; *p1 == COMMAND_CHAR ; p1++)
+ ;
+ // Locate the end of the word
+ for (p2 = p1 ; *p2 && (*p2 != ' ') ; p2++)
+ ;
+ // Extract the word and look for an alias in the list
+ word = g_strndup(p1, p2-p1);
+ value = settings_get(SETTINGS_TYPE_ALIAS, (const char*)word);
+ g_free(word);
+
+ if (value)
+ newline = g_strdup_printf("%c%s%s", COMMAND_CHAR, value, p2);
+
+ return newline;
+}
+
+// cmd_get
+// Finds command in the command list structure.
+// Returns a pointer to the cmd entry, or NULL if command not found.
+cmd *cmd_get(const char *command)
+{
+ const char *p1, *p2;
+ char *com;
+ GSList *sl_com;
+
+ // Ignore leading COMMAND_CHAR
+ for (p1 = command ; *p1 == COMMAND_CHAR ; p1++)
+ ;
+ // Locate the end of the command
+ for (p2 = p1 ; *p2 && (*p2 != ' ') ; p2++)
+ ;
+ // Copy the clean command
+ com = g_strndup(p1, p2-p1);
+
+ // Look for command in the list
+ for (sl_com=Commands; sl_com; sl_com = g_slist_next(sl_com)) {
+ if (!strcasecmp(com, ((cmd*)sl_com->data)->name))
+ break;
+ }
+ g_free(com);
+
+ if (sl_com) // Command has been found.
+ return (cmd*)sl_com->data;
+ return NULL;
+}
+
+// process_command(line, iscmd)
+// Process a command line.
+// If iscmd is TRUE, process the command even if verbatim mmode is set;
+// it is intended to be used for key bindings.
+// Return 255 if this is the /quit command, and 0 for the other commands.
+int process_command(const char *line, guint iscmd)
+{
+ char *p;
+ char *xpline;
+ cmd *curcmd;
+
+ if (!line)
+ return 0;
+
+ // We do alias expansion here
+ if (iscmd || scr_get_multimode() != 2)
+ xpline = expandalias(line);
+ else
+ xpline = (char*)line; // No expansion in verbatim multi-line mode
+
+ // We want to use a copy
+ if (xpline == line)
+ xpline = g_strdup(line);
+
+ // Remove trailing spaces:
+ for (p=xpline ; *p ; p++)
+ ;
+ for (p-- ; p>xpline && (*p == ' ') ; p--)
+ *p = 0;
+
+ // Command "quit"?
+ if ((iscmd || scr_get_multimode() != 2)
+ && (!strncasecmp(xpline, mkcmdstr("quit"), strlen(mkcmdstr("quit"))))) {
+ if (!xpline[5] || xpline[5] == ' ') {
+ g_free(xpline);
+ return 255;
+ }
+ } else if (iscmd && !strncasecmp(xpline, "quit", 4) &&
+ (!xpline[4] || xpline[4] == ' ')) {
+ // If iscmd is true we can have the command without the command prefix
+ // character (usually '/').
+ g_free(xpline);
+ return 255;
+ }
+
+ // If verbatim multi-line mode, we check if another /msay command is typed
+ if (!iscmd && scr_get_multimode() == 2
+ && (strncasecmp(xpline, mkcmdstr("msay "), strlen(mkcmdstr("msay "))))) {
+ // It isn't an /msay command
+ scr_append_multiline(xpline);
+ g_free(xpline);
+ return 0;
+ }
+
+ // Commands handling
+ curcmd = cmd_get(xpline);
+
+ if (!curcmd) {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized command. "
+ "Please see the manual for a list of known commands.");
+ g_free(xpline);
+ return 0;
+ }
+ if (!curcmd->func) {
+ scr_LogPrint(LPRINT_NORMAL,
+ "This functionality is not yet implemented, sorry.");
+ g_free(xpline);
+ return 0;
+ }
+ // Lets go to the command parameters
+ for (p = xpline+1; *p && (*p != ' ') ; p++)
+ ;
+ // Skip spaces
+ while (*p && (*p == ' '))
+ p++;
+ // Call command-specific function
+ retval_for_cmds = 0;
+#ifdef MODULES_ENABLE
+ if (curcmd->userdata)
+ (*(void (*)(char *p, gpointer u))curcmd->func)(p, curcmd->userdata);
+ else
+ (*curcmd->func)(p);
+#else
+ (*curcmd->func)(p);
+#endif
+ g_free(xpline);
+ return retval_for_cmds;
+}
+
+// process_line(line)
+// Process a command/message line.
+// If this isn't a command, this is a message and it is sent to the
+// currently selected buddy.
+// Return 255 if the line is the /quit command, or 0.
+int process_line(const char *line)
+{
+ if (!*line) { // User only pressed enter
+ if (scr_get_multimode()) {
+ scr_append_multiline("");
+ return 0;
+ }
+ if (current_buddy) {
+ if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP)
+ do_group("toggle");
+ else {
+ // Enter chat mode
+ scr_set_chatmode(TRUE);
+ scr_ShowBuddyWindow();
+ }
+ }
+ return 0;
+ }
+
+ if (*line != COMMAND_CHAR) {
+ // This isn't a command
+ if (scr_get_multimode())
+ scr_append_multiline(line);
+ else
+ do_say_internal((char*)line, 0);
+ return 0;
+ }
+
+ /* It is _probably_ a command -- except for verbatim multi-line mode */
+ return process_command(line, FALSE);
+}
+
+// Helper routine for buffer item_{lock,unlock,toggle_lock}
+// "lock" values: 1=lock 0=unlock -1=invert
+static void roster_buddylock(char *bjid, int lock)
+{
+ gpointer bud = NULL;
+ bool may_need_refresh = FALSE;
+
+ // Allow special jid "" or "." (current buddy)
+ if (bjid && (!*bjid || !strcmp(bjid, ".")))
+ bjid = NULL;
+
+ if (bjid) {
+ // The JID has been specified. Quick check...
+ if (check_jid_syntax(bjid)) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "<%s> is not a valid Jabber ID.", bjid);
+ } else {
+ // Find the buddy
+ GSList *roster_elt;
+ roster_elt = roster_find(bjid, jidsearch,
+ ROSTER_TYPE_USER|ROSTER_TYPE_ROOM);
+ if (roster_elt)
+ bud = roster_elt->data;
+ else
+ scr_LogPrint(LPRINT_NORMAL, "This jid isn't in the roster.");
+ may_need_refresh = TRUE;
+ }
+ } else {
+ // Use the current buddy
+ if (current_buddy)
+ bud = BUDDATA(current_buddy);
+ }
+
+ // Update the ROSTER_FLAG_USRLOCK flag
+ if (bud) {
+ if (lock == -1)
+ lock = !(buddy_getflags(bud) & ROSTER_FLAG_USRLOCK);
+ buddy_setflags(bud, ROSTER_FLAG_USRLOCK, lock);
+ if (may_need_refresh) {
+ buddylist_build();
+ update_roster = TRUE;
+ }
+ }
+}
+
+// display_and_free_note(note, winId)
+// Display the note information in the winId buffer, and free note
+// (winId is a bare jid or NULL for the status window, in which case we
+// display the note jid too)
+static void display_and_free_note(struct annotation *note, const char *winId)
+{
+ gchar tbuf[128];
+ GString *sbuf;
+ guint msg_flag = HBB_PREFIX_INFO;
+ /* We use the flag prefix_info for the first line, and prefix_cont
+ for the other lines, for better readability */
+
+ if (!note)
+ return;
+
+ sbuf = g_string_new("");
+
+ if (!winId) {
+ // We're writing to the status window, so let's show the jid too.
+ g_string_printf(sbuf, "Annotation on <%s>", note->jid);
+ scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
+ msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT;
+ }
+
+ // If we have the creation date, display it
+ if (note->cdate) {
+ strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S",
+ localtime(¬e->cdate));
+ g_string_printf(sbuf, "Note created %s", tbuf);
+ scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
+ msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT;
+ }
+ // If we have the modification date, display it
+ // unless it's the same as the creation date
+ if (note->mdate && note->mdate != note->cdate) {
+ strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S",
+ localtime(¬e->mdate));
+ g_string_printf(sbuf, "Note modified %s", tbuf);
+ scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
+ msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT;
+ }
+ // Note text
+ g_string_printf(sbuf, "Note: %s", note->text);
+ scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
+
+ g_string_free(sbuf, TRUE);
+ g_free(note->text);
+ g_free(note->jid);
+ g_free(note);
+}
+
+static void display_all_annotations(void)
+{
+ GSList *notes;
+ notes = xmpp_get_all_storage_rosternotes();
+
+ if (!notes)
+ return;
+
+ // Call display_and_free_note() for each note,
+ // with winId = NULL (special window)
+ g_slist_foreach(notes, (GFunc)&display_and_free_note, NULL);
+ scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
+ update_roster = TRUE;
+ g_slist_free(notes);
+}
+
+static void roster_note(char *arg)
+{
+ const char *bjid;
+ guint type;
+
+ if (!current_buddy)
+ return;
+
+ bjid = buddy_getjid(BUDDATA(current_buddy));
+ type = buddy_gettype(BUDDATA(current_buddy));
+
+ if (!bjid && type == ROSTER_TYPE_SPECIAL && !arg) {
+ // We're in the status window (the only special buffer currently)
+ // Let's display all server notes
+ display_all_annotations();
+ return;
+ }
+
+ if (!bjid || (type != ROSTER_TYPE_USER &&
+ type != ROSTER_TYPE_ROOM &&
+ type != ROSTER_TYPE_AGENT)) {
+ scr_LogPrint(LPRINT_NORMAL, "This item can't have a note.");
+ return;
+ }
+
+ if (arg && *arg) { // Set a note
+ gchar *msg, *notetxt;
+ msg = to_utf8(arg);
+ if (!strcmp(msg, "-"))
+ notetxt = NULL; // delete note
+ else
+ notetxt = msg;
+ xmpp_set_storage_rosternotes(bjid, notetxt);
+ g_free(msg);
+ } else { // Display a note
+ struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
+ if (note) {
+ display_and_free_note(note, bjid);
+ } else {
+ scr_WriteIncomingMessage(bjid, "This item doesn't have a note.", 0,
+ HBB_PREFIX_INFO, 0);
+ }
+ }
+}
+
+// roster_updown(updown, nitems)
+// updown: -1=up, +1=down
+inline static void roster_updown(int updown, char *nitems)
+{
+ int nbitems;
+
+ if (!nitems || !*nitems)
+ nbitems = 1;
+ else
+ nbitems = strtol(nitems, NULL, 10);
+
+ if (nbitems > 0)
+ scr_RosterUpDown(updown, nbitems);
+}
+
+/* Commands callback functions */
+/* All these do_*() functions will be called with a "arg" parameter */
+/* (with arg not null) */
+
+static void do_roster(char *arg)
+{
+ char **paramlst;
+ char *subcmd;
+
+ paramlst = split_arg(arg, 2, 1); // subcmd, arg
+ subcmd = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!subcmd || !*subcmd) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!strcasecmp(subcmd, "top")) {
+ scr_RosterTop();
+ update_roster = TRUE;
+ } else if (!strcasecmp(subcmd, "bottom")) {
+ scr_RosterBottom();
+ update_roster = TRUE;
+ } else if (!strcasecmp(subcmd, "hide")) {
+ scr_RosterVisibility(0);
+ } else if (!strcasecmp(subcmd, "show")) {
+ scr_RosterVisibility(1);
+ } else if (!strcasecmp(subcmd, "toggle")) {
+ scr_RosterVisibility(-1);
+ } else if (!strcasecmp(subcmd, "hide_offline")) {
+ buddylist_set_hide_offline_buddies(TRUE);
+ if (current_buddy)
+ buddylist_build();
+ update_roster = TRUE;
+ } else if (!strcasecmp(subcmd, "show_offline")) {
+ buddylist_set_hide_offline_buddies(FALSE);
+ buddylist_build();
+ update_roster = TRUE;
+ } else if (!strcasecmp(subcmd, "toggle_offline")) {
+ buddylist_set_hide_offline_buddies(-1);
+ buddylist_build();
+ update_roster = TRUE;
+ } else if (!strcasecmp(subcmd, "display")) {
+ scr_RosterDisplay(arg);
+ } else if (!strcasecmp(subcmd, "item_lock")) {
+ roster_buddylock(arg, 1);
+ } else if (!strcasecmp(subcmd, "item_unlock")) {
+ roster_buddylock(arg, 0);
+ } else if (!strcasecmp(subcmd, "item_toggle_lock")) {
+ roster_buddylock(arg, -1);
+ } else if (!strcasecmp(subcmd, "unread_first")) {
+ scr_RosterUnreadMessage(0);
+ } else if (!strcasecmp(subcmd, "unread_next")) {
+ scr_RosterUnreadMessage(1);
+ } else if (!strcasecmp(subcmd, "alternate")) {
+ scr_RosterJumpAlternate();
+ } else if (!strncasecmp(subcmd, "search", 6)) {
+ strip_arg_special_chars(arg);
+ if (!arg || !*arg) {
+ scr_LogPrint(LPRINT_NORMAL, "What name or JID are you looking for?");
+ free_arg_lst(paramlst);
+ return;
+ }
+ scr_RosterSearch(arg);
+ update_roster = TRUE;
+ } else if (!strcasecmp(subcmd, "up")) {
+ roster_updown(-1, arg);
+ } else if (!strcasecmp(subcmd, "down")) {
+ roster_updown(1, arg);
+ } else if (!strcasecmp(subcmd, "group_prev")) {
+ scr_RosterPrevGroup();
+ } else if (!strcasecmp(subcmd, "group_next")) {
+ scr_RosterNextGroup();
+ } else if (!strcasecmp(subcmd, "note")) {
+ roster_note(arg);
+ } else
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+ free_arg_lst(paramlst);
+}
+
+void do_color(char *arg)
+{
+ char **paramlst;
+ char *subcmd;
+
+ paramlst = split_arg(arg, 2, 1); // subcmd, arg
+ subcmd = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!subcmd || !*subcmd) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!strcasecmp(subcmd, "roster")) {
+ char *status, *wildcard, *color;
+ char **arglist = split_arg(arg, 3, 0);
+
+ status = *arglist;
+ wildcard = to_utf8(arglist[1]);
+ color = arglist[2];
+
+ if (status && !strcmp(status, "clear")) { // Not a color command, clear all
+ scr_RosterClearColor();
+ update_roster = TRUE;
+ } else {
+ if (!status || !*status || !wildcard || !*wildcard || !color || !*color) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing argument");
+ } else {
+ update_roster = scr_RosterColor(status, wildcard, color)
+ || update_roster;
+ }
+ }
+ free_arg_lst(arglist);
+ g_free(wildcard);
+ } else if (!strcasecmp(subcmd, "muc")) {
+ char **arglist = split_arg(arg, 2, 0);
+ char *free_muc = to_utf8(*arglist);
+ const char *muc = free_muc, *mode = arglist[1];
+ if (!muc || !*muc)
+ scr_LogPrint(LPRINT_NORMAL, "What MUC?");
+ else {
+ if (!strcmp(muc, "."))
+ if (!(muc = CURRENT_JID))
+ scr_LogPrint(LPRINT_NORMAL, "No JID selected");
+ if (muc) {
+ if (check_jid_syntax(muc) && strcmp(muc, "*"))
+ scr_LogPrint(LPRINT_NORMAL, "Not a JID");
+ else {
+ if (!mode || !*mode || !strcasecmp(mode, "on"))
+ scr_MucColor(muc, MC_ALL);
+ else if (!strcasecmp(mode, "preset"))
+ scr_MucColor(muc, MC_PRESET);
+ else if (!strcasecmp(mode, "off"))
+ scr_MucColor(muc, MC_OFF);
+ else if (!strcmp(mode, "-"))
+ scr_MucColor(muc, MC_REMOVE);
+ else
+ scr_LogPrint(LPRINT_NORMAL, "Unknown coloring mode");
+ }
+ }
+ }
+ free_arg_lst(arglist);
+ g_free(free_muc);
+ } else if (!strcasecmp(subcmd, "mucnick")) {
+ char **arglist = split_arg(arg, 2, 0);
+ const char *nick = *arglist, *color = arglist[1];
+ if (!nick || !*nick || !color || !*color)
+ scr_LogPrint(LPRINT_NORMAL, "Missing argument");
+ else
+ scr_MucNickColor(nick, color);
+ free_arg_lst(arglist);
+ } else
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+ free_arg_lst(paramlst);
+}
+
+// cmd_setstatus(recipient, arg)
+// Set your Jabber status.
+// - if recipient is not NULL, the status is sent to this contact only
+// - arg must be "status message" (message is optional)
+void cmd_setstatus(const char *recipient, const char *arg)
+{
+ char **paramlst;
+ char *status;
+ char *msg;
+ enum imstatus st;
+
+ if (!lm_connection_is_authenticated(lconnection)) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+ return;
+ }
+
+ // It makes sense to reset autoaway before changing the status
+ // (esp. for FIFO or remote commands) or the behaviour could be
+ // unexpected...
+ if (!recipient)
+ scr_CheckAutoAway(TRUE);
+
+ paramlst = split_arg(arg, 2, 1); // status, message
+ status = *paramlst;
+ msg = *(paramlst+1);
+
+ if (!status) {
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!strcasecmp(status, IMSTATUS_OFFLINE)) st = offline;
+ else if (!strcasecmp(status, IMSTATUS_ONLINE)) st = available;
+ else if (!strcasecmp(status, IMSTATUS_AVAILABLE)) st = available;
+ else if (!strcasecmp(status, IMSTATUS_AWAY)) st = away;
+ else if (!strcasecmp(status, IMSTATUS_INVISIBLE)) st = invisible;
+ else if (!strcasecmp(status, IMSTATUS_DONOTDISTURB)) st = dontdisturb;
+ else if (!strcasecmp(status, IMSTATUS_NOTAVAILABLE)) st = notavail;
+ else if (!strcasecmp(status, IMSTATUS_FREE4CHAT)) st = freeforchat;
+ else if (!strcasecmp(status, "message")) {
+ if (!msg || !*msg) {
+ // We want a message. If there's none, we give up.
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ free_arg_lst(paramlst);
+ return;
+ }
+ st = xmpp_getstatus(); // Preserve current status
+ } else {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized status!");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ // Use provided message
+ if (msg && !*msg) {
+ msg = NULL;
+ }
+
+ // If a recipient is specified, let's don't use default status messages
+ if (recipient && !msg)
+ msg = "";
+
+ xmpp_setstatus(st, recipient, msg, FALSE);
+
+ free_arg_lst(paramlst);
+}
+
+static void do_status(char *arg)
+{
+ if (!*arg) {
+ const char *sm = xmpp_getstatusmsg();
+ scr_LogPrint(LPRINT_NORMAL, "Your status is: [%c] %s",
+ imstatus2char[xmpp_getstatus()],
+ (sm ? sm : ""));
+ return;
+ }
+ arg = to_utf8(arg);
+ cmd_setstatus(NULL, arg);
+ g_free(arg);
+}
+
+static void do_status_to(char *arg)
+{
+ char **paramlst;
+ char *fjid, *st, *msg;
+ char *jid_utf8 = NULL;
+
+ paramlst = split_arg(arg, 3, 1); // jid, status, [message]
+ fjid = *paramlst;
+ st = *(paramlst+1);
+ msg = *(paramlst+2);
+
+ if (!fjid || !st) {
+ scr_LogPrint(LPRINT_NORMAL,
+ "Please specify both a Jabber ID and a status.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ // Allow things like /status_to "" away
+ if (!*fjid || !strcmp(fjid, "."))
+ fjid = NULL;
+
+ if (fjid) {
+ // The JID has been specified. Quick check...
+ if (check_jid_syntax(fjid)) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "<%s> is not a valid Jabber ID.", fjid);
+ fjid = NULL;
+ } else {
+ // Convert jid to lowercase
+ char *p = fjid;
+ for ( ; *p && *p != JID_RESOURCE_SEPARATOR; p++)
+ *p = tolower(*p);
+ fjid = jid_utf8 = to_utf8(fjid);
+ }
+ } else {
+ // Add the current buddy
+ if (current_buddy)
+ fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
+ if (!fjid)
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
+ }
+
+ if (fjid) {
+ char *cmdline;
+ if (!msg)
+ msg = "";
+ msg = to_utf8(msg);
+ cmdline = g_strdup_printf("%s %s", st, msg);
+ scr_LogPrint(LPRINT_LOGNORM, "Sending to <%s> /status %s", fjid, cmdline);
+ cmd_setstatus(fjid, cmdline);
+ g_free(msg);
+ g_free(cmdline);
+ g_free(jid_utf8);
+ }
+ free_arg_lst(paramlst);
+}
+
+static void do_add(char *arg)
+{
+ char **paramlst;
+ char *id, *nick;
+ char *jid_utf8 = NULL;
+
+ if (!lm_connection_is_authenticated(lconnection)) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+ return;
+ }
+
+ paramlst = split_arg(arg, 2, 0); // jid, [nickname]
+ id = *paramlst;
+ nick = *(paramlst+1);
+
+ if (!id)
+ nick = NULL; // Allow things like: /add "" nick
+ else if (!*id || !strcmp(id, "."))
+ id = NULL;
+
+ if (id) {
+ // The JID has been specified. Quick check...
+ if (check_jid_syntax(id)) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "<%s> is not a valid Jabber ID.", id);
+ id = NULL;
+ } else {
+ mc_strtolower(id);
+ // Actually an UTF-8 id isn't needed because only the bare jid will
+ // be used.
+ id = jid_utf8 = to_utf8(id);
+ }
+ } else {
+ // Add the current buddy
+ if (current_buddy)
+ id = (char*)buddy_getjid(BUDDATA(current_buddy));
+ if (!id)
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
+ }
+
+ if (nick)
+ nick = to_utf8(nick);
+
+ if (id) {
+ // 2nd parameter = optional nickname
+ xmpp_addbuddy(id, nick, NULL);
+ scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
+ id);
+ }
+
+ g_free(jid_utf8);
+ g_free(nick);
+ free_arg_lst(paramlst);
+}
+
+static void do_del(char *arg)
+{
+ const char *bjid;
+
+ if (*arg) {
+ scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
+ "the currently-selected buddy will be deleted.");
+ return;
+ }
+
+ if (!current_buddy)
+ return;
+ bjid = buddy_getjid(BUDDATA(current_buddy));
+ if (!bjid)
+ return;
+
+ if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_ROOM) {
+ // This is a chatroom
+ if (buddy_getinsideroom(BUDDATA(current_buddy))) {
+ scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!");
+ return;
+ }
+ }
+
+ // Close the buffer
+ scr_BufferPurge(1, NULL);
+
+ scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", bjid);
+ xmpp_delbuddy(bjid);
+ scr_UpdateBuddyWindow();
+}
+
+static void do_group(char *arg)
+{
+ gpointer group = NULL;
+ guint leave_buddywindow;
+ char **paramlst;
+ char *subcmd;
+ enum { group_toggle = -1, group_unfold = 0, group_fold = 1 } group_state = 0;
+
+ if (!*arg) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ return;
+ }
+
+ if (!current_buddy)
+ return;
+
+ paramlst = split_arg(arg, 2, 0); // subcmd, [arg]
+ subcmd = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!subcmd || !*subcmd)
+ goto do_group_return; // Should not happen anyway
+
+ if (arg && *arg) {
+ GSList *roster_elt;
+ char *group_utf8 = to_utf8(arg);
+ roster_elt = roster_find(group_utf8, namesearch, ROSTER_TYPE_GROUP);
+ g_free(group_utf8);
+ if (roster_elt)
+ group = buddy_getgroup(roster_elt->data);
+ } else {
+ group = buddy_getgroup(BUDDATA(current_buddy));
+ }
+ if (!group)
+ goto do_group_return;
+
+ // We'll have to redraw the chat window if we're not currently on the group
+ // entry itself, because it means we'll have to leave the current buddy
+ // chat window.
+ leave_buddywindow = (group != BUDDATA(current_buddy) &&
+ group == buddy_getgroup(BUDDATA(current_buddy)));
+
+
+ if (!(buddy_gettype(group) & ROSTER_TYPE_GROUP)) {
+ scr_LogPrint(LPRINT_NORMAL, "You need to select a group.");
+ goto do_group_return;
+ }
+
+ if (!strcasecmp(subcmd, "expand") || !strcasecmp(subcmd, "unfold"))
+ group_state = group_unfold;
+ else if (!strcasecmp(subcmd, "shrink") || !strcasecmp(subcmd, "fold"))
+ group_state = group_fold;
+ else if (!strcasecmp(subcmd, "toggle"))
+ group_state = group_toggle;
+ else {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+ goto do_group_return;
+ }
+
+ if (group_state != group_unfold && leave_buddywindow)
+ scr_RosterPrevGroup();
+
+ buddy_hide_group(group, group_state);
+
+ buddylist_build();
+ update_roster = TRUE;
+
+do_group_return:
+ free_arg_lst(paramlst);
+}
+
+static int send_message_to(const char *fjid, const char *msg, const char *subj,
+ LmMessageSubType type_overwrite, bool quiet)
+{
+ char *bare_jid, *rp;
+ char *hmsg;
+ gint crypted;
+ gint retval = 0;
+ int isroom;
+ gpointer xep184 = NULL;
+
+ if (!lm_connection_is_authenticated(lconnection)) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+ return 1;
+ }
+ if (!fjid || !*fjid) {
+ scr_LogPrint(LPRINT_NORMAL, "You must specify a Jabber ID.");
+ return 1;
+ }
+ if (!msg || !*msg) {
+ scr_LogPrint(LPRINT_NORMAL, "You must specify a message.");
+ return 1;
+ }
+ if (check_jid_syntax((char*)fjid)) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "<%s> is not a valid Jabber ID.", fjid);
+ return 1;
+ }
+
+ // We must use the bare jid in hk_message_out()
+ rp = strchr(fjid, JID_RESOURCE_SEPARATOR);
+ if (rp)
+ bare_jid = g_strndup(fjid, rp - fjid);
+ else
+ bare_jid = (char*)fjid;
+
+ if (!quiet) {
+ // Jump to window, create one if needed
+ scr_RosterJumpJid(bare_jid);
+ }
+
+ // Check if we're sending a message to a conference room
+ // If not, we must make sure rp is NULL, for hk_message_out()
+ isroom = !!roster_find(bare_jid, jidsearch, ROSTER_TYPE_ROOM);
+ if (rp) {
+ if (isroom) rp++;
+ else rp = NULL;
+ }
+ isroom = isroom && (!rp || !*rp);
+
+ // local part (UI, logging, etc.)
+ if (subj)
+ hmsg = g_strdup_printf("[%s]\n%s", subj, msg);
+ else
+ hmsg = (char*)msg;
+
+ // Network part
+ xmpp_send_msg(fjid, msg, (isroom ? ROSTER_TYPE_ROOM : ROSTER_TYPE_USER),
+ subj, FALSE, &crypted, type_overwrite, &xep184);
+
+ if (crypted == -1) {
+ scr_LogPrint(LPRINT_LOGNORM, "Encryption error. Message was not sent.");
+ retval = 1;
+ goto send_message_to_return;
+ }
+
+ // Hook
+ if (!isroom)
+ hk_message_out(bare_jid, rp, 0, hmsg, crypted, xep184);
+
+send_message_to_return:
+ if (hmsg != msg) g_free(hmsg);
+ if (rp) g_free(bare_jid);
+ return retval;
+}
+
+// send_message(msg, subj, type_overwrite)
+// Write the message in the buddy's window and send the message on
+// the network.
+static void send_message(const char *msg, const char *subj,
+ LmMessageSubType type_overwrite)
+{
+ const char *bjid;
+
+ if (!current_buddy) {
+ scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected.");
+ return;
+ }
+
+ bjid = CURRENT_JID;
+ if (!bjid) {
+ scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected.");
+ return;
+ }
+
+ send_message_to(bjid, msg, subj, type_overwrite, FALSE);
+}
+
+static LmMessageSubType scan_mtype(char **arg)
+{
+ //Try splitting it
+ char **parlist = split_arg(*arg, 2, 1);
+ LmMessageSubType result = LM_MESSAGE_SUB_TYPE_NOT_SET;
+ //Is it any good parameter?
+ if (parlist && *parlist) {
+ if (!strcmp("-n", *parlist)) {
+ result = LM_MESSAGE_SUB_TYPE_NORMAL;
+ } else if (!strcmp("-h", *parlist)) {
+ result = LM_MESSAGE_SUB_TYPE_HEADLINE;
+ }
+ if (result != LM_MESSAGE_SUB_TYPE_NOT_SET || (!strcmp("--", *parlist)))
+ *arg += strlen(*arg) - (parlist[1] ? strlen(parlist[1]) : 0);
+ }
+ //Anything found? -> skip it
+ free_arg_lst(parlist);
+ return result;
+}
+
+static void do_say_internal(char *arg, int parse_flags)
+{
+ gpointer bud;
+ LmMessageSubType msgtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
+
+ scr_set_chatmode(TRUE);
+ scr_ShowBuddyWindow();
+
+ if (!current_buddy) {
+ scr_LogPrint(LPRINT_NORMAL,
+ "Whom are you talking to? Please select a buddy.");
+ return;
+ }
+
+ bud = BUDDATA(current_buddy);
+ if (!(buddy_gettype(bud) &
+ (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) {
+ scr_LogPrint(LPRINT_NORMAL, "This is not a user.");
+ return;
+ }
+
+ buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
+ if (parse_flags)
+ msgtype = scan_mtype(&arg);
+ arg = to_utf8(arg);
+ send_message(arg, NULL, msgtype);
+ g_free(arg);
+}
+
+static void do_say(char *arg) {
+ do_say_internal(arg, 1);
+}
+
+static void do_msay(char *arg)
+{
+ /* Parameters: begin verbatim abort send send_to */
+ char **paramlst;
+ char *subcmd;
+
+ paramlst = split_arg(arg, 2, 1); // subcmd, arg
+ subcmd = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!subcmd || !*subcmd) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ scr_LogPrint(LPRINT_NORMAL, "Please read the manual before using "
+ "the /msay command.");
+ scr_LogPrint(LPRINT_NORMAL, "(Use \"%s begin\" to enter "
+ "multi-line mode...)", mkcmdstr("msay"));
+ goto do_msay_return;
+ }
+
+ if (!strcasecmp(subcmd, "toggle")) {
+ if (scr_get_multimode())
+ subcmd = "send";
+ else
+ subcmd = "begin";
+ } else if (!strcasecmp(subcmd, "toggle_verbatim")) {
+ if (scr_get_multimode())
+ subcmd = "send";
+ else
+ subcmd = "verbatim";
+ }
+
+ if (!strcasecmp(subcmd, "abort")) {
+ if (scr_get_multimode())
+ scr_LogPrint(LPRINT_NORMAL, "Leaving multi-line message mode.");
+ scr_set_multimode(FALSE, NULL);
+ goto do_msay_return;
+ } else if ((!strcasecmp(subcmd, "begin")) ||
+ (!strcasecmp(subcmd, "verbatim"))) {
+ bool verbat;
+ gchar *subj_utf8 = to_utf8(arg);
+ if (!strcasecmp(subcmd, "verbatim")) {
+ scr_set_multimode(2, subj_utf8);
+ verbat = TRUE;
+ } else {
+ scr_set_multimode(1, subj_utf8);
+ verbat = FALSE;
+ }
+
+ scr_LogPrint(LPRINT_NORMAL, "Entered %smulti-line message mode.",
+ verbat ? "VERBATIM " : "");
+ scr_LogPrint(LPRINT_NORMAL, "Select a buddy and use \"%s send\" "
+ "when your message is ready.", mkcmdstr("msay"));
+ if (verbat)
+ scr_LogPrint(LPRINT_NORMAL, "Use \"%s abort\" to abort this mode.",
+ mkcmdstr("msay"));
+ g_free(subj_utf8);
+ goto do_msay_return;
+ } else if (strcasecmp(subcmd, "send") && strcasecmp(subcmd, "send_to")) {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+ goto do_msay_return;
+ }
+
+ /* send/send_to command */
+
+ if (!scr_get_multimode()) {
+ scr_LogPrint(LPRINT_NORMAL, "No message to send. "
+ "Use \"%s begin\" first.", mkcmdstr("msay"));
+ goto do_msay_return;
+ }
+
+ scr_set_chatmode(TRUE);
+ scr_ShowBuddyWindow();
+
+ if (!strcasecmp(subcmd, "send_to")) {
+ int err = FALSE;
+ gchar *msg_utf8;
+ LmMessageSubType msg_type = scan_mtype(&arg);
+ // Let's send to the specified JID. We leave now if there
+ // has been an error (so we don't leave multi-line mode).
+ arg = to_utf8(arg);
+ msg_utf8 = to_utf8(scr_get_multiline());
+ if (msg_utf8) {
+ err = send_message_to(arg, msg_utf8, scr_get_multimode_subj(), msg_type,
+ FALSE);
+ g_free(msg_utf8);
+ }
+ g_free(arg);
+ if (err)
+ goto do_msay_return;
+ } else { // Send to currently selected buddy
+ gpointer bud;
+ gchar *msg_utf8;
+
+ if (!current_buddy) {
+ scr_LogPrint(LPRINT_NORMAL, "Whom are you talking to?");
+ goto do_msay_return;
+ }
+
+ bud = BUDDATA(current_buddy);
+ if (!(buddy_gettype(bud) &
+ (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) {
+ scr_LogPrint(LPRINT_NORMAL, "This is not a user.");
+ goto do_msay_return;
+ }
+
+ buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
+ msg_utf8 = to_utf8(scr_get_multiline());
+ if (msg_utf8) {
+ send_message(msg_utf8, scr_get_multimode_subj(), scan_mtype(&arg));
+ g_free(msg_utf8);
+ }
+ }
+ scr_set_multimode(FALSE, NULL);
+ scr_LogPrint(LPRINT_NORMAL, "You have left multi-line message mode.");
+do_msay_return:
+ free_arg_lst(paramlst);
+}
+
+// load_message_from_file(filename)
+// Read the whole content of a file.
+// The data are converted to UTF8, they should be freed by the caller after
+// use.
+char *load_message_from_file(const char *filename)
+{
+ FILE *fd;
+ struct stat buf;
+ char *msgbuf, *msgbuf_utf8;
+ char *p;
+ char *next_utf8_char;
+ size_t len;
+
+ fd = fopen(filename, "r");
+
+ if (!fd || fstat(fileno(fd), &buf)) {
+ scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename);
+ return NULL;
+ }
+ if (!buf.st_size || buf.st_size >= HBB_BLOCKSIZE) {
+ if (!buf.st_size)
+ scr_LogPrint(LPRINT_LOGNORM, "Message file is empty (%s)", filename);
+ else
+ scr_LogPrint(LPRINT_LOGNORM, "Message file is too big (%s)", filename);
+ fclose(fd);
+ return NULL;
+ }
+
+ msgbuf = g_new0(char, HBB_BLOCKSIZE);
+ len = fread(msgbuf, 1, HBB_BLOCKSIZE-1, fd);
+ fclose(fd);
+
+ next_utf8_char = msgbuf;
+
+ // Check there is no binary data. It must be a *message* file!
+ for (p = msgbuf ; *p ; p++) {
+ if (utf8_mode) {
+ if (p == next_utf8_char) {
+ if (!iswprint(get_char(p)) && *p != '\n' && *p != '\t')
+ break;
+ next_utf8_char = next_char(p);
+ }
+ } else {
+ unsigned char sc = *p;
+ if (!iswprint(sc) && sc != '\n' && sc != '\t')
+ break;
+ }
+ }
+
+ if (*p || (size_t)(p-msgbuf) != len) { // We're not at the End Of Line...
+ scr_LogPrint(LPRINT_LOGNORM, "Message file contains "
+ "invalid characters (%s)", filename);
+ g_free(msgbuf);
+ return NULL;
+ }
+
+ // p is now at the EOL
+ // Let's strip trailing newlines
+ if (p > msgbuf)
+ p--;
+ while (p > msgbuf && *p == '\n')
+ *p-- = 0;
+
+ // It could be empty, once the trailing newlines are gone
+ if (p == msgbuf && *p == '\n') {
+ scr_LogPrint(LPRINT_LOGNORM, "Message file is empty (%s)", filename);
+ g_free(msgbuf);
+ return NULL;
+ }
+
+ msgbuf_utf8 = to_utf8(msgbuf);
+
+ if (!msgbuf_utf8 && msgbuf)
+ scr_LogPrint(LPRINT_LOGNORM, "Message file charset conversion error (%s)",
+ filename);
+ g_free(msgbuf);
+ return msgbuf_utf8;
+}
+
+static void do_say_to(char *arg)
+{
+ char **paramlst;
+ char *fjid, *msg;
+ char *file = NULL;
+ LmMessageSubType msg_type = LM_MESSAGE_SUB_TYPE_NOT_SET;
+ bool quiet = FALSE;
+
+ if (!lm_connection_is_authenticated(lconnection)) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+ return;
+ }
+
+ msg_type = scan_mtype(&arg);
+ paramlst = split_arg(arg, 2, 1); // jid, message (or option, jid, message)
+
+ if (!*paramlst) { // No parameter?
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ // Check for an option parameter
+ while (*paramlst) {
+ if (!strcmp(*paramlst, "-q")) {
+ char **oldparamlst = paramlst;
+ paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, message
+ free_arg_lst(oldparamlst);
+ quiet = TRUE;
+ } else if (!strcmp(*paramlst, "-f")) {
+ char **oldparamlst = paramlst;
+ paramlst = split_arg(*(oldparamlst+1), 2, 1); // filename, jid
+ free_arg_lst(oldparamlst);
+ if (!*paramlst) {
+ scr_LogPrint(LPRINT_NORMAL, "Wrong usage.");
+ return;
+ }
+ file = g_strdup(*paramlst);
+ // One more parameter shift...
+ oldparamlst = paramlst;
+ paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, nothing
+ free_arg_lst(oldparamlst);
+ } else
+ break;
+ }
+
+ if (!*paramlst) {
+ scr_LogPrint(LPRINT_NORMAL, "Wrong usage.");
+ return;
+ }
+
+ fjid = *paramlst;
+ msg = *(paramlst+1);
+
+ if (!strcmp(fjid, ".")) {
+ // Send the message to the current buddy
+ if (current_buddy)
+ fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
+ if (!fjid) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
+ free_arg_lst(paramlst);
+ return;
+ }
+ } else if (check_jid_syntax(fjid)) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ fjid = to_utf8(fjid);
+ if (!file) {
+ msg = to_utf8(msg);
+ } else {
+ char *filename_xp;
+ if (msg)
+ scr_LogPrint(LPRINT_NORMAL, "say_to: extra parameter ignored.");
+ filename_xp = expand_filename(file);
+ msg = load_message_from_file(filename_xp);
+ g_free(filename_xp);
+ g_free(file);
+ }
+
+ send_message_to(fjid, msg, NULL, msg_type, quiet);
+
+ g_free(fjid);
+ g_free(msg);
+ free_arg_lst(paramlst);
+}
+
+// buffer_updown(updown, nblines)
+// updown: -1=up, +1=down
+inline static void buffer_updown(int updown, char *nlines)
+{
+ int nblines;
+
+ if (!nlines || !*nlines)
+ nblines = 0;
+ else
+ nblines = strtol(nlines, NULL, 10);
+
+ if (nblines >= 0)
+ scr_BufferScrollUpDown(updown, nblines);
+}
+
+static void buffer_search(int direction, char *arg)
+{
+ if (!arg || !*arg) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ return;
+ }
+
+ scr_BufferSearch(direction, arg);
+}
+
+static void buffer_date(char *date)
+{
+ time_t t;
+
+ if (!date || !*date) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ return;
+ }
+
+ strip_arg_special_chars(date);
+
+ t = from_iso8601(date, 0);
+ if (t)
+ scr_BufferDate(t);
+ else
+ scr_LogPrint(LPRINT_NORMAL, "The date you specified is "
+ "not correctly formatted or invalid.");
+}
+
+static void buffer_percent(char *arg1, char *arg2)
+{
+ // Basically, user has typed "%arg1 arg2"
+ // "%50" -> arg1 = 50, arg2 null pointer
+ // "% 50" -> arg1 = \0, arg2 = 50
+
+ if (!*arg1 && (!arg2 || !*arg2)) { // No value
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ return;
+ }
+
+ if (*arg1 && arg2 && *arg2) { // Two values
+ scr_LogPrint(LPRINT_NORMAL, "Wrong parameters.");
+ return;
+ }
+
+ scr_BufferPercent(atoi((*arg1 ? arg1 : arg2)));
+}
+
+static void do_buffer(char *arg)
+{
+ char **paramlst;
+ char *subcmd;
+
+ if (!current_buddy)
+ return;
+
+ paramlst = split_arg(arg, 2, 1); // subcmd, arg
+ subcmd = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!subcmd || !*subcmd) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP &&
+ strcasecmp(subcmd, "close_all")) {
+ scr_LogPrint(LPRINT_NORMAL, "Groups have no buffer.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!strcasecmp(subcmd, "top")) {
+ scr_BufferTopBottom(-1);
+ } else if (!strcasecmp(subcmd, "bottom")) {
+ scr_BufferTopBottom(1);
+ } else if (!strcasecmp(subcmd, "clear")) {
+ scr_BufferClear();
+ } else if (!strcasecmp(subcmd, "close")) {
+ scr_BufferPurge(1, arg);
+ } else if (!strcasecmp(subcmd, "close_all")) {
+ scr_BufferPurgeAll(1);
+ } else if (!strcasecmp(subcmd, "purge")) {
+ scr_BufferPurge(0, arg);
+ } else if (!strcasecmp(subcmd, "scroll_lock")) {
+ scr_BufferScrollLock(1);
+ } else if (!strcasecmp(subcmd, "scroll_unlock")) {
+ scr_BufferScrollLock(0);
+ } else if (!strcasecmp(subcmd, "scroll_toggle")) {
+ scr_BufferScrollLock(-1);
+ } else if (!strcasecmp(subcmd, "up")) {
+ buffer_updown(-1, arg);
+ } else if (!strcasecmp(subcmd, "down")) {
+ buffer_updown(1, arg);
+ } else if (!strcasecmp(subcmd, "search_backward")) {
+ strip_arg_special_chars(arg);
+ buffer_search(-1, arg);
+ } else if (!strcasecmp(subcmd, "search_forward")) {
+ strip_arg_special_chars(arg);
+ buffer_search(1, arg);
+ } else if (!strcasecmp(subcmd, "date")) {
+ buffer_date(arg);
+ } else if (*subcmd == '%') {
+ buffer_percent(subcmd+1, arg);
+ } else if (!strcasecmp(subcmd, "save")) {
+ scr_BufferDump(arg);
+ } else if (!strcasecmp(subcmd, "list")) {
+ scr_BufferList();
+ } else {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+ }
+
+ free_arg_lst(paramlst);
+}
+
+static void do_clear(char *arg) // Alias for "buffer clear"
+{
+ do_buffer("clear");
+}
+
+static void do_info(char *arg)
+{
+ gpointer bud;
+ const char *bjid, *name;
+ guint type, on_srv;
+ char *buffer;
+ enum subscr esub;
+
+ if (!current_buddy)
+ return;
+ bud = BUDDATA(current_buddy);
+
+ bjid = buddy_getjid(bud);
+ name = buddy_getname(bud);
+ type = buddy_gettype(bud);
+ esub = buddy_getsubscription(bud);
+ on_srv = buddy_getonserverflag(bud);
+
+ buffer = g_new(char, 4096);
+
+ if (bjid) {
+ GSList *resources, *p_res;
+ char *bstr = "unknown";
+
+ // Enter chat mode
+ scr_set_chatmode(TRUE);
+ scr_ShowBuddyWindow();
+
+ snprintf(buffer, 4095, "jid: <%s>", bjid);
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+ if (name) {
+ snprintf(buffer, 4095, "Name: %s", name);
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+ }
+
+ if (type == ROSTER_TYPE_USER) bstr = "user";
+ else if (type == ROSTER_TYPE_ROOM) bstr = "chatroom";
+ else if (type == ROSTER_TYPE_AGENT) bstr = "agent";
+ snprintf(buffer, 127, "Type: %s", bstr);
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+
+ if (!on_srv) {
+ scr_WriteIncomingMessage(bjid, "(Local item, not on the server)",
+ 0, HBB_PREFIX_INFO, 0);
+ }
+
+ if (esub == sub_both) bstr = "both";
+ else if (esub & sub_from) bstr = "from";
+ else if (esub & sub_to) bstr = "to";
+ else bstr = "none";
+ snprintf(buffer, 64, "Subscription: %s", bstr);
+ if (esub & sub_pending)
+ strcat(buffer, " (pending)");
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+
+ resources = buddy_getresources(bud);
+ if (!resources && type == ROSTER_TYPE_USER) {
+ // No resource; display last status message, if any.
+ const char *rst_msg = buddy_getstatusmsg(bud, "");
+ if (rst_msg) {
+ snprintf(buffer, 4095, "Last status message: %s", rst_msg);
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+ }
+ }
+ for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
+ gchar rprio;
+ enum imstatus rstatus;
+ const char *rst_msg;
+ time_t rst_time;
+ struct pgp_data *rpgp;
+
+ rprio = buddy_getresourceprio(bud, p_res->data);
+ rstatus = buddy_getstatus(bud, p_res->data);
+ rst_msg = buddy_getstatusmsg(bud, p_res->data);
+ rst_time = buddy_getstatustime(bud, p_res->data);
+ rpgp = buddy_resource_pgp(bud, p_res->data);
+
+ snprintf(buffer, 4095, "Resource: [%c] (%d) %s", imstatus2char[rstatus],
+ rprio, (char*)p_res->data);
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+ if (rst_msg) {
+ snprintf(buffer, 4095, "Status message: %s", rst_msg);
+ scr_WriteIncomingMessage(bjid, buffer,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ }
+ if (rst_time) {
+ char tbuf[128];
+
+ strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", localtime(&rst_time));
+ snprintf(buffer, 127, "Status timestamp: %s", tbuf);
+ scr_WriteIncomingMessage(bjid, buffer,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ }
+#ifdef HAVE_GPGME
+ if (rpgp && rpgp->sign_keyid) {
+ snprintf(buffer, 4095, "PGP key id: %s", rpgp->sign_keyid);
+ scr_WriteIncomingMessage(bjid, buffer,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ if (rpgp->last_sigsum) {
+ gpgme_sigsum_t ss = rpgp->last_sigsum;
+ snprintf(buffer, 4095, "Last PGP signature: %s",
+ (ss & GPGME_SIGSUM_GREEN ? "good":
+ (ss & GPGME_SIGSUM_RED ? "bad" : "unknown")));
+ scr_WriteIncomingMessage(bjid, buffer,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ }
+ }
+#endif
+ g_free(p_res->data);
+ }
+ g_slist_free(resources);
+ } else { /* Item has no jid */
+ if (name) scr_LogPrint(LPRINT_NORMAL, "Name: %s", name);
+ scr_LogPrint(LPRINT_NORMAL, "Type: %s",
+ type == ROSTER_TYPE_GROUP ? "group" :
+ (type == ROSTER_TYPE_SPECIAL ? "special" : "unknown"));
+ }
+ g_free(buffer);
+
+ // Tell the user if this item has an annotation.
+ if (type == ROSTER_TYPE_USER ||
+ type == ROSTER_TYPE_ROOM ||
+ type == ROSTER_TYPE_AGENT) {
+ struct annotation *note = xmpp_get_storage_rosternotes(bjid, TRUE);
+ if (note) {
+ // We do not display the note, we just tell the user.
+ g_free(note->text);
+ g_free(note->jid);
+ g_free(note);
+ scr_WriteIncomingMessage(bjid, "(This item has an annotation)", 0,
+ HBB_PREFIX_INFO, 0);
+ }
+ }
+}
+
+// room_names() is a variation of do_info(), for chatrooms only
+static void room_names(gpointer bud, char *arg)
+{
+ const char *bjid;
+ char *buffer;
+ GSList *resources, *p_res;
+ enum { style_normal = 0, style_detail, style_short,
+ style_quiet, style_compact } style = 0;
+
+ if (*arg) {
+ if (!strcasecmp(arg, "--short"))
+ style = style_short;
+ else if (!strcasecmp(arg, "--quiet"))
+ style = style_quiet;
+ else if (!strcasecmp(arg, "--detail"))
+ style = style_detail;
+ else if (!strcasecmp(arg, "--compact"))
+ style = style_compact;
+ else {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+ return;
+ }
+ }
+
+ // Enter chat mode
+ scr_set_chatmode(TRUE);
+ scr_ShowBuddyWindow();
+
+ bjid = buddy_getjid(bud);
+
+ buffer = g_new(char, 4096);
+ strncpy(buffer, "Room members:", 127);
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+
+ resources = buddy_getresources(bud);
+ for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
+ enum imstatus rstatus;
+ const char *rst_msg;
+
+ rstatus = buddy_getstatus(bud, p_res->data);
+ rst_msg = buddy_getstatusmsg(bud, p_res->data);
+
+ if (style == style_short) {
+ snprintf(buffer, 4095, "[%c] %s%s%s", imstatus2char[rstatus],
+ (char*)p_res->data,
+ rst_msg ? " -- " : "", rst_msg ? rst_msg : "");
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+ } else if (style == style_compact) {
+ enum imrole role = buddy_getrole(bud, p_res->data);
+ enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
+ bool showaffil = (affil != affil_none);
+
+ snprintf(buffer, 4095, "[%c] %s (%s%s%s)",
+ imstatus2char[rstatus], (char*)p_res->data,
+ showaffil ? straffil[affil] : "\0",
+ showaffil ? "/" : "\0",
+ strrole[role]);
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+ } else {
+ // (Style "normal", "detail" or "quiet")
+ snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
+ (char*)p_res->data);
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+ if (rst_msg && style != style_quiet) {
+ snprintf(buffer, 4095, "Status message: %s", rst_msg);
+ scr_WriteIncomingMessage(bjid, buffer,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ }
+ if (style == style_detail) {
+ enum imrole role = buddy_getrole(bud, p_res->data);
+ enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
+
+ snprintf(buffer, 4095, "Role: %s", strrole[role]);
+ scr_WriteIncomingMessage(bjid, buffer,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ if (affil != affil_none) {
+ snprintf(buffer, 4095, "Affiliat.: %s", straffil[affil]);
+ scr_WriteIncomingMessage(bjid, buffer,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ }
+ }
+ }
+ g_free(p_res->data);
+ }
+ g_slist_free(resources);
+ g_free(buffer);
+}
+
+static void move_group_member(gpointer bud, void *groupnamedata)
+{
+ const char *bjid, *name, *groupname;
+
+ groupname = (char *)groupnamedata;
+
+ bjid = buddy_getjid(bud);
+ name = buddy_getname(bud);
+
+ xmpp_updatebuddy(bjid, name, *groupname ? groupname : NULL);
+}
+
+static void do_rename(char *arg)
+{
+ gpointer bud;
+ const char *bjid, *group;
+ guint type, on_srv;
+ char *newname, *p;
+ char *name_utf8;
+
+ if (!current_buddy)
+ return;
+ bud = BUDDATA(current_buddy);
+
+ bjid = buddy_getjid(bud);
+ group = buddy_getgroupname(bud);
+ type = buddy_gettype(bud);
+ on_srv = buddy_getonserverflag(bud);
+
+ if (type & ROSTER_TYPE_SPECIAL) {
+ scr_LogPrint(LPRINT_NORMAL, "You can't rename this item.");
+ return;
+ }
+
+ if (!*arg && !(type & ROSTER_TYPE_GROUP)) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a new name.");
+ return;
+ }
+
+ if (!(type & ROSTER_TYPE_GROUP) && !on_srv) {
+ scr_LogPrint(LPRINT_NORMAL,
+ "Note: this item will be added to your server roster.");
+ // If this is a MUC room w/o bookmark, let's give a small hint...
+ if ((type & ROSTER_TYPE_ROOM) && !xmpp_is_bookmarked(bjid)) {
+ scr_LogPrint(LPRINT_NORMAL,
+ "You should add a room bookmark or it will not be "
+ "recognized as a MUC room next time you run mcabber.");
+ }
+ }
+
+ newname = g_strdup(arg);
+ // Remove trailing space
+ for (p = newname; *p; p++) ;
+ while (p > newname && *p == ' ') *p = 0;
+
+ strip_arg_special_chars(newname);
+
+ name_utf8 = to_utf8(newname);
+
+ if (type & ROSTER_TYPE_GROUP) {
+ // Rename a whole group
+ foreach_group_member(bud, &move_group_member, name_utf8);
+ // Let's jump to the previous buddy, because this group name should
+ // disappear when we receive the server answer.
+ scr_RosterUpDown(-1, 1);
+ } else {
+ // Rename a single buddy
+ guint del_name = 0;
+ if (!*newname || !strcmp(arg, "-"))
+ del_name = TRUE;
+ buddy_setname(bud, (del_name ? (char*)bjid : name_utf8));
+ xmpp_updatebuddy(bjid, (del_name ? NULL : name_utf8), group);
+ }
+
+ g_free(name_utf8);
+ g_free(newname);
+ update_roster = TRUE;
+}
+
+static void do_move(char *arg)
+{
+ gpointer bud;
+ const char *bjid, *name, *oldgroupname;
+ guint type;
+ char *newgroupname, *p;
+ char *group_utf8;
+
+ if (!current_buddy)
+ return;
+ bud = BUDDATA(current_buddy);
+
+ bjid = buddy_getjid(bud);
+ name = buddy_getname(bud);
+ type = buddy_gettype(bud);
+
+ oldgroupname = buddy_getgroupname(bud);
+
+ if (type & ROSTER_TYPE_GROUP) {
+ scr_LogPrint(LPRINT_NORMAL, "You can't move groups!");
+ return;
+ }
+ if (type & ROSTER_TYPE_SPECIAL) {
+ scr_LogPrint(LPRINT_NORMAL, "You can't move this item.");
+ return;
+ }
+
+ newgroupname = g_strdup(arg);
+ // Remove trailing space
+ for (p = newgroupname; *p; p++) ;
+ while (p > newgroupname && *p == ' ') *p-- = 0;
+
+ strip_arg_special_chars(newgroupname);
+
+ group_utf8 = to_utf8(newgroupname);
+ if (strcmp(oldgroupname, group_utf8)) {
+ guint msgflag;
+
+ xmpp_updatebuddy(bjid, name, *group_utf8 ? group_utf8 : NULL);
+ scr_RosterUpDown(-1, 1);
+
+ // If the buddy has a pending message flag,
+ // we remove it temporarily in order to reset the global group
+ // flag. We set it back once the buddy is in the new group,
+ // which will update the new group's flag.
+ msgflag = buddy_getflags(bud) & ROSTER_FLAG_MSG;
+ if (msgflag)
+ roster_msg_setflag(bjid, FALSE, FALSE);
+ buddy_setgroup(bud, group_utf8);
+ if (msgflag)
+ roster_msg_setflag(bjid, FALSE, TRUE);
+ }
+
+ g_free(group_utf8);
+ g_free(newgroupname);
+ update_roster = TRUE;
+}
+
+static void print_option_cb(char *k, char *v, void *f)
+{
+ char *format = (char *)f;
+ scr_LogPrint (LPRINT_NORMAL, format, k, v);
+}
+
+static void do_set(char *arg)
+{
+ guint assign;
+ gchar *option, *value;
+ gchar *option_utf8;
+
+ if (!*arg) {
+ // list all set options
+ settings_foreach(SETTINGS_TYPE_OPTION, print_option_cb, "%s = [%s]");
+ return;
+ }
+
+ assign = parse_assigment(arg, &option, &value);
+ if (!option) {
+ scr_LogPrint(LPRINT_NORMAL, "Set what option?");
+ return;
+ }
+ option_utf8 = to_utf8(option);
+ g_free(option);
+ if (!assign) { // This is a query
+ const char *val = settings_opt_get(option_utf8);
+ if (val)
+ scr_LogPrint(LPRINT_NORMAL, "%s = [%s]", option_utf8, val);
+ else
+ scr_LogPrint(LPRINT_NORMAL, "Option %s is not set", option_utf8);
+ g_free(option_utf8);
+ return;
+ }
+ // Update the option
+ // Maybe some options should be protected when user is connected (server,
+ // username, etc.). And we should catch some options here, too
+ // (hide_offline_buddies for ex.)
+ if (!value) {
+ settings_del(SETTINGS_TYPE_OPTION, option_utf8);
+ } else {
+ gchar *value_utf8 = to_utf8(value);
+ settings_set(SETTINGS_TYPE_OPTION, option_utf8, value_utf8);
+ g_free(value_utf8);
+ g_free(value);
+ }
+ g_free(option_utf8);
+}
+
+static void dump_alias(char *k, char *v, void *param)
+{
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, "Alias %s = %s", k, v);
+}
+
+static void do_alias(char *arg)
+{
+ guint assign;
+ gchar *alias, *value;
+
+ assign = parse_assigment(arg, &alias, &value);
+ if (!alias) {
+ settings_foreach(SETTINGS_TYPE_ALIAS, &dump_alias, NULL);
+ return;
+ }
+ if (!assign) { // This is a query
+ const char *val = settings_get(SETTINGS_TYPE_ALIAS, alias);
+ // NOTE: LPRINT_NOTUTF8 here, see below why it isn't encoded...
+ if (val)
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, "%s = %s", alias, val);
+ else
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "Alias '%s' does not exist", alias);
+ goto do_alias_return;
+ }
+ // Check the alias does not conflict with a registered command
+ if (cmd_get(alias)) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "'%s' is a reserved word!", alias);
+ goto do_alias_return;
+ }
+ // Update the alias
+ if (!value) {
+ if (settings_get(SETTINGS_TYPE_ALIAS, alias)) {
+ settings_del(SETTINGS_TYPE_ALIAS, alias);
+ // Remove alias from the completion list
+ compl_del_category_word(COMPL_CMD, alias);
+ }
+ } else {
+ /* Add alias to the completion list, if not already in.
+ NOTE: We're not UTF8-encoding "alias" and "value" here because UTF-8 is
+ not yet supported in the UI... (and we use the values in the completion
+ system)
+ */
+ if (!settings_get(SETTINGS_TYPE_ALIAS, alias))
+ compl_add_category_word(COMPL_CMD, alias);
+ settings_set(SETTINGS_TYPE_ALIAS, alias, value);
+ g_free(value);
+ }
+do_alias_return:
+ g_free(alias);
+}
+
+static void dump_bind(char *k, char *v, void *param)
+{
+ scr_LogPrint(LPRINT_NORMAL, "Key %4s is bound to: %s", k, v);
+}
+
+static void do_bind(char *arg)
+{
+ guint assign;
+ gchar *k_code, *value;
+
+ assign = parse_assigment(arg, &k_code, &value);
+ if (!k_code) {
+ settings_foreach(SETTINGS_TYPE_BINDING, &dump_bind, NULL);
+ return;
+ }
+ if (!assign) { // This is a query
+ const char *val = settings_get(SETTINGS_TYPE_BINDING, k_code);
+ if (val)
+ scr_LogPrint(LPRINT_NORMAL, "Key %s is bound to: %s", k_code, val);
+ else
+ scr_LogPrint(LPRINT_NORMAL, "Key %s is not bound.", k_code);
+ g_free(k_code);
+ return;
+ }
+ // Update the key binding
+ if (!value) {
+ settings_del(SETTINGS_TYPE_BINDING, k_code);
+ } else {
+ gchar *value_utf8 = to_utf8(value);
+ settings_set(SETTINGS_TYPE_BINDING, k_code, value_utf8);
+ g_free(value_utf8);
+ g_free(value);
+ }
+ g_free(k_code);
+}
+
+static void do_rawxml(char *arg)
+{
+ char **paramlst;
+ char *subcmd;
+
+ if (!lm_connection_is_authenticated(lconnection)) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+ return;
+ }
+
+ paramlst = split_arg(arg, 2, 1); // subcmd, arg
+ subcmd = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!subcmd || !*subcmd) {
+ scr_LogPrint(LPRINT_NORMAL, "Please read the manual page"
+ " before using /rawxml :-)");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!strcasecmp(subcmd, "send")) {
+ gchar *buffer;
+
+ if (!subcmd || !*subcmd) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ // We don't strip_arg_special_chars() here, because it would be a pain for
+ // the user to escape quotes in a XML stream...
+
+ buffer = to_utf8(arg);
+ if (buffer) {
+ scr_LogPrint(LPRINT_NORMAL, "Sending XML string");
+ lm_connection_send_raw(lconnection, buffer, NULL);
+ g_free(buffer);
+ } else {
+ scr_LogPrint(LPRINT_NORMAL, "Conversion error in XML string.");
+ }
+ } else {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+ }
+
+ free_arg_lst(paramlst);
+}
+
+// check_room_subcommand(arg, param_needed, buddy_must_be_a_room)
+// - Check if this is a room, if buddy_must_be_a_room is not null
+// - Check there is at least 1 parameter, if param_needed is true
+// - Return null if one of the checks fails, or a pointer to the first
+// non-space character.
+static char *check_room_subcommand(char *arg, bool param_needed,
+ gpointer buddy_must_be_a_room)
+{
+ if (buddy_must_be_a_room &&
+ !(buddy_gettype(buddy_must_be_a_room) & ROSTER_TYPE_ROOM)) {
+ scr_LogPrint(LPRINT_NORMAL, "This isn't a conference room.");
+ return NULL;
+ }
+
+ if (param_needed) {
+ if (!arg) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ return NULL;
+ }
+ }
+
+ if (arg)
+ return arg;
+ else
+ return "";
+}
+
+static void room_join(gpointer bud, char *arg)
+{
+ char **paramlst;
+ char *roomname, *nick, *pass;
+ char *roomname_tmp = NULL;
+ char *pass_utf8;
+
+ paramlst = split_arg(arg, 3, 0); // roomid, nickname, password
+ roomname = *paramlst;
+ nick = *(paramlst+1);
+ pass = *(paramlst+2);
+
+ if (!roomname)
+ nick = NULL;
+ if (!nick)
+ pass = NULL;
+
+ if (!roomname || !strcmp(roomname, ".")) {
+ // If the current_buddy is recognized as a room, the room name
+ // can be omitted (or "." can be used).
+ if (!bud || !(buddy_gettype(bud) & ROSTER_TYPE_ROOM)) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a room name.");
+ free_arg_lst(paramlst);
+ return;
+ }
+ roomname = (char*)buddy_getjid(bud);
+ } else if (strchr(roomname, '/')) {
+ scr_LogPrint(LPRINT_NORMAL, "Invalid room name.");
+ free_arg_lst(paramlst);
+ return;
+ } else {
+ // The room id has been specified. Let's convert it and use it.
+ mc_strtolower(roomname);
+ roomname = roomname_tmp = to_utf8(roomname);
+ }
+
+ // If no nickname is provided with the /join command,
+ // we try to get a default nickname.
+ if (!nick || !*nick)
+ nick = default_muc_nickname(roomname);
+ else
+ nick = to_utf8(nick);
+ // If we still have no nickname, give up
+ if (!nick || !*nick) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a nickname.");
+ g_free(nick);
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ pass_utf8 = to_utf8(pass);
+
+ xmpp_room_join(roomname, nick, pass_utf8);
+
+ scr_LogPrint(LPRINT_LOGNORM, "Sent a join request to <%s>...", roomname);
+
+ g_free(roomname_tmp);
+ g_free(nick);
+ g_free(pass_utf8);
+ buddylist_build();
+ update_roster = TRUE;
+ free_arg_lst(paramlst);
+}
+
+static void room_invite(gpointer bud, char *arg)
+{
+ char **paramlst;
+ const gchar *roomname;
+ char* fjid;
+ gchar *reason_utf8;
+
+ paramlst = split_arg(arg, 2, 1); // jid, [reason]
+ fjid = *paramlst;
+ arg = *(paramlst+1);
+ // An empty reason is no reason...
+ if (arg && !*arg)
+ arg = NULL;
+
+ if (!fjid || !*fjid) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing or incorrect Jabber ID.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ roomname = buddy_getjid(bud);
+ reason_utf8 = to_utf8(arg);
+ xmpp_room_invite(roomname, fjid, reason_utf8);
+ scr_LogPrint(LPRINT_LOGNORM, "Invitation sent to <%s>.", fjid);
+ g_free(reason_utf8);
+ free_arg_lst(paramlst);
+}
+
+static void room_affil(gpointer bud, char *arg)
+{
+ char **paramlst;
+ gchar *fjid, *rolename;
+ struct role_affil ra;
+ const char *roomid = buddy_getjid(bud);
+
+ paramlst = split_arg(arg, 3, 1); // jid, new_affil, [reason]
+ fjid = *paramlst;
+ rolename = *(paramlst+1);
+ arg = *(paramlst+2);
+
+ if (!fjid || !*fjid || !rolename || !*rolename) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify both a Jabber ID and a role.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ ra.type = type_affil;
+ ra.val.affil = affil_none;
+ for (; ra.val.affil < imaffiliation_size; ra.val.affil++)
+ if (!strcasecmp(rolename, straffil[ra.val.affil]))
+ break;
+
+ if (ra.val.affil < imaffiliation_size) {
+ gchar *jid_utf8, *reason_utf8;
+ jid_utf8 = to_utf8(fjid);
+ reason_utf8 = to_utf8(arg);
+ xmpp_room_setattrib(roomid, jid_utf8, NULL, ra, reason_utf8);
+ g_free(jid_utf8);
+ g_free(reason_utf8);
+ } else
+ scr_LogPrint(LPRINT_NORMAL, "Wrong affiliation parameter.");
+
+ free_arg_lst(paramlst);
+}
+
+static void room_role(gpointer bud, char *arg)
+{
+ char **paramlst;
+ gchar *fjid, *rolename;
+ struct role_affil ra;
+ const char *roomid = buddy_getjid(bud);
+
+ paramlst = split_arg(arg, 3, 1); // jid, new_role, [reason]
+ fjid = *paramlst;
+ rolename = *(paramlst+1);
+ arg = *(paramlst+2);
+
+ if (!fjid || !*fjid || !rolename || !*rolename) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify both a Jabber ID and a role.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ ra.type = type_role;
+ ra.val.role = role_none;
+ for (; ra.val.role < imrole_size; ra.val.role++)
+ if (!strcasecmp(rolename, strrole[ra.val.role]))
+ break;
+
+ if (ra.val.role < imrole_size) {
+ gchar *jid_utf8, *reason_utf8;
+ jid_utf8 = to_utf8(fjid);
+ reason_utf8 = to_utf8(arg);
+ xmpp_room_setattrib(roomid, jid_utf8, NULL, ra, reason_utf8);
+ g_free(jid_utf8);
+ g_free(reason_utf8);
+ } else
+ scr_LogPrint(LPRINT_NORMAL, "Wrong role parameter.");
+
+ free_arg_lst(paramlst);
+}
+
+
+// The expected argument is a Jabber id
+static void room_ban(gpointer bud, char *arg)
+{
+ char **paramlst;
+ gchar *fjid, *bjid;
+ const gchar *banjid;
+ gchar *jid_utf8, *reason_utf8;
+ struct role_affil ra;
+ const char *roomid = buddy_getjid(bud);
+
+ paramlst = split_arg(arg, 2, 1); // jid, [reason]
+ fjid = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!fjid || !*fjid) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ ra.type = type_affil;
+ ra.val.affil = affil_outcast;
+
+ bjid = jidtodisp(fjid);
+ jid_utf8 = to_utf8(bjid);
+
+ // If the argument doesn't look like a jid, we'll try to find a matching
+ // nickname.
+ if (!strchr(bjid, JID_DOMAIN_SEPARATOR) || check_jid_syntax(bjid)) {
+ const gchar *tmp;
+ // We want the initial argument, so the fjid variable, because
+ // we don't want to strip a resource-like string from the nickname!
+ g_free(jid_utf8);
+ jid_utf8 = to_utf8(fjid);
+ tmp = buddy_getrjid(bud, jid_utf8);
+ if (!tmp) {
+ scr_LogPrint(LPRINT_NORMAL, "Wrong JID or nickname");
+ goto room_ban_return;
+ }
+ banjid = jidtodisp(tmp);
+ } else
+ banjid = jid_utf8;
+
+ scr_LogPrint(LPRINT_NORMAL, "Requesting a ban for %s", banjid);
+
+ reason_utf8 = to_utf8(arg);
+ xmpp_room_setattrib(roomid, banjid, NULL, ra, reason_utf8);
+ g_free(reason_utf8);
+
+room_ban_return:
+ g_free(bjid);
+ g_free(jid_utf8);
+ free_arg_lst(paramlst);
+}
+
+// The expected argument is a Jabber id
+static void room_unban(gpointer bud, char *arg)
+{
+ gchar *fjid = arg;
+ gchar *jid_utf8;
+ struct role_affil ra;
+ const char *roomid = buddy_getjid(bud);
+
+ if (!fjid || !*fjid) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
+ return;
+ }
+
+ ra.type = type_affil;
+ ra.val.affil = affil_none;
+
+ jid_utf8 = to_utf8(fjid);
+ xmpp_room_setattrib(roomid, jid_utf8, NULL, ra, NULL);
+ g_free(jid_utf8);
+}
+
+// The expected argument is a nickname
+static void room_kick(gpointer bud, char *arg)
+{
+ char **paramlst;
+ gchar *nick;
+ gchar *nick_utf8, *reason_utf8;
+ struct role_affil ra;
+ const char *roomid = buddy_getjid(bud);
+
+ paramlst = split_arg(arg, 2, 1); // nickname, [reason]
+ nick = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!nick || !*nick) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a nickname.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ ra.type = type_role;
+ ra.val.affil = role_none;
+
+ nick_utf8 = to_utf8(nick);
+ reason_utf8 = to_utf8(arg);
+ xmpp_room_setattrib(roomid, NULL, nick_utf8, ra, reason_utf8);
+ g_free(nick_utf8);
+ g_free(reason_utf8);
+
+ free_arg_lst(paramlst);
+}
+
+void cmd_room_leave(gpointer bud, char *arg)
+{
+ gchar *roomid, *desc;
+ const char *nickname;
+
+ nickname = buddy_getnickname(bud);
+ if (!nickname) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not in this room.");
+ return;
+ }
+
+ roomid = g_strdup_printf("%s/%s", buddy_getjid(bud), nickname);
+ desc = to_utf8(arg);
+ xmpp_setstatus(offline, roomid, desc, TRUE);
+ g_free(desc);
+ g_free(roomid);
+}
+
+static void room_nick(gpointer bud, char *arg)
+{
+ if (!buddy_getinsideroom(bud)) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not in this room.");
+ return;
+ }
+
+ if (!arg || !*arg) {
+ const char *nick = buddy_getnickname(bud);
+ if (nick)
+ scr_LogPrint(LPRINT_NORMAL, "Your nickname is: %s", nick);
+ else
+ scr_LogPrint(LPRINT_NORMAL, "You have no nickname in this room.");
+ } else {
+ gchar *nick = to_utf8(arg);
+ strip_arg_special_chars(nick);
+ xmpp_room_join(buddy_getjid(bud), nick, NULL);
+ g_free(nick);
+ }
+}
+
+static void room_privmsg(gpointer bud, char *arg)
+{
+ char **paramlst;
+ gchar *fjid_utf8, *nick, *nick_utf8, *msg;
+
+ paramlst = split_arg(arg, 2, 1); // nickname, message
+ nick = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!nick || !*nick || !arg || !*arg) {
+ scr_LogPrint(LPRINT_NORMAL,
+ "Please specify both a Jabber ID and a message.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ nick_utf8 = to_utf8(nick);
+ fjid_utf8 = g_strdup_printf("%s/%s", buddy_getjid(bud), nick_utf8);
+ g_free (nick_utf8);
+ msg = to_utf8(arg);
+ send_message_to(fjid_utf8, msg, NULL, LM_MESSAGE_SUB_TYPE_NOT_SET, FALSE);
+ g_free(fjid_utf8);
+ g_free(msg);
+ free_arg_lst(paramlst);
+}
+
+static void room_remove(gpointer bud, char *arg)
+{
+ if (*arg) {
+ scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
+ "the currently-selected room will be removed.");
+ return;
+ }
+
+ // Quick check: if there are resources, we haven't left
+ if (buddy_getinsideroom(bud)) {
+ scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!");
+ return;
+ }
+ // Delete the room
+ roster_del_user(buddy_getjid(bud));
+ scr_UpdateBuddyWindow();
+ buddylist_build();
+ update_roster = TRUE;
+}
+
+static void room_topic(gpointer bud, char *arg)
+{
+ if (!buddy_getinsideroom(bud)) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not in this room.");
+ return;
+ }
+
+ // If no parameter is given, display the current topic
+ if (!*arg) {
+ const char *topic = buddy_gettopic(bud);
+ if (topic)
+ scr_LogPrint(LPRINT_NORMAL, "Topic: %s", topic);
+ else
+ scr_LogPrint(LPRINT_NORMAL, "No topic has been set.");
+ return;
+ }
+
+ // If arg is "-", let's clear the topic
+ if (!strcmp(arg, "-"))
+ arg = NULL;
+
+ arg = to_utf8(arg);
+ // Set the topic
+ xmpp_send_msg(buddy_getjid(bud), NULL, ROSTER_TYPE_ROOM, arg ? arg : "",
+ FALSE, NULL, LM_MESSAGE_SUB_TYPE_NOT_SET, NULL);
+ g_free(arg);
+}
+
+static void room_destroy(gpointer bud, char *arg)
+{
+ gchar *msg;
+
+ if (arg && *arg)
+ msg = to_utf8(arg);
+ else
+ msg = NULL;
+
+ xmpp_room_destroy(buddy_getjid(bud), NULL, msg);
+ g_free(msg);
+}
+
+static void room_unlock(gpointer bud, char *arg)
+{
+ if (*arg) {
+ scr_LogPrint(LPRINT_NORMAL, "Unknown parameter.");
+ return;
+ }
+
+ xmpp_room_unlock(buddy_getjid(bud));
+}
+
+static void room_setopt(gpointer bud, char *arg)
+{
+ char **paramlst;
+ char *param, *value;
+ enum { opt_none = 0, opt_printstatus, opt_autowhois } option = 0;
+
+ paramlst = split_arg(arg, 2, 1); // param, value
+ param = *paramlst;
+ value = *(paramlst+1);
+ if (!param) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a room option.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!strcasecmp(param, "print_status"))
+ option = opt_printstatus;
+ else if (!strcasecmp(param, "auto_whois"))
+ option = opt_autowhois;
+ else {
+ scr_LogPrint(LPRINT_NORMAL, "Wrong option!");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ // If no value is given, display the current value
+ if (!value) {
+ const char *strval;
+ if (option == opt_printstatus)
+ strval = strprintstatus[buddy_getprintstatus(bud)];
+ else
+ strval = strautowhois[buddy_getautowhois(bud)];
+ scr_LogPrint(LPRINT_NORMAL, "%s is set to: %s", param, strval);
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (option == opt_printstatus) {
+ enum room_printstatus eval;
+ if (!strcasecmp(value, "none"))
+ eval = status_none;
+ else if (!strcasecmp(value, "in_and_out"))
+ eval = status_in_and_out;
+ else if (!strcasecmp(value, "all"))
+ eval = status_all;
+ else {
+ eval = status_default;
+ if (strcasecmp(value, "default") != 0)
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized value, assuming default...");
+ }
+ buddy_setprintstatus(bud, eval);
+ } else if (option == opt_autowhois) {
+ enum room_autowhois eval;
+ if (!strcasecmp(value, "on"))
+ eval = autowhois_on;
+ else if (!strcasecmp(value, "off"))
+ eval = autowhois_off;
+ else {
+ eval = autowhois_default;
+ if (strcasecmp(value, "default") != 0)
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized value, assuming default...");
+ }
+ buddy_setautowhois(bud, eval);
+ }
+
+ free_arg_lst(paramlst);
+}
+
+// cmd_room_whois(..)
+// If interactive is TRUE, chatmode can be enabled.
+void cmd_room_whois(gpointer bud, char *arg, guint interactive)
+{
+ char **paramlst;
+ gchar *nick, *buffer;
+ const char *bjid, *realjid;
+ const char *rst_msg;
+ gchar rprio;
+ enum imstatus rstatus;
+ enum imrole role;
+ enum imaffiliation affil;
+ time_t rst_time;
+ guint msg_flag = HBB_PREFIX_INFO;
+
+ paramlst = split_arg(arg, 1, 0); // nickname
+ nick = *paramlst;
+
+ if (!nick || !*nick) {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a nickname.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ nick = to_utf8(nick);
+
+ if (interactive) {
+ // Enter chat mode
+ scr_set_chatmode(TRUE);
+ scr_ShowBuddyWindow();
+ } else
+ msg_flag |= HBB_PREFIX_NOFLAG;
+
+ bjid = buddy_getjid(bud);
+ rstatus = buddy_getstatus(bud, nick);
+
+ if (rstatus == offline) {
+ scr_LogPrint(LPRINT_NORMAL, "No such member: %s", nick);
+ free_arg_lst(paramlst);
+ g_free(nick);
+ return;
+ }
+
+ rst_time = buddy_getstatustime(bud, nick);
+ rprio = buddy_getresourceprio(bud, nick);
+ rst_msg = buddy_getstatusmsg(bud, nick);
+ if (!rst_msg) rst_msg = "";
+
+ role = buddy_getrole(bud, nick);
+ affil = buddy_getaffil(bud, nick);
+ realjid = buddy_getrjid(bud, nick);
+
+ buffer = g_new(char, 4096);
+
+ snprintf(buffer, 4095, "Whois [%s]", nick);
+ scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag, 0);
+ snprintf(buffer, 4095, "Status : [%c] %s", imstatus2char[rstatus],
+ rst_msg);
+ scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
+
+ if (rst_time) {
+ char tbuf[128];
+
+ strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", localtime(&rst_time));
+ snprintf(buffer, 127, "Timestamp: %s", tbuf);
+ scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
+ }
+
+ if (realjid) {
+ snprintf(buffer, 4095, "JID : <%s>", realjid);
+ scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
+ }
+
+ snprintf(buffer, 4095, "Role : %s", strrole[role]);
+ scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
+ snprintf(buffer, 4095, "Affiliat.: %s", straffil[affil]);
+ scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
+ snprintf(buffer, 4095, "Priority : %d", rprio);
+ scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
+
+ scr_WriteIncomingMessage(bjid, "End of WHOIS", 0, msg_flag, 0);
+
+ g_free(buffer);
+ g_free(nick);
+ free_arg_lst(paramlst);
+}
+
+static void room_bookmark(gpointer bud, char *arg)
+{
+ const char *roomid;
+ const char *name = NULL, *nick = NULL;
+ char *tmpnick = NULL;
+ enum room_autowhois autowhois = 0;
+ enum room_printstatus printstatus = 0;
+ enum { bm_add = 0, bm_del = 1 } action = 0;
+ int autojoin = 0;
+ int nick_set = 0;
+
+ if (arg && *arg) {
+ // /room bookmark [add|del] [[+|-]autojoin] [-|nick]
+ char **paramlst;
+ char **pp;
+
+ paramlst = split_arg(arg, 3, 0); // At most 3 parameters
+ for (pp = paramlst; *pp; pp++) {
+ if (!strcasecmp(*pp, "add"))
+ action = bm_add;
+ else if (!strcasecmp(*pp, "del"))
+ action = bm_del;
+ else if (!strcasecmp(*pp, "-autojoin"))
+ autojoin = 0;
+ else if (!strcasecmp(*pp, "+autojoin") || !strcasecmp(*pp, "autojoin"))
+ autojoin = 1;
+ else if (!strcmp(*pp, "-"))
+ nick_set = 1;
+ else {
+ nick_set = 1;
+ nick = tmpnick = to_utf8 (*pp);
+ }
+ }
+ free_arg_lst(paramlst);
+ }
+
+ roomid = buddy_getjid(bud);
+
+ if (action == bm_add) {
+ name = buddy_getname(bud);
+ if (!nick_set)
+ nick = buddy_getnickname(bud);
+ printstatus = buddy_getprintstatus(bud);
+ autowhois = buddy_getautowhois(bud);
+ }
+
+ xmpp_set_storage_bookmark(roomid, name, nick, NULL, autojoin,
+ printstatus, autowhois);
+ g_free (tmpnick);
+}
+
+static void display_all_bookmarks(void)
+{
+ GSList *bm, *bmp;
+ GString *sbuf;
+ struct bookmark *bm_elt;
+
+ bm = xmpp_get_all_storage_bookmarks();
+
+ if (!bm)
+ return;
+
+ sbuf = g_string_new("");
+
+ scr_WriteIncomingMessage(NULL, "List of MUC bookmarks:",
+ 0, HBB_PREFIX_INFO, 0);
+
+ for (bmp = bm; bmp; bmp = g_slist_next(bmp)) {
+ bm_elt = bmp->data;
+ g_string_printf(sbuf, "%c <%s>",
+ (bm_elt->autojoin ? '*' : ' '), bm_elt->roomjid);
+ if (bm_elt->nick)
+ g_string_append_printf(sbuf, " (%s)", bm_elt->nick);
+ if (bm_elt->name)
+ g_string_append_printf(sbuf, " %s", bm_elt->name);
+ g_free(bm_elt->roomjid);
+ g_free(bm_elt->name);
+ g_free(bm_elt->nick);
+ g_free(bm_elt);
+ scr_WriteIncomingMessage(NULL, sbuf->str,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ }
+
+ scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
+ update_roster = TRUE;
+ g_string_free(sbuf, TRUE);
+ g_slist_free(bm);
+}
+
+#ifdef MODULES_ENABLE
+static gint module_list_comparator(gconstpointer arg1, gconstpointer arg2)
+{
+ const loaded_module_t *module = arg1;
+ const char *name = arg2;
+ return g_strcmp0(module->name, name);
+}
+
+static void do_load(char *arg)
+{
+ GModule *mod;
+ GSList *lmod;
+ char *mdir, *path;
+ if (!arg || !*arg) {
+ scr_LogPrint(LPRINT_LOGNORM, "Missing modulename.");
+ return;
+ }
+ lmod = g_slist_find_custom(loaded_modules, arg, module_list_comparator);
+ if (lmod) {
+ scr_LogPrint(LPRINT_LOGNORM, "Module %s is already loaded.", arg);
+ return;
+ }
+ mdir = expand_filename(settings_opt_get("modules_dir"));
+ path = g_module_build_path(mdir, arg);
+ mod = g_module_open(path, G_MODULE_BIND_LAZY);
+ if (!mod)
+ scr_LogPrint(LPRINT_LOGNORM, "Module loading failed: %s",
+ g_module_error());
+ else {
+ loaded_module_t *module = g_new(loaded_module_t, 1);
+ module->name = g_strdup(arg);
+ module->module = mod;
+ loaded_modules = g_slist_prepend(loaded_modules, module);
+ scr_LogPrint(LPRINT_LOGNORM, "Loaded module %s.", arg);
+ }
+ g_free(path);
+ g_free(mdir);
+}
+
+static void do_unload(char *arg)
+{
+ GSList *module;
+ if (!arg || !*arg) {
+ scr_LogPrint(LPRINT_LOGNORM, "Missing modulename.");
+ return;
+ }
+ module = g_slist_find_custom(loaded_modules, arg, module_list_comparator);
+ if (module) {
+ loaded_module_t *mod = module->data;
+ if (!g_module_close(mod->module))
+ scr_LogPrint(LPRINT_LOGNORM, "Module unloading failed: %s",
+ g_module_error());
+ else {
+ g_free(mod->name);
+ g_free(mod);
+ loaded_modules = g_slist_delete_link(loaded_modules, module);
+ scr_LogPrint(LPRINT_LOGNORM, "Unloaded module %s.", arg);
+ }
+ } else
+ scr_LogPrint(LPRINT_LOGNORM, "Module %s not loaded.", arg);
+}
+#endif
+
+static void do_room(char *arg)
+{
+ char **paramlst;
+ char *subcmd;
+ gpointer bud;
+
+ if (!lm_connection_is_authenticated(lconnection)) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+ return;
+ }
+
+ paramlst = split_arg(arg, 2, 1); // subcmd, arg
+ subcmd = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!subcmd || !*subcmd) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (current_buddy) {
+ bud = BUDDATA(current_buddy);
+ } else {
+ if (strcasecmp(subcmd, "join")) {
+ free_arg_lst(paramlst);
+ return;
+ }
+ // "room join" is a special case, we don't need to have a valid
+ // current_buddy.
+ bud = NULL;
+ }
+
+ if (!strcasecmp(subcmd, "join")) {
+ if ((arg = check_room_subcommand(arg, FALSE, NULL)) != NULL)
+ room_join(bud, arg);
+ } else if (!strcasecmp(subcmd, "invite")) {
+ if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
+ room_invite(bud, arg);
+ } else if (!strcasecmp(subcmd, "affil")) {
+ if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
+ room_affil(bud, arg);
+ } else if (!strcasecmp(subcmd, "role")) {
+ if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
+ room_role(bud, arg);
+ } else if (!strcasecmp(subcmd, "ban")) {
+ if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
+ room_ban(bud, arg);
+ } else if (!strcasecmp(subcmd, "unban")) {
+ if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
+ room_unban(bud, arg);
+ } else if (!strcasecmp(subcmd, "kick")) {
+ if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
+ room_kick(bud, arg);
+ } else if (!strcasecmp(subcmd, "leave")) {
+ if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
+ cmd_room_leave(bud, arg);
+ } else if (!strcasecmp(subcmd, "names")) {
+ if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
+ room_names(bud, arg);
+ } else if (!strcasecmp(subcmd, "nick")) {
+ if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
+ room_nick(bud, arg);
+ } else if (!strcasecmp(subcmd, "privmsg")) {
+ if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
+ room_privmsg(bud, arg);
+ } else if (!strcasecmp(subcmd, "remove")) {
+ if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
+ room_remove(bud, arg);
+ } else if (!strcasecmp(subcmd, "destroy")) {
+ if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
+ room_destroy(bud, arg);
+ } else if (!strcasecmp(subcmd, "unlock")) {
+ if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
+ room_unlock(bud, arg);
+ } else if (!strcasecmp(subcmd, "setopt")) {
+ if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
+ room_setopt(bud, arg);
+ } else if (!strcasecmp(subcmd, "topic")) {
+ if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
+ room_topic(bud, arg);
+ } else if (!strcasecmp(subcmd, "whois")) {
+ if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
+ cmd_room_whois(bud, arg, TRUE);
+ } else if (!strcasecmp(subcmd, "bookmark")) {
+ if (!arg && !buddy_getjid(BUDDATA(current_buddy)) &&
+ buddy_gettype(BUDDATA(current_buddy)) == ROSTER_TYPE_SPECIAL)
+ display_all_bookmarks();
+ else if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
+ room_bookmark(bud, arg);
+ } else {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+ }
+
+ free_arg_lst(paramlst);
+}
+
+static void do_authorization(char *arg)
+{
+ char **paramlst;
+ char *subcmd;
+ char *jid_utf8;
+
+ if (!lm_connection_is_authenticated(lconnection)) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+ return;
+ }
+
+ paramlst = split_arg(arg, 2, 0); // subcmd, [jid]
+ subcmd = *paramlst;
+ arg = *(paramlst+1);
+
+ if (!subcmd || !*subcmd) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+ goto do_authorization_return;
+ }
+
+ // Use the provided jid, if it looks valid
+ if (arg) {
+ if (!*arg) {
+ // If no jid is provided, we use the current selected buddy
+ arg = NULL;
+ } else {
+ if (check_jid_syntax(arg)) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "<%s> is not a valid Jabber ID.", arg);
+ goto do_authorization_return;
+ }
+ }
+ }
+
+ if (!arg) { // Use the current selected buddy's jid
+ gpointer bud;
+ guint type;
+
+ if (!current_buddy)
+ goto do_authorization_return;
+ bud = BUDDATA(current_buddy);
+
+ jid_utf8 = arg = (char*)buddy_getjid(bud);
+ type = buddy_gettype(bud);
+
+ if (!(type & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT))) {
+ scr_LogPrint(LPRINT_NORMAL, "Invalid buddy.");
+ goto do_authorization_return;
+ }
+ } else {
+ jid_utf8 = to_utf8(arg);
+ }
+
+ if (!strcasecmp(subcmd, "allow")) {
+ xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_SUBSCRIBED);
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Sent presence subscription approval to <%s>.",
+ jid_utf8);
+ } else if (!strcasecmp(subcmd, "cancel")) {
+ xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED);
+ scr_LogPrint(LPRINT_LOGNORM,
+ "<%s> will no longer receive your presence updates.",
+ jid_utf8);
+ } else if (!strcasecmp(subcmd, "request")) {
+ xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_SUBSCRIBE);
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Sent presence notification request to <%s>.", jid_utf8);
+ } else if (!strcasecmp(subcmd, "request_unsubscribe")) {
+ xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE);
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Sent presence notification unsubscription request to <%s>.",
+ jid_utf8);
+ } else {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+ }
+
+ // Only free jid_utf8 if it has been allocated, i.e. if != arg.
+ if (jid_utf8 && jid_utf8 != arg)
+ g_free(jid_utf8);
+do_authorization_return:
+ free_arg_lst(paramlst);
+}
+
+static void do_version(char *arg)
+{
+ gchar *ver = mcabber_version();
+ scr_LogPrint(LPRINT_NORMAL, "This is mcabber version %s.", ver);
+ g_free(ver);
+}
+
+static void do_request(char *arg)
+{
+ char **paramlst;
+ char *fjid, *type;
+ enum iqreq_type numtype = iqreq_none;
+ char *jid_utf8 = NULL;
+
+ paramlst = split_arg(arg, 2, 0); // type, jid
+ type = *paramlst;
+ fjid = *(paramlst+1);
+
+ if (type) {
+ // Quick check...
+ if (!strcasecmp(type, "version"))
+ numtype = iqreq_version;
+ else if (!strcasecmp(type, "time"))
+ numtype = iqreq_time;
+ else if (!strcasecmp(type, "last"))
+ numtype = iqreq_last;
+ else if (!strcasecmp(type, "vcard"))
+ numtype = iqreq_vcard;
+ }
+
+ if (!type || !numtype) {
+ scr_LogPrint(LPRINT_NORMAL,
+ "Please specify a query type (version, time...).");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!lm_connection_is_authenticated(lconnection)) {
+ scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ // Allow special jid "" or "." (current buddy)
+ if (fjid && (!*fjid || !strcmp(fjid, ".")))
+ fjid = NULL;
+
+ if (fjid) {
+ // The JID has been specified. Quick check...
+ if (check_jid_syntax(fjid)) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "<%s> is not a valid Jabber ID.", fjid);
+ fjid = NULL;
+ } else {
+ // Convert jid to lowercase
+ char *p;
+ for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
+ *p = tolower(*p);
+ fjid = jid_utf8 = to_utf8(fjid);
+ }
+ } else {
+ // Add the current buddy
+ if (current_buddy)
+ fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
+ if (!fjid)
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
+ }
+
+ if (fjid) {
+ switch (numtype) {
+ case iqreq_version:
+ case iqreq_time:
+ case iqreq_last:
+ case iqreq_vcard:
+ xmpp_request(fjid, numtype);
+ break;
+ default:
+ break;
+ }
+ }
+ g_free(jid_utf8);
+ free_arg_lst(paramlst);
+}
+
+static void do_event(char *arg)
+{
+ char **paramlst;
+ char *evid, *subcmd;
+ int action = -1;
+ GSList *evidlst;
+
+ paramlst = split_arg(arg, 2, 0); // id, subcmd
+ evid = *paramlst;
+ subcmd = *(paramlst+1);
+
+ if (!evid || !subcmd) {
+ // Special case: /event list
+ if (evid && !strcasecmp(evid, "list"))
+ evs_display_list();
+ else
+ scr_LogPrint(LPRINT_NORMAL,
+ "Missing parameter. Usage: /event num action");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!strcasecmp(subcmd, "reject"))
+ action = 0;
+ else if (!strcasecmp(subcmd, "accept"))
+ action = 1;
+ else if (!strcasecmp(subcmd, "ignore"))
+ action = 2;
+
+ if (action == -1) {
+ scr_LogPrint(LPRINT_NORMAL, "Wrong action parameter.");
+ } else if (action >= 0 && action <= 2) {
+ GSList *p;
+
+ if (action == 2) {
+ action = EVS_CONTEXT_CANCEL;
+ } else {
+ action += EVS_CONTEXT_USER;
+ }
+
+ if (!strcmp(evid, "*")) {
+ // Use completion list
+ evidlst = evs_geteventslist(FALSE);
+ } else {
+ // Let's create a slist with the provided event id
+ evidlst = g_slist_append(NULL, g_strdup(evid));
+ }
+ for (p = evidlst; p; p = g_slist_next(p)) {
+ if (evs_callback(p->data, action) == -1) {
+ scr_LogPrint(LPRINT_NORMAL, "Event %s not found.", p->data);
+ }
+ g_free(p->data);
+ }
+ g_slist_free(evidlst);
+ }
+
+ free_arg_lst(paramlst);
+}
+
+static void do_pgp(char *arg)
+{
+ char **paramlst;
+ char *fjid, *subcmd, *keyid;
+ enum {
+ pgp_none,
+ pgp_enable,
+ pgp_disable,
+ pgp_setkey,
+ pgp_force,
+ pgp_info
+ } op = 0;
+ int force = FALSE;
+
+ paramlst = split_arg(arg, 3, 0); // subcmd, jid, [key]
+ subcmd = *paramlst;
+ fjid = *(paramlst+1);
+ keyid = *(paramlst+2);
+
+ if (!subcmd)
+ fjid = NULL;
+ if (!fjid)
+ keyid = NULL;
+
+ if (subcmd) {
+ if (!strcasecmp(subcmd, "enable"))
+ op = pgp_enable;
+ else if (!strcasecmp(subcmd, "disable"))
+ op = pgp_disable;
+ else if (!strcasecmp(subcmd, "setkey"))
+ op = pgp_setkey;
+ else if ((!strcasecmp(subcmd, "force")) ||
+ (!strcasecmp(subcmd, "+force"))) {
+ op = pgp_force;
+ force = TRUE;
+ } else if (!strcasecmp(subcmd, "-force"))
+ op = pgp_force;
+ else if (!strcasecmp(subcmd, "info"))
+ op = pgp_info;
+ }
+
+ if (!op) {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized or missing parameter!");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ // Allow special jid "" or "." (current buddy)
+ if (fjid && (!*fjid || !strcmp(fjid, ".")))
+ fjid = NULL;
+
+ if (fjid) {
+ // The JID has been specified. Quick check...
+ if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "<%s> is not a valid Jabber ID.", fjid);
+ fjid = NULL;
+ } else {
+ // Convert jid to lowercase and strip resource
+ char *p;
+ for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
+ *p = tolower(*p);
+ if (*p == JID_RESOURCE_SEPARATOR)
+ *p = '\0';
+ }
+ } else {
+ gpointer bud = NULL;
+ if (current_buddy)
+ bud = BUDDATA(current_buddy);
+ if (bud) {
+ guint type = buddy_gettype(bud);
+ if (type & ROSTER_TYPE_USER) // Is it a user?
+ fjid = (char*)buddy_getjid(bud);
+ else
+ scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
+ }
+ }
+
+ if (fjid) { // fjid is actually a bare jid...
+ guint disabled;
+ GString *sbuf;
+ switch (op) {
+ case pgp_enable:
+ case pgp_disable:
+ settings_pgp_setdisabled(fjid, (op == pgp_disable ? TRUE : FALSE));
+ break;
+ case pgp_force:
+ settings_pgp_setforce(fjid, force);
+ break;
+ case pgp_setkey:
+ settings_pgp_setkeyid(fjid, keyid);
+ break;
+ case pgp_info:
+ sbuf = g_string_new("");
+ if (settings_pgp_getkeyid(fjid)) {
+ g_string_printf(sbuf, "PGP Encryption key id: %s",
+ settings_pgp_getkeyid(fjid));
+ scr_WriteIncomingMessage(fjid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
+ }
+ disabled = settings_pgp_getdisabled(fjid);
+ g_string_printf(sbuf, "PGP encryption is %s",
+ (disabled ? "disabled" : "enabled"));
+ scr_WriteIncomingMessage(fjid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
+ if (!disabled && settings_pgp_getforce(fjid)) {
+ scr_WriteIncomingMessage(fjid,
+ "Encryption enforced (no negotiation)",
+ 0, HBB_PREFIX_INFO, 0);
+ }
+ g_string_free(sbuf, TRUE);
+ break;
+ default:
+ break;
+ }
+ } else {
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
+ }
+
+ free_arg_lst(paramlst);
+}
+
+static void do_otr(char *arg)
+{
+#ifdef HAVE_LIBOTR
+ char **paramlst;
+ char *fjid, *subcmd, *keyid;
+ enum {
+ otr_none,
+ otr_start,
+ otr_stop,
+ otr_fpr,
+ otr_smpq,
+ otr_smpr,
+ otr_smpa,
+ otr_k,
+ otr_info
+ } op = 0;
+
+ if (!otr_enabled()) {
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Warning: OTR hasn't been enabled -- command ignored.");
+ return;
+ }
+
+ paramlst = split_arg(arg, 3, 0); // subcmd, jid, [key]
+ subcmd = *paramlst;
+ fjid = *(paramlst+1);
+ keyid = *(paramlst+2);
+
+ if (!subcmd)
+ fjid = NULL;
+ if (!fjid)
+ keyid = NULL;
+
+ if (subcmd) {
+ if (!strcasecmp(subcmd, "start"))
+ op = otr_start;
+ else if (!strcasecmp(subcmd, "stop"))
+ op = otr_stop;
+ else if (!strcasecmp(subcmd, "fingerprint"))
+ op = otr_fpr;
+ else if (!strcasecmp(subcmd, "smpq"))
+ op = otr_smpq;
+ else if (!strcasecmp(subcmd, "smpr"))
+ op = otr_smpr;
+ else if (!strcasecmp(subcmd, "smpa"))
+ op = otr_smpa;
+ else if (!strcasecmp(subcmd, "key"))
+ op = otr_k;
+ else if (!strcasecmp(subcmd, "info"))
+ op = otr_info;
+ }
+
+ if (!op) {
+ scr_LogPrint(LPRINT_NORMAL, "Unrecognized or missing parameter!");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (op == otr_k)
+ otr_key();
+ else {
+ // Allow special jid "" or "." (current buddy)
+ if (fjid && (!*fjid || !strcmp(fjid, ".")))
+ fjid = NULL;
+
+ if (fjid) {
+ // The JID has been specified. Quick check...
+ if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "<%s> is not a valid Jabber ID.", fjid);
+ fjid = NULL;
+ } else {
+ // Convert jid to lowercase and strip resource
+ char *p;
+ for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
+ *p = tolower(*p);
+ if (*p == JID_RESOURCE_SEPARATOR)
+ *p = '\0';
+ }
+ } else {
+ gpointer bud = NULL;
+ if (current_buddy)
+ bud = BUDDATA(current_buddy);
+ if (bud) {
+ guint type = buddy_gettype(bud);
+ if (type & ROSTER_TYPE_USER) // Is it a user?
+ fjid = (char*)buddy_getjid(bud);
+ else
+ scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
+ }
+ }
+
+ if (fjid) { // fjid is actually a bare jid...
+ switch (op) {
+ case otr_start:
+ otr_establish(fjid); break;
+ case otr_stop:
+ otr_disconnect(fjid); break;
+ case otr_fpr:
+ otr_fingerprint(fjid, keyid); break;
+ case otr_smpq:
+ otr_smp_query(fjid, keyid); break;
+ case otr_smpr:
+ otr_smp_respond(fjid, keyid); break;
+ case otr_smpa:
+ otr_smp_abort(fjid); break;
+ case otr_info:
+ otr_print_info(fjid); break;
+ default:
+ break;
+ }
+ } else
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
+ }
+ free_arg_lst(paramlst);
+
+#else
+ scr_LogPrint(LPRINT_NORMAL, "Please recompile mcabber with libotr enabled.");
+#endif /* HAVE_LIBOTR */
+}
+
+#ifdef HAVE_LIBOTR
+static char *string_for_otrpolicy(enum otr_policy p)
+{
+ switch (p) {
+ case plain: return "plain";
+ case opportunistic: return "opportunistic";
+ case manual: return "manual";
+ case always: return "always";
+ default: return "unknown";
+ }
+}
+
+static void dump_otrpolicy(char *k, char *v, void *nothing)
+{
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, "otrpolicy for %s: %s", k,
+ string_for_otrpolicy(*(enum otr_policy*)v));
+}
+#endif
+
+static void do_otrpolicy(char *arg)
+{
+#ifdef HAVE_LIBOTR
+ char **paramlst;
+ char *fjid, *policy;
+ enum otr_policy p;
+
+ paramlst = split_arg(arg, 2, 0); // [jid|default] policy
+ fjid = *paramlst;
+ policy = *(paramlst+1);
+
+ if (!fjid && !policy) {
+ scr_LogPrint(LPRINT_NORMAL, "default otrpolicy: %s",
+ string_for_otrpolicy(settings_otr_getpolicy(NULL)));
+ settings_foreach(SETTINGS_TYPE_OTR, &dump_otrpolicy, NULL);
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!policy) {
+ scr_LogPrint(LPRINT_NORMAL,
+ "Please call otrpolicy correctly: /otrpolicy (default|jid) "
+ "(plain|manual|opportunistic|always)");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!strcasecmp(policy, "plain"))
+ p = plain;
+ else if (!strcasecmp(policy, "manual"))
+ p = manual;
+ else if (!strcasecmp(policy, "opportunistic"))
+ p = opportunistic;
+ else if (!strcasecmp(policy, "always"))
+ p = always;
+ else {
+ /* Fail, we don't know _this_ policy*/
+ scr_LogPrint(LPRINT_NORMAL, "mcabber doesn't support _this_ policy!");
+ free_arg_lst(paramlst);
+ return;
+ }
+
+ if (!strcasecmp(fjid, "default") || !strcasecmp(fjid, "*")) {
+ /*set default policy*/
+ settings_otr_setpolicy(NULL, p);
+ free_arg_lst(paramlst);
+ return;
+ }
+ // Allow special jid "" or "." (current buddy)
+ if (fjid && (!*fjid || !strcmp(fjid, ".")))
+ fjid = NULL;
+
+ if (fjid) {
+ // The JID has been specified. Quick check...
+ if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "<%s> is not a valid Jabber ID.", fjid);
+ fjid = NULL;
+ } else {
+ // Convert jid to lowercase and strip resource
+ char *p;
+ for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
+ *p = tolower(*p);
+ if (*p == JID_RESOURCE_SEPARATOR)
+ *p = '\0';
+ }
+ } else {
+ gpointer bud = NULL;
+ if (current_buddy)
+ bud = BUDDATA(current_buddy);
+ if (bud) {
+ guint type = buddy_gettype(bud);
+ if (type & ROSTER_TYPE_USER) // Is it a user?
+ fjid = (char*)buddy_getjid(bud);
+ else
+ scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
+ }
+ }
+
+ if (fjid)
+ settings_otr_setpolicy(fjid, p);
+ else
+ scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
+
+ free_arg_lst(paramlst);
+#else
+ scr_LogPrint(LPRINT_NORMAL, "Please recompile mcabber with libotr enabled.");
+#endif /* HAVE_LIBOTR */
+}
+
+/* !!!
+ After changing the /iline arguments names here, you must change ones
+ in init_bindings().
+*/
+static void do_iline(char *arg)
+{
+ if (!strcasecmp(arg, "fword")) {
+ readline_forward_word();
+ } else if (!strcasecmp(arg, "bword")) {
+ readline_backward_word();
+ } else if (!strcasecmp(arg, "word_fdel")) {
+ readline_forward_kill_word();
+ } else if (!strcasecmp(arg, "word_bdel")) {
+ readline_backward_kill_word();
+ } else if (!strcasecmp(arg, "word_upcase")) {
+ readline_updowncase_word(1);
+ } else if (!strcasecmp(arg, "word_downcase")) {
+ readline_updowncase_word(0);
+ } else if (!strcasecmp(arg, "word_capit")) {
+ readline_capitalize_word();
+ } else if (!strcasecmp(arg, "fchar")) {
+ readline_forward_char();
+ } else if (!strcasecmp(arg, "bchar")) {
+ readline_backward_char();
+ } else if (!strcasecmp(arg, "char_fdel")) {
+ readline_forward_kill_char();
+ } else if (!strcasecmp(arg, "char_bdel")) {
+ readline_backward_kill_char();
+ } else if (!strcasecmp(arg, "char_swap")) {
+ readline_transpose_chars();
+ } else if (!strcasecmp(arg, "hist_beginning_search_bwd")) {
+ readline_hist_beginning_search_bwd();
+ } else if (!strcasecmp(arg, "hist_beginning_search_fwd")) {
+ readline_hist_beginning_search_fwd();
+ } else if (!strcasecmp(arg, "hist_prev")) {
+ readline_hist_prev();
+ } else if (!strcasecmp(arg, "hist_next")) {
+ readline_hist_next();
+ } else if (!strcasecmp(arg, "iline_start")) {
+ readline_iline_start();
+ } else if (!strcasecmp(arg, "iline_end")) {
+ readline_iline_end();
+ } else if (!strcasecmp(arg, "iline_fdel")) {
+ readline_forward_kill_iline();
+ } else if (!strcasecmp(arg, "iline_bdel")) {
+ readline_backward_kill_iline();
+ } else if (!strcasecmp(arg, "send_multiline")) {
+ readline_send_multiline();
+ } else if (!strcasecmp(arg, "iline_accept")) {
+ retval_for_cmds = readline_accept_line(FALSE);
+ } else if (!strcasecmp(arg, "iline_accept_down_hist")) {
+ retval_for_cmds = readline_accept_line(TRUE);
+ } else if (!strcasecmp(arg, "compl_cancel")) {
+ readline_cancel_completion();
+ } else if (!strcasecmp(arg, "compl_do")) {
+ readline_do_completion();
+ }
+}
+
+static void do_screen_refresh(char *arg)
+{
+ readline_refresh_screen();
+}
+
+static void do_chat_disable(char *arg)
+{
+ guint show_roster;
+
+ if (arg && !strcasecmp(arg, "--show-roster"))
+ show_roster = 1;
+ else
+ show_roster = 0;
+
+ readline_disable_chat_mode(show_roster);
+}
+
+static void do_source(char *arg)
+{
+ static int recur_level;
+ gchar *filename, *expfname;
+ if (!*arg) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing filename.");
+ return;
+ }
+ if (recur_level > 20) {
+ scr_LogPrint(LPRINT_LOGNORM, "** Too many source commands!");
+ return;
+ }
+ filename = g_strdup(arg);
+ strip_arg_special_chars(filename);
+ expfname = expand_filename(filename);
+ recur_level++;
+ cfg_read_file(expfname, FALSE);
+ recur_level--;
+ g_free(filename);
+ g_free(expfname);
+}
+
+static void do_connect(char *arg)
+{
+ xmpp_connect();
+}
+
+static void do_disconnect(char *arg)
+{
+ xmpp_disconnect();
+}
+
+static void do_help(char *arg)
+{
+ help_process(arg);
+}
+
+static void do_echo(char *arg)
+{
+ if (arg)
+ scr_print_logwindow(arg);
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/commands.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,36 @@
+#ifndef __MCABBER_COMMANDS_H__
+#define __MCABBER_COMMANDS_H__ 1
+
+#include <glib.h>
+
+#include <mcabber/config.h>
+
+// Command structure
+typedef struct {
+ char name[32];
+ const char *help;
+ guint completion_flags[2];
+ void (*func)(char *);
+#ifdef MODULES_ENABLE
+ gpointer userdata;
+#endif
+} cmd;
+
+void cmd_init(void);
+cmd *cmd_get(const char *command);
+int process_line(const char *line);
+int process_command(const char *line, guint iscmd);
+char *expandalias(const char *line);
+#ifdef MODULES_ENABLE
+void cmd_deinit(void);
+gpointer cmd_del(const char *name);
+void cmd_add(const char *name, const char *help, guint flags1, guint flags2, void (*f)(char*), gpointer userdata);
+#endif
+
+void cmd_room_whois(gpointer bud, char *nick_locale, guint interactive);
+void cmd_room_leave(gpointer bud, char *arg);
+void cmd_setstatus(const char *recipient, const char *arg);
+
+#endif /* __MCABBER_COMMANDS_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/compl.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,286 @@
+/*
+ * compl.c -- Completion system
+ *
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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
+ */
+
+/* Usage, basically:
+ * - new_completion(); // 1. Initialization
+ * - complete(); // 2. 1st completion
+ * - cancel_completion(); // 3a. 2nd completion / cancel previous
+ * - complete(); // 3b. 2nd completion / complete
+ * ...
+ * - done_completion(); // n. finished -- free allocated areas
+ *
+ */
+
+#include <string.h>
+
+#include "compl.h"
+#include "utf8.h"
+#include "roster.h"
+#include "events.h"
+
+// Completion structure
+typedef struct {
+ GSList *list; // list of matches
+ guint len_prefix; // length of text already typed by the user
+ guint len_compl; // length of the last completion
+ GSList *next; // pointer to next completion to try
+} compl;
+
+// Category structure
+typedef struct {
+ guint flag;
+ GSList *words;
+} category;
+
+static GSList *Categories;
+static compl *InputCompl;
+
+#ifdef MODULES_ENABLE
+guint registered_cats = COMPL_CMD|COMPL_JID|COMPL_URLJID|COMPL_NAME| \
+ COMPL_STATUS|COMPL_FILENAME|COMPL_ROSTER|COMPL_BUFFER| \
+ COMPL_GROUP|COMPL_GROUPNAME|COMPL_MULTILINE|COMPL_ROOM| \
+ COMPL_RESOURCE|COMPL_AUTH|COMPL_REQUEST|COMPL_EVENTS| \
+ COMPL_EVENTSID|COMPL_PGP|COMPL_COLOR| \
+ COMPL_OTR|COMPL_OTRPOLICY| \
+ 0;
+
+// compl_new_category()
+// Reserves id for new completion category.
+// Returns 0, if no more categories can be allocated.
+// Note, that user should not make any assumptions about id nature,
+// as it is likely to change in future.
+guint compl_new_category (void)
+{
+ guint i = 0;
+ while ((registered_cats >> i) & 1)
+ i++;
+ if (i >= sizeof (guint)*8)
+ return 0;
+ else {
+ guint id = 1 << i;
+ registered_cats |= id;
+ return id;
+ }
+}
+
+// compl_del_category (id)
+// Frees reserved id for category.
+// Note, that for now it not validates its input, so, be careful
+// and specify exactly what you get from compl_new_category.
+void compl_del_category (guint id)
+{
+ registered_cats &= ~id;
+}
+#endif
+
+// new_completion(prefix, compl_cat)
+// . prefix = beginning of the word, typed by the user
+// . compl_cat = pointer to a completion category list (list of *char)
+// Set the InputCompl pointer to an allocated compl structure.
+// done_completion() must be called when finished.
+// Returns the number of possible completions.
+guint new_completion(char *prefix, GSList *compl_cat)
+{
+ compl *c;
+ GSList *sl_cat;
+ size_t len = strlen(prefix);
+
+ if (InputCompl) { // This should not happen, but hey...
+ cancel_completion();
+ }
+
+ c = g_new0(compl, 1);
+ // Build the list of matches
+ for (sl_cat = compl_cat; sl_cat; sl_cat = g_slist_next(sl_cat)) {
+ char *word = sl_cat->data;
+ if (!strncasecmp(prefix, word, len)) {
+ if (strlen(word) != len)
+ c->list = g_slist_append(c->list, g_strdup(word+len)); // TODO sort
+ }
+ }
+ c->next = c->list;
+ InputCompl = c;
+ return g_slist_length(c->list);
+}
+
+// done_completion();
+void done_completion(void)
+{
+ GSList *clp;
+
+ if (!InputCompl) return;
+
+ // Free the current completion list
+ for (clp = InputCompl->list; clp; clp = g_slist_next(clp))
+ g_free(clp->data);
+ g_slist_free(InputCompl->list);
+ g_free(InputCompl);
+ InputCompl = NULL;
+}
+
+// cancel_completion()
+// Returns the number of chars to delete to cancel the completion
+//guint cancel_completion(compl *c)
+guint cancel_completion(void)
+{
+ if (!InputCompl) return 0;
+ return InputCompl->len_compl;
+}
+
+// Returns pointer to text to insert, NULL if no completion.
+const char *complete()
+{
+ compl* c = InputCompl;
+ char *r;
+
+ if (!InputCompl) return NULL;
+
+ if (!c->next) {
+ c->next = c->list; // back to the beginning
+ c->len_compl = 0;
+ return NULL;
+ }
+ r = (char*)c->next->data;
+ c->next = g_slist_next(c->next);
+ if (!utf8_mode) {
+ c->len_compl = strlen(r);
+ } else {
+ char *wc;
+ c->len_compl = 0;
+ for (wc = r; *wc; wc = next_char(wc))
+ c->len_compl++;
+ }
+ return r;
+}
+
+
+/* Categories functions */
+
+// compl_add_category_word(categ, command)
+// Adds a keyword as a possible completion in category categ.
+void compl_add_category_word(guint categ, const char *word)
+{
+ GSList *sl_cat;
+ category *cat;
+ char *nword;
+ // Look for category
+ for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
+ if (categ == ((category*)sl_cat->data)->flag)
+ break;
+ }
+ if (!sl_cat) { // Category not found, let's create it
+ cat = g_new0(category, 1);
+ cat->flag = categ;
+ Categories = g_slist_append(Categories, cat);
+ } else
+ cat = (category*)sl_cat->data;
+
+ // If word is not space-terminated, we add one trailing space
+ for (nword = (char*)word; *nword; nword++)
+ ;
+ if (nword > word) nword--;
+ if (*nword != ' ') { // Add a space
+ nword = g_strdup_printf("%s ", word);
+ } else { // word is fine
+ nword = g_strdup(word);
+ }
+
+ // TODO Check word does not already exist
+ cat->words = g_slist_append(cat->words, nword); // TODO sort
+}
+
+// compl_del_category_word(categ, command)
+// Removes a keyword from category categ in completion list.
+void compl_del_category_word(guint categ, const char *word)
+{
+ GSList *sl_cat, *sl_elt;
+ category *cat;
+ char *nword;
+ // Look for category
+ for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
+ if (categ == ((category*)sl_cat->data)->flag)
+ break;
+ }
+ if (!sl_cat) return; // Category not found, finished!
+
+ cat = (category*)sl_cat->data;
+
+ // If word is not space-terminated, we add one trailing space
+ for (nword = (char*)word; *nword; nword++)
+ ;
+ if (nword > word) nword--;
+ if (*nword != ' ') { // Add a space
+ nword = g_strdup_printf("%s ", word);
+ } else { // word is fine
+ nword = g_strdup(word);
+ }
+
+ sl_elt = cat->words;
+ while (sl_elt) {
+ if (!strcasecmp((char*)sl_elt->data, nword)) {
+ g_free(sl_elt->data);
+ cat->words = g_slist_delete_link(cat->words, sl_elt);
+ break; // Only remove first occurence
+ }
+ sl_elt = g_slist_next(sl_elt);
+ }
+}
+
+// compl_get_category_list()
+// Returns a slist of all words in the categories specified by the given flags
+// Iff this function sets *dynlist to TRUE, then the caller must free the
+// whole list after use.
+GSList *compl_get_category_list(guint cat_flags, guint *dynlist)
+{
+ GSList *sl_cat;
+
+ *dynlist = FALSE;
+
+ // Look for category
+ // XXX Actually that's not that simple... cat_flags can be a combination
+ // of several flags!
+ for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
+ if (cat_flags == ((category*)sl_cat->data)->flag)
+ break;
+ }
+ if (sl_cat) // Category was found, easy...
+ return ((category*)sl_cat->data)->words;
+
+ // Handle dynamic SLists
+ *dynlist = TRUE;
+ if (cat_flags == COMPL_GROUPNAME) {
+ return compl_list(ROSTER_TYPE_GROUP);
+ }
+ if (cat_flags == COMPL_JID) {
+ return compl_list(ROSTER_TYPE_USER);
+ }
+ if (cat_flags == COMPL_RESOURCE) {
+ return buddy_getresources_locale(NULL);
+ }
+ if (cat_flags == COMPL_EVENTSID) {
+ return evs_geteventslist(TRUE);
+ }
+
+ *dynlist = FALSE;
+ return NULL;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/compl.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,47 @@
+#ifndef __MCABBER_COMPL_H__
+#define __MCABBER_COMPL_H__ 1
+
+#include <glib.h>
+
+#include <mcabber/config.h>
+
+#define COMPL_CMD (1U<<0)
+#define COMPL_JID (1U<<1)
+#define COMPL_URLJID (1U<<2) // Not implemented yet
+#define COMPL_NAME (1U<<3) // Not implemented yet
+#define COMPL_STATUS (1U<<4)
+#define COMPL_FILENAME (1U<<5) // Not implemented yet
+#define COMPL_ROSTER (1U<<6)
+#define COMPL_BUFFER (1U<<7)
+#define COMPL_GROUP (1U<<8)
+#define COMPL_GROUPNAME (1U<<9)
+#define COMPL_MULTILINE (1U<<10)
+#define COMPL_ROOM (1U<<11)
+#define COMPL_RESOURCE (1U<<12)
+#define COMPL_AUTH (1U<<13)
+#define COMPL_REQUEST (1U<<14)
+#define COMPL_EVENTS (1U<<15)
+#define COMPL_EVENTSID (1U<<16)
+#define COMPL_PGP (1U<<17)
+#define COMPL_COLOR (1U<<18)
+#define COMPL_OTR (1U<<19)
+#define COMPL_OTRPOLICY (1U<<20)
+#ifdef MODULES_ENABLE
+#define COMPL_MAX_BUILTIN (1U<<20)
+
+guint compl_new_category (void);
+void compl_del_category (guint id);
+#endif
+
+void compl_add_category_word(guint, const char *command);
+void compl_del_category_word(guint categ, const char *word);
+GSList *compl_get_category_list(guint cat_flags, guint *dynlist);
+
+guint new_completion(char *prefix, GSList *compl_cat);
+void done_completion(void);
+guint cancel_completion(void);
+const char *complete(void);
+
+#endif /* __MCABBER_COMPL_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/events.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,180 @@
+/*
+ * events.c -- Events fonctions
+ *
+ * Copyright (C) 2006-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 <string.h>
+#include "events.h"
+#include "logprint.h"
+
+static GSList *evs_list; // Events list
+
+static eviqs *evs_find(const char *evid);
+
+// evs_new(type, timeout)
+// Create an events structure.
+eviqs *evs_new(guint8 type, time_t timeout)
+{
+ static guint evs_idn;
+ eviqs *new_evs;
+ time_t now_t;
+ char *stridn;
+
+ if (!++evs_idn)
+ evs_idn = 1;
+ /* Check for wrapping, we shouldn't reuse ids */
+ stridn = g_strdup_printf("%d", evs_idn);
+ if (evs_find(stridn)) {
+ g_free(stridn);
+ // We could try another id but for now giving up should be fine...
+ return NULL;
+ }
+
+ new_evs = g_new0(eviqs, 1);
+ time(&now_t);
+ new_evs->ts_create = now_t;
+ if (timeout)
+ new_evs->ts_expire = now_t + timeout;
+ new_evs->type = type;
+ new_evs->id = stridn;
+
+ if(!g_slist_length(evs_list))
+ g_timeout_add_seconds(20, evs_check_timeout, NULL);
+ evs_list = g_slist_append(evs_list, new_evs);
+ return new_evs;
+}
+
+int evs_del(const char *evid)
+{
+ GSList *p;
+ eviqs *i;
+
+ if (!evid) return 1;
+
+ for (p = evs_list; p; p = g_slist_next(p)) {
+ i = p->data;
+ if (!strcmp(evid, i->id))
+ break;
+ }
+ if (p) {
+ g_free(i->id);
+ g_free(i->data);
+ g_free(i->desc);
+ g_free(i);
+ evs_list = g_slist_remove(evs_list, p->data);
+ return 0; // Ok, deleted
+ }
+ return -1; // Not found
+}
+
+static eviqs *evs_find(const char *evid)
+{
+ GSList *p;
+ eviqs *i;
+
+ if (!evid) return NULL;
+
+ for (p = evs_list; p; p = g_slist_next(p)) {
+ i = p->data;
+ if (!strcmp(evid, i->id))
+ return i;
+ }
+ return NULL;
+}
+
+// evs_callback(evid, evcontext)
+// Callback processing for the specified event.
+// Return 0 in case of success, -1 if the evid hasn't been found.
+int evs_callback(const char *evid, guint evcontext)
+{
+ eviqs *i;
+
+ i = evs_find(evid);
+ if (!i) return -1;
+
+ // IQ processing
+ // Note: If xml_result is NULL, this is a timeout
+ if (i->callback)
+ (void)(*i->callback)(i, evcontext);
+
+ evs_del(evid);
+ return 0;
+}
+
+gboolean evs_check_timeout()
+{
+ time_t now_t;
+ GSList *p;
+ eviqs *i;
+
+ time(&now_t);
+ p = evs_list;
+ if (!p)
+ return FALSE;
+ while (p) {
+ i = p->data;
+ // We must get next IQ eviqs element now because the current one
+ // could be freed.
+ p = g_slist_next(p);
+
+ if ((!i->ts_expire && now_t > i->ts_create + EVS_MAX_TIMEOUT) ||
+ (i->ts_expire && now_t > i->ts_expire)) {
+ evs_callback(i->id, EVS_CONTEXT_TIMEOUT);
+ }
+ }
+ return TRUE;
+}
+
+void evs_display_list(void)
+{
+ GSList *p;
+ eviqs *i;
+
+ scr_LogPrint(LPRINT_LOGNORM, "Events list:");
+ for (p = evs_list; p; p = g_slist_next(p)) {
+ i = p->data;
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Id: %-3s %s", i->id, (i->desc ? i->desc : ""));
+ }
+ scr_LogPrint(LPRINT_LOGNORM, "End of events list.");
+}
+
+// evs_geteventslist(bool comp)
+// Return a singly-linked-list of events ids, for the completion system.
+// If comp is true, the string "list" is added (it's a completion argument).
+// Note: the caller should free the list (and data) after use.
+GSList *evs_geteventslist(int compl)
+{
+ GSList *evidlist = NULL, *p;
+ eviqs *i;
+
+ for (p = evs_list; p; p = g_slist_next(p)) {
+ i = p->data;
+ evidlist = g_slist_append(evidlist, g_strdup(i->id));
+ }
+
+ if (compl) {
+ // Last item is the "list" subcommand.
+ evidlist = g_slist_append(evidlist, g_strdup("list"));
+ }
+ return evidlist;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/events.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,48 @@
+#ifndef __MCABBER_EVENTS_H__
+#define __MCABBER_EVENTS_H__ 1
+
+#include <mcabber/config.h>
+
+#define EVS_DEFAULT_TIMEOUT 90
+#define EVS_MAX_TIMEOUT 432000
+
+#define EVS_CONTEXT_TIMEOUT 0U
+#define EVS_CONTEXT_CANCEL 1U
+#define EVS_CONTEXT_USER 2U
+
+typedef enum {
+ EVS_TYPE_SUBSCRIPTION = 1,
+ EVS_TYPE_INVITATION = 2,
+#ifdef MODULES_ENABLE
+ EVS_TYPE_USER = 3,
+#endif
+} evs_type;
+
+/* Common structure for events (evs) and IQ requests (iqs) */
+typedef struct {
+ char *id;
+ time_t ts_create;
+ time_t ts_expire;
+ guint8 type;
+ gpointer data;
+ int (*callback)();
+ char *desc;
+} eviqs;
+
+typedef struct {
+ char* to;
+ char* from;
+ char* passwd;
+ char* reason;
+} event_muc_invitation;
+
+eviqs *evs_new(guint8 type, time_t timeout);
+int evs_del(const char *evid);
+int evs_callback(const char *evid, guint evcontext);
+gboolean evs_check_timeout();
+void evs_display_list(void);
+GSList *evs_geteventslist(int forcompl);
+
+#endif /* __MCABBER_EVENTS_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/fifo.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,178 @@
+/*
+ * fifo.c -- Read commands from a named pipe
+ *
+ * Copyright (C) 2008,2009 Mikael Berthe <mikael@lilotux.net>
+ * Copyrigth (C) 2009 Myhailo Danylenko <isbear@ukrpost.net>
+ *
+ * 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 <stdlib.h>
+#include <glib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "commands.h"
+#include "logprint.h"
+#include "utils.h"
+#include "settings.h"
+#include "main.h"
+
+static char *fifo_name = NULL;
+static GIOChannel *fifo_channel = NULL;
+
+static const char *FIFO_ENV_NAME = "MCABBER_FIFO";
+
+static gboolean attach_fifo(const char *name);
+
+static guint fifo_callback(GIOChannel *channel,
+ GIOCondition condition,
+ gpointer data)
+{
+ if (condition & (G_IO_IN|G_IO_PRI)) {
+ GIOStatus chstat;
+ gchar *buf;
+ gsize endpos;
+
+ chstat = g_io_channel_read_line(channel, &buf, NULL, &endpos, NULL);
+ if (chstat == G_IO_STATUS_ERROR || chstat == G_IO_STATUS_EOF) {
+ if (!attach_fifo(fifo_name))
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Reopening fifo failed! Fifo will not work from now!");
+ return FALSE;
+ }
+ if (buf) {
+ guint logflag;
+ guint fifo_ignore = settings_opt_get_int("fifo_ignore");
+
+ if (endpos)
+ buf[endpos] = '\0';
+
+ if (settings_opt_get_int("fifo_hide_commands"))
+ logflag = LPRINT_LOG;
+ else
+ logflag = LPRINT_LOGNORM;
+ scr_LogPrint(logflag, "%s FIFO command: %s",
+ (fifo_ignore ? "Ignoring" : "Executing"), buf);
+ if (!fifo_ignore) {
+ if (process_command(buf, TRUE) == 255)
+ mcabber_set_terminate_ui();
+ }
+
+ g_free(buf);
+ }
+ } else if (condition & (G_IO_ERR|G_IO_NVAL|G_IO_HUP)) {
+ if (!attach_fifo(fifo_name))
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Reopening fifo failed! Fifo will not work from now!");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void fifo_destroy_callback(gpointer data)
+{
+ GIOChannel *channel = (GIOChannel *)data;
+ g_io_channel_unref(channel);
+}
+
+static gboolean check_fifo(const char *name)
+{
+ struct stat finfo;
+ if (stat(name, &finfo) == -1) {
+ /* some unknown error */
+ if (errno != ENOENT)
+ return FALSE;
+ /* fifo not yet exists */
+ if (mkfifo(name, S_IRUSR|S_IWUSR) != -1)
+ return check_fifo(name);
+ else
+ return FALSE;
+ }
+
+ /* file exists */
+ if (S_ISFIFO(finfo.st_mode))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean attach_fifo(const char *name)
+{
+ GSource *source;
+ int fd = open (name, O_RDONLY|O_NONBLOCK);
+ if (fd == -1)
+ return FALSE;
+
+ fifo_channel = g_io_channel_unix_new(fd);
+
+ g_io_channel_set_flags(fifo_channel, G_IO_FLAG_NONBLOCK, NULL);
+ g_io_channel_set_encoding(fifo_channel, NULL, NULL);
+ g_io_channel_set_close_on_unref(fifo_channel, TRUE);
+
+ source = g_io_create_watch(fifo_channel,
+ G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL);
+ g_source_set_callback(source, (GSourceFunc)fifo_callback,
+ (gpointer)fifo_channel,
+ (GDestroyNotify)fifo_destroy_callback);
+ g_source_attach(source, main_context);
+
+ return TRUE;
+}
+
+int fifo_init(const char *fifo_path)
+{
+ if (fifo_path) {
+ fifo_name = expand_filename(fifo_path);
+
+ if (!check_fifo(fifo_name)) {
+ scr_LogPrint(LPRINT_LOGNORM, "WARNING: Cannot create the FIFO. "
+ "%s already exists and is not a pipe", fifo_name);
+ g_free(fifo_name);
+ return -1;
+ }
+ } else if (fifo_name)
+ g_source_remove_by_user_data(fifo_channel);
+ else
+ return -1;
+
+ if (!attach_fifo(fifo_name)) {
+ scr_LogPrint(LPRINT_LOGNORM, "Error: Cannot open fifo");
+ return -1;
+ }
+
+ setenv(FIFO_ENV_NAME, fifo_name, 1);
+
+ scr_LogPrint(LPRINT_LOGNORM, "FIFO initialized (%s)", fifo_path);
+ return 1;
+}
+
+void fifo_deinit(void)
+{
+ unsetenv(FIFO_ENV_NAME);
+
+ /* destroy open fifo */
+ unlink(fifo_name);
+ g_source_remove_by_user_data(fifo_channel);
+ /* channel itself should be destroyed by destruction callback */
+ g_free(fifo_name);
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/fifo.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,9 @@
+#ifndef __MCABBER_FIFO_H__
+#define __MCABBER_FIFO_H__ 1
+
+int fifo_init(const char *fifo_path);
+void fifo_deinit(void);
+
+#endif /* __MCABBER_FIFO_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/hbuf.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,516 @@
+/*
+ * hbuf.c -- History buffer implementation
+ *
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "hbuf.h"
+#include "utils.h"
+#include "utf8.h"
+#include "screen.h"
+
+
+/* This is a private structure type */
+
+typedef struct {
+ char *ptr;
+ char *ptr_end; // beginning of the block
+ char *ptr_end_alloc; // end of the current persistent block
+ guchar flags;
+
+ // XXX This should certainly be a pointer, and be allocated only when needed
+ // (for ex. when HBB_FLAG_PERSISTENT is set).
+ struct { // hbuf_line_info
+ time_t timestamp;
+ unsigned mucnicklen;
+ guint flags;
+ gpointer xep184;
+ } prefix;
+} hbuf_block;
+
+
+// do_wrap(p_hbuf, first_hbuf_elt, width)
+// Wrap hbuf lines with the specified width.
+// '\n' are handled by this routine (they are removed and persistent lines
+// are created).
+// All hbuf elements are processed, starting from first_hbuf_elt.
+static inline void do_wrap(GList **p_hbuf, GList *first_hbuf_elt,
+ unsigned int width)
+{
+ GList *curr_elt = first_hbuf_elt;
+
+ // Let's add non-persistent blocs if necessary
+ // - If there are '\n' in the string
+ // - If length > width (and width != 0)
+ while (curr_elt) {
+ hbuf_block *hbuf_b_curr, *hbuf_b_prev;
+ char *c, *end;
+ char *br = NULL; // break pointer
+ char *cr = NULL; // CR pointer
+ unsigned int cur_w = 0;
+
+ // We want to break where we can find a space char or a CR
+
+ hbuf_b_curr = (hbuf_block*)(curr_elt->data);
+ hbuf_b_prev = hbuf_b_curr;
+ c = hbuf_b_curr->ptr;
+
+ while (*c && (!width || cur_w <= width)) {
+ if (*c == '\n') {
+ br = cr = c;
+ *c = 0;
+ break;
+ }
+ if (iswblank(get_char(c)))
+ br = c;
+ cur_w += get_char_width(c);
+ c = next_char(c);
+ }
+
+ if (cr || (*c && cur_w > width)) {
+ if (!br || br == hbuf_b_curr->ptr)
+ br = c;
+ else
+ br = next_char(br);
+ end = hbuf_b_curr->ptr_end;
+ hbuf_b_curr->ptr_end = br;
+ // Create another block
+ hbuf_b_curr = g_new0(hbuf_block, 1);
+ // The block must be persistent after a CR
+ if (cr) {
+ hbuf_b_curr->ptr = hbuf_b_prev->ptr_end + 1; // == cr+1
+ hbuf_b_curr->flags = HBB_FLAG_PERSISTENT;
+ } else {
+ hbuf_b_curr->ptr = hbuf_b_prev->ptr_end; // == br
+ hbuf_b_curr->flags = 0;
+ }
+ hbuf_b_curr->ptr_end = end;
+ hbuf_b_curr->ptr_end_alloc = hbuf_b_prev->ptr_end_alloc;
+ // This is OK because insert_before(NULL) == append():
+ *p_hbuf = g_list_insert_before(*p_hbuf, curr_elt->next, hbuf_b_curr);
+ }
+ curr_elt = g_list_next(curr_elt);
+ }
+}
+
+// hbuf_add_line(p_hbuf, text, prefix_flags, width, maxhbufblocks)
+// Add a line to the given buffer. If width is not null, then lines are
+// wrapped at this length.
+// maxhbufblocks is the maximum number of hbuf blocks we can allocate. If
+// null, there is no limit. If non-null, it should be >= 2.
+//
+// Note 1: Splitting according to width won't work if there are tabs; they
+// should be expanded before.
+// Note 2: width does not include the ending \0.
+void hbuf_add_line(GList **p_hbuf, const char *text, time_t timestamp,
+ guint prefix_flags, guint width, guint maxhbufblocks,
+ unsigned mucnicklen, gpointer xep184)
+{
+ GList *curr_elt;
+ char *line;
+ guint hbb_blocksize, textlen;
+ hbuf_block *hbuf_block_elt;
+
+ if (!text) return;
+
+ prefix_flags |= (xep184 ? HBB_PREFIX_RECEIPT : 0);
+
+ textlen = strlen(text);
+ hbb_blocksize = MAX(textlen+1, HBB_BLOCKSIZE);
+
+ hbuf_block_elt = g_new0(hbuf_block, 1);
+ hbuf_block_elt->prefix.timestamp = timestamp;
+ hbuf_block_elt->prefix.flags = prefix_flags;
+ hbuf_block_elt->prefix.mucnicklen = mucnicklen;
+ hbuf_block_elt->prefix.xep184 = xep184;
+ if (!*p_hbuf) {
+ hbuf_block_elt->ptr = g_new(char, hbb_blocksize);
+ if (!hbuf_block_elt->ptr) {
+ g_free(hbuf_block_elt);
+ return;
+ }
+ hbuf_block_elt->flags = HBB_FLAG_ALLOC | HBB_FLAG_PERSISTENT;
+ hbuf_block_elt->ptr_end_alloc = hbuf_block_elt->ptr + hbb_blocksize;
+ } else {
+ hbuf_block *hbuf_b_prev;
+ // Set p_hbuf to the end of the list, to speed up history loading
+ // (or CPU time will be used by g_list_last() for each line)
+ *p_hbuf = g_list_last(*p_hbuf);
+ hbuf_b_prev = (*p_hbuf)->data;
+ hbuf_block_elt->ptr = hbuf_b_prev->ptr_end;
+ hbuf_block_elt->flags = HBB_FLAG_PERSISTENT;
+ hbuf_block_elt->ptr_end_alloc = hbuf_b_prev->ptr_end_alloc;
+ }
+ *p_hbuf = g_list_append(*p_hbuf, hbuf_block_elt);
+
+ if (hbuf_block_elt->ptr + textlen >= hbuf_block_elt->ptr_end_alloc) {
+ // Too long for the current allocated bloc, we need another one
+ if (!maxhbufblocks || textlen >= HBB_BLOCKSIZE) {
+ // No limit, let's allocate a new block
+ // If the message text is big, we won't bother to reuse an old block
+ // as well (it could be too small and cause a segfault).
+ hbuf_block_elt->ptr = g_new0(char, hbb_blocksize);
+ hbuf_block_elt->ptr_end_alloc = hbuf_block_elt->ptr + hbb_blocksize;
+ // XXX We should check the return value.
+ } else {
+ GList *hbuf_head, *hbuf_elt;
+ hbuf_block *hbuf_b_elt;
+ guint n = 0;
+ hbuf_head = g_list_first(*p_hbuf);
+ // We need at least 2 allocated blocks
+ if (maxhbufblocks == 1)
+ maxhbufblocks = 2;
+ // Let's count the number of allocated areas
+ for (hbuf_elt = hbuf_head; hbuf_elt; hbuf_elt = g_list_next(hbuf_elt)) {
+ hbuf_b_elt = (hbuf_block*)(hbuf_elt->data);
+ if (hbuf_b_elt->flags & HBB_FLAG_ALLOC)
+ n++;
+ }
+ // If we can't allocate a new area, reuse the previous block(s)
+ if (n < maxhbufblocks) {
+ hbuf_block_elt->ptr = g_new0(char, hbb_blocksize);
+ hbuf_block_elt->ptr_end_alloc = hbuf_block_elt->ptr + hbb_blocksize;
+ } else {
+ // Let's use an old block, and free the extra blocks if needed
+ char *allocated_block = NULL;
+ char *end_of_allocated_block = NULL;
+ while (n >= maxhbufblocks) {
+ int start_of_block = 1;
+ for (hbuf_elt = hbuf_head; hbuf_elt; hbuf_elt = hbuf_head) {
+ hbuf_b_elt = (hbuf_block*)(hbuf_elt->data);
+ if (hbuf_b_elt->flags & HBB_FLAG_ALLOC) {
+ if (start_of_block-- == 0)
+ break;
+ if (n == maxhbufblocks) {
+ allocated_block = hbuf_b_elt->ptr;
+ end_of_allocated_block = hbuf_b_elt->ptr_end_alloc;
+ } else {
+ g_free(hbuf_b_elt->ptr);
+ }
+ }
+ g_free(hbuf_b_elt);
+ hbuf_head = *p_hbuf = g_list_delete_link(hbuf_head, hbuf_elt);
+ }
+ n--;
+ }
+ memset(allocated_block, 0, end_of_allocated_block-allocated_block);
+ hbuf_block_elt->ptr = allocated_block;
+ hbuf_block_elt->ptr_end_alloc = end_of_allocated_block;
+ }
+ }
+ hbuf_block_elt->flags = HBB_FLAG_ALLOC | HBB_FLAG_PERSISTENT;
+ }
+
+ line = hbuf_block_elt->ptr;
+ // Ok, now we can copy the text..
+ strcpy(line, text);
+ hbuf_block_elt->ptr_end = line + textlen + 1;
+
+ curr_elt = g_list_last(*p_hbuf);
+
+ // Wrap lines and handle CRs ('\n')
+ do_wrap(p_hbuf, curr_elt, width);
+}
+
+// hbuf_free()
+// Destroys all hbuf list.
+void hbuf_free(GList **p_hbuf)
+{
+ hbuf_block *hbuf_b_elt;
+ GList *hbuf_elt;
+ GList *first_elt = g_list_first(*p_hbuf);
+
+ for (hbuf_elt = first_elt; hbuf_elt; hbuf_elt = g_list_next(hbuf_elt)) {
+ hbuf_b_elt = (hbuf_block*)(hbuf_elt->data);
+ if (hbuf_b_elt->flags & HBB_FLAG_ALLOC) {
+ g_free(hbuf_b_elt->ptr);
+ }
+ g_free(hbuf_b_elt);
+ }
+
+ g_list_free(first_elt);
+ *p_hbuf = NULL;
+}
+
+// hbuf_rebuild()
+// Rebuild all hbuf list, with the new width.
+// If width == 0, lines are not wrapped.
+void hbuf_rebuild(GList **p_hbuf, unsigned int width)
+{
+ GList *first_elt, *curr_elt, *next_elt;
+ hbuf_block *hbuf_b_curr, *hbuf_b_next;
+
+ // *p_hbuf needs to be the head of the list
+ first_elt = *p_hbuf = g_list_first(*p_hbuf);
+
+ // #1 Remove non-persistent blocks (ptr_end should be updated!)
+ curr_elt = first_elt;
+ while (curr_elt) {
+ next_elt = g_list_next(curr_elt);
+ // Last element?
+ if (!next_elt)
+ break;
+ hbuf_b_curr = (hbuf_block*)(curr_elt->data);
+ hbuf_b_next = (hbuf_block*)(next_elt->data);
+ // Is next line not-persistent?
+ if (!(hbuf_b_next->flags & HBB_FLAG_PERSISTENT)) {
+ hbuf_b_curr->ptr_end = hbuf_b_next->ptr_end;
+ g_free(hbuf_b_next);
+ curr_elt = g_list_delete_link(curr_elt, next_elt);
+ } else
+ curr_elt = next_elt;
+ }
+ // #2 Go back to head and create non-persistent blocks when needed
+ if (width)
+ do_wrap(p_hbuf, first_elt, width);
+}
+
+// hbuf_previous_persistent()
+// Returns the previous persistent block (line). If the given line is
+// persistent, then it is returned.
+// This function is used for example when resizing a buffer. If the top of the
+// screen is on a non-persistent block, then a screen resize could destroy this
+// line...
+GList *hbuf_previous_persistent(GList *l_line)
+{
+ hbuf_block *hbuf_b_elt;
+
+ while (l_line) {
+ hbuf_b_elt = (hbuf_block*)l_line->data;
+ if (hbuf_b_elt->flags & HBB_FLAG_PERSISTENT)
+ return l_line;
+ l_line = g_list_previous(l_line);
+ }
+
+ return NULL;
+}
+
+// hbuf_get_lines(hbuf, n)
+// Returns an array of n hbb_line pointers
+// (The first line will be the line currently pointed by hbuf)
+// Note: The caller should free the array, the hbb_line pointers and the
+// text pointers after use.
+hbb_line **hbuf_get_lines(GList *hbuf, unsigned int n)
+{
+ unsigned int i;
+ hbuf_block *blk;
+ guint last_persist_prefixflags = 0;
+ GList *last_persist; // last persistent flags
+ hbb_line **array, **array_elt;
+
+ // To be able to correctly highlight multi-line messages,
+ // we need to look at the last non-null prefix, which should be the first
+ // line of the message.
+ last_persist = hbuf_previous_persistent(hbuf);
+ while (last_persist) {
+ blk = (hbuf_block*)last_persist->data;
+ if ((blk->flags & HBB_FLAG_PERSISTENT) && blk->prefix.flags) {
+ last_persist_prefixflags = blk->prefix.flags;
+ break;
+ }
+ last_persist = g_list_previous(last_persist);
+ }
+
+ array = g_new0(hbb_line*, n);
+ array_elt = array;
+
+ for (i = 0 ; i < n ; i++) {
+ if (hbuf) {
+ int maxlen;
+
+ blk = (hbuf_block*)(hbuf->data);
+ maxlen = blk->ptr_end - blk->ptr;
+ *array_elt = (hbb_line*)g_new(hbb_line, 1);
+ (*array_elt)->timestamp = blk->prefix.timestamp;
+ (*array_elt)->flags = blk->prefix.flags;
+ (*array_elt)->mucnicklen = blk->prefix.mucnicklen;
+ (*array_elt)->text = g_strndup(blk->ptr, maxlen);
+
+ if ((blk->flags & HBB_FLAG_PERSISTENT) && blk->prefix.flags) {
+ last_persist_prefixflags = blk->prefix.flags;
+ } else {
+ // Propagate highlighting flags
+ (*array_elt)->flags |= last_persist_prefixflags &
+ (HBB_PREFIX_HLIGHT_OUT | HBB_PREFIX_HLIGHT |
+ HBB_PREFIX_INFO | HBB_PREFIX_IN);
+ // Continuation of a message - omit the prefix
+ (*array_elt)->flags |= HBB_PREFIX_CONT;
+ (*array_elt)->mucnicklen = 0; // The nick is in the first one
+ }
+
+ hbuf = g_list_next(hbuf);
+ } else
+ break;
+
+ array_elt++;
+ }
+
+ return array;
+}
+
+// hbuf_search(hbuf, direction, string)
+// Look backward/forward for a line containing string in the history buffer
+// Search starts at hbuf, and goes forward if direction == 1, backward if -1
+GList *hbuf_search(GList *hbuf, int direction, const char *string)
+{
+ hbuf_block *blk;
+
+ for (;;) {
+ if (direction > 0)
+ hbuf = g_list_next(hbuf);
+ else
+ hbuf = g_list_previous(hbuf);
+
+ if (!hbuf) break;
+
+ blk = (hbuf_block*)(hbuf->data);
+ // XXX blk->ptr is (maybe) not really correct, because the match should
+ // not be after ptr_end. We should check that...
+ if (strcasestr(blk->ptr, string))
+ break;
+ }
+
+ return hbuf;
+}
+
+// hbuf_jump_date(hbuf, t)
+// Return a pointer to the first line after date t in the history buffer
+GList *hbuf_jump_date(GList *hbuf, time_t t)
+{
+ hbuf_block *blk;
+
+ hbuf = g_list_first(hbuf);
+
+ for ( ; hbuf && g_list_next(hbuf); hbuf = g_list_next(hbuf)) {
+ blk = (hbuf_block*)(hbuf->data);
+ if (blk->prefix.timestamp >= t) break;
+ }
+
+ return hbuf;
+}
+
+// hbuf_jump_percent(hbuf, pc)
+// Return a pointer to the line at % pc of the history buffer
+GList *hbuf_jump_percent(GList *hbuf, int pc)
+{
+ guint hlen;
+
+ hbuf = g_list_first(hbuf);
+ hlen = g_list_length(hbuf);
+
+ return g_list_nth(hbuf, pc*hlen/100);
+}
+
+// hbuf_dump_to_file(hbuf, filename)
+// Save the buffer to a file.
+void hbuf_dump_to_file(GList *hbuf, const char *filename)
+{
+ hbuf_block *blk;
+ hbb_line line;
+ guint last_persist_prefixflags = 0;
+ guint prefixwidth;
+ char pref[96];
+ FILE *fp;
+ struct stat statbuf;
+
+ if (!stat(filename, &statbuf)) {
+ scr_LogPrint(LPRINT_NORMAL, "The file already exists.");
+ return;
+ }
+ fp = fopen(filename, "w");
+ if (!fp) {
+ scr_LogPrint(LPRINT_NORMAL, "Unable to open the file.");
+ return;
+ }
+
+ prefixwidth = scr_getprefixwidth();
+ prefixwidth = MIN(prefixwidth, sizeof pref);
+
+ for (hbuf = g_list_first(hbuf); hbuf; hbuf = g_list_next(hbuf)) {
+ int maxlen;
+
+ blk = (hbuf_block*)(hbuf->data);
+ maxlen = blk->ptr_end - blk->ptr;
+
+ memset(&line, 0, sizeof(line));
+ line.timestamp = blk->prefix.timestamp;
+ line.flags = blk->prefix.flags;
+ line.mucnicklen = blk->prefix.mucnicklen;
+ line.text = g_strndup(blk->ptr, maxlen);
+
+ if ((blk->flags & HBB_FLAG_PERSISTENT) && blk->prefix.flags) {
+ last_persist_prefixflags = blk->prefix.flags;
+ } else {
+ // Propagate highlighting flags
+ line.flags |= last_persist_prefixflags &
+ (HBB_PREFIX_HLIGHT_OUT | HBB_PREFIX_HLIGHT |
+ HBB_PREFIX_INFO | HBB_PREFIX_IN);
+ // Continuation of a message - omit the prefix
+ line.flags |= HBB_PREFIX_CONT;
+ line.mucnicklen = 0; // The nick is in the first one
+ }
+
+ scr_line_prefix(&line, pref, prefixwidth);
+ fprintf(fp, "%s%s\n", pref, line.text);
+ }
+
+ fclose(fp);
+ return;
+}
+
+// hbuf_remove_receipt(hbuf, xep184)
+// Remove the Receipt Flag for the message with the given xep184 id
+// Returns TRUE if it was found and removed, otherwise FALSE
+gboolean hbuf_remove_receipt(GList *hbuf, gpointer xep184)
+{
+ hbuf_block *blk;
+
+ hbuf = g_list_first(hbuf);
+
+ for ( ; hbuf; hbuf = g_list_next(hbuf)) {
+ blk = (hbuf_block*)(hbuf->data);
+ if (blk->prefix.xep184 == xep184) {
+ blk->prefix.xep184 = NULL;
+ blk->prefix.flags ^= HBB_PREFIX_RECEIPT;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// hbuf_get_blocks_number()
+// Returns the number of allocated hbuf_block's.
+guint hbuf_get_blocks_number(GList *hbuf)
+{
+ hbuf_block *hbuf_b_elt;
+ guint count = 0U;
+
+ for (hbuf = g_list_first(hbuf); hbuf; hbuf = g_list_next(hbuf)) {
+ hbuf_b_elt = (hbuf_block*)(hbuf->data);
+ if (hbuf_b_elt->flags & HBB_FLAG_ALLOC)
+ count++;
+ }
+ return count;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/hbuf.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,59 @@
+#ifndef __MCABBER_HBUF_H__
+#define __MCABBER_HBUF_H__ 1
+
+#include <time.h>
+#include <glib.h>
+
+// With current implementation a message must fit in a hbuf block,
+// so we shouldn't choose a too small size.
+#define HBB_BLOCKSIZE 8192 // > 20 please
+
+// Flags:
+// - ALLOC: the ptr data has been allocated, it can be freed
+// - PERSISTENT: this is a new history line
+#define HBB_FLAG_ALLOC 1
+#define HBB_FLAG_PERSISTENT 2
+
+#define HBB_PREFIX_IN (1U<<0)
+#define HBB_PREFIX_OUT (1U<<1)
+#define HBB_PREFIX_STATUS (1U<<2)
+#define HBB_PREFIX_AUTH (1U<<3)
+#define HBB_PREFIX_INFO (1U<<4)
+#define HBB_PREFIX_ERR (1U<<5)
+#define HBB_PREFIX_NOFLAG (1U<<6)
+#define HBB_PREFIX_HLIGHT_OUT (1U<<7)
+#define HBB_PREFIX_HLIGHT (1U<<8)
+#define HBB_PREFIX_NONE (1U<<9)
+#define HBB_PREFIX_SPECIAL (1U<<10)
+#define HBB_PREFIX_PGPCRYPT (1U<<11)
+#define HBB_PREFIX_OTRCRYPT (1U<<12)
+#define HBB_PREFIX_CONT (1U<<13)
+#define HBB_PREFIX_RECEIPT (1U<<14)
+
+typedef struct {
+ time_t timestamp;
+ guint flags;
+ unsigned mucnicklen;
+ char *text;
+} hbb_line;
+
+void hbuf_add_line(GList **p_hbuf, const char *text, time_t timestamp,
+ guint prefix_flags, guint width, guint maxhbufblocks,
+ unsigned mucnicklen, gpointer xep184);
+void hbuf_free(GList **p_hbuf);
+void hbuf_rebuild(GList **p_hbuf, unsigned int width);
+GList *hbuf_previous_persistent(GList *l_line);
+
+hbb_line **hbuf_get_lines(GList *hbuf, unsigned int n);
+GList *hbuf_search(GList *hbuf, int direction, const char *string);
+GList *hbuf_jump_date(GList *hbuf, time_t t);
+GList *hbuf_jump_percent(GList *hbuf, int pc);
+gboolean hbuf_remove_receipt(GList *hbuf, gpointer xep184);
+
+void hbuf_dump_to_file(GList *hbuf, const char *filename);
+
+guint hbuf_get_blocks_number(GList *p_hbuf);
+
+#endif /* __MCABBER_HBUF_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/help.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,135 @@
+/*
+ * help.c -- Help command
+ *
+ * Copyright (C) 2006-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <glib.h>
+
+#include "settings.h"
+#include "logprint.h"
+#include "utils.h"
+#include "screen.h"
+
+#define DEFAULT_LANG "en"
+
+// get_lang()
+// Return the language code string (a 2-letters string).
+static const char *get_lang(void) {
+ static const char *lang_str = DEFAULT_LANG;
+#ifdef DATA_DIR
+ static char lang[3];
+ const char *opt_l;
+ opt_l = settings_opt_get("lang");
+ if (opt_l && strlen(opt_l) == 2 && isalpha(opt_l[0]) && isalpha(opt_l[1])) {
+ strncpy(lang, opt_l, sizeof(lang));
+ mc_strtolower(lang);
+ lang_str = lang;
+ }
+#endif /* DATA_DIR */
+ return lang_str;
+}
+
+// help_process(string)
+// Display help about the "string" command.
+// If string is null, display general help.
+// Return 0 in case of success.
+int help_process(char *string)
+{
+#ifndef DATA_DIR
+ scr_LogPrint(LPRINT_NORMAL, "Help isn't available.");
+ return -1;
+#else
+ const char *lang;
+ FILE *fp;
+ char *helpfiles_dir, *filename;
+ char *data;
+ const int datasize = 4096;
+ int linecount = 0;
+ char *p;
+
+ // Check string is ok
+ for (p = string; p && *p; p++) {
+ if (!isalnum(*p) && *p != '_' && *p != '-') {
+ scr_LogPrint(LPRINT_NORMAL, "Cannot find help (invalid keyword).");
+ return 1;
+ }
+ }
+
+ // Look for help file
+ lang = get_lang();
+ helpfiles_dir = g_strdup_printf("%s/mcabber/help", DATA_DIR);
+ p = NULL;
+
+ if (string && *string) {
+ p = g_strdup(string);
+ mc_strtolower(p);
+ filename = g_strdup_printf("%s/%s/hlp_%s.txt", helpfiles_dir, lang, p);
+ } else
+ filename = g_strdup_printf("%s/%s/hlp.txt", helpfiles_dir, lang);
+
+ fp = fopen(filename, "r");
+
+ if (!(fp) && (g_strcmp0(lang, DEFAULT_LANG)) ) {
+ g_free(filename);
+ if (p)
+ filename = g_strdup_printf("%s/%s/hlp_%s.txt", helpfiles_dir, DEFAULT_LANG, p);
+ else
+ filename = g_strdup_printf("%s/%s/hlp.txt", helpfiles_dir, DEFAULT_LANG);
+
+ fp = fopen(filename, "r");
+ }
+ g_free(p);
+ g_free(filename);
+ g_free(helpfiles_dir);
+
+ if (!fp) {
+ scr_LogPrint(LPRINT_NORMAL, "No help found.");
+ return -1;
+ }
+
+ data = g_new(char, datasize);
+ while (!feof(fp)) {
+ if (fgets(data, datasize, fp) == NULL) break;
+ // Strip trailing newline
+ for (p = data; *p; p++) ;
+ if (p > data)
+ p--;
+ if (*p == '\n' || *p == '\r')
+ *p = '\0';
+ // Displaty the help line
+ scr_LogPrint(LPRINT_NORMAL, "%s", data);
+ linecount++;
+ }
+ fclose(fp);
+ g_free(data);
+
+ if (linecount) {
+ scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
+ update_roster = TRUE;
+ }
+
+ return 0;
+#endif /* DATA_DIR */
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/help.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,8 @@
+#ifndef __MCABBER_HELP_H__
+#define __MCABBER_HELP_H__ 1
+
+int help_process(char *string);
+
+#endif /* __MCABBER_HELP_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/histolog.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,536 @@
+/*
+ * histolog.c -- File history handling
+ *
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "histolog.h"
+#include "hbuf.h"
+#include "utils.h"
+#include "screen.h"
+#include "settings.h"
+#include "utils.h"
+#include "roster.h"
+#include "xmpp.h"
+
+static guint UseFileLogging;
+static guint FileLoadLogs;
+static char *RootDir;
+
+
+// user_histo_file(jid)
+// Returns history filename for the given jid
+// Note: the caller *must* free the filename after use (if not null).
+static char *user_histo_file(const char *bjid)
+{
+ char *filename;
+ char *lowerid;
+
+ if (!(UseFileLogging || FileLoadLogs))
+ return NULL;
+
+ lowerid = g_strdup(bjid);
+ if (!lowerid)
+ return NULL;
+ mc_strtolower(lowerid);
+
+ filename = g_strdup_printf("%s%s", RootDir, lowerid);
+ g_free(lowerid);
+ return filename;
+}
+
+char *hlog_get_log_jid(const char *bjid)
+{
+ struct stat bufstat;
+ char *path;
+ char *log_jid = NULL;
+
+ path = user_histo_file(bjid);
+ while (path) {
+ if (lstat(path, &bufstat) != 0)
+ break;
+ if (S_ISLNK(bufstat.st_mode)) {
+ g_free(log_jid);
+ log_jid = g_new0(char, bufstat.st_size+1);
+ if (readlink(path, log_jid, bufstat.st_size) < 0) return NULL;
+ g_free(path);
+ path = user_histo_file(log_jid);
+ } else
+ break;
+ }
+
+ g_free(path);
+ return log_jid;
+}
+
+// write_histo_line()
+// Adds a history (multi-)line to the jid's history logfile
+static void write_histo_line(const char *bjid,
+ time_t timestamp, guchar type, guchar info, const char *data)
+{
+ guint len = 0;
+ FILE *fp;
+ time_t ts;
+ const char *p;
+ char *filename;
+ char str_ts[20];
+ int err;
+
+ if (!UseFileLogging)
+ return;
+
+ // Do not log status messages when 'logging_ignore_status' is set
+ if (type == 'S' && settings_opt_get_int("logging_ignore_status"))
+ return;
+
+ filename = user_histo_file(bjid);
+
+ // If timestamp is null, get current date
+ if (timestamp)
+ ts = timestamp;
+ else
+ time(&ts);
+
+ if (!data)
+ data = "";
+
+ // Count number of extra lines
+ for (p=data ; *p ; p++)
+ if (*p == '\n') len++;
+
+ /* Line format: "TI yyyymmddThh:mm:ssZ LLL [data]"
+ * T=Type, I=Info, yyyymmddThh:mm:ssZ=date, LLL=0-padded-len
+ *
+ * Types:
+ * - M message Info: S (send) R (receive) I (info)
+ * - S status Info: [_ofdnai]
+ * We don't check them, we trust the caller.
+ * (Info messages are not sent nor received, they're generated
+ * locally by mcabber.)
+ */
+
+ fp = fopen(filename, "a");
+ g_free(filename);
+ if (!fp) {
+ scr_LogPrint(LPRINT_LOGNORM, "Unable to write history "
+ "(cannot open logfile)");
+ return;
+ }
+
+ to_iso8601(str_ts, ts);
+ err = fprintf(fp, "%c%c %-18.18s %03d %s\n", type, info, str_ts, len, data);
+ fclose(fp);
+ if (err < 0) {
+ scr_LogPrint(LPRINT_LOGNORM, "Error while writing to log file: %s",
+ strerror(errno));
+ }
+}
+
+// hlog_read_history()
+// Reads the jid's history logfile
+void hlog_read_history(const char *bjid, GList **p_buddyhbuf, guint width)
+{
+ char *filename;
+ guchar type, info;
+ char *data, *tail;
+ guint data_size;
+ char *xtext;
+ time_t timestamp;
+ guint prefix_flags;
+ guint len;
+ FILE *fp;
+ struct stat bufstat;
+ guint err = 0;
+ guint ln = 0; // line number
+ time_t starttime;
+ int max_num_of_blocks;
+
+ if (!FileLoadLogs)
+ return;
+
+ if ((roster_gettype(bjid) & ROSTER_TYPE_ROOM) &&
+ (settings_opt_get_int("load_muc_logs") != 1))
+ return;
+
+ data_size = HBB_BLOCKSIZE+32;
+ data = g_new(char, data_size);
+ if (!data) {
+ scr_LogPrint(LPRINT_LOGNORM, "Not enough memory to read history file");
+ return;
+ }
+
+ filename = user_histo_file(bjid);
+
+ fp = fopen(filename, "r");
+ g_free(filename);
+ if (!fp) {
+ g_free(data);
+ return;
+ }
+
+ // If file is large (> 3MB here), display a message to inform the user
+ // (it can take a while...)
+ if (!fstat(fileno(fp), &bufstat)) {
+ if (bufstat.st_size > 3145728) {
+ scr_LogPrint(LPRINT_NORMAL, "Reading <%s> history file...", bjid);
+ scr_DoUpdate();
+ }
+ }
+
+ max_num_of_blocks = get_max_history_blocks();
+
+ starttime = 0L;
+ if (settings_opt_get_int("max_history_age") > 0) {
+ int maxdays = settings_opt_get_int("max_history_age");
+ time(&starttime);
+ if (maxdays >= starttime/86400L)
+ starttime = 0L;
+ else
+ starttime -= maxdays * 86400L;
+ }
+
+ /* See write_histo_line() for line format... */
+ while (!feof(fp)) {
+ guint dataoffset = 25;
+ guint noeol;
+
+ if (fgets(data, data_size-1, fp) == NULL)
+ break;
+ ln++;
+
+ while (1) {
+ for (tail = data; *tail; tail++) ;
+ noeol = (*(tail-1) != '\n');
+ if (!noeol)
+ break;
+ /* TODO: duplicated code... could do better... */
+ if (tail == data + data_size-2) {
+ // The buffer is too small to contain the whole line.
+ // Let's allocate some more space.
+ if (!max_num_of_blocks ||
+ data_size/HBB_BLOCKSIZE < 5U*max_num_of_blocks) {
+ guint toffset = tail - data;
+ // Allocate one more block.
+ data_size = HBB_BLOCKSIZE * (1 + data_size/HBB_BLOCKSIZE);
+ data = g_renew(char, data, data_size);
+ // Update the tail pointer, as the data may have been moved.
+ tail = data + toffset;
+ if (fgets(tail, data_size-1 - (tail-data), fp) == NULL)
+ break;
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "Line too long in history file!");
+ ln--;
+ break;
+ }
+ }
+ }
+
+ type = data[0];
+ info = data[1];
+
+ if ((type != 'M' && type != 'S') ||
+ ((data[11] != 'T') || (data[20] != 'Z') ||
+ (data[21] != ' ') ||
+ (data[25] != ' ' && data[26] != ' '))) {
+ if (!err) {
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Error in history file format (%s), l.%u", bjid, ln);
+ err = 1;
+ }
+ continue;
+ }
+ // The number of lines can be written with 3 or 4 bytes.
+ if (data[25] != ' ') dataoffset = 26;
+ data[21] = data[dataoffset] = 0;
+ timestamp = from_iso8601(&data[3], 1);
+ len = (guint) atoi(&data[22]);
+
+ // Some checks
+ if (((type == 'M') && (info != 'S' && info != 'R' && info != 'I')) ||
+ ((type == 'S') && (!strchr("_OFDNAI", info)))) {
+ if (!err) {
+ scr_LogPrint(LPRINT_LOGNORM, "Error in history file format (%s), l.%u",
+ bjid, ln);
+ err = 1;
+ }
+ continue;
+ }
+
+ while (len--) {
+ ln++;
+ if (fgets(tail, data_size-1 - (tail-data), fp) == NULL)
+ break;
+
+ while (*tail) tail++;
+ noeol = (*(tail-1) != '\n');
+ if (tail == data + data_size-2 && (len || noeol)) {
+ // The buffer is too small to contain the whole message.
+ // Let's allocate some more space.
+ if (!max_num_of_blocks ||
+ data_size/HBB_BLOCKSIZE < 5U*max_num_of_blocks) {
+ guint toffset = tail - data;
+ // If the line hasn't been read completely and we reallocate the
+ // buffer, we want to read one more time.
+ if (noeol)
+ len++;
+ // Allocate one more block.
+ data_size = HBB_BLOCKSIZE * (1 + data_size/HBB_BLOCKSIZE);
+ data = g_renew(char, data, data_size);
+ // Update the tail pointer, as the data may have been moved.
+ tail = data + toffset;
+ } else {
+ // There will probably be a parse error on next read, because
+ // this message hasn't been read entirely.
+ scr_LogPrint(LPRINT_LOGNORM, "Message too big in history file!");
+ }
+ }
+ }
+ // Remove last CR (we keep it if the line is empty, too)
+ if ((tail > data+dataoffset+1) && (*(tail-1) == '\n'))
+ *(tail-1) = 0;
+
+ // Check if the data is older than max_history_age
+ if (starttime) {
+ if (timestamp > starttime)
+ starttime = 0L; // From now on, load everything
+ else
+ continue;
+ }
+
+ if (type == 'M') {
+ char *converted;
+ if (info == 'S') {
+ prefix_flags = HBB_PREFIX_OUT | HBB_PREFIX_HLIGHT_OUT;
+ } else {
+ prefix_flags = HBB_PREFIX_IN;
+ if (info == 'I')
+ prefix_flags = HBB_PREFIX_INFO;
+ }
+ converted = from_utf8(&data[dataoffset+1]);
+ if (converted) {
+ xtext = ut_expand_tabs(converted); // Expand tabs
+ hbuf_add_line(p_buddyhbuf, xtext, timestamp, prefix_flags, width,
+ max_num_of_blocks, 0, NULL);
+ if (xtext != converted)
+ g_free(xtext);
+ g_free(converted);
+ }
+ err = 0;
+ }
+ }
+ fclose(fp);
+ g_free(data);
+}
+
+// hlog_enable()
+// Enable logging to files. If root_dir is NULL, then $HOME/.mcabber is used.
+// If loadfiles is TRUE, we will try to load buddies history logs from file.
+void hlog_enable(guint enable, const char *root_dir, guint loadfiles)
+{
+ UseFileLogging = enable;
+ FileLoadLogs = loadfiles;
+
+ if (enable || loadfiles) {
+ if (root_dir) {
+ char *xp_root_dir;
+ int l = strlen(root_dir);
+ if (l < 1) {
+ scr_LogPrint(LPRINT_LOGNORM, "Error: logging dir name too short");
+ UseFileLogging = FileLoadLogs = FALSE;
+ return;
+ }
+ xp_root_dir = expand_filename(root_dir);
+ // RootDir must be slash-terminated
+ if (root_dir[l-1] == '/') {
+ RootDir = xp_root_dir;
+ } else {
+ RootDir = g_strdup_printf("%s/", xp_root_dir);
+ g_free(xp_root_dir);
+ }
+ } else {
+ char *home = getenv("HOME");
+ const char *dir = "/.mcabber/histo/";
+ RootDir = g_strdup_printf("%s%s", home, dir);
+ }
+ // Check directory permissions (should not be readable by group/others)
+ if (checkset_perm(RootDir, TRUE) == -1) {
+ // The directory does not actually exists
+ g_free(RootDir);
+ RootDir = NULL;
+ scr_LogPrint(LPRINT_LOGNORM, "ERROR: Cannot access "
+ "history log directory, logging DISABLED");
+ UseFileLogging = FileLoadLogs = FALSE;
+ }
+ } else { // Disable history logging
+ g_free(RootDir);
+ RootDir = NULL;
+ }
+}
+
+guint hlog_is_enabled(void)
+{
+ return UseFileLogging;
+}
+
+inline void hlog_write_message(const char *bjid, time_t timestamp, int sent,
+ const char *msg)
+{
+ guchar info;
+ /* sent=1 message sent by mcabber
+ * sent=0 message received by mcabber
+ * sent=-1 local info message
+ */
+ if (sent == 1)
+ info = 'S';
+ else if (sent == 0)
+ info = 'R';
+ else
+ info = 'I';
+ write_histo_line(bjid, timestamp, 'M', info, msg);
+}
+
+inline void hlog_write_status(const char *bjid, time_t timestamp,
+ enum imstatus status, const char *status_msg)
+{
+ // XXX Check status value?
+ write_histo_line(bjid, timestamp, 'S', toupper(imstatus2char[status]),
+ status_msg);
+}
+
+
+// hlog_save_state()
+// If enabled, save the current state of the roster
+// (i.e. pending messages) to a temporary file.
+void hlog_save_state(void)
+{
+ gpointer unread_ptr, first_unread;
+ const char *bjid;
+ char *statefile_xp;
+ FILE *fp;
+ const char *statefile = settings_opt_get("statefile");
+
+ if (!statefile || !UseFileLogging)
+ return;
+
+ statefile_xp = expand_filename(statefile);
+ fp = fopen(statefile_xp, "w");
+ if (!fp) {
+ scr_LogPrint(LPRINT_NORMAL, "Cannot open state file [%s]",
+ strerror(errno));
+ goto hlog_save_state_return;
+ }
+
+ if (!lm_connection_is_authenticated(lconnection)) {
+ // We're not connected. Let's use the unread_jids hash.
+ GList *unread_jid = unread_jid_get_list();
+ unread_ptr = unread_jid;
+ for ( ; unread_jid ; unread_jid = g_list_next(unread_jid))
+ fprintf(fp, "%s\n", (char*)unread_jid->data);
+ g_list_free(unread_ptr);
+ goto hlog_save_state_return;
+ }
+
+ if (!current_buddy) // Safety check -- shouldn't happen.
+ goto hlog_save_state_return;
+
+ // We're connected. Let's use unread_msg().
+ unread_ptr = first_unread = unread_msg(NULL);
+ if (!first_unread)
+ goto hlog_save_state_return;
+
+ do {
+ guint type = buddy_gettype(unread_ptr);
+ if (type & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)) {
+ bjid = buddy_getjid(unread_ptr);
+ if (bjid)
+ fprintf(fp, "%s\n", bjid);
+ }
+ unread_ptr = unread_msg(unread_ptr);
+ } while (unread_ptr && unread_ptr != first_unread);
+
+hlog_save_state_return:
+ if (fp) {
+ long filelen = ftell(fp);
+ fclose(fp);
+ if (!filelen)
+ unlink(statefile_xp);
+ }
+ g_free(statefile_xp);
+}
+
+// hlog_load_state()
+// If enabled, load the current state of the roster
+// (i.e. pending messages) from a temporary file.
+// This function adds the JIDs to the unread_jids hash table,
+// so it should only be called at startup.
+void hlog_load_state(void)
+{
+ char bjid[1024];
+ char *statefile_xp;
+ FILE *fp;
+ const char *statefile = settings_opt_get("statefile");
+
+ if (!statefile || !UseFileLogging)
+ return;
+
+ statefile_xp = expand_filename(statefile);
+ fp = fopen(statefile_xp, "r");
+ if (fp) {
+ char *eol;
+ while (!feof(fp)) {
+ if (fgets(bjid, sizeof bjid, fp) == NULL)
+ break;
+ // Let's remove the trailing newline.
+ // Also remove whitespace, if the file as been (badly) manually modified.
+ for (eol = bjid; *eol; eol++) ;
+ for (eol--; eol >= bjid && (*eol == '\n' || *eol == ' '); *eol-- = 0) ;
+ // Safety checks...
+ if (!bjid[0])
+ continue;
+ if (check_jid_syntax(bjid)) {
+ scr_LogPrint(LPRINT_LOGNORM,
+ "ERROR: Invalid JID in state file. Corrupted file?");
+ break;
+ }
+ // Display a warning if there are pending messages but the user
+ // won't see them because load_log isn't set.
+ if (!FileLoadLogs) {
+ scr_LogPrint(LPRINT_LOGNORM, "WARNING: unread message from <%s>.",
+ bjid);
+ scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
+ }
+ // Add the JID to unread_jids. It will be used when the contact is
+ // added to the roster.
+ unread_jid_add(bjid);
+ }
+ fclose(fp);
+ }
+ g_free(statefile_xp);
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/histolog.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,20 @@
+#ifndef __MCABBER_HISTOLOG_H__
+#define __MCABBER_HISTOLOG_H__ 1
+
+#include <glib.h>
+
+#include <mcabber/xmpp.h>
+
+void hlog_enable(guint enable, const char *root_dir, guint loadfile);
+char *hlog_get_log_jid(const char *bjid);
+void hlog_read_history(const char *bjid, GList **p_buddyhbuf, guint width);
+void hlog_write_message(const char *bjid, time_t timestamp, int sent,
+ const char *msg);
+void hlog_write_status(const char *bjid, time_t timestamp,
+ enum imstatus status, const char *status_msg);
+void hlog_save_state(void);
+void hlog_load_state(void);
+
+#endif /* __MCABBER_HISTOLOG_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/hooks.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,668 @@
+/*
+ * hooks.c -- Hooks layer
+ *
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 <loudmouth/loudmouth.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "hooks.h"
+#include "screen.h"
+#include "roster.h"
+#include "histolog.h"
+#include "hbuf.h"
+#include "settings.h"
+#include "utils.h"
+#include "utf8.h"
+#include "commands.h"
+#include "main.h"
+
+#ifdef MODULES_ENABLE
+#include <glib.h>
+
+typedef struct {
+ hk_handler_t handler;
+ guint32 flags;
+ gpointer userdata;
+} hook_list_data_t;
+
+static GSList *hk_handler_queue = NULL;
+
+void hk_add_handler (hk_handler_t handler, guint32 flags, gpointer userdata)
+{
+ hook_list_data_t *h = g_new (hook_list_data_t, 1);
+ h->handler = handler;
+ h->flags = flags;
+ h->userdata = userdata;
+ hk_handler_queue = g_slist_append (hk_handler_queue, h);
+}
+
+static gint hk_queue_search_cb (hook_list_data_t *a, hook_list_data_t *b)
+{
+ if (a->handler == b->handler && a->userdata == b->userdata)
+ return 0;
+ else
+ return 1;
+}
+
+void hk_del_handler (hk_handler_t handler, gpointer userdata)
+{
+ hook_list_data_t h = { handler, 0, userdata };
+ GSList *el = g_slist_find_custom (hk_handler_queue, &h, (GCompareFunc) hk_queue_search_cb);
+ if (el) {
+ g_free (el->data);
+ hk_handler_queue = g_slist_delete_link (hk_handler_queue, el);
+ }
+}
+#endif
+
+static char *extcmd;
+
+static const char *COMMAND_ME = "/me ";
+
+void hk_message_in(const char *bjid, const char *resname,
+ time_t timestamp, const char *msg, LmMessageSubType type,
+ guint encrypted)
+{
+ int new_guy = FALSE;
+ int is_groupchat = FALSE; // groupchat message
+ int is_room = FALSE; // window is a room window
+ int log_muc_conf = FALSE;
+ int active_window = FALSE;
+ int message_flags = 0;
+ guint rtype = ROSTER_TYPE_USER;
+ char *wmsg = NULL, *bmsg = NULL, *mmsg = NULL;
+ GSList *roster_usr;
+ unsigned mucnicklen = 0;
+ const char *ename = NULL;
+
+ if (encrypted == ENCRYPTED_PGP)
+ message_flags |= HBB_PREFIX_PGPCRYPT;
+ else if (encrypted == ENCRYPTED_OTR)
+ message_flags |= HBB_PREFIX_OTRCRYPT;
+
+ if (type == LM_MESSAGE_SUB_TYPE_GROUPCHAT) {
+ rtype = ROSTER_TYPE_ROOM;
+ is_groupchat = TRUE;
+ log_muc_conf = settings_opt_get_int("log_muc_conf");
+ if (!resname) {
+ message_flags = HBB_PREFIX_INFO | HBB_PREFIX_NOFLAG;
+ resname = "";
+ wmsg = bmsg = g_strdup_printf("~ %s", msg);
+ } else {
+ wmsg = bmsg = g_strdup_printf("<%s> %s", resname, msg);
+ mucnicklen = strlen(resname) + 2;
+ if (!strncmp(msg, COMMAND_ME, strlen(COMMAND_ME)))
+ wmsg = mmsg = g_strdup_printf("*%s %s", resname, msg+4);
+ }
+ } else {
+ bmsg = g_strdup(msg);
+ if (!strncmp(msg, COMMAND_ME, strlen(COMMAND_ME))) {
+ gchar *shortid = g_strdup(bjid);
+ if (settings_opt_get_int("buddy_me_fulljid") == FALSE) {
+ gchar *p = strchr(shortid, '@'); // Truncate the jid
+ if (p)
+ *p = '\0';
+ }
+ wmsg = mmsg = g_strdup_printf("*%s %s", shortid, msg+4);
+ g_free(shortid);
+ } else
+ wmsg = (char*) msg;
+ }
+
+ // If this user isn't in the roster, we add it
+ roster_usr = roster_find(bjid, jidsearch, 0);
+ if (!roster_usr) {
+ new_guy = TRUE;
+ roster_usr = roster_add_user(bjid, NULL, NULL, rtype, sub_none, -1);
+ if (!roster_usr) { // Shouldn't happen...
+ scr_LogPrint(LPRINT_LOGNORM, "ERROR: unable to add buddy!");
+ g_free(bmsg);
+ g_free(mmsg);
+ return;
+ }
+ } else if (is_groupchat) {
+ // Make sure the type is ROOM
+ buddy_settype(roster_usr->data, ROSTER_TYPE_ROOM);
+ }
+
+ is_room = !!(buddy_gettype(roster_usr->data) & ROSTER_TYPE_ROOM);
+
+ if (is_room) {
+ if (!is_groupchat) {
+ // This is a private message from a room participant
+ g_free(bmsg);
+ if (!resname) {
+ resname = "";
+ wmsg = bmsg = g_strdup(msg);
+ } else {
+ wmsg = bmsg = g_strdup_printf("PRIV#<%s> %s", resname, msg);
+ if (!strncmp(msg, COMMAND_ME, strlen(COMMAND_ME))) {
+ g_free(mmsg);
+ wmsg = mmsg = g_strdup_printf("PRIV#*%s %s", resname, msg+4);
+ }
+ }
+ message_flags |= HBB_PREFIX_HLIGHT;
+ } else {
+ // This is a regular chatroom message.
+ const char *nick = buddy_getnickname(roster_usr->data);
+
+ if (nick) {
+ // Let's see if we are the message sender, in which case we'll
+ // highlight it.
+ if (resname && !strcmp(resname, nick)) {
+ message_flags |= HBB_PREFIX_HLIGHT_OUT;
+ } else if (!settings_opt_get_int("muc_disable_nick_hl")) {
+ // We're not the sender. Can we see our nick?
+ const char *msgptr = msg;
+ while ((msgptr = strcasestr(msgptr, nick)) != NULL) {
+ const char *leftb, *rightb;
+ // The message contains our nick. Let's check it's not
+ // in the middle of another word (i.e. preceded/followed
+ // immediately by an alphanumeric character or an underscore.
+ rightb = msgptr+strlen(nick);
+ if (msgptr == msg)
+ leftb = NULL;
+ else
+ leftb = prev_char((char*)msgptr, msg);
+ msgptr = next_char((char*)msgptr);
+ // Check left boundary
+ if (leftb && (iswalnum(get_char(leftb)) || get_char(leftb) == '_'))
+ continue;
+ // Check right boundary
+ if (!iswalnum(get_char(rightb)) && get_char(rightb) != '_')
+ message_flags |= HBB_PREFIX_HLIGHT;
+ }
+ }
+ }
+ }
+ }
+
+ if (type == LM_MESSAGE_SUB_TYPE_ERROR) {
+ message_flags = HBB_PREFIX_ERR | HBB_PREFIX_IN;
+ scr_LogPrint(LPRINT_LOGNORM, "Error message received from <%s>", bjid);
+ }
+
+ // Note: the hlog_write should not be called first, because in some
+ // cases scr_WriteIncomingMessage() will load the history and we'd
+ // have the message twice...
+ scr_WriteIncomingMessage(bjid, wmsg, timestamp, message_flags, mucnicklen);
+
+ // We don't log the modified message, but the original one
+ if (wmsg == mmsg)
+ wmsg = bmsg;
+
+ // - We don't log the message if it is an error message
+ // - We don't log the message if it is a private conf. message
+ // - We don't log the message if it is groupchat message and the log_muc_conf
+ // option is off (and it is not a history line)
+ if (!(message_flags & HBB_PREFIX_ERR) &&
+ (!is_room || (is_groupchat && log_muc_conf && !timestamp)))
+ hlog_write_message(bjid, timestamp, 0, wmsg);
+
+ if (settings_opt_get_int("events_ignore_active_window") &&
+ current_buddy && scr_get_chatmode()) {
+ gpointer bud = BUDDATA(current_buddy);
+ if (bud) {
+ const char *cjid = buddy_getjid(bud);
+ if (cjid && !strcasecmp(cjid, bjid))
+ active_window = TRUE;
+ }
+ }
+
+ if (settings_opt_get_int("eventcmd_use_nickname"))
+ ename = roster_getname(bjid);
+
+#ifdef MODULES_ENABLE
+ {
+ GSList *h = hk_handler_queue;
+ if (h) {
+#if 0
+ hk_arg_t *args = g_new (hk_arg_t, 5);
+ args[0].name = "hook";
+ args[0].value = "hook-message-in";
+ args[1].name = "jid";
+ args[1].value = bjid;
+ args[2].name = "message";
+ args[2].value = wmsg;
+ args[3].name = "groupchat";
+ args[3].value = is_groupchat ? "true" : "false";
+ args[4].name = NULL;
+ args[4].value = NULL;
+#else
+ // We can use a const array for keys/static values, so modules
+ // can do fast known to them args check by just comparing pointers...
+ hk_arg_t args[] = {
+ { "hook", "hook-message-in" },
+ { "jid", bjid },
+ { "message", wmsg },
+ { "groupchat", is_groupchat ? "true" : "false" },
+ { NULL, NULL },
+ };
+#endif
+ while (h) {
+ hook_list_data_t *data = h->data;
+ if (data->flags & HOOK_MESSAGE_IN)
+ (data->handler) (HOOK_MESSAGE_IN, args, data->userdata);
+ h = g_slist_next (h);
+ }
+ }
+ }
+#endif
+
+ // External command
+ // - We do not call hk_ext_cmd() for history lines in MUC
+ // - We do call hk_ext_cmd() for private messages in a room
+ // - We do call hk_ext_cmd() for messages to the current window
+ if (!active_window && ((is_groupchat && !timestamp) || !is_groupchat))
+ hk_ext_cmd(ename ? ename : bjid, (is_groupchat ? 'G' : 'M'), 'R', wmsg);
+
+ // Display the sender in the log window
+ if ((!is_groupchat) && !(message_flags & HBB_PREFIX_ERR) &&
+ settings_opt_get_int("log_display_sender")) {
+ const char *name = roster_getname(bjid);
+ if (!name) name = "";
+ scr_LogPrint(LPRINT_NORMAL, "Message received from %s <%s/%s>",
+ name, bjid, (resname ? resname : ""));
+ }
+
+ // Beep, if enabled:
+ // - if it's a private message
+ // - if it's a public message and it's highlighted
+ if (settings_opt_get_int("beep_on_message")) {
+ if ((!is_groupchat && !(message_flags & HBB_PREFIX_ERR)) ||
+ (is_groupchat && (message_flags & HBB_PREFIX_HLIGHT)))
+ scr_Beep();
+ }
+
+ // We need to update the roster if the sender is unknown or
+ // if the sender is offline/invisible and a filter is set.
+ if (new_guy ||
+ (buddy_getstatus(roster_usr->data, NULL) == offline &&
+ buddylist_isset_filter()))
+ {
+ update_roster = TRUE;
+ }
+
+ g_free(bmsg);
+ g_free(mmsg);
+}
+
+// hk_message_out()
+// nick should be set for private messages in a chat room, and null for
+// normal messages.
+void hk_message_out(const char *bjid, const char *nick,
+ time_t timestamp, const char *msg,
+ guint encrypted, gpointer xep184)
+{
+ char *wmsg = NULL, *bmsg = NULL, *mmsg = NULL;
+ guint cryptflag = 0;
+
+ if (nick) {
+ wmsg = bmsg = g_strdup_printf("PRIV#<%s> %s", nick, msg);
+ if (!strncmp(msg, COMMAND_ME, strlen(COMMAND_ME))) {
+ const char *mynick = roster_getnickname(bjid);
+ wmsg = mmsg = g_strdup_printf("PRIV#<%s> *%s %s", nick,
+ (mynick ? mynick : "me"), msg+4);
+ }
+ } else {
+ wmsg = (char*)msg;
+ if (!strncmp(msg, COMMAND_ME, strlen(COMMAND_ME))) {
+ char *myid = jid_get_username(settings_opt_get("jid"));
+ if (myid) {
+ wmsg = mmsg = g_strdup_printf("*%s %s", myid, msg+4);
+ g_free(myid);
+ }
+ }
+ }
+
+ // Note: the hlog_write should not be called first, because in some
+ // cases scr_WriteOutgoingMessage() will load the history and we'd
+ // have the message twice...
+ if (encrypted == ENCRYPTED_PGP)
+ cryptflag = HBB_PREFIX_PGPCRYPT;
+ else if (encrypted == ENCRYPTED_OTR)
+ cryptflag = HBB_PREFIX_OTRCRYPT;
+ scr_WriteOutgoingMessage(bjid, wmsg, cryptflag, xep184);
+
+ // We don't log private messages
+ if (!nick)
+ hlog_write_message(bjid, timestamp, 1, msg);
+
+#ifdef MODULES_ENABLE
+ {
+ GSList *h = hk_handler_queue;
+ if (h) {
+ hk_arg_t args[] = {
+ { "hook", "hook-message-out" },
+ { "jid", bjid },
+ { "message", wmsg },
+ { NULL, NULL },
+ };
+ while (h) {
+ hook_list_data_t *data = h->data;
+ if (data->flags & HOOK_MESSAGE_OUT)
+ (data->handler) (HOOK_MESSAGE_OUT, args, data->userdata);
+ h = g_slist_next (h);
+ }
+ }
+ }
+#endif
+
+ // External command
+ hk_ext_cmd(bjid, 'M', 'S', NULL);
+
+ g_free(bmsg);
+ g_free(mmsg);
+}
+
+void hk_statuschange(const char *bjid, const char *resname, gchar prio,
+ time_t timestamp, enum imstatus status,
+ const char *status_msg)
+{
+ int st_in_buf;
+ enum imstatus oldstat;
+ char *bn;
+ char *logsmsg;
+ const char *rn = (resname ? resname : "");
+ const char *ename = NULL;
+
+ if (settings_opt_get_int("eventcmd_use_nickname"))
+ ename = roster_getname(bjid);
+
+ oldstat = roster_getstatus(bjid, resname);
+
+ st_in_buf = settings_opt_get_int("show_status_in_buffer");
+
+ if (settings_opt_get_int("log_display_presence")) {
+ int buddy_format = settings_opt_get_int("buddy_format");
+ bn = NULL;
+ if (buddy_format) {
+ const char *name = roster_getname(bjid);
+ if (name && strcmp(name, bjid)) {
+ if (buddy_format == 1)
+ bn = g_strdup_printf("%s <%s/%s>", name, bjid, rn);
+ else if (buddy_format == 2)
+ bn = g_strdup_printf("%s/%s", name, rn);
+ else if (buddy_format == 3)
+ bn = g_strdup_printf("%s", name);
+ }
+ }
+
+ if (!bn)
+ bn = g_strdup_printf("<%s/%s>", bjid, rn);
+
+ logsmsg = g_strdup(status_msg ? status_msg : "");
+ replace_nl_with_dots(logsmsg);
+
+ scr_LogPrint(LPRINT_LOGNORM, "Buddy status has changed: [%c>%c] %s %s",
+ imstatus2char[oldstat], imstatus2char[status], bn, logsmsg);
+ g_free(logsmsg);
+ g_free(bn);
+ }
+
+ if (st_in_buf == 2 ||
+ (st_in_buf == 1 && (status == offline || oldstat == offline))) {
+ // Write the status change in the buddy's buffer, only if it already exists
+ if (scr_BuddyBufferExists(bjid)) {
+ bn = g_strdup_printf("Buddy status has changed: [%c>%c] %s",
+ imstatus2char[oldstat], imstatus2char[status],
+ ((status_msg) ? status_msg : ""));
+ scr_WriteIncomingMessage(bjid, bn, timestamp,
+ HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+ g_free(bn);
+ }
+ }
+
+ roster_setstatus(bjid, rn, prio, status, status_msg, timestamp,
+ role_none, affil_none, NULL);
+ buddylist_build();
+ scr_DrawRoster();
+ hlog_write_status(bjid, timestamp, status, status_msg);
+
+#ifdef MODULES_ENABLE
+ {
+ GSList *h = hk_handler_queue;
+ if (h) {
+ char os[2] = " \0";
+ char ns[2] = " \0";
+ hk_arg_t args[] = {
+ { "hook", "hook-status-change" },
+ { "jid", bjid },
+ { "resource", rn },
+ { "old_status", os },
+ { "new_status", ns },
+ { "message", status_msg ? status_msg : "" },
+ { NULL, NULL },
+ };
+ os[0] = imstatus2char[oldstat];
+ ns[0] = imstatus2char[status];
+ while (h) {
+ hook_list_data_t *data = h->data;
+ if (data->flags & HOOK_STATUS_CHANGE)
+ (data->handler) (HOOK_STATUS_CHANGE, args, data->userdata);
+ h = g_slist_next (h);
+ }
+ }
+ }
+#endif
+
+ // External command
+ hk_ext_cmd(ename ? ename : bjid, 'S', imstatus2char[status], NULL);
+}
+
+void hk_mystatuschange(time_t timestamp, enum imstatus old_status,
+ enum imstatus new_status, const char *msg)
+{
+ scr_LogPrint(LPRINT_LOGNORM, "Your status has been set: [%c>%c] %s",
+ imstatus2char[old_status], imstatus2char[new_status],
+ (msg ? msg : ""));
+
+#ifdef MODULES_ENABLE
+ {
+ GSList *h = hk_handler_queue;
+ if (h) {
+ char ns[2] = " \0";
+ hk_arg_t args[] = {
+ { "hook", "hook-my-status-change" },
+ { "new_status", ns },
+ { "message", msg ? msg : "" },
+ { NULL, NULL },
+ };
+ ns[0] = imstatus2char[new_status];
+ while (h) {
+ hook_list_data_t *data = h->data;
+ if (data->flags & HOOK_MY_STATUS_CHANGE)
+ (data->handler) (HOOK_MY_STATUS_CHANGE, args, data->userdata);
+ h = g_slist_next (h);
+ }
+ }
+ }
+#endif
+
+ //hlog_write_status(NULL, 0, status);
+}
+
+
+/* Internal commands */
+
+void hook_execute_internal(const char *hookname)
+{
+ const char *hook_command;
+ char *buf;
+ char *cmdline;
+
+#ifdef MODULES_ENABLE
+ {
+ GSList *h = hk_handler_queue;
+ if (h) {
+ hk_arg_t args[] = {
+ { "hook", hookname },
+ { NULL, NULL },
+ };
+ while (h) {
+ hook_list_data_t *data = h->data;
+ if (data->flags & HOOK_INTERNAL)
+ (data->handler) (HOOK_INTERNAL, args, data->userdata);
+ h = g_slist_next (h);
+ }
+ }
+ }
+#endif
+
+ hook_command = settings_opt_get(hookname);
+ if (!hook_command)
+ return;
+
+ buf = g_strdup_printf("Running %s...", hookname);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+
+ cmdline = from_utf8(hook_command);
+ if (process_command(cmdline, TRUE) == 255)
+ mcabber_set_terminate_ui();
+
+ g_free(cmdline);
+ g_free(buf);
+}
+
+
+/* External commands */
+
+// hk_ext_cmd_init()
+// Initialize external command variable.
+// Can be called with parameter NULL to reset and free memory.
+void hk_ext_cmd_init(const char *command)
+{
+ if (extcmd) {
+ g_free(extcmd);
+ extcmd = NULL;
+ }
+ if (command)
+ extcmd = expand_filename(command);
+}
+
+// hk_ext_cmd()
+// Launch an external command (process) for the given event.
+// For now, data should be NULL.
+void hk_ext_cmd(const char *bjid, guchar type, guchar info, const char *data)
+{
+ pid_t pid;
+ char *arg_type = NULL;
+ char *arg_info = NULL;
+ char *arg_data = NULL;
+ char status_str[2];
+ char *datafname = NULL;
+ char unread_str[16];
+
+ if (!extcmd) return;
+
+ // Prepare arg_* (external command parameters)
+ switch (type) {
+ case 'M': /* Normal message */
+ arg_type = "MSG";
+ if (info == 'R')
+ arg_info = "IN";
+ else if (info == 'S')
+ arg_info = "OUT";
+ break;
+ case 'G': /* Groupchat message */
+ arg_type = "MSG";
+ arg_info = "MUC";
+ break;
+ case 'S': /* Status change */
+ arg_type = "STATUS";
+ if (strchr(imstatus2char, tolower(info))) {
+ status_str[0] = toupper(info);
+ status_str[1] = 0;
+ arg_info = status_str;
+ }
+ break;
+ case 'U': /* Unread buffer count */
+ arg_type = "UNREAD";
+ g_snprintf(unread_str, sizeof unread_str, "%d", info);
+ arg_info = unread_str; /* number of remaining unread bjids */
+ break;
+ default:
+ return;
+ }
+
+ if (!arg_type || !arg_info) return;
+
+ if (strchr("MG", type) && data && settings_opt_get_int("event_log_files")) {
+ int fd;
+ const char *prefix;
+ char *prefix_xp = NULL;
+ char *data_locale;
+
+ data_locale = from_utf8(data);
+ prefix = settings_opt_get("event_log_dir");
+ if (prefix)
+ prefix = prefix_xp = expand_filename(prefix);
+ else
+ prefix = ut_get_tmpdir();
+ datafname = g_strdup_printf("%s/mcabber-%d.XXXXXX", prefix, getpid());
+ g_free(prefix_xp);
+
+ // XXX Some old systems may require us to set umask first.
+ fd = mkstemp(datafname);
+ if (fd == -1) {
+ g_free(datafname);
+ datafname = NULL;
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Unable to create temp file for external command.");
+ } else {
+ size_t data_locale_len = strlen(data_locale);
+ ssize_t a = write(fd, data_locale, data_locale_len);
+ ssize_t b = write(fd, "\n", 1);
+ if ((size_t)a != data_locale_len || b != 1) {
+ g_free(datafname);
+ datafname = NULL;
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Unable to write to temp file for external command.");
+ }
+ close(fd);
+ arg_data = datafname;
+ }
+ g_free(data_locale);
+ }
+
+ if ((pid=fork()) == -1) {
+ scr_LogPrint(LPRINT_LOGNORM, "Fork error, cannot launch external command.");
+ g_free(datafname);
+ return;
+ }
+
+ if (pid == 0) { // child
+ // Close standard file descriptors
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ if (execl(extcmd, extcmd, arg_type, arg_info, bjid, arg_data,
+ (char *)NULL) == -1) {
+ // scr_LogPrint(LPRINT_LOGNORM, "Cannot execute external command.");
+ exit(1);
+ }
+ }
+ g_free(datafname);
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/hooks.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,53 @@
+#ifndef __MCABBER_HOOKS_H__
+#define __MCABBER_HOOKS_H__ 1
+
+#include <time.h>
+#include <loudmouth/loudmouth.h>
+#include <mcabber/xmpp.h>
+
+// These two defines are used by hk_message_{in,out} arguments
+#define ENCRYPTED_PGP 1
+#define ENCRYPTED_OTR 2
+
+#include <mcabber/config.h>
+#ifdef MODULES_ENABLE
+#include <glib.h>
+
+#define HOOK_MESSAGE_IN ( 0x00000001 )
+#define HOOK_MESSAGE_OUT ( 0x00000002 )
+#define HOOK_STATUS_CHANGE ( 0x00000004 )
+#define HOOK_MY_STATUS_CHANGE ( 0x00000008 )
+#define HOOK_INTERNAL ( 0x00000010 )
+
+typedef struct {
+ const char *name;
+ const char *value;
+} hk_arg_t;
+
+typedef void (*hk_handler_t) (guint32 flags, hk_arg_t *args, gpointer userdata);
+
+void hk_add_handler (hk_handler_t handler, guint32 flags, gpointer userdata);
+void hk_del_handler (hk_handler_t handler, gpointer userdata);
+#endif
+
+void hk_message_in(const char *bjid, const char *resname,
+ time_t timestamp, const char *msg, LmMessageSubType type,
+ guint encrypted);
+void hk_message_out(const char *bjid, const char *nickname,
+ time_t timestamp, const char *msg,
+ guint encrypted, gpointer xep184);
+void hk_statuschange(const char *bjid, const char *resname, gchar prio,
+ time_t timestamp, enum imstatus status,
+ char const *status_msg);
+void hk_mystatuschange(time_t timestamp,
+ enum imstatus old_status,
+ enum imstatus new_status, const char *msg);
+
+void hook_execute_internal(const char *hookname);
+
+void hk_ext_cmd_init(const char *command);
+void hk_ext_cmd(const char *bjid, guchar type, guchar info, const char *data);
+
+#endif /* __MCABBER_HOOKS_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/logprint.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,20 @@
+#ifndef __MCABBER_LOGPRINT_H__
+#define __MCABBER_LOGPRINT_H__ 1
+
+// Flags for scr_LogPrint()
+#define LPRINT_NORMAL 1U // Display in log window
+#define LPRINT_LOG 2U // Log to file (if enabled)
+#define LPRINT_DEBUG 4U // Debug message (log if enabled)
+#define LPRINT_NOTUTF8 8U // Do not convert from UTF-8 to locale
+
+// For convenience...
+#define LPRINT_LOGNORM (LPRINT_NORMAL|LPRINT_LOG)
+
+void scr_print_logwindow(const char *string);
+void scr_LogPrint(unsigned int flag, const char *fmt, ...);
+
+void scr_DoUpdate(void);
+
+#endif /* __MCABBER_LOGPRINT_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/main.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,494 @@
+/*
+ * main.c
+ *
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ * Parts of this file come from Cabber <cabber@ajmacias.com>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <glib.h>
+#include <config.h>
+#include <poll.h>
+
+#include "caps.h"
+#include "screen.h"
+#include "settings.h"
+#include "roster.h"
+#include "commands.h"
+#include "histolog.h"
+#include "hooks.h"
+#include "utils.h"
+#include "pgp.h"
+#include "otr.h"
+#include "fifo.h"
+#include "xmpp.h"
+
+#ifdef ENABLE_HGCSET
+# include "hgcset.h"
+#endif
+
+#ifndef WAIT_ANY
+# define WAIT_ANY -1
+#endif
+
+static unsigned int terminate_ui;
+GMainContext *main_context;
+
+static gboolean update_screen = TRUE;
+
+static struct termios *backup_termios;
+
+char *mcabber_version(void)
+{
+ char *ver;
+#ifdef HGCSET
+ ver = g_strdup_printf("%s (%s)", PACKAGE_VERSION, HGCSET);
+#else
+ ver = g_strdup(PACKAGE_VERSION);
+#endif
+ return ver;
+}
+
+static void mcabber_terminate(const char *msg)
+{
+ fifo_deinit();
+ xmpp_disconnect();
+ scr_TerminateCurses();
+
+ // Restore term settings, if needed.
+ if (backup_termios)
+ tcsetattr(fileno(stdin), TCSAFLUSH, backup_termios);
+
+ if (msg)
+ fprintf(stderr, "%s\n", msg);
+ printf("Bye!\n");
+ exit(EXIT_SUCCESS);
+}
+
+void sig_handler(int signum)
+{
+ if (signum == SIGCHLD) {
+ int status;
+ pid_t pid;
+ do {
+ pid = waitpid (WAIT_ANY, &status, WNOHANG);
+ // Check the exit status value if 'eventcmd_checkstatus' is set
+ if (settings_opt_get_int("eventcmd_checkstatus")) {
+ if (pid > 0) {
+ // exit status 2 -> beep
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 2) {
+ scr_Beep();
+ }
+ }
+ }
+ } while (pid > 0);
+ signal(SIGCHLD, sig_handler);
+ } else if (signum == SIGTERM) {
+ mcabber_terminate("Killed by SIGTERM");
+ } else if (signum == SIGINT) {
+ mcabber_terminate("Killed by SIGINT");
+#ifdef USE_SIGWINCH
+ } else if (signum == SIGWINCH) {
+ ungetch(KEY_RESIZE);
+#endif
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "Caught signal: %d", signum);
+ }
+}
+
+// ask_password(what)
+// Return the password, or NULL.
+// The string must be freed after use.
+static char *ask_password(const char *what)
+{
+ char *password, *p;
+ size_t passsize = 128;
+ struct termios orig, new;
+
+ password = g_new0(char, passsize);
+
+ /* Turn echoing off and fail if we can't. */
+ if (tcgetattr(fileno(stdin), &orig) != 0) return NULL;
+ backup_termios = &orig;
+
+ new = orig;
+ new.c_lflag &= ~ECHO;
+ if (tcsetattr(fileno(stdin), TCSAFLUSH, &new) != 0) return NULL;
+
+ /* Read the password. */
+ printf("Please enter %s: ", what);
+ if (fgets(password, passsize, stdin) == NULL) return NULL;
+
+ /* Restore terminal. */
+ tcsetattr(fileno(stdin), TCSAFLUSH, &orig);
+ printf("\n");
+ backup_termios = NULL;
+
+ for (p = (char*)password; *p; p++)
+ ;
+ for ( ; p > (char*)password ; p--)
+ if (*p == '\n' || *p == '\r') *p = 0;
+
+ return password;
+}
+
+static void credits(void)
+{
+ const char *v_fmt = "MCabber %s -- Email: mcabber [at] lilotux [dot] net\n";
+ char *v = mcabber_version();
+ printf(v_fmt, v);
+ scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8, v_fmt, v);
+ g_free(v);
+}
+
+static void compile_options(void)
+{
+ puts("Installation data directory: " DATA_DIR "\n");
+#ifdef HAVE_UNICODE
+ puts("Compiled with unicode support.");
+#endif
+#ifdef MODULES_ENABLE
+ puts ("Compiled with modules support.");
+#endif
+#ifdef HAVE_GPGME
+ puts("Compiled with GPG support.");
+#endif
+#ifdef HAVE_LIBOTR
+ puts("Compiled with OTR support.");
+#endif
+#ifdef WITH_ENCHANT
+ puts("Compiled with Enchant support.");
+#endif
+#ifdef WITH_ASPELL
+ puts("Compiled with Aspell support.");
+#endif
+#ifdef ENABLE_DEBUG
+ puts("Compiled with debugging support.");
+#endif
+}
+
+static void main_init_pgp(void)
+{
+#ifdef HAVE_GPGME
+ const char *pk, *pp;
+ char *typed_passwd = NULL;
+ char *p;
+ bool pgp_invalid = FALSE;
+ bool pgp_agent;
+ int retries;
+
+ p = getenv("GPG_AGENT_INFO");
+ pgp_agent = (p && strchr(p, ':'));
+
+ pk = settings_opt_get("pgp_private_key");
+ pp = settings_opt_get("pgp_passphrase");
+
+ if (settings_opt_get("pgp_passphrase_retries"))
+ retries = settings_opt_get_int("pgp_passphrase_retries");
+ else
+ retries = 2;
+
+ if (!pk) {
+ scr_LogPrint(LPRINT_LOGNORM, "WARNING: unknown PGP private key");
+ pgp_invalid = TRUE;
+ } else if (!(pp || pgp_agent)) {
+ // Request PGP passphrase
+ pp = typed_passwd = ask_password("PGP passphrase");
+ }
+ gpg_init(pk, pp);
+ // Erase password from the settings array
+ if (pp) {
+ memset((char*)pp, 0, strlen(pp));
+ if (typed_passwd)
+ g_free(typed_passwd);
+ else
+ settings_set(SETTINGS_TYPE_OPTION, "pgp_passphrase", NULL);
+ }
+ if (!pgp_agent && pk && pp && gpg_test_passphrase()) {
+ // Let's check the pasphrase
+ int i;
+ for (i = 1; retries < 0 || i <= retries; i++) {
+ typed_passwd = ask_password("PGP passphrase"); // Ask again...
+ if (typed_passwd) {
+ gpg_set_passphrase(typed_passwd);
+ memset(typed_passwd, 0, strlen(typed_passwd));
+ g_free(typed_passwd);
+ }
+ if (!gpg_test_passphrase())
+ break; // Ok
+ }
+ if (i > retries)
+ pgp_invalid = TRUE;
+ }
+ if (pgp_invalid)
+ scr_LogPrint(LPRINT_LOGNORM, "WARNING: PGP key/pass invalid");
+#else /* not HAVE_GPGME */
+ scr_LogPrint(LPRINT_LOGNORM, "WARNING: not compiled with PGP support");
+#endif /* HAVE_GPGME */
+}
+
+void mcabber_set_terminate_ui(void)
+{
+ terminate_ui = TRUE;
+}
+
+typedef struct {
+ GSource source;
+ GPollFD pollfd;
+} mcabber_source_t;
+
+static gboolean mcabber_source_prepare(GSource *source, gint *timeout)
+{
+ *timeout = -1;
+ return FALSE;
+}
+
+static gboolean mcabber_source_check(GSource *source)
+{
+ mcabber_source_t *mc_source = (mcabber_source_t *) source;
+ gushort revents = mc_source->pollfd.revents;
+ if (revents)
+ return TRUE;
+ return FALSE;
+}
+
+static gboolean keyboard_activity(void)
+{
+ keycode kcode;
+
+ if (terminate_ui) {
+ return FALSE;
+ }
+ scr_DoUpdate();
+ scr_Getch(&kcode);
+
+ while (kcode.value != ERR) {
+ process_key(kcode);
+ update_screen = TRUE;
+ scr_Getch(&kcode);
+ }
+ scr_CheckAutoAway(FALSE);
+
+ return TRUE;
+}
+
+static gboolean mcabber_source_dispatch(GSource *source, GSourceFunc callback,
+ gpointer udata) {
+ return keyboard_activity();
+}
+
+static GSourceFuncs mcabber_source_funcs = {
+ mcabber_source_prepare,
+ mcabber_source_check,
+ mcabber_source_dispatch,
+ NULL,
+ NULL,
+ NULL
+};
+
+int main(int argc, char **argv)
+{
+ char *configFile = NULL;
+ const char *optstring;
+ int optval, optval2;
+ int ret;
+
+ credits();
+
+ signal(SIGTERM, sig_handler);
+ signal(SIGINT, sig_handler);
+ signal(SIGCHLD, sig_handler);
+#ifdef USE_SIGWINCH
+ signal(SIGWINCH, sig_handler);
+#endif
+ signal(SIGPIPE, SIG_IGN);
+
+ /* Parse command line options */
+ while (1) {
+ int c = getopt(argc, argv, "hVf:");
+ if (c == -1) {
+ break;
+ } else
+ switch (c) {
+ case 'h':
+ case '?':
+ printf("Usage: %s [-h|-V|-f mcabberrc_file]\n\n", argv[0]);
+ return (c == 'h' ? 0 : -1);
+ case 'V':
+ compile_options();
+ return 0;
+ case 'f':
+ configFile = g_strdup(optarg);
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr, "Usage: %s [-h|-V|-f mcabberrc_file]\n\n", argv[0]);
+ return -1;
+ }
+
+ /* Initialize command system, roster and default key bindings */
+ cmd_init();
+ roster_init();
+ settings_init();
+ scr_init_bindings();
+ caps_init();
+ /* Initialize charset */
+ scr_InitLocaleCharSet();
+
+ /* Parsing config file... */
+ ret = cfg_read_file(configFile, TRUE);
+ /* free() configFile if it has been allocated during options parsing */
+ g_free(configFile);
+ /* Leave if there was an error in the config. file */
+ if (ret == -2)
+ exit(EXIT_FAILURE);
+
+ optstring = settings_opt_get("tracelog_file");
+ if (optstring)
+ ut_InitDebug(settings_opt_get_int("tracelog_level"), optstring);
+
+ /* If no password is stored, we ask for it before entering
+ ncurses mode -- unless the username is unknown. */
+ if (settings_opt_get("jid") && !settings_opt_get("password")) {
+ const char *p;
+ char *pwd;
+ p = settings_opt_get("server");
+ if (p)
+ printf("Server: %s\n", p);
+ p = settings_opt_get("jid");
+ if (p)
+ printf("User JID: %s\n", p);
+
+ pwd = ask_password("Jabber password");
+ settings_set(SETTINGS_TYPE_OPTION, "password", pwd);
+ g_free(pwd);
+ }
+
+ /* Initialize PGP system
+ We do it before ncurses initialization because we may need to request
+ a passphrase. */
+ if (settings_opt_get_int("pgp"))
+ main_init_pgp();
+
+ /* Initialize N-Curses */
+ scr_LogPrint(LPRINT_DEBUG, "Initializing N-Curses...");
+ scr_InitCurses();
+ scr_DrawMainWindow(TRUE);
+
+ optval = (settings_opt_get_int("logging") > 0);
+ optval2 = (settings_opt_get_int("load_logs") > 0);
+ if (optval || optval2)
+ hlog_enable(optval, settings_opt_get("logging_dir"), optval2);
+
+#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
+ /* Initialize spelling */
+ if (settings_opt_get_int("spell_enable")) {
+ spellcheck_init();
+ }
+#endif
+
+ optstring = settings_opt_get("events_command");
+ if (optstring)
+ hk_ext_cmd_init(optstring);
+
+ optstring = settings_opt_get("roster_display_filter");
+ if (optstring)
+ scr_RosterDisplay(optstring);
+ // Empty filter isn't allowed...
+ if (!buddylist_get_filter())
+ scr_RosterDisplay("*");
+
+ chatstates_disabled = settings_opt_get_int("disable_chatstates");
+
+ /* Initialize FIFO named pipe */
+ fifo_init(settings_opt_get("fifo_name"));
+
+ /* Load previous roster state */
+ hlog_load_state();
+
+ main_context = g_main_context_default();
+
+ if (ret < 0) {
+ scr_LogPrint(LPRINT_NORMAL, "No configuration file has been found.");
+ scr_ShowBuddyWindow();
+ } else {
+ /* Connection */
+ xmpp_connect();
+ }
+
+ { // add keypress processing source
+ GSource *mc_source = g_source_new(&mcabber_source_funcs,
+ sizeof(mcabber_source_t));
+ GPollFD *mc_pollfd = &(((mcabber_source_t *)mc_source)->pollfd);
+ mc_pollfd->fd = STDIN_FILENO;
+ mc_pollfd->events = POLLIN|POLLERR|POLLPRI;
+ mc_pollfd->revents = 0;
+ g_source_add_poll(mc_source, mc_pollfd);
+ g_source_attach(mc_source, main_context);
+
+ scr_LogPrint(LPRINT_DEBUG, "Entering into main loop...");
+
+ while(!terminate_ui) {
+ if (g_main_context_iteration(main_context, TRUE) == FALSE)
+ keyboard_activity();
+ if (update_roster)
+ scr_DrawRoster();
+ if(update_screen)
+ scr_DoUpdate();
+ }
+
+ g_source_destroy(mc_source);
+ g_source_unref(mc_source);
+ }
+
+ scr_TerminateCurses();
+#ifdef MODULES_ENABLE
+ cmd_deinit();
+#endif
+ fifo_deinit();
+#ifdef HAVE_LIBOTR
+ otr_terminate();
+#endif
+ xmpp_disconnect();
+#ifdef HAVE_GPGME
+ gpg_terminate();
+#endif
+#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
+ /* Deinitialize spelling */
+ if (settings_opt_get_int("spell_enable"))
+ spellcheck_deinit();
+#endif
+ /* Save pending message state */
+ hlog_save_state();
+ caps_free();
+
+ printf("\n\nThanks for using mcabber!\n");
+
+ return 0;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/main.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,11 @@
+#ifndef __MCABBER_MAIN_H__
+#define __MCABBER_MAIN_H__ 1
+
+extern GMainContext *main_context;
+
+void mcabber_set_terminate_ui(void);
+char *mcabber_version(void);
+
+#endif
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/nohtml.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,162 @@
+/*
+ * nohtml.c -- (X)HTML helper functions
+ *
+ * Copyright (C) 2008,2009 Mikael Berthe <mikael@lilotux.net>
+ * Some portions come from the jabberd project, see below.
+ *
+ * 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 parts come from libjabber/str.c:
+ * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <config.h>
+
+
+/* html_strip(htmlbuf)
+ * Remove html entities from htmlbuf and try to convert it to plain text.
+ * The caller must g_free the string after use.
+ * Code mostly derived from strunescape(), in libjabber.
+ */
+char *html_strip(const char *htmlbuf)
+{
+ int i, j=0;
+ char *nohtml;
+
+ if (!htmlbuf) return(NULL);
+
+ nohtml = g_strdup(htmlbuf);
+
+ if (!strchr(htmlbuf, '&') && !strchr(htmlbuf, '<'))
+ return(nohtml);
+
+ for (i = 0; i < (int)strlen(htmlbuf); i++) {
+ if (htmlbuf[i] == '&') {
+ if (!strncmp(&htmlbuf[i],"&",5)) {
+ nohtml[j] = '&';
+ i += 4;
+ } else if (!strncmp(&htmlbuf[i],""", 6)) {
+ nohtml[j] = '\"';
+ i += 5;
+ } else if (!strncmp(&htmlbuf[i],"'", 6)) {
+ nohtml[j] = '\'';
+ i += 5;
+ } else if (!strncmp(&htmlbuf[i],"<", 4)) {
+ nohtml[j] = '<';
+ i += 3;
+ } else if (!strncmp(&htmlbuf[i],">", 4)) {
+ nohtml[j] = '>';
+ i += 3;
+ }
+ } else if (!strncmp(&htmlbuf[i],"<br>", 4) ||
+ !strncmp(&htmlbuf[i],"<br/>", 5)) {
+ nohtml[j] = '\n';
+ i += (htmlbuf[i+3] == '/' ? 4 : 3);
+ } else if (htmlbuf[i] == '<') {
+ /* Let's strip all unknown tags */
+ j--;
+ while (htmlbuf[++i] != '>');
+ } else
+ nohtml[j] = htmlbuf[i];
+ j++;
+ }
+ nohtml[j] = '\0';
+ return nohtml;
+}
+
+/* html_escape(text)
+ * Add (x)html entities to the text.
+ * The caller must g_free the string after use.
+ * Code mostly derived from strescape(), in libjabber.
+ */
+char *html_escape(const char *text)
+{
+ int i, j;
+ int oldlen, newlen;
+ char *html;
+
+ if (!text) return(NULL);
+
+ oldlen = newlen = strlen(text);
+
+ for (i = 0; i < oldlen; i++) {
+ switch(text[i])
+ {
+ case '&':
+ newlen += 5;
+ break;
+ case '\'':
+ newlen += 6;
+ break;
+ case '\"':
+ newlen += 6;
+ break;
+ case '<':
+ newlen += 4;
+ break;
+ case '>':
+ newlen += 4;
+ break;
+ case '\n':
+ newlen += 5;
+ }
+ }
+
+ if (oldlen == newlen)
+ return g_strdup(text);
+
+ html = g_new0(char, newlen+1);
+
+ for (i = j = 0; i < oldlen; i++) {
+ switch(text[i])
+ {
+ case '&':
+ memcpy(&html[j], "&", 5);
+ j += 5;
+ break;
+ case '\'':
+ memcpy(&html[j], "'", 6);
+ j += 6;
+ break;
+ case '\"':
+ memcpy(&html[j], """, 6);
+ j += 6;
+ break;
+ case '<':
+ memcpy(&html[j], "<", 4);
+ j += 4;
+ break;
+ case '>':
+ memcpy(&html[j], ">", 4);
+ j += 4;
+ break;
+ case '\n':
+ memcpy(&html[j], "<br/>", 5);
+ j += 5;
+ break;
+ default:
+ html[j++] = text[i];
+ }
+ }
+ return html;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/nohtml.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,9 @@
+#ifndef __MCABBER_NOHTML_H__
+#define __MCABBER_NOHTML_H__ 1
+
+char *html_strip(const char *buf);
+char *html_escape(const char *text);
+
+#endif /* __MCABBER_NOHTML_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/otr.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,777 @@
+/*
+ * otr.c -- Off-The-Record Messaging for mcabber
+ *
+ * Copyright (C) 2007-2009 Frank Zschockelt <mcabber_otr@freakysoft.de>
+ *
+ * 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 <config.h>
+#include <glib.h>
+
+#ifdef HAVE_LIBOTR
+
+#include "hbuf.h"
+#include "logprint.h"
+#include "nohtml.h"
+#include "otr.h"
+#include "roster.h"
+#include "screen.h"
+#include "settings.h"
+#include "utils.h"
+#include "xmpp.h"
+
+#define OTR_PROTOCOL_NAME "jabber"
+
+static OtrlUserState userstate = NULL;
+static char *account = NULL;
+static char *keyfile = NULL;
+static char *fprfile = NULL;
+
+static int otr_is_enabled = FALSE;
+
+static OtrlPolicy cb_policy (void *opdata, ConnContext *ctx);
+static void cb_create_privkey (void *opdata,
+ const char *accountname,
+ const char *protocol);
+static int cb_is_logged_in (void *opdata,
+ const char *accountname,
+ const char *protocol,
+ const char *recipient);
+static void cb_inject_message (void *opdata,
+ const char *accountname,
+ const char *protocol,
+ const char *recipient,
+ const char *message);
+static void cb_notify (void *opdata,
+ OtrlNotifyLevel level,
+ const char *accountname,
+ const char *protocol,
+ const char *username,
+ const char *title,
+ const char *primary,
+ const char *secondary);
+static int cb_display_otr_message(void *opdata,
+ const char *accountname,
+ const char *protocol,
+ const char *username,
+ const char *msg);
+static void cb_update_context_list(void *opdata);
+static const char *cb_protocol_name (void *opdata, const char *protocol);
+static void cb_protocol_name_free (void *opdata,
+ const char *protocol_name);
+static void cb_new_fingerprint (void *opdata, OtrlUserState us,
+ const char *accountname,
+ const char *protocol,
+ const char *username,
+ unsigned char fingerprint[20]);
+static void cb_write_fingerprints (void *opdata);
+static void cb_gone_secure (void *opdata, ConnContext *context);
+static void cb_gone_insecure (void *opdata, ConnContext *context);
+static void cb_still_secure (void *opdata, ConnContext *context,
+ int is_reply);
+static void cb_log_message (void *opdata, const char *message);
+static int cb_max_message_size (void *opdata, ConnContext *context);
+
+static OtrlMessageAppOps ops =
+{
+ cb_policy,
+ cb_create_privkey,
+ cb_is_logged_in,
+ cb_inject_message,
+ cb_notify,
+ cb_display_otr_message,
+ cb_update_context_list,
+ cb_protocol_name,
+ cb_protocol_name_free,
+ cb_new_fingerprint,
+ cb_write_fingerprints,
+ cb_gone_secure,
+ cb_gone_insecure,
+ cb_still_secure,
+ cb_log_message,
+ cb_max_message_size,
+ NULL, /*account_name*/
+ NULL /*account_name_free*/
+};
+
+static void otr_message_disconnect(ConnContext *ctx);
+static ConnContext *otr_get_context(const char *buddy);
+static void otr_startstop(const char *buddy, int start);
+static void otr_handle_smp_tlvs(OtrlTLV *tlvs, ConnContext *ctx);
+
+static char *otr_get_dir(void);
+
+void otr_init(const char *fjid)
+{
+ char *root;
+
+ if (userstate) //already initialised
+ return;
+
+ otr_is_enabled = !!settings_opt_get_int("otr");
+
+ if (!otr_is_enabled)
+ return;
+
+ OTRL_INIT;
+
+ userstate = otrl_userstate_create();
+
+ root = otr_get_dir();
+ account = jidtodisp(fjid);
+ keyfile = g_strdup_printf("%s%s.key", root, account);
+ fprfile = g_strdup_printf("%s%s.fpr", root, account);
+ g_free(root);
+
+ if (otrl_privkey_read(userstate, keyfile)){
+ scr_LogPrint(LPRINT_LOGNORM, "Could not read OTR key from %s", keyfile);
+ cb_create_privkey(NULL, account, OTR_PROTOCOL_NAME);
+ }
+ if (otrl_privkey_read_fingerprints(userstate, fprfile, NULL, NULL)){
+ scr_LogPrint(LPRINT_LOGNORM, "Could not read OTR fingerprints from %s",
+ fprfile);
+ }
+}
+
+void otr_terminate(void)
+{
+ ConnContext *ctx;
+
+ if (!otr_is_enabled)
+ return;
+
+ for (ctx = userstate->context_root; ctx; ctx = ctx->next)
+ if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
+ otr_message_disconnect(ctx);
+
+ g_free(account);
+ account = NULL;
+
+ /* XXX This #ifdef is a quick workaround: when mcabber
+ * is linked to both gnutls and libotr, libgcrypt will
+ * segfault when we call otrl_userstate_free().
+ * This is reported to be a bug in libgcrypt :-/
+ * Mikael
+ */
+#if defined(HAVE_GNUTLS) && !defined(HAVE_OPENSSL) //TODO: broken now
+ if (!settings_opt_get_int("ssl"))
+#endif
+ otrl_userstate_free(userstate);
+
+ userstate = NULL;
+ g_free(keyfile);
+ keyfile = NULL;
+}
+
+static char *otr_get_dir(void)
+{
+ const char *configured_dir = settings_opt_get("otr_dir");
+
+ if (configured_dir && *configured_dir) {
+ char *xp_conf_dir;
+ int l;
+ xp_conf_dir = expand_filename(configured_dir);
+ // The path must be slash-terminated
+ l = strlen(xp_conf_dir);
+ if (xp_conf_dir[l-1] != '/') {
+ char *xp_conf_dir_tmp = xp_conf_dir;
+ xp_conf_dir = g_strdup_printf("%s/", xp_conf_dir_tmp);
+ g_free(xp_conf_dir_tmp);
+ }
+ return xp_conf_dir;
+ } else {
+ return expand_filename("~/.mcabber/otr/");
+ }
+}
+
+static ConnContext *otr_get_context(const char *buddy)
+{
+ int null = 0;
+ ConnContext *ctx;
+ char *lowcasebuddy = g_strdup(buddy);
+
+ mc_strtolower(lowcasebuddy);
+ ctx = otrl_context_find(userstate, lowcasebuddy, account, OTR_PROTOCOL_NAME,
+ 1, &null, NULL, NULL);
+ g_free(lowcasebuddy);
+ return ctx;
+}
+
+static void otr_message_disconnect(ConnContext *ctx)
+{
+ if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
+ cb_gone_insecure(NULL, ctx);
+ otrl_message_disconnect(userstate, &ops, NULL, ctx->accountname,
+ ctx->protocol, ctx->username);
+}
+
+static void otr_startstop(const char *buddy, int start)
+{
+ char *msg = NULL;
+ ConnContext *ctx = otr_get_context(buddy);
+
+ if (!userstate || !ctx)
+ return;
+
+ if (start && ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
+ otr_message_disconnect(ctx);
+
+ if (start) {
+ OtrlPolicy policy = cb_policy(NULL, ctx);
+ if (policy == plain) {
+ scr_LogPrint(LPRINT_LOGNORM, "The OTR policy for this user is set to"
+ " plain. You have to change it first.");
+ return;
+ }
+ msg = otrl_proto_default_query_msg(ctx->accountname, policy);
+ cb_inject_message(NULL, ctx->accountname, ctx->protocol, ctx->username,
+ msg);
+ free (msg);
+ }
+ else
+ otr_message_disconnect(ctx);
+}
+
+void otr_establish(const char *buddy)
+{
+ otr_startstop(buddy, 1);
+}
+
+void otr_disconnect(const char *buddy)
+{
+ otr_startstop(buddy, 0);
+}
+
+void otr_fingerprint(const char *buddy, const char *trust)
+{
+ char fpr[45], *tr;
+ ConnContext *ctx = otr_get_context(buddy);
+ if (!userstate || !ctx)
+ return;
+
+ if (!ctx->active_fingerprint || !ctx->active_fingerprint->fingerprint) {
+ scr_LogPrint(LPRINT_LOGNORM,
+ "No active fingerprint - start OTR for this buddy first.");
+ return;
+ }
+
+ otrl_privkey_hash_to_human(fpr, ctx->active_fingerprint->fingerprint);
+ if (trust) {
+ if (strcmp(fpr, trust) == 0)
+ otrl_context_set_trust(ctx->active_fingerprint, "trust");
+ else
+ otrl_context_set_trust(ctx->active_fingerprint, NULL);
+ }
+
+ tr = ctx->active_fingerprint->trust;
+ scr_LogPrint(LPRINT_LOGNORM, "%s [%44s]: %s", ctx->username, fpr,
+ tr && *tr ? "trusted" : "untrusted");
+ cb_write_fingerprints(NULL);
+}
+
+static void otr_handle_smp_tlvs(OtrlTLV *tlvs, ConnContext *ctx)
+{
+ OtrlTLV *tlv = NULL;
+ char *sbuf = NULL;
+ NextExpectedSMP nextMsg = ctx->smstate->nextExpected;
+
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
+ if (tlv) {
+ if (nextMsg != OTRL_SMP_EXPECT1)
+ otr_smp_abort(ctx->username);
+ else {
+ sbuf = g_strdup_printf("OTR: Received SMP Initiation. "
+ "Answer with /otr smpr %s $secret",
+ ctx->username);
+ }
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
+ if (tlv) {
+ if (nextMsg != OTRL_SMP_EXPECT2)
+ otr_smp_abort(ctx->username);
+ else {
+ sbuf = g_strdup("OTR: Received SMP Response.");
+ /* If we received TLV2, we will send TLV3 and expect TLV4 */
+ ctx->smstate->nextExpected = OTRL_SMP_EXPECT4;
+ }
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
+ if (tlv) {
+ if (nextMsg != OTRL_SMP_EXPECT3)
+ otr_smp_abort(ctx->username);
+ else {
+ /* If we received TLV3, we will send TLV4
+ * We will not expect more messages, so prepare for next SMP */
+ ctx->smstate->nextExpected = OTRL_SMP_EXPECT1;
+ /* Report result to user */
+ if (ctx->active_fingerprint && ctx->active_fingerprint->trust &&
+ *ctx->active_fingerprint->trust != '\0')
+ sbuf = g_strdup("OTR: SMP succeeded");
+ else
+ sbuf = g_strdup("OTR: SMP failed");
+ }
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
+ if (tlv) {
+ if (nextMsg != OTRL_SMP_EXPECT4)
+ otr_smp_abort(ctx->username);
+ else {
+ /* We will not expect more messages, so prepare for next SMP */
+ ctx->smstate->nextExpected = OTRL_SMP_EXPECT1;
+ /* Report result to user */
+ if (ctx->active_fingerprint && ctx->active_fingerprint->trust &&
+ *ctx->active_fingerprint->trust != '\0')
+ sbuf = g_strdup("OTR: SMP succeeded");
+ else
+ sbuf = g_strdup("OTR: SMP failed");
+ }
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
+ if (tlv) {
+ /* The message we are waiting for will not arrive, so reset
+ * and prepare for the next SMP */
+ sbuf = g_strdup("OTR: SMP aborted by your buddy");
+ ctx->smstate->nextExpected = OTRL_SMP_EXPECT1;
+ }
+
+ if (sbuf) {
+ scr_WriteIncomingMessage(ctx->username, sbuf, 0, HBB_PREFIX_INFO, 0);
+ g_free(sbuf);
+ }
+}
+
+/*
+ * returns whether a otr_message was received
+ * sets *otr_data to NULL, when it was an internal otr message
+ */
+int otr_receive(char **otr_data, const char *buddy, int *free_msg)
+{
+ int ignore_message;
+ char *newmessage = NULL;
+ OtrlTLV *tlvs = NULL;
+ OtrlTLV *tlv = NULL;
+ ConnContext *ctx;
+
+ ctx = otr_get_context(buddy);
+ *free_msg = 0;
+ ignore_message = otrl_message_receiving(userstate, &ops, NULL,
+ ctx->accountname, ctx->protocol,
+ ctx->username, *otr_data,
+ &newmessage, &tlvs,NULL, NULL);
+
+
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
+ if (tlv) {
+ /* Notify the user that the other side disconnected. */
+ if (ctx) {
+ cb_gone_insecure(NULL, ctx);
+ otr_disconnect(ctx->username);
+ }
+ }
+
+ otr_handle_smp_tlvs(tlvs, ctx);
+
+ if (tlvs != NULL)
+ otrl_tlv_free(tlvs);
+
+ if (ignore_message)
+ *otr_data = NULL;
+
+ if (!ignore_message && newmessage) {
+ *free_msg = 1;
+ *otr_data = html_strip(newmessage);
+ otrl_message_free(newmessage);
+ if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
+ return 1;
+ }
+ return 0;
+}
+
+int otr_send(char **msg, const char *buddy)
+{
+ gcry_error_t err;
+ char *newmessage = NULL;
+ char *htmlmsg;
+ ConnContext *ctx = otr_get_context(buddy);
+
+ if (ctx->msgstate == OTRL_MSGSTATE_PLAINTEXT)
+ err = otrl_message_sending(userstate, &ops, NULL, ctx->accountname,
+ ctx->protocol, ctx->username, *msg, NULL,
+ &newmessage, NULL, NULL);
+ else {
+ htmlmsg = html_escape(*msg);
+ err = otrl_message_sending(userstate, &ops, NULL, ctx->accountname,
+ ctx->protocol, ctx->username, htmlmsg, NULL,
+ &newmessage, NULL, NULL);
+ g_free(htmlmsg);
+ }
+
+ if (err)
+ *msg = NULL; /*something went wrong, don't send the plain-message! */
+
+ if (!err && newmessage) {
+ *msg = g_strdup(newmessage);
+ otrl_message_free(newmessage);
+ if (cb_policy(NULL, ctx) & OTRL_POLICY_REQUIRE_ENCRYPTION ||
+ ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
+ return 1;
+ }
+ return 0;
+}
+
+/* Prints OTR connection state */
+void otr_print_info(const char *buddy)
+{
+ const char *state, *auth, *policy;
+ ConnContext *ctx = otr_get_context(buddy);
+ OtrlPolicy p = cb_policy(ctx->app_data, ctx);
+
+ if (!userstate || !ctx)
+ return;
+
+ switch (ctx->msgstate) {
+ case OTRL_MSGSTATE_PLAINTEXT: state = "plaintext"; break;
+ case OTRL_MSGSTATE_ENCRYPTED:
+ switch (ctx->protocol_version) {
+ case 1: state = "encrypted V1"; break;
+ case 2: state = "encrypted V2"; break;
+ default:state = "encrypted";
+ };
+ break;
+ case OTRL_MSGSTATE_FINISHED: state = "finished"; break;
+ default: state = "unknown state";
+ }
+ switch (ctx->auth.authstate) {
+ case OTRL_AUTHSTATE_NONE:
+ switch (ctx->otr_offer) {
+ case OFFER_NOT: auth = "no offer sent"; break;
+ case OFFER_SENT: auth = "offer sent"; break;
+ case OFFER_ACCEPTED: auth = "offer accepted"; break;
+ case OFFER_REJECTED: auth = "offer rejected"; break;
+ default: auth = "unknown auth";
+ }
+ break;
+ case OTRL_AUTHSTATE_AWAITING_DHKEY:
+ auth = "awaiting D-H key"; break;
+ case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
+ auth = "awaiting reveal signature"; break;
+ case OTRL_AUTHSTATE_AWAITING_SIG:
+ auth = "awaiting signature"; break;
+ case OTRL_AUTHSTATE_V1_SETUP:
+ auth = "v1 setup"; break;
+ default:
+ auth = "unknown auth";
+ }
+ if (p == OTRL_POLICY_NEVER)
+ policy = "plain";
+ else if (p == (OTRL_POLICY_OPPORTUNISTIC & ~OTRL_POLICY_ALLOW_V1))
+ policy = "opportunistic";
+ else if (p == (OTRL_POLICY_MANUAL & ~OTRL_POLICY_ALLOW_V1))
+ policy = "manual";
+ else if (p == (OTRL_POLICY_ALWAYS & ~OTRL_POLICY_ALLOW_V1))
+ policy = "always";
+ else
+ policy = "unknown";
+
+ scr_LogPrint(LPRINT_LOGNORM, "%s: %s (%s) [%s]",
+ ctx->username, state, auth, policy);
+}
+
+static ConnContext *otr_context_encrypted(const char *buddy)
+{
+ ConnContext *ctx = otr_get_context(buddy);
+
+ if (!userstate || !ctx || ctx->msgstate != OTRL_MSGSTATE_ENCRYPTED){
+ scr_LogPrint(LPRINT_LOGNORM,
+ "You have to start an OTR channel with %s before you can "
+ "use SMP.", buddy);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void otr_smp_query(const char *buddy, const char *secret)
+{
+ ConnContext *ctx = otr_context_encrypted(buddy);
+
+ if (!secret) {
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Using SMP without a secret isn't a good idea.");
+ return;
+ }
+
+ if (ctx) {
+ otrl_message_initiate_smp(userstate, &ops, NULL, ctx,
+ (const unsigned char *)secret,
+ strlen(secret));
+ scr_WriteIncomingMessage(ctx->username,
+ "OTR: Socialist Millionaires' Protocol "
+ "initiated.", 0, HBB_PREFIX_INFO, 0);
+ }
+}
+
+void otr_smp_respond(const char *buddy, const char *secret)
+{
+ ConnContext *ctx = otr_context_encrypted(buddy);
+
+ if (!secret) {
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Using SMP without a secret isn't a good idea.");
+ return;
+ }
+
+ if (ctx) {
+ if (!ctx->smstate->secret) {
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Don't call smpr until you have received an SMP "
+ "Initiation!");
+ return;
+ }
+ otrl_message_respond_smp(userstate, &ops, NULL, ctx,
+ (const unsigned char *)secret,
+ strlen(secret));
+ scr_WriteIncomingMessage(ctx->username,
+ "OTR: Socialist Millionaires' Protocol: "
+ "response sent", 0, HBB_PREFIX_INFO, 0);
+ }
+}
+
+void otr_smp_abort(const char *buddy)
+{
+ ConnContext *ctx = otr_context_encrypted(buddy);
+
+ if (ctx) {
+ otrl_message_abort_smp(userstate, &ops, NULL, ctx);
+ scr_WriteIncomingMessage(ctx->username,
+ "OTR: Socialist Millionaires' Protocol aborted.",
+ 0, HBB_PREFIX_INFO, 0);
+ }
+}
+
+void otr_key(void)
+{
+ OtrlPrivKey *key;
+ char readable[45] = "";
+
+ if(!userstate)
+ return;
+ for (key = userstate->privkey_root; key; key = key->next) {
+ otrl_privkey_fingerprint(userstate, readable, key->accountname,
+ key->protocol);
+ scr_LogPrint(LPRINT_LOGNORM, "%s: %s", key->accountname, readable);
+ }
+}
+
+/* Return the OTR policy for the given context. */
+static OtrlPolicy cb_policy(void *opdata, ConnContext *ctx)
+{
+ enum otr_policy p = settings_otr_getpolicy(NULL);
+
+ if(ctx)
+ if(settings_otr_getpolicy(ctx->username))
+ p = settings_otr_getpolicy(ctx->username);
+
+ switch (p) {
+ case plain:
+ return OTRL_POLICY_NEVER;
+ case opportunistic:
+ return OTRL_POLICY_OPPORTUNISTIC & ~OTRL_POLICY_ALLOW_V1;
+ case manual:
+ return OTRL_POLICY_MANUAL & ~OTRL_POLICY_ALLOW_V1;
+ case always:
+ return OTRL_POLICY_ALWAYS & ~OTRL_POLICY_ALLOW_V1;
+ }
+
+ return OTRL_POLICY_MANUAL & ~OTRL_POLICY_ALLOW_V1;
+}
+
+/* Create a private key for the given accountname/protocol if
+ * desired. */
+static void cb_create_privkey(void *opdata, const char *accountname,
+ const char *protocol)
+{
+ gcry_error_t e;
+ char *root;
+
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Generating new OTR key for %s. This may take a while...",
+ accountname);
+ scr_DoUpdate();
+
+ e = otrl_privkey_generate(userstate, keyfile, accountname, protocol);
+
+ if (e) {
+ root = otr_get_dir();
+ scr_LogPrint(LPRINT_LOGNORM, "OTR key generation failed! Please mkdir "
+ "%s if you want to use otr encryption.", root);
+ g_free(root);
+ }
+ else
+ scr_LogPrint(LPRINT_LOGNORM, "OTR key generated.");
+}
+
+/* Report whether you think the given user is online. Return 1 if
+ * you think he is, 0 if you think he isn't, -1 if you're not sure.
+ * If you return 1, messages such as heartbeats or other
+ * notifications may be sent to the user, which could result in "not
+ * logged in" errors if you're wrong. */
+static int cb_is_logged_in(void *opdata, const char *accountname,
+ const char *protocol, const char *recipient)
+{
+ int ret = (roster_getstatus(recipient, NULL) != offline);
+ return ret;
+}
+
+/* Send the given IM to the given recipient from the given
+ * accountname/protocol. */
+static void cb_inject_message(void *opdata, const char *accountname,
+ const char *protocol, const char *recipient,
+ const char *message)
+{
+ if (roster_gettype(recipient) == ROSTER_TYPE_USER)
+ xmpp_send_msg(recipient, message, ROSTER_TYPE_USER, "", TRUE, NULL,
+ LM_MESSAGE_SUB_TYPE_NOT_SET, NULL);
+}
+
+/* Display a notification message for a particular
+ * accountname / protocol / username conversation. */
+static void cb_notify(void *opdata, OtrlNotifyLevel level,
+ const char *accountname, const char *protocol,
+ const char *username, const char *title,
+ const char *primary, const char *secondary)
+{
+ char *type;
+ char *sbuf = NULL;
+ switch (level) {
+ case OTRL_NOTIFY_ERROR: type = "error"; break;
+ case OTRL_NOTIFY_WARNING: type = "warning"; break;
+ case OTRL_NOTIFY_INFO: type = "info"; break;
+ default: type = "unknown";
+ }
+ sbuf = g_strdup_printf("OTR %s:%s\n%s\n%s",type,title, primary, secondary);
+ scr_WriteIncomingMessage(username, sbuf, 0, HBB_PREFIX_INFO, 0);
+ g_free(sbuf);
+}
+
+/* Display an OTR control message for a particular
+ * accountname / protocol / username conversation. Return 0 if you are able
+ * to successfully display it. If you return non-0 (or if this
+ * function is NULL), the control message will be displayed inline,
+ * as a received message, or else by using the above notify()
+ * callback. */
+static int cb_display_otr_message(void *opdata, const char *accountname,
+ const char *protocol, const char *username,
+ const char *msg)
+{
+ char *strippedmsg = html_strip(msg);
+ scr_WriteIncomingMessage(username, strippedmsg, 0, HBB_PREFIX_INFO, 0);
+ g_free(strippedmsg);
+ return 0;
+}
+
+/* When the list of ConnContexts changes (including a change in
+ * state), this is called so the UI can be updated. */
+static void cb_update_context_list(void *opdata)
+{
+ /*maybe introduce new status characters for mcabber,
+ * then use this function (?!)*/
+}
+
+/* Return a newly allocated string containing a human-friendly name
+ * for the given protocol id */
+static const char *cb_protocol_name(void *opdata, const char *protocol)
+{
+ return protocol;
+}
+
+/* Deallocate a string allocated by protocol_name */
+static void cb_protocol_name_free (void *opdata, const char *protocol_name)
+{
+ /* We didn't allocated memory, so we don't have to free anything :p */
+}
+
+/* A new fingerprint for the given user has been received. */
+static void cb_new_fingerprint(void *opdata, OtrlUserState us,
+ const char *accountname, const char *protocol,
+ const char *username,
+ unsigned char fingerprint[20])
+{
+ char *sbuf = NULL;
+ char readable[45];
+
+ otrl_privkey_hash_to_human(readable, fingerprint);
+ sbuf = g_strdup_printf("OTR: new fingerprint: %s", readable);
+ scr_WriteIncomingMessage(username, sbuf, 0, HBB_PREFIX_INFO, 0);
+ g_free(sbuf);
+}
+
+/* The list of known fingerprints has changed. Write them to disk. */
+static void cb_write_fingerprints(void *opdata)
+{
+ otrl_privkey_write_fingerprints(userstate, fprfile);
+}
+
+/* A ConnContext has entered a secure state. */
+static void cb_gone_secure(void *opdata, ConnContext *context)
+{
+ scr_WriteIncomingMessage(context->username, "OTR: channel established", 0,
+ HBB_PREFIX_INFO, 0);
+}
+
+/* A ConnContext has left a secure state. */
+static void cb_gone_insecure(void *opdata, ConnContext *context)
+{
+ scr_WriteIncomingMessage(context->username, "OTR: channel closed", 0,
+ HBB_PREFIX_INFO, 0);
+}
+
+/* We have completed an authentication, using the D-H keys we
+ * already knew. is_reply indicates whether we initiated the AKE. */
+static void cb_still_secure(void *opdata, ConnContext *context, int is_reply)
+{
+ scr_WriteIncomingMessage(context->username, "OTR: channel reestablished", 0,
+ HBB_PREFIX_INFO, 0);
+}
+
+/* Log a message. The passed message will end in "\n". */
+static void cb_log_message(void *opdata, const char *message)
+{
+ scr_LogPrint(LPRINT_DEBUG, "OTR: %s", message);
+}
+
+/* Find the maximum message size supported by this protocol. */
+static int cb_max_message_size(void *opdata, ConnContext *context)
+{
+ return 8192;
+}
+
+int otr_enabled(void)
+{
+ return otr_is_enabled;
+}
+
+#else /* !HAVE_LIBOTR */
+
+int otr_enabled(void)
+{
+ return FALSE;
+}
+
+#endif /* HAVE_LIBOTR */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/otr.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,42 @@
+#ifndef __MCABBER_OTR_H__
+#define __MCABBER_OTR_H__ 1
+
+#include <mcabber/config.h>
+
+#ifdef HAVE_LIBOTR
+
+#include <libotr/proto.h>
+#include <libotr/message.h>
+#include <libotr/privkey.h>
+
+enum otr_policy {
+ plain,
+ opportunistic,
+ manual,
+ always
+};
+
+void otr_init(const char *jid);
+void otr_terminate(void);
+
+void otr_establish (const char * buddy);
+void otr_disconnect (const char * buddy);
+void otr_fingerprint(const char * buddy, const char * trust);
+void otr_print_info (const char * buddy);
+
+void otr_smp_query (const char * buddy, const char * secret);
+void otr_smp_respond(const char * buddy, const char * secret);
+void otr_smp_abort (const char * buddy);
+
+void otr_key (void);
+
+int otr_receive (char **otr_data, const char * buddy, int * free_msg);
+int otr_send (char **msg, const char *buddy);
+
+#endif /* HAVE_LIBOTR */
+
+int otr_enabled (void);
+
+#endif /* __MCABBER_OTR_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/pgp.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,475 @@
+/*
+ * pgp.c -- PGP utility functions
+ *
+ * Copyright (C) 2006-2009 Mikael Berthe <mikael@lilotux.net>
+ * Some parts inspired by centericq (impgp.cc)
+ *
+ * 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 <config.h>
+
+#ifdef HAVE_GPGME
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <locale.h>
+#include <sys/mman.h>
+#include <glib.h>
+
+#include "pgp.h"
+#include "logprint.h"
+
+#define MIN_GPGME_VERSION "1.0.0"
+
+static struct gpg_struct
+{
+ int enabled;
+ char *private_key;
+ char *passphrase;
+} gpg;
+
+
+// gpg_init(priv_key, passphrase)
+// Initialize the GPG sub-systems. This function must be invoked early.
+// Note: priv_key & passphrase are optional, they can be set later.
+// This function returns 0 if gpgme is available and initialized;
+// if not it returns the gpgme error code.
+int gpg_init(const char *priv_key, const char *passphrase)
+{
+ gpgme_error_t err;
+
+ // Check for version and OpenPGP protocol support.
+ if (!gpgme_check_version(MIN_GPGME_VERSION)) {
+ scr_LogPrint(LPRINT_LOGNORM,
+ "GPGME initialization error: Bad library version");
+ return -1;
+ }
+
+ err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
+ if (err) {
+ scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
+ "GPGME initialization error: %s", gpgme_strerror(err));
+ return err;
+ }
+
+ // Set the locale information.
+ gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
+ gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
+
+ // Store private data.
+ gpg_set_private_key(priv_key);
+ gpg_set_passphrase(passphrase);
+
+ gpg.enabled = 1;
+ return 0;
+}
+
+// gpg_terminate()
+// Destroy data and free memory.
+void gpg_terminate(void)
+{
+ gpg.enabled = 0;
+ gpg_set_passphrase(NULL);
+ gpg_set_private_key(NULL);
+}
+
+// gpg_set_passphrase(passphrase)
+// Set the current passphrase (use NULL to erase it).
+void gpg_set_passphrase(const char *passphrase)
+{
+ // Remove current passphrase
+ if (gpg.passphrase) {
+ ssize_t len = strlen(gpg.passphrase);
+ memset(gpg.passphrase, 0, len);
+ munlock(gpg.passphrase, len);
+ g_free(gpg.passphrase);
+ }
+ if (passphrase) {
+ gpg.passphrase = g_strdup(passphrase);
+ mlock(gpg.passphrase, strlen(gpg.passphrase));
+ } else {
+ gpg.passphrase = NULL;
+ }
+}
+
+// gpg_set_private_key(keyid)
+// Set the current private key id (use NULL to unset it).
+void gpg_set_private_key(const char *priv_keyid)
+{
+ g_free(gpg.private_key);
+ if (priv_keyid)
+ gpg.private_key = g_strdup(priv_keyid);
+ else
+ gpg.private_key = NULL;
+}
+
+// strip_header_footer(data)
+// Remove PGP header & footer from data.
+// Return a new string, or NULL.
+// The string must be freed by the caller with g_free() when no longer needed.
+static char *strip_header_footer(const char *data)
+{
+ char *p, *q;
+
+ if (!data)
+ return NULL;
+
+ // p: beginning of real data
+ // q: end of real data
+
+ // Strip header (to the first empty line)
+ p = strstr(data, "\n\n");
+ if (!p)
+ return g_strdup(data);
+
+ // Strip footer
+ // We want to remove the last lines, until the line beginning with a '-'
+ p += 2;
+ for (q = p ; *q; q++) ;
+ // (q is at the end of data now)
+ for (q--; q > p && (*q != '\n' || *(q+1) != '-'); q--) ;
+
+ if (q <= p)
+ return NULL; // Shouldn't happen...
+
+ return g_strndup(p, q-p);
+}
+
+// GCC ignores casts to void, thus we need to hack around that
+static inline void ignore(void*x) {}
+
+// passphrase_cb()
+// GPGME passphrase callback function.
+static gpgme_error_t passphrase_cb(void *hook, const char *uid_hint,
+ const char *passphrase_info, int prev_was_bad, int fd)
+{
+ ssize_t len;
+
+ // Abort if we do not have the password.
+ if (!gpg.passphrase) {
+ ignore((void*)write(fd, "\n", 1)); // We have an error anyway, thus it does
+ // not matter if we fail again.
+ return gpg_error(GPG_ERR_CANCELED);
+ }
+
+ // Write the passphrase to the file descriptor.
+ len = strlen(gpg.passphrase);
+ if (write(fd, gpg.passphrase, len) != len)
+ return gpg_error(GPG_ERR_CANCELED);
+ if (write(fd, "\n", 1) != 1)
+ return gpg_error(GPG_ERR_CANCELED);
+
+ return 0; // Success
+}
+
+// gpg_verify(gpg_data, text, *sigsum)
+// Verify that gpg_data is a correct signature for text.
+// Return the key id (or fingerprint), and set *sigsum to
+// the gpgme signature summary value.
+// The returned string must be freed with g_free() after use.
+char *gpg_verify(const char *gpg_data, const char *text,
+ gpgme_sigsum_t *sigsum)
+{
+ gpgme_ctx_t ctx;
+ gpgme_data_t data_sign, data_text;
+ char *data;
+ char *verified_key = NULL;
+ gpgme_key_t key;
+ gpgme_error_t err;
+ const char prefix[] = "-----BEGIN PGP SIGNATURE-----\n\n";
+ const char suffix[] = "\n-----END PGP SIGNATURE-----\n";
+
+ // Reset the summary.
+ *sigsum = 0;
+
+ if (!gpg.enabled)
+ return NULL;
+
+ err = gpgme_new(&ctx);
+ if (err) {
+ scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
+ "GPGME error: %s", gpgme_strerror(err));
+ return NULL;
+ }
+
+ gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
+
+ // Surround the given data with the prefix & suffix
+ data = g_new(char, sizeof(prefix) + sizeof(suffix) + strlen(gpg_data));
+ strcpy(data, prefix);
+ strcat(data, gpg_data);
+ strcat(data, suffix);
+
+ err = gpgme_data_new_from_mem(&data_sign, data, strlen(data), 0);
+ if (!err) {
+ err = gpgme_data_new_from_mem(&data_text, text, strlen(text), 0);
+ if (!err) {
+ err = gpgme_op_verify(ctx, data_sign, data_text, 0);
+ if (!err) {
+ gpgme_verify_result_t vr = gpgme_op_verify_result(ctx);
+ if (vr && vr->signatures) {
+ char *r = vr->signatures->fpr;
+ // Found the fingerprint. Let's try to get the key id.
+ if (!gpgme_get_key(ctx, r, &key, 0) && key) {
+ r = key->subkeys->keyid;
+ gpgme_key_release(key);
+ }
+ // r is a static variable, let's copy it.
+ verified_key = g_strdup(r);
+ *sigsum = vr->signatures->summary;
+ // For some reason summary could be 0 when status is 0 too,
+ // which means the signature is valid...
+ if (!*sigsum && !vr->signatures->status)
+ *sigsum = GPGME_SIGSUM_GREEN;
+ }
+ }
+ gpgme_data_release(data_text);
+ }
+ gpgme_data_release(data_sign);
+ }
+ if (err)
+ scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
+ "GPGME verification error: %s", gpgme_strerror(err));
+ gpgme_release(ctx);
+ g_free(data);
+ return verified_key;
+}
+
+// gpg_sign(gpg_data)
+// Return a signature of gpg_data (or NULL).
+// The returned string must be freed with g_free() after use.
+char *gpg_sign(const char *gpg_data)
+{
+ gpgme_ctx_t ctx;
+ gpgme_data_t in, out;
+ char *p;
+ char *signed_data = NULL;
+ size_t nread;
+ gpgme_key_t key;
+ gpgme_error_t err;
+
+ if (!gpg.enabled || !gpg.private_key)
+ return NULL;
+
+ err = gpgme_new(&ctx);
+ if (err) {
+ scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
+ "GPGME error: %s", gpgme_strerror(err));
+ return NULL;
+ }
+
+ gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
+ gpgme_set_textmode(ctx, 0);
+ gpgme_set_armor(ctx, 1);
+
+ p = getenv("GPG_AGENT_INFO");
+ if (!(p && strchr(p, ':')))
+ gpgme_set_passphrase_cb(ctx, passphrase_cb, 0);
+
+ err = gpgme_get_key(ctx, gpg.private_key, &key, 1);
+ if (err || !key) {
+ scr_LogPrint(LPRINT_LOGNORM, "GPGME error: private key not found");
+ gpgme_release(ctx);
+ return NULL;
+ }
+
+ gpgme_signers_clear(ctx);
+ gpgme_signers_add(ctx, key);
+ gpgme_key_release(key);
+ err = gpgme_data_new_from_mem(&in, gpg_data, strlen(gpg_data), 0);
+ if (!err) {
+ err = gpgme_data_new(&out);
+ if (!err) {
+ err = gpgme_op_sign(ctx, in, out, GPGME_SIG_MODE_DETACH);
+ if (!err) {
+ signed_data = gpgme_data_release_and_get_mem(out, &nread);
+ if (signed_data) {
+ // We need to add a trailing NULL
+ char *dd = g_strndup(signed_data, nread);
+ free(signed_data);
+ signed_data = strip_header_footer(dd);
+ g_free(dd);
+ }
+ } else {
+ gpgme_data_release(out);
+ }
+ }
+ gpgme_data_release(in);
+ }
+ if (err && err != GPG_ERR_CANCELED)
+ scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
+ "GPGME signature error: %s", gpgme_strerror(err));
+ gpgme_release(ctx);
+ return signed_data;
+}
+
+// gpg_decrypt(gpg_data)
+// Return decrypted gpg_data (or NULL).
+// The returned string must be freed with g_free() after use.
+char *gpg_decrypt(const char *gpg_data)
+{
+ gpgme_ctx_t ctx;
+ gpgme_data_t in, out;
+ char *p, *data;
+ char *decrypted_data = NULL;
+ size_t nread;
+ gpgme_error_t err;
+ const char prefix[] = "-----BEGIN PGP MESSAGE-----\n\n";
+ const char suffix[] = "\n-----END PGP MESSAGE-----\n";
+
+ if (!gpg.enabled)
+ return NULL;
+
+ err = gpgme_new(&ctx);
+ if (err) {
+ scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
+ "GPGME error: %s", gpgme_strerror(err));
+ return NULL;
+ }
+
+ gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
+
+ p = getenv("GPG_AGENT_INFO");
+ if (!(p && strchr(p, ':')))
+ gpgme_set_passphrase_cb(ctx, passphrase_cb, 0);
+
+ // Surround the given data with the prefix & suffix
+ data = g_new(char, sizeof(prefix) + sizeof(suffix) + strlen(gpg_data));
+ strcpy(data, prefix);
+ strcat(data, gpg_data);
+ strcat(data, suffix);
+
+ err = gpgme_data_new_from_mem(&in, data, strlen(data), 0);
+ if (!err) {
+ err = gpgme_data_new(&out);
+ if (!err) {
+ err = gpgme_op_decrypt(ctx, in, out);
+ if (!err) {
+ decrypted_data = gpgme_data_release_and_get_mem(out, &nread);
+ if (decrypted_data) {
+ // We need to add a trailing NULL
+ char *dd = g_strndup(decrypted_data, nread);
+ free(decrypted_data);
+ decrypted_data = dd;
+ }
+ } else {
+ gpgme_data_release(out);
+ }
+ }
+ gpgme_data_release(in);
+ }
+ if (err && err != GPG_ERR_CANCELED)
+ scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
+ "GPGME decryption error: %s", gpgme_strerror(err));
+ gpgme_release(ctx);
+ g_free(data);
+ return decrypted_data;
+}
+
+// gpg_encrypt(gpg_data, keyid)
+// Return encrypted gpg_data with the key keyid (or NULL).
+// The returned string must be freed with g_free() after use.
+char *gpg_encrypt(const char *gpg_data, const char *keyid)
+{
+ gpgme_ctx_t ctx;
+ gpgme_data_t in, out;
+ char *encrypted_data = NULL, *edata;
+ size_t nread;
+ gpgme_key_t key;
+ gpgme_error_t err;
+
+ if (!gpg.enabled)
+ return NULL;
+
+ err = gpgme_new(&ctx);
+ if (err) {
+ scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
+ "GPGME error: %s", gpgme_strerror(err));
+ return NULL;
+ }
+
+ gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
+ gpgme_set_textmode(ctx, 0);
+ gpgme_set_armor(ctx, 1);
+
+ err = gpgme_get_key(ctx, keyid, &key, 0);
+ if (!err && key) {
+ gpgme_key_t keys[] = { key, 0 };
+ err = gpgme_data_new_from_mem(&in, gpg_data, strlen(gpg_data), 0);
+ if (!err) {
+ err = gpgme_data_new(&out);
+ if (!err) {
+ err = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
+ if (!err)
+ encrypted_data = gpgme_data_release_and_get_mem(out, &nread);
+ else
+ gpgme_data_release(out);
+ }
+ gpgme_data_release(in);
+ }
+ gpgme_key_release(key);
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "GPGME encryption error: key not found");
+ err = 0;
+ }
+ if (err && err != GPG_ERR_CANCELED)
+ scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
+ "GPGME encryption error: %s", gpgme_strerror(err));
+ gpgme_release(ctx);
+ edata = strip_header_footer(encrypted_data);
+ if (encrypted_data)
+ free(encrypted_data);
+ return edata;
+}
+
+// gpg_test_passphrase()
+// Test the current gpg.passphrase with gpg.private_key.
+// If the test doesn't succeed, the passphrase is cleared and a non-null
+// value is returned.
+int gpg_test_passphrase(void)
+{
+ char *s;
+
+ if (!gpg.private_key)
+ return -1; // No private key...
+
+ s = gpg_sign("test");
+ if (s) {
+ free(s);
+ return 0; // Ok, test successful
+ }
+ // The passphrase is wrong (if provided)
+ gpg_set_passphrase(NULL);
+ return -1;
+}
+
+int gpg_enabled(void)
+{
+ return gpg.enabled;
+}
+
+#else /* not HAVE_GPGME */
+
+int gpg_enabled(void)
+{
+ return 0;
+}
+
+#endif /* HAVE_GPGME */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/pgp.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,29 @@
+#ifndef __MCABBER_PGP_H__
+#define __MCABBER_PGP_H__ 1
+
+#include <mcabber/config.h>
+
+#ifdef HAVE_GPGME
+
+#define GPGME_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_USER_1
+#include <gpgme.h>
+
+int gpg_init(const char *priv_key, const char *passphrase);
+void gpg_terminate(void);
+void gpg_set_passphrase(const char *passphrase);
+void gpg_set_private_key(const char *priv_keyid);
+char *gpg_verify(const char *gpg_data, const char *text,
+ gpgme_sigsum_t *sigsum);
+char *gpg_sign(const char *gpg_data);
+char *gpg_decrypt(const char *gpg_data);
+char *gpg_encrypt(const char *gpg_data, const char *keyid);
+
+int gpg_test_passphrase(void);
+
+#endif /* HAVE_GPGME */
+
+int gpg_enabled(void);
+
+#endif /* __MCABBER_PGP_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/roster.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,1624 @@
+/*
+ * roster.c -- Local roster implementation
+ *
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 <string.h>
+
+#include "roster.h"
+#include "utils.h"
+#include "hooks.h"
+
+extern void hlog_save_state(void);
+
+char *strrole[] = { /* Should match enum in roster.h */
+ "none",
+ "moderator",
+ "participant",
+ "visitor"
+};
+
+char *straffil[] = { /* Should match enum in roster.h */
+ "none",
+ "owner",
+ "admin",
+ "member",
+ "outcast"
+};
+
+char *strprintstatus[] = { /* Should match enum in roster.h */
+ "default",
+ "none",
+ "in_and_out",
+ "all"
+};
+
+char *strautowhois[] = { /* Should match enum in roster.h */
+ "default",
+ "off",
+ "on",
+};
+
+/* Resource structure */
+
+typedef struct {
+ gchar *name;
+ gchar prio;
+ enum imstatus status;
+ gchar *status_msg;
+ time_t status_timestamp;
+ enum imrole role;
+ enum imaffiliation affil;
+ gchar *realjid; /* for chatrooms, if buddy's real jid is known */
+ guint events;
+ char *caps;
+#ifdef JEP0022
+ struct jep0022 jep22;
+#endif
+#ifdef JEP0085
+ struct jep0085 jep85;
+#endif
+#ifdef HAVE_GPGME
+ struct pgp_data pgpdata;
+#endif
+} res;
+
+/* This is a private structure type for the roster */
+
+typedef struct {
+ gchar *name;
+ gchar *jid;
+ guint type;
+ enum subscr subscription;
+ GSList *resource;
+
+ /* For groupchats */
+ gchar *nickname;
+ gchar *topic;
+ guint inside_room;
+ guint print_status;
+ guint auto_whois;
+
+ /* on_server is TRUE if the item is present on the server roster */
+ guint on_server;
+
+ /* To keep track of last status message */
+ gchar *offline_status_message;
+
+ /* Flag used for the UI */
+ guint flags;
+
+ // list: user -> points to his group; group -> points to its users list
+ GSList *list;
+} roster;
+
+
+/* ### Variables ### */
+
+static guchar display_filter;
+static GSList *groups;
+static GSList *unread_list;
+static GHashTable *unread_jids;
+GList *buddylist;
+GList *current_buddy;
+GList *alternate_buddy;
+
+static roster roster_special;
+
+static int unread_jid_del(const char *jid);
+
+#define DFILTER_ALL 63
+#define DFILTER_ONLINE 62
+
+
+/* ### Initialization ### */
+
+void roster_init(void)
+{
+ roster_special.name = SPECIAL_BUFFER_STATUS_ID;
+ roster_special.type = ROSTER_TYPE_SPECIAL;
+}
+
+/* ### Resources functions ### */
+
+static inline void free_resource_data(res *p_res)
+{
+ if (!p_res)
+ return;
+ g_free((gchar*)p_res->status_msg);
+ g_free((gchar*)p_res->name);
+ g_free((gchar*)p_res->realjid);
+#ifdef JEP0022
+ g_free(p_res->jep22.last_msgid_sent);
+ g_free(p_res->jep22.last_msgid_rcvd);
+#endif
+#ifdef HAVE_GPGME
+ g_free(p_res->pgpdata.sign_keyid);
+#endif
+ g_free(p_res->caps);
+ g_free(p_res);
+}
+
+static void free_all_resources(GSList **reslist)
+{
+ GSList *lip;
+
+ for (lip = *reslist; lip ; lip = g_slist_next(lip))
+ free_resource_data((res*)lip->data);
+ // Free all nodes but the first (which is static)
+ g_slist_free(*reslist);
+ *reslist = NULL;
+}
+
+// Resources are sorted in ascending order
+static gint resource_compare_prio(res *a, res *b) {
+ //return (a->prio - b->prio);
+ if (a->prio < b->prio) return -1;
+ else return 1;
+}
+
+// get_resource(rost, resname)
+// Return a pointer to the resource with name resname, in rost's resources list
+// - if rost has no resources, return NULL
+// - if resname is defined, return the match or NULL
+// - if resname is NULL, the last resource is returned, currently
+// This could change in the future, because we should return the best one
+// (priority? last used? and fall back to the first resource)
+//
+static res *get_resource(roster *rost, const char *resname)
+{
+ GSList *p;
+ res *r = NULL;
+
+ for (p = rost->resource; p; p = g_slist_next(p)) {
+ r = p->data;
+ if (resname && !strcmp(r->name, resname))
+ return r;
+ }
+
+ // The last resource is one of the resources with the highest priority,
+ // however, we don't know if it is the more-recently-used.
+ if (!resname) return r;
+ return NULL;
+}
+
+// get_or_add_resource(rost, resname, priority)
+// - if there is a "resname" resource in rost's resources, return a pointer
+// on this resource
+// - if not, add the resource, set the name, and return a pointer on this
+// new resource
+static res *get_or_add_resource(roster *rost, const char *resname, gchar prio)
+{
+ GSList *p;
+ res *nres;
+
+ if (!resname) return NULL;
+
+ for (p = rost->resource; p; p = g_slist_next(p)) {
+ res *r = p->data;
+ if (!strcmp(r->name, resname)) {
+ if (prio != r->prio) {
+ r->prio = prio;
+ rost->resource = g_slist_sort(rost->resource,
+ (GCompareFunc)&resource_compare_prio);
+ }
+ return r;
+ }
+ }
+
+ // Resource not found
+ nres = g_new0(res, 1);
+ nres->name = g_strdup(resname);
+ nres->prio = prio;
+ rost->resource = g_slist_insert_sorted(rost->resource, nres,
+ (GCompareFunc)&resource_compare_prio);
+ return nres;
+}
+
+static void del_resource(roster *rost, const char *resname)
+{
+ GSList *p;
+ GSList *p_res_elt = NULL;
+ res *p_res;
+
+ if (!resname) return;
+
+ for (p = rost->resource; p; p = g_slist_next(p)) {
+ res *r = p->data;
+ if (!strcmp(r->name, resname))
+ p_res_elt = p;
+ }
+
+ if (!p_res_elt) return; // Resource not found
+
+ p_res = p_res_elt->data;
+
+ // Keep a copy of the status message when a buddy goes offline
+ if (g_slist_length(rost->resource) == 1) {
+ g_free(rost->offline_status_message);
+ rost->offline_status_message = p_res->status_msg;
+ p_res->status_msg = NULL;
+ }
+
+ // Free allocations and delete resource node
+ free_resource_data(p_res);
+ rost->resource = g_slist_delete_link(rost->resource, p_res_elt);
+ return;
+}
+
+
+/* ### Roster functions ### */
+
+static inline void free_roster_user_data(roster *roster_usr)
+{
+ if (!roster_usr)
+ return;
+ g_free((gchar*)roster_usr->jid);
+ g_free((gchar*)roster_usr->name);
+ g_free((gchar*)roster_usr->nickname);
+ g_free((gchar*)roster_usr->topic);
+ g_free((gchar*)roster_usr->offline_status_message);
+ free_all_resources(&roster_usr->resource);
+ g_free(roster_usr);
+}
+
+// Comparison function used to search in the roster (compares jids and types)
+static gint roster_compare_jid_type(roster *a, roster *b) {
+ if (! (a->type & b->type))
+ return -1; // arbitrary (but should be != 0, of course)
+ return strcasecmp(a->jid, b->jid);
+}
+
+// Comparison function used to search in the roster (compares names and types)
+static gint roster_compare_name_type(roster *a, roster *b) {
+ if (! (a->type & b->type))
+ return -1; // arbitrary (but should be != 0, of course)
+ return strcmp(a->name, b->name);
+}
+
+// Comparison function used to sort the roster (by name)
+static gint roster_compare_name(roster *a, roster *b) {
+ return strcmp(a->name, b->name);
+}
+
+// Finds a roster element (user, group, agent...), by jid or name
+// If roster_type is 0, returns match of any type.
+// Returns the roster GSList element, or NULL if jid/name not found
+GSList *roster_find(const char *jidname, enum findwhat type, guint roster_type)
+{
+ GSList *sl_roster_elt = groups;
+ GSList *resource;
+ roster sample;
+ GCompareFunc comp;
+
+ if (!jidname) return NULL;
+
+ if (!roster_type)
+ roster_type = ROSTER_TYPE_USER | ROSTER_TYPE_ROOM |
+ ROSTER_TYPE_AGENT | ROSTER_TYPE_GROUP;
+
+ sample.type = roster_type;
+ if (type == jidsearch) {
+ sample.jid = (gchar*)jidname;
+ comp = (GCompareFunc)&roster_compare_jid_type;
+ } else if (type == namesearch) {
+ sample.name = (gchar*)jidname;
+ comp = (GCompareFunc)&roster_compare_name_type;
+ } else
+ return NULL; // Should not happen...
+
+ while (sl_roster_elt) {
+ roster *roster_elt = (roster*)sl_roster_elt->data;
+ if (roster_type & ROSTER_TYPE_GROUP) {
+ if ((type == namesearch) && !strcmp(jidname, roster_elt->name))
+ return sl_roster_elt;
+ }
+ resource = g_slist_find_custom(roster_elt->list, &sample, comp);
+ if (resource) return resource;
+ sl_roster_elt = g_slist_next(sl_roster_elt);
+ }
+ return NULL;
+}
+
+// Returns pointer to new group, or existing group with that name
+GSList *roster_add_group(const char *name)
+{
+ roster *roster_grp;
+ GSList *p_group;
+
+ // #1 Check name doesn't already exist
+ p_group = roster_find(name, namesearch, ROSTER_TYPE_GROUP);
+ if (!p_group) {
+ // #2 Create the group node
+ roster_grp = g_new0(roster, 1);
+ roster_grp->name = g_strdup(name);
+ roster_grp->type = ROSTER_TYPE_GROUP;
+ // #3 Insert (sorted)
+ groups = g_slist_insert_sorted(groups, roster_grp,
+ (GCompareFunc)&roster_compare_name);
+ p_group = roster_find(name, namesearch, ROSTER_TYPE_GROUP);
+ }
+ return p_group;
+}
+
+// Returns a pointer to the new user, or existing user with that name
+// Note: if onserver is -1, the flag won't be changed.
+GSList *roster_add_user(const char *jid, const char *name, const char *group,
+ guint type, enum subscr esub, gint onserver)
+{
+ roster *roster_usr;
+ roster *my_group;
+ GSList *slist;
+
+ if ((type != ROSTER_TYPE_USER) &&
+ (type != ROSTER_TYPE_ROOM) &&
+ (type != ROSTER_TYPE_AGENT)) {
+ // XXX Error message?
+ return NULL;
+ }
+
+ // Let's be arbitrary: default group has an empty name ("").
+ if (!group) group = "";
+
+ // #1 Check this user doesn't already exist
+ slist = roster_find(jid, jidsearch, 0);
+ if (slist) {
+ char *oldgroupname;
+ // That's an update
+ roster_usr = slist->data;
+ roster_usr->subscription = esub;
+ if (onserver >= 0)
+ buddy_setonserverflag(slist->data, onserver);
+ if (name)
+ buddy_setname(slist->data, (char*)name);
+ // Let's check if the group name has changed
+ oldgroupname = ((roster*)((GSList*)roster_usr->list)->data)->name;
+ if (group && strcmp(oldgroupname, group)) {
+ buddy_setgroup(slist->data, (char*)group);
+ // Note: buddy_setgroup() updates the user lists so we cannot
+ // use slist anymore.
+ return roster_find(jid, jidsearch, 0);
+ }
+ return slist;
+ }
+ // #2 add group if necessary
+ slist = roster_add_group(group);
+ if (!slist) return NULL;
+ my_group = (roster*)slist->data;
+ // #3 Create user node
+ roster_usr = g_new0(roster, 1);
+ roster_usr->jid = g_strdup(jid);
+ if (name) {
+ roster_usr->name = g_strdup(name);
+ } else {
+ gchar *p, *str = g_strdup(jid);
+ p = strchr(str, JID_RESOURCE_SEPARATOR);
+ if (p) *p = '\0';
+ roster_usr->name = g_strdup(str);
+ g_free(str);
+ }
+ if (unread_jid_del(jid)) {
+ roster_usr->flags |= ROSTER_FLAG_MSG;
+ // Append the roster_usr to unread_list
+ unread_list = g_slist_append(unread_list, roster_usr);
+ }
+ roster_usr->type = type;
+ roster_usr->subscription = esub;
+ roster_usr->list = slist; // (my_group SList element)
+ if (onserver == 1)
+ roster_usr->on_server = TRUE;
+ // #4 Insert node (sorted)
+ my_group->list = g_slist_insert_sorted(my_group->list, roster_usr,
+ (GCompareFunc)&roster_compare_name);
+ return roster_find(jid, jidsearch, type);
+}
+
+// Removes user (jid) from roster, frees allocated memory
+void roster_del_user(const char *jid)
+{
+ GSList *sl_user, *sl_group;
+ GSList **sl_group_listptr;
+ roster *roster_usr;
+ GSList *node;
+
+ sl_user = roster_find(jid, jidsearch,
+ ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM);
+ if (sl_user == NULL)
+ return;
+ roster_usr = (roster*)sl_user->data;
+
+ // Remove (if present) from unread messages list
+ node = g_slist_find(unread_list, roster_usr);
+ if (node) unread_list = g_slist_delete_link(unread_list, node);
+ // If there is a pending unread message, keep track of it
+ if (roster_usr->flags & ROSTER_FLAG_MSG)
+ unread_jid_add(roster_usr->jid);
+
+ sl_group = roster_usr->list;
+
+ // Let's free roster_usr memory (jid, name, status message...)
+ free_roster_user_data(roster_usr);
+
+ // That's a little complex, we need to dereference twice
+ sl_group_listptr = &((roster*)(sl_group->data))->list;
+ *sl_group_listptr = g_slist_delete_link(*sl_group_listptr, sl_user);
+
+ // We need to rebuild the list
+ if (current_buddy)
+ buddylist_build();
+ // TODO What we could do, too, is to check if the deleted node is
+ // current_buddy, in which case we could move current_buddy to the
+ // previous (or next) node.
+}
+
+// Free all roster data and call buddylist_build() to free the buddylist.
+void roster_free(void)
+{
+ GSList *sl_grp = groups;
+
+ // Free unread_list
+ if (unread_list) {
+ g_slist_free(unread_list);
+ unread_list = NULL;
+ }
+
+ // Walk through groups
+ while (sl_grp) {
+ roster *roster_grp = (roster*)sl_grp->data;
+ GSList *sl_usr = roster_grp->list;
+ // Walk through this group users
+ while (sl_usr) {
+ roster *roster_usr = (roster*)sl_usr->data;
+ // If there is a pending unread message, keep track of it
+ if (roster_usr->flags & ROSTER_FLAG_MSG)
+ unread_jid_add(roster_usr->jid);
+ // Free roster_usr data (jid, name, status message...)
+ free_roster_user_data(roster_usr);
+ sl_usr = g_slist_next(sl_usr);
+ }
+ // Free group's users list
+ if (roster_grp->list)
+ g_slist_free(roster_grp->list);
+ // Free group's name and jid
+ g_free((gchar*)roster_grp->jid);
+ g_free((gchar*)roster_grp->name);
+ g_free(roster_grp);
+ sl_grp = g_slist_next(sl_grp);
+ }
+ // Free groups list
+ if (groups) {
+ g_slist_free(groups);
+ groups = NULL;
+ // Update (i.e. free) buddylist
+ if (buddylist)
+ buddylist_build();
+ }
+}
+
+// roster_setstatus()
+// Note: resname, role, affil and realjid are for room members only
+void roster_setstatus(const char *jid, const char *resname, gchar prio,
+ enum imstatus bstat, const char *status_msg,
+ time_t status_time,
+ enum imrole role, enum imaffiliation affil,
+ const char *realjid)
+{
+ GSList *sl_user;
+ roster *roster_usr;
+ res *p_res;
+
+ sl_user = roster_find(jid, jidsearch,
+ ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
+ // If we can't find it, we add it
+ if (sl_user == NULL)
+ sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER,
+ sub_none, -1);
+
+ // If there is no resource name, we can leave now
+ if (!resname) return;
+
+ roster_usr = (roster*)sl_user->data;
+
+ // New or updated resource
+ p_res = get_or_add_resource(roster_usr, resname, prio);
+ p_res->status = bstat;
+ if (p_res->status_msg) {
+ g_free((gchar*)p_res->status_msg);
+ p_res->status_msg = NULL;
+ }
+ if (status_msg)
+ p_res->status_msg = g_strdup(status_msg);
+ if (!status_time)
+ time(&status_time);
+ p_res->status_timestamp = status_time;
+
+ p_res->role = role;
+ p_res->affil = affil;
+
+ if (p_res->realjid) {
+ g_free((gchar*)p_res->realjid);
+ p_res->realjid = NULL;
+ }
+ if (realjid)
+ p_res->realjid = g_strdup(realjid);
+
+ // If bstat is offline, we MUST delete the resource, actually
+ if (bstat == offline) {
+ del_resource(roster_usr, resname);
+ return;
+ }
+}
+
+// roster_setflags()
+// Set one or several flags to value (TRUE/FALSE)
+void roster_setflags(const char *jid, guint flags, guint value)
+{
+ GSList *sl_user;
+ roster *roster_usr;
+
+ sl_user = roster_find(jid, jidsearch,
+ ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
+ if (sl_user == NULL)
+ return;
+
+ roster_usr = (roster*)sl_user->data;
+ if (value)
+ roster_usr->flags |= flags;
+ else
+ roster_usr->flags &= ~flags;
+}
+
+// roster_msg_setflag()
+// Set the ROSTER_FLAG_MSG to the given value for the given jid.
+// It will update the buddy's group message flag.
+// Update the unread messages list too.
+void roster_msg_setflag(const char *jid, guint special, guint value)
+{
+ GSList *sl_user;
+ roster *roster_usr, *roster_grp;
+ int new_roster_item = FALSE;
+ guint unread_list_modified = FALSE;
+
+ if (special) {
+ //sl_user = roster_find(jid, namesearch, ROSTER_TYPE_SPECIAL);
+ //if (!sl_user) return;
+ //roster_usr = (roster*)sl_user->data;
+ roster_usr = &roster_special;
+ if (value) {
+ if (!(roster_usr->flags & ROSTER_FLAG_MSG))
+ unread_list_modified = TRUE;
+ roster_usr->flags |= ROSTER_FLAG_MSG;
+ // Append the roster_usr to unread_list, but avoid duplicates
+ if (!g_slist_find(unread_list, roster_usr))
+ unread_list = g_slist_append(unread_list, roster_usr);
+ } else {
+ if (roster_usr->flags & ROSTER_FLAG_MSG)
+ unread_list_modified = TRUE;
+ roster_usr->flags &= ~ROSTER_FLAG_MSG;
+ if (unread_list) {
+ GSList *node = g_slist_find(unread_list, roster_usr);
+ if (node)
+ unread_list = g_slist_delete_link(unread_list, node);
+ }
+ }
+ goto roster_msg_setflag_return;
+ }
+
+ sl_user = roster_find(jid, jidsearch,
+ ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
+ // If we can't find it, we add it
+ if (sl_user == NULL) {
+ sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER, sub_none, -1);
+ new_roster_item = TRUE;
+ }
+
+ roster_usr = (roster*)sl_user->data;
+ roster_grp = (roster*)roster_usr->list->data;
+ if (value) {
+ if (!(roster_usr->flags & ROSTER_FLAG_MSG))
+ unread_list_modified = TRUE;
+ // Message flag is TRUE. This is easy, we just have to set both flags
+ // to TRUE...
+ roster_usr->flags |= ROSTER_FLAG_MSG;
+ roster_grp->flags |= ROSTER_FLAG_MSG; // group
+ // Append the roster_usr to unread_list, but avoid duplicates
+ if (!g_slist_find(unread_list, roster_usr))
+ unread_list = g_slist_append(unread_list, roster_usr);
+ } else {
+ // Message flag is FALSE.
+ guint msg = FALSE;
+ if (roster_usr->flags & ROSTER_FLAG_MSG)
+ unread_list_modified = TRUE;
+ roster_usr->flags &= ~ROSTER_FLAG_MSG;
+ if (unread_list) {
+ GSList *node = g_slist_find(unread_list, roster_usr);
+ if (node)
+ unread_list = g_slist_delete_link(unread_list, node);
+ }
+ // For the group value we need to watch all buddies in this group;
+ // if one is flagged, then the group will be flagged.
+ // I will re-use sl_user and roster_usr here, as they aren't used
+ // anymore.
+ sl_user = roster_grp->list;
+ while (sl_user) {
+ roster_usr = (roster*)sl_user->data;
+ if (roster_usr->flags & ROSTER_FLAG_MSG) {
+ msg = TRUE;
+ break;
+ }
+ sl_user = g_slist_next(sl_user);
+ }
+ if (!msg)
+ roster_grp->flags &= ~ROSTER_FLAG_MSG;
+ else
+ roster_grp->flags |= ROSTER_FLAG_MSG;
+ // Actually the "else" part is useless, because the group
+ // ROSTER_FLAG_MSG should already be set...
+ }
+
+ if (buddylist && (new_roster_item || !g_list_find(buddylist, roster_usr)))
+ buddylist_build();
+
+roster_msg_setflag_return:
+ if (unread_list_modified) {
+ guint unread_count = g_slist_length(unread_list);
+ hlog_save_state();
+ /* Call external command */
+ hk_ext_cmd("", 'U', (guchar)MIN(255, unread_count), NULL);
+ }
+}
+
+const char *roster_getname(const char *jid)
+{
+ GSList *sl_user;
+ roster *roster_usr;
+
+ sl_user = roster_find(jid, jidsearch,
+ ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
+ if (sl_user == NULL)
+ return NULL; // Not in the roster...
+
+ roster_usr = (roster*)sl_user->data;
+ return roster_usr->name;
+}
+
+const char *roster_getnickname(const char *jid)
+{
+ GSList *sl_user;
+ roster *roster_usr;
+
+ sl_user = roster_find(jid, jidsearch,
+ ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
+ if (sl_user == NULL)
+ return NULL; // Not in the roster...
+
+ roster_usr = (roster*)sl_user->data;
+ return roster_usr->nickname;
+}
+
+void roster_settype(const char *jid, guint type)
+{
+ GSList *sl_user;
+ roster *roster_usr;
+
+ if ((sl_user = roster_find(jid, jidsearch, 0)) == NULL)
+ return;
+
+ roster_usr = (roster*)sl_user->data;
+ roster_usr->type = type;
+}
+
+enum imstatus roster_getstatus(const char *jid, const char *resname)
+{
+ GSList *sl_user;
+ roster *roster_usr;
+ res *p_res;
+
+ sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
+ if (sl_user == NULL)
+ return offline; // Not in the roster, anyway...
+
+ roster_usr = (roster*)sl_user->data;
+ p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->status;
+ return offline;
+}
+
+const char *roster_getstatusmsg(const char *jid, const char *resname)
+{
+ GSList *sl_user;
+ roster *roster_usr;
+ res *p_res;
+
+ sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
+ if (sl_user == NULL)
+ return NULL; // Not in the roster, anyway...
+
+ roster_usr = (roster*)sl_user->data;
+ p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->status_msg;
+ return roster_usr->offline_status_message;
+}
+
+guint roster_gettype(const char *jid)
+{
+ GSList *sl_user;
+ roster *roster_usr;
+
+ if ((sl_user = roster_find(jid, jidsearch, 0)) == NULL)
+ return 0;
+
+ roster_usr = (roster*)sl_user->data;
+ return roster_usr->type;
+}
+
+guint roster_getsubscription(const char *jid)
+{
+ GSList *sl_user;
+ roster *roster_usr;
+
+ if ((sl_user = roster_find(jid, jidsearch, 0)) == NULL)
+ return 0;
+
+ roster_usr = (roster*)sl_user->data;
+ return roster_usr->subscription;
+}
+
+// roster_unsubscribed()
+// We have lost buddy's presence updates; this function clears the status
+// message, sets the buddy offline and frees the resources
+void roster_unsubscribed(const char *jid)
+{
+ GSList *sl_user;
+ roster *roster_usr;
+
+ sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
+ if (sl_user == NULL)
+ return;
+
+ roster_usr = (roster*)sl_user->data;
+ free_all_resources(&roster_usr->resource);
+}
+
+
+/* ### BuddyList functions ### */
+
+// buddylist_set_hide_offline_buddies(hide)
+// "hide" values: 1=hide 0=show_all -1=invert
+void buddylist_set_hide_offline_buddies(int hide)
+{
+ if (hide < 0) { // NEG (invert)
+ if (display_filter == DFILTER_ALL)
+ display_filter = DFILTER_ONLINE;
+ else
+ display_filter = DFILTER_ALL;
+ } else if (hide == 0) { // FALSE (don't hide -- andfo_)
+ display_filter = DFILTER_ALL;
+ } else { // TRUE (hide -- andfo)
+ display_filter = DFILTER_ONLINE;
+ }
+}
+
+int buddylist_isset_filter(void)
+{
+ return (display_filter != DFILTER_ALL);
+}
+
+int buddylist_is_status_filtered(enum imstatus status)
+{
+ return display_filter & (1 << status);
+}
+
+void buddylist_set_filter(guchar filter)
+{
+ display_filter = filter;
+}
+
+guchar buddylist_get_filter(void)
+{
+ return display_filter;
+}
+
+// buddylist_build()
+// Creates the buddylist from the roster entries.
+void buddylist_build(void)
+{
+ GSList *sl_roster_elt = groups;
+ roster *roster_elt;
+ roster *roster_current_buddy = NULL;
+ roster *roster_alternate_buddy = NULL;
+ int shrunk_group;
+
+ // We need to remember which buddy is selected.
+ if (current_buddy)
+ roster_current_buddy = BUDDATA(current_buddy);
+ current_buddy = NULL;
+ if (alternate_buddy)
+ roster_alternate_buddy = BUDDATA(alternate_buddy);
+ alternate_buddy = NULL;
+
+ // Destroy old buddylist
+ if (buddylist) {
+ g_list_free(buddylist);
+ buddylist = NULL;
+ }
+
+ buddylist = g_list_append(buddylist, &roster_special);
+
+ // Create the new list
+ while (sl_roster_elt) {
+ GSList *sl_roster_usrelt;
+ roster *roster_usrelt;
+ guint pending_group = TRUE;
+ roster_elt = (roster*) sl_roster_elt->data;
+
+ shrunk_group = roster_elt->flags & ROSTER_FLAG_HIDE;
+
+ sl_roster_usrelt = roster_elt->list;
+ while (sl_roster_usrelt) {
+ roster_usrelt = (roster*) sl_roster_usrelt->data;
+
+ // Buddy will be added if either:
+ // - buddy's status matches the display_filter
+ // - buddy has a lock (for example the buddy window is currently open)
+ // - buddy has a pending (non-read) message
+ // - group isn't hidden (shrunk)
+ // - this is the current_buddy
+ if (roster_usrelt == roster_current_buddy ||
+ buddylist_is_status_filtered(buddy_getstatus((gpointer)roster_usrelt,
+ NULL)) ||
+ (buddy_getflags((gpointer)roster_usrelt) &
+ (ROSTER_FLAG_LOCK | ROSTER_FLAG_USRLOCK | ROSTER_FLAG_MSG))) {
+ // This user should be added. Maybe the group hasn't been added yet?
+ if (pending_group) {
+ // It hasn't been done yet
+ buddylist = g_list_append(buddylist, roster_elt);
+ pending_group = FALSE;
+ }
+ // Add user
+ // XXX Should we add the user if there is a message and
+ // the group is shrunk? If so, we'd need to check LOCK flag too,
+ // perhaps...
+ if (!shrunk_group)
+ buddylist = g_list_append(buddylist, roster_usrelt);
+ }
+
+ sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
+ }
+ sl_roster_elt = g_slist_next(sl_roster_elt);
+ }
+
+ // Check if we can find our saved current_buddy...
+ if (roster_current_buddy)
+ current_buddy = g_list_find(buddylist, roster_current_buddy);
+ if (roster_alternate_buddy)
+ alternate_buddy = g_list_find(buddylist, roster_alternate_buddy);
+ // current_buddy initialization
+ if (!current_buddy || (g_list_position(buddylist, current_buddy) == -1))
+ current_buddy = g_list_first(buddylist);
+}
+
+// buddy_hide_group(roster, hide)
+// "hide" values: 1=hide 0=show_all -1=invert
+void buddy_hide_group(gpointer rosterdata, int hide)
+{
+ roster *roster_usr = rosterdata;
+ if (hide > 0) // TRUE (hide)
+ roster_usr->flags |= ROSTER_FLAG_HIDE;
+ else if (hide < 0) // NEG (invert)
+ roster_usr->flags ^= ROSTER_FLAG_HIDE;
+ else // FALSE (don't hide)
+ roster_usr->flags &= ~ROSTER_FLAG_HIDE;
+}
+
+const char *buddy_getjid(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ if (!rosterdata)
+ return NULL;
+ return roster_usr->jid;
+}
+
+// buddy_setgroup()
+// Change the group of current buddy
+//
+// Note: buddy_setgroup() updates the user lists.
+//
+void buddy_setgroup(gpointer rosterdata, char *newgroupname)
+{
+ roster *roster_usr = rosterdata;
+ GSList **sl_group;
+ GSList *sl_newgroup;
+ roster *my_newgroup;
+
+ // A group has no group :)
+ if (roster_usr->type & ROSTER_TYPE_GROUP) return;
+
+ // Add newgroup if necessary
+ if (!newgroupname) newgroupname = "";
+ sl_newgroup = roster_add_group(newgroupname);
+ if (!sl_newgroup) return;
+ my_newgroup = (roster*)sl_newgroup->data;
+
+ // Remove the buddy from current group
+ sl_group = &((roster*)((GSList*)roster_usr->list)->data)->list;
+ *sl_group = g_slist_remove(*sl_group, rosterdata);
+
+ // Remove old group if it is empty
+ if (!*sl_group) {
+ roster *roster_grp = (roster*)((GSList*)roster_usr->list)->data;
+ g_free((gchar*)roster_grp->jid);
+ g_free((gchar*)roster_grp->name);
+ g_free(roster_grp);
+ groups = g_slist_remove(groups, roster_grp);
+ }
+
+ // Add the buddy to its new group
+ roster_usr->list = sl_newgroup; // (my_newgroup SList element)
+ my_newgroup->list = g_slist_insert_sorted(my_newgroup->list, roster_usr,
+ (GCompareFunc)&roster_compare_name);
+
+ buddylist_build();
+}
+
+void buddy_setname(gpointer rosterdata, char *newname)
+{
+ roster *roster_usr = rosterdata;
+ GSList **sl_group;
+
+ // TODO For groups, we need to check for unicity
+ // However, renaming a group boils down to moving all its buddies to
+ // another group, so calling this function is not really necessary...
+ if (roster_usr->type & ROSTER_TYPE_GROUP) return;
+
+ if (roster_usr->name) {
+ g_free((gchar*)roster_usr->name);
+ roster_usr->name = NULL;
+ }
+ if (newname)
+ roster_usr->name = g_strdup(newname);
+
+ // We need to resort the group list
+ sl_group = &((roster*)((GSList*)roster_usr->list)->data)->list;
+ *sl_group = g_slist_sort(*sl_group, (GCompareFunc)&roster_compare_name);
+
+ buddylist_build();
+}
+
+const char *buddy_getname(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ return roster_usr->name;
+}
+
+// buddy_setnickname(buddy, newnickname)
+// Only for chatrooms
+void buddy_setnickname(gpointer rosterdata, const char *newname)
+{
+ roster *roster_usr = rosterdata;
+
+ if (!(roster_usr->type & ROSTER_TYPE_ROOM)) return; // XXX Error message?
+
+ if (roster_usr->nickname) {
+ g_free((gchar*)roster_usr->nickname);
+ roster_usr->nickname = NULL;
+ }
+ if (newname)
+ roster_usr->nickname = g_strdup(newname);
+}
+
+const char *buddy_getnickname(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ return roster_usr->nickname;
+}
+
+// buddy_setinsideroom(buddy, inside)
+// Only for chatrooms
+void buddy_setinsideroom(gpointer rosterdata, guint inside)
+{
+ roster *roster_usr = rosterdata;
+
+ if (!(roster_usr->type & ROSTER_TYPE_ROOM)) return;
+
+ roster_usr->inside_room = inside;
+}
+
+guint buddy_getinsideroom(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ return roster_usr->inside_room;
+}
+
+// buddy_settopic(buddy, newtopic)
+// Only for chatrooms
+void buddy_settopic(gpointer rosterdata, const char *newtopic)
+{
+ roster *roster_usr = rosterdata;
+
+ if (!(roster_usr->type & ROSTER_TYPE_ROOM)) return;
+
+ if (roster_usr->topic) {
+ g_free((gchar*)roster_usr->topic);
+ roster_usr->topic = NULL;
+ }
+ if (newtopic)
+ roster_usr->topic = g_strdup(newtopic);
+}
+
+const char *buddy_gettopic(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ return roster_usr->topic;
+}
+
+void buddy_setprintstatus(gpointer rosterdata, enum room_printstatus pstatus)
+{
+ roster *roster_usr = rosterdata;
+ roster_usr->print_status = pstatus;
+}
+
+enum room_printstatus buddy_getprintstatus(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ return roster_usr->print_status;
+}
+
+void buddy_setautowhois(gpointer rosterdata, enum room_autowhois awhois)
+{
+ roster *roster_usr = rosterdata;
+ roster_usr->auto_whois = awhois;
+}
+
+enum room_autowhois buddy_getautowhois(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ return roster_usr->auto_whois;
+}
+
+// buddy_getgroupname()
+// Returns a pointer on buddy's group name.
+const char *buddy_getgroupname(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+
+ if (roster_usr->type & ROSTER_TYPE_GROUP)
+ return roster_usr->name;
+
+ if (roster_usr->type & ROSTER_TYPE_SPECIAL)
+ return NULL;
+
+ // This is a user
+ return ((roster*)((GSList*)roster_usr->list)->data)->name;
+}
+
+// buddy_getgroup()
+// Returns a pointer on buddy's group.
+gpointer buddy_getgroup(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+
+ if (roster_usr->type & ROSTER_TYPE_GROUP)
+ return rosterdata;
+
+ if (roster_usr->type & ROSTER_TYPE_SPECIAL)
+ return NULL;
+
+ // This is a user
+ return (gpointer)((GSList*)roster_usr->list)->data;
+}
+
+void buddy_settype(gpointer rosterdata, guint type)
+{
+ roster *roster_usr = rosterdata;
+ roster_usr->type = type;
+}
+
+guint buddy_gettype(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ return roster_usr->type;
+}
+
+guint buddy_getsubscription(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ return roster_usr->subscription;
+}
+
+enum imstatus buddy_getstatus(gpointer rosterdata, const char *resname)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->status;
+ return offline;
+}
+
+const char *buddy_getstatusmsg(gpointer rosterdata, const char *resname)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->status_msg;
+ return roster_usr->offline_status_message;
+}
+
+time_t buddy_getstatustime(gpointer rosterdata, const char *resname)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->status_timestamp;
+ return 0;
+}
+
+gchar buddy_getresourceprio(gpointer rosterdata, const char *resname)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->prio;
+ return 0;
+}
+
+guint buddy_resource_getevents(gpointer rosterdata, const char *resname)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->events;
+ return ROSTER_EVENT_NONE;
+}
+
+void buddy_resource_setevents(gpointer rosterdata, const char *resname,
+ guint events)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ p_res->events = events;
+}
+
+char *buddy_resource_getcaps(gpointer rosterdata, const char *resname)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->caps;
+ return NULL;
+}
+
+void buddy_resource_setcaps(gpointer rosterdata, const char *resname,
+ const char *caps)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res) {
+ g_free(p_res->caps);
+ p_res->caps = g_strdup(caps);
+ }
+}
+
+struct jep0022 *buddy_resource_jep22(gpointer rosterdata, const char *resname)
+{
+#ifdef JEP0022
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return &p_res->jep22;
+#endif
+ return NULL;
+}
+
+struct jep0085 *buddy_resource_jep85(gpointer rosterdata, const char *resname)
+{
+#ifdef JEP0085
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return &p_res->jep85;
+#endif
+ return NULL;
+}
+
+struct pgp_data *buddy_resource_pgp(gpointer rosterdata, const char *resname)
+{
+#ifdef HAVE_GPGME
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return &p_res->pgpdata;
+#endif
+ return NULL;
+}
+
+enum imrole buddy_getrole(gpointer rosterdata, const char *resname)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->role;
+ return role_none;
+}
+
+enum imaffiliation buddy_getaffil(gpointer rosterdata, const char *resname)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->affil;
+ return affil_none;
+}
+
+const char *buddy_getrjid(gpointer rosterdata, const char *resname)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res)
+ return p_res->realjid;
+ return NULL;
+}
+
+// buddy_getresources(roster_data)
+// Return a singly-linked-list of resource names
+// Note: the caller should free the list (and data) after use
+// If roster_data is null, the current buddy is selected
+GSList *buddy_getresources(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ GSList *reslist = NULL, *lp;
+
+ if (!roster_usr) {
+ if (!current_buddy) return NULL;
+ roster_usr = BUDDATA(current_buddy);
+ }
+ for (lp = roster_usr->resource; lp; lp = g_slist_next(lp))
+ reslist = g_slist_append(reslist, g_strdup(((res*)lp->data)->name));
+
+ return reslist;
+}
+
+// buddy_getresources_locale(roster_data)
+// Same as buddy_getresources() but names are converted to user's locale
+// Note: the caller should free the list (and data) after use
+GSList *buddy_getresources_locale(gpointer rosterdata)
+{
+ GSList *reslist, *lp;
+
+ reslist = buddy_getresources(rosterdata);
+ // Convert each item to UI's locale
+ for (lp = reslist; lp; lp = g_slist_next(lp)) {
+ gchar *oldname = lp->data;
+ lp->data = from_utf8(oldname);
+ if (lp->data)
+ g_free(oldname);
+ else
+ lp->data = oldname;
+ }
+ return reslist;
+}
+
+/*
+// buddy_isresource(roster_data)
+// Return true if there is at least one resource
+// (which means, for a room, that it isn't empty)
+int buddy_isresource(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ if (!roster_usr)
+ return FALSE;
+ if (roster_usr->resource)
+ return TRUE;
+ return FALSE;
+}
+*/
+
+// buddy_resource_setname(roster_data, oldname, newname)
+// Useful for nickname change in a MUC room
+void buddy_resource_setname(gpointer rosterdata, const char *resname,
+ const char *newname)
+{
+ roster *roster_usr = rosterdata;
+ res *p_res = get_resource(roster_usr, resname);
+ if (p_res) {
+ if (p_res->name) {
+ g_free((gchar*)p_res->name);
+ p_res->name = NULL;
+ }
+ if (newname)
+ p_res->name = g_strdup(newname);
+ }
+}
+
+// buddy_del_all_resources()
+// Remove all resources from the specified buddy
+void buddy_del_all_resources(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+
+ while (roster_usr->resource) {
+ res *r = roster_usr->resource->data;
+ del_resource(roster_usr, r->name);
+ }
+}
+
+// buddy_setflags()
+// Set one or several flags to value (TRUE/FALSE)
+void buddy_setflags(gpointer rosterdata, guint flags, guint value)
+{
+ roster *roster_usr = rosterdata;
+ if (value)
+ roster_usr->flags |= flags;
+ else
+ roster_usr->flags &= ~flags;
+}
+
+guint buddy_getflags(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ return roster_usr->flags;
+}
+
+// buddy_setonserverflag()
+// Set the on_server flag
+void buddy_setonserverflag(gpointer rosterdata, guint onserver)
+{
+ roster *roster_usr = rosterdata;
+ roster_usr->on_server = onserver;
+}
+
+guint buddy_getonserverflag(gpointer rosterdata)
+{
+ roster *roster_usr = rosterdata;
+ return roster_usr->on_server;
+}
+
+// buddy_search_jid(jid)
+// Look for a buddy with specified jid.
+// Search begins at buddylist; if no match is found in the the buddylist,
+// return NULL;
+GList *buddy_search_jid(const char *jid)
+{
+ GList *buddy;
+ roster *roster_usr;
+
+ if (!buddylist) return NULL;
+
+ for (buddy = buddylist; buddy; buddy = g_list_next(buddy)) {
+ roster_usr = (roster*)buddy->data;
+ if (roster_usr->jid && !strcasecmp(roster_usr->jid, jid))
+ return buddy;
+ }
+ return NULL;
+}
+
+// buddy_search(string)
+// Look for a buddy whose name or jid contains string.
+// Search begins at current_buddy; if no match is found in the the buddylist,
+// return NULL;
+GList *buddy_search(char *string)
+{
+ GList *buddy = current_buddy;
+ roster *roster_usr;
+ if (!buddylist || !current_buddy) return NULL;
+ for (;;) {
+ gchar *jid_locale, *name_locale;
+ char *found = NULL;
+
+ buddy = g_list_next(buddy);
+ if (!buddy)
+ buddy = buddylist;
+
+ roster_usr = (roster*)buddy->data;
+
+ jid_locale = from_utf8(roster_usr->jid);
+ if (jid_locale) {
+ found = strcasestr(jid_locale, string);
+ g_free(jid_locale);
+ if (found)
+ return buddy;
+ }
+ name_locale = from_utf8(roster_usr->name);
+ if (name_locale) {
+ found = strcasestr(name_locale, string);
+ g_free(name_locale);
+ if (found)
+ return buddy;
+ }
+
+ if (buddy == current_buddy)
+ return NULL; // Back to the beginning, and no match found
+ }
+}
+
+// foreach_buddy(roster_type, pfunction, param)
+// Call pfunction(buddy, param) for each buddy from the roster with
+// type matching roster_type.
+void foreach_buddy(guint roster_type,
+ void (*pfunc)(gpointer rosterdata, void *param),
+ void *param)
+{
+ GSList *sl_roster_elt = groups;
+ roster *roster_elt;
+ GSList *sl_roster_usrelt;
+ roster *roster_usrelt;
+
+ while (sl_roster_elt) { // group list loop
+ roster_elt = (roster*) sl_roster_elt->data;
+ if (roster_elt->type & ROSTER_TYPE_SPECIAL)
+ continue; // Skip special items
+ sl_roster_usrelt = roster_elt->list;
+ while (sl_roster_usrelt) { // user list loop
+ roster_usrelt = (roster*) sl_roster_usrelt->data;
+
+ if (roster_usrelt->type & roster_type)
+ pfunc(roster_usrelt, param);
+
+ sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
+ }
+ sl_roster_elt = g_slist_next(sl_roster_elt);
+ }
+}
+
+// foreach_group_member(group, pfunction, param)
+// Call pfunction(buddy, param) for each buddy in the specified group.
+void foreach_group_member(gpointer groupdata,
+ void (*pfunc)(gpointer rosterdata, void *param),
+ void *param)
+{
+ roster *roster_elt;
+ GSList *sl_roster_usrelt;
+ roster *roster_usrelt;
+
+ roster_elt = groupdata;
+
+ if (!(roster_elt->type & ROSTER_TYPE_GROUP))
+ return;
+
+ sl_roster_usrelt = roster_elt->list;
+ while (sl_roster_usrelt) { // user list loop
+ roster_usrelt = (roster*) sl_roster_usrelt->data;
+
+ pfunc(roster_usrelt, param);
+ sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
+ }
+}
+
+// compl_list(type)
+// Returns a list of jid's or groups. (For commands completion)
+// type: ROSTER_TYPE_USER (jid's) or ROSTER_TYPE_GROUP (group names)
+// The list should be freed by the caller after use.
+GSList *compl_list(guint type)
+{
+ GSList *list = NULL;
+ GSList *sl_roster_elt = groups;
+ roster *roster_elt;
+ GSList *sl_roster_usrelt;
+ roster *roster_usrelt;
+
+ while (sl_roster_elt) { // group list loop
+ roster_elt = (roster*) sl_roster_elt->data;
+
+ if (roster_elt->type & ROSTER_TYPE_SPECIAL)
+ continue; // Skip special items
+
+ if (type == ROSTER_TYPE_GROUP) { // (group names)
+ if (roster_elt->name && *(roster_elt->name))
+ list = g_slist_append(list, from_utf8(roster_elt->name));
+ } else { // ROSTER_TYPE_USER (jid) (or agent, or chatroom...)
+ sl_roster_usrelt = roster_elt->list;
+ while (sl_roster_usrelt) { // user list loop
+ roster_usrelt = (roster*) sl_roster_usrelt->data;
+
+ if (roster_usrelt->jid)
+ list = g_slist_append(list, from_utf8(roster_usrelt->jid));
+
+ sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
+ }
+ }
+ sl_roster_elt = g_slist_next(sl_roster_elt);
+ }
+
+ return list;
+}
+
+// unread_msg(rosterdata)
+// Return the next buddy with an unread message. If the parameter is NULL,
+// return the first buddy with an unread message.
+gpointer unread_msg(gpointer rosterdata)
+{
+ GSList *unread, *next_unread;
+
+ if (!unread_list)
+ return NULL;
+
+ // First unread message
+ if (!rosterdata)
+ return unread_list->data;
+
+ unread = g_slist_find(unread_list, rosterdata);
+ if (!unread)
+ return unread_list->data;
+
+ next_unread = g_slist_next(unread);
+ if (next_unread)
+ return next_unread->data;
+ return unread_list->data;
+}
+
+
+/* ### "unread_jids" functions ###
+ *
+ * The unread_jids hash table is used to keep track of the buddies with
+ * unread messages when a disconnection occurs.
+ * When removing a buddy with an unread message from the roster, the
+ * jid should be added to the unread_jids table. When adding a buddy to
+ * the roster, we check if (s)he had a pending unread message.
+ */
+
+// unread_jid_add(jid)
+// Add jid to the unread_jids hash table
+void unread_jid_add(const char *jid)
+{
+ if (!unread_jids) {
+ // Initialize unread_jids hash table
+ unread_jids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ }
+ // The 2nd unread_jids is an arbitrary non-null pointer:
+ g_hash_table_insert(unread_jids, g_strdup(jid), unread_jids);
+}
+
+// unread_jid_del(jid)
+// Return TRUE if jid is found in the table (and remove it), FALSE if not
+static int unread_jid_del(const char *jid)
+{
+ if (!unread_jids)
+ return FALSE;
+ return g_hash_table_remove(unread_jids, jid);
+}
+
+// Helper function for unread_jid_get_list()
+static void add_to_unreadjids(gpointer key, gpointer value, gpointer udata)
+{
+ GList **listp = udata;
+ *listp = g_list_append(*listp, key);
+}
+
+// unread_jid_get_list()
+// Return the JID list.
+// The content of the list should not be modified or freed.
+// The caller should call g_list_free() after use.
+GList *unread_jid_get_list(void)
+{
+ GList *list = NULL;
+
+ if (!unread_jids)
+ return NULL;
+
+ // g_hash_table_get_keys() is only in glib >= 2.14
+ //return g_hash_table_get_keys(unread_jids);
+
+ g_hash_table_foreach(unread_jids, add_to_unreadjids, &list);
+ return list;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/roster.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,247 @@
+#ifndef __MCABBER_ROSTER_H__
+#define __MCABBER_ROSTER_H__ 1
+
+#include <glib.h>
+#include <time.h>
+
+#include <mcabber/pgp.h>
+#include <mcabber/config.h>
+
+#define SPECIAL_BUFFER_STATUS_ID "[status]"
+
+enum imstatus {
+ offline,
+ available,
+ freeforchat,
+ dontdisturb,
+ notavail,
+ away,
+ invisible,
+ imstatus_size
+};
+
+extern char imstatus2char[]; // Should match enum above
+
+enum imrole {
+ role_none,
+ role_moderator,
+ role_participant,
+ role_visitor,
+ imrole_size
+};
+
+extern char *strrole[]; // Should match enum above
+
+enum imaffiliation {
+ affil_none,
+ affil_owner,
+ affil_admin,
+ affil_member,
+ affil_outcast,
+ imaffiliation_size
+};
+
+extern char *straffil[]; // Should match enum above
+
+enum subscr {
+ sub_none = 0,
+ sub_pending = 1,
+ sub_to = 1 << 1,
+ sub_from = 1 << 2,
+ sub_both = sub_to|sub_from,
+ sub_remove = 1 << 3
+};
+
+enum findwhat {
+ jidsearch,
+ namesearch
+};
+
+extern char *strprintstatus[];
+
+// Note: do not change the ordering as these values are visible
+// to the user (option 'muc_print_status')!
+enum room_printstatus {
+ status_default,
+ status_none,
+ status_in_and_out,
+ status_all
+};
+
+extern char *strautowhois[];
+
+enum room_autowhois {
+ autowhois_default,
+ autowhois_off,
+ autowhois_on
+};
+
+struct role_affil {
+ enum { type_role, type_affil } type;
+ union {
+ enum imrole role;
+ enum imaffiliation affil;
+ } val;
+};
+
+// Roster_type is a set of flags, so values should be 2^n
+#define ROSTER_TYPE_USER 1U
+#define ROSTER_TYPE_GROUP (1U<<1)
+#define ROSTER_TYPE_AGENT (1U<<2)
+#define ROSTER_TYPE_ROOM (1U<<3)
+#define ROSTER_TYPE_SPECIAL (1U<<4)
+
+// Flags:
+#define ROSTER_FLAG_MSG 1U // Message not read
+#define ROSTER_FLAG_HIDE (1U<<1) // Group hidden (or buddy window closed)
+#define ROSTER_FLAG_LOCK (1U<<2) // Node should not be removed from buddylist
+#define ROSTER_FLAG_USRLOCK (1U<<3) // Node should not be removed from buddylist
+// ROSTER_FLAG_LOCAL (1U<<4) // Buddy not on server's roster (??)
+
+#define JEP0022
+#define JEP0085
+
+struct jep0022 {
+ guint support;
+ guint last_state_sent;
+ gchar *last_msgid_sent;
+ guint last_state_rcvd;
+ gchar *last_msgid_rcvd;
+};
+struct jep0085 {
+ guint support;
+ guint last_state_sent;
+ guint last_state_rcvd;
+};
+
+enum chatstate_support {
+ CHATSTATES_SUPPORT_UNKNOWN = 0,
+ CHATSTATES_SUPPORT_PROBED,
+ CHATSTATES_SUPPORT_NONE,
+ CHATSTATES_SUPPORT_OK
+};
+
+struct pgp_data {
+ gchar *sign_keyid; // KeyId used by the contact to sign their presence/msg
+#ifdef HAVE_GPGME
+ gpgme_sigsum_t last_sigsum; // Last signature summary
+#endif
+};
+
+/* Message event and chat state flags */
+#define ROSTER_EVENT_NONE 0U
+/* JEP-22 Message Events */
+#define ROSTER_EVENT_OFFLINE (1U<<0)
+#define ROSTER_EVENT_DELIVERED (1U<<1)
+#define ROSTER_EVENT_DISPLAYED (1U<<2)
+/* JEP-22 & JEP-85 */
+#define ROSTER_EVENT_COMPOSING (1U<<3)
+/* JEP-85 Chat State Notifications */
+#define ROSTER_EVENT_ACTIVE (1U<<4)
+#define ROSTER_EVENT_PAUSED (1U<<5)
+#define ROSTER_EVENT_INACTIVE (1U<<6)
+#define ROSTER_EVENT_GONE (1U<<7)
+
+extern GList *buddylist;
+extern GList *current_buddy;
+extern GList *alternate_buddy;
+
+// Macros...
+
+#define BUDDATA(glist_node) ((glist_node)->data)
+#define CURRENT_JID buddy_getjid(BUDDATA(current_buddy))
+
+// Prototypes...
+void roster_init(void);
+GSList *roster_add_group(const char *name);
+GSList *roster_add_user(const char *jid, const char *name, const char *group,
+ guint type, enum subscr esub, gint on_server);
+GSList *roster_find(const char *jidname, enum findwhat type, guint roster_type);
+void roster_del_user(const char *jid);
+void roster_free(void);
+void roster_setstatus(const char *jid, const char *resname, gchar prio,
+ enum imstatus bstat, const char *status_msg,
+ time_t timestamp,
+ enum imrole role, enum imaffiliation affil,
+ const char *realjid);
+void roster_setflags(const char *jid, guint flags, guint value);
+void roster_msg_setflag(const char *jid, guint special, guint value);
+const char *roster_getname(const char *jid);
+const char *roster_getnickname(const char *jid);
+void roster_settype(const char *jid, guint type);
+enum imstatus roster_getstatus(const char *jid, const char *resname);
+const char *roster_getstatusmsg(const char *jid, const char *resname);
+guint roster_gettype(const char *jid);
+guint roster_getsubscription(const char *jid);
+void roster_unsubscribed(const char *jid);
+
+void buddylist_build(void);
+void buddy_hide_group(gpointer rosterdata, int hide);
+void buddylist_set_hide_offline_buddies(int hide);
+int buddylist_isset_filter(void);
+int buddylist_is_status_filtered(enum imstatus status);
+void buddylist_set_filter(guchar);
+guchar buddylist_get_filter(void);
+const char *buddy_getjid(gpointer rosterdata);
+void buddy_setname(gpointer rosterdata, char *newname);
+const char *buddy_getname(gpointer rosterdata);
+void buddy_setnickname(gpointer rosterdata, const char *newname);
+const char *buddy_getnickname(gpointer rosterdata);
+void buddy_setinsideroom(gpointer rosterdata, guint inside);
+guint buddy_getinsideroom(gpointer rosterdata);
+void buddy_settopic(gpointer rosterdata, const char *newtopic);
+const char *buddy_gettopic(gpointer rosterdata);
+void buddy_setprintstatus(gpointer rosterdata, enum room_printstatus);
+enum room_printstatus buddy_getprintstatus(gpointer rosterdata);
+void buddy_setautowhois(gpointer rosterdata, enum room_autowhois);
+enum room_autowhois buddy_getautowhois(gpointer rosterdata);
+void buddy_settype(gpointer rosterdata, guint type);
+guint buddy_gettype(gpointer rosterdata);
+guint buddy_getsubscription(gpointer rosterdata);
+void buddy_setgroup(gpointer rosterdata, char *newgroupname);
+const char *buddy_getgroupname(gpointer rosterdata);
+gpointer buddy_getgroup(gpointer rosterdata);
+enum imstatus buddy_getstatus(gpointer rosterdata, const char *resname);
+const char *buddy_getstatusmsg(gpointer rosterdata, const char *resname);
+time_t buddy_getstatustime(gpointer rosterdata, const char *resname);
+gchar buddy_getresourceprio(gpointer rosterdata, const char *resname);
+//int buddy_isresource(gpointer rosterdata);
+GSList *buddy_getresources(gpointer rosterdata);
+GSList *buddy_getresources_locale(gpointer rosterdata);
+void buddy_resource_setname(gpointer rosterdata, const char *resname,
+ const char *newname);
+void buddy_resource_setevents(gpointer rosterdata, const char *resname,
+ guint event);
+guint buddy_resource_getevents(gpointer rosterdata, const char *resname);
+void buddy_resource_setcaps(gpointer rosterdata, const char *resname,
+ const char *caps);
+char *buddy_resource_getcaps(gpointer rosterdata, const char *resname);
+struct jep0022 *buddy_resource_jep22(gpointer rosterdata, const char *resname);
+struct jep0085 *buddy_resource_jep85(gpointer rosterdata, const char *resname);
+struct pgp_data *buddy_resource_pgp(gpointer rosterdata, const char *resname);
+enum imrole buddy_getrole(gpointer rosterdata, const char *resname);
+enum imaffiliation buddy_getaffil(gpointer rosterdata, const char *resname);
+const char *buddy_getrjid(gpointer rosterdata, const char *resname);
+void buddy_del_all_resources(gpointer rosterdata);
+void buddy_setflags(gpointer rosterdata, guint flags, guint value);
+guint buddy_getflags(gpointer rosterdata);
+void buddy_setonserverflag(gpointer rosterdata, guint onserver);
+guint buddy_getonserverflag(gpointer rosterdata);
+GList *buddy_search_jid(const char *jid);
+GList *buddy_search(char *string);
+void foreach_buddy(guint roster_type,
+ void (*pfunc)(gpointer rosterdata, void *param),
+ void *param);
+void foreach_group_member(gpointer groupdata,
+ void (*pfunc)(gpointer rosterdata, void *param),
+ void *param);
+gpointer unread_msg(gpointer rosterdata);
+
+void unread_jid_add(const char *jid);
+GList *unread_jid_get_list(void);
+
+GSList *compl_list(guint type);
+
+#endif /* __MCABBER_ROSTER_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/screen.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,4124 @@
+/*
+ * screen.c -- UI stuff
+ *
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ * Parts of this file come from the Cabber project <cabber@ajmacias.com>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+
+#include <config.h>
+#include <locale.h>
+#include <assert.h>
+#ifdef USE_SIGWINCH
+# include <sys/ioctl.h>
+# include <termios.h>
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_LOCALCHARSET_H
+# include <localcharset.h>
+#else
+# include <langinfo.h>
+#endif
+
+#ifdef WITH_ENCHANT
+# include <enchant.h>
+#endif
+
+#ifdef WITH_ASPELL
+# include <aspell.h>
+#endif
+
+#include "screen.h"
+#include "utf8.h"
+#include "hbuf.h"
+#include "commands.h"
+#include "compl.h"
+#include "roster.h"
+#include "histolog.h"
+#include "settings.h"
+#include "utils.h"
+#include "xmpp.h"
+#include "main.h"
+
+#define get_color(col) (COLOR_PAIR(col)|COLOR_ATTRIB[col])
+#define compose_color(col) (COLOR_PAIR(col->color_pair)|col->color_attrib)
+
+#define DEFAULT_LOG_WIN_HEIGHT (5+2)
+#define DEFAULT_ROSTER_WIDTH 24
+#define CHAT_WIN_HEIGHT (maxY-1-Log_Win_Height)
+
+const char *LocaleCharSet = "C";
+
+static unsigned short int Log_Win_Height;
+static unsigned short int Roster_Width;
+
+static inline void check_offset(int);
+static void scr_cancel_current_completion(void);
+static void scr_end_current_completion(void);
+static void scr_insert_text(const char*);
+static void scr_handle_tab(void);
+
+#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
+static void spellcheck(char *, char *);
+#endif
+
+static GHashTable *winbufhash;
+
+typedef struct {
+ GList *hbuf;
+ GList *top; // If top is NULL, we'll display the last lines
+ char cleared; // For ex, user has issued a /clear command...
+ char lock;
+} buffdata;
+
+typedef struct {
+ WINDOW *win;
+ PANEL *panel;
+ buffdata *bd;
+} winbuf;
+
+struct dimensions {
+ int l;
+ int c;
+};
+
+static WINDOW *rosterWnd, *chatWnd, *activechatWnd, *inputWnd, *logWnd;
+static WINDOW *mainstatusWnd, *chatstatusWnd;
+static PANEL *rosterPanel, *chatPanel, *activechatPanel, *inputPanel;
+static PANEL *mainstatusPanel, *chatstatusPanel;
+static PANEL *logPanel;
+static int maxY, maxX;
+static int prev_chatwidth;
+static winbuf *statusWindow;
+static winbuf *currentWindow;
+static GList *statushbuf;
+
+static int roster_hidden;
+static int chatmode;
+static int multimode;
+static char *multiline, *multimode_subj;
+int update_roster;
+int utf8_mode = 0;
+static bool Curses;
+static bool log_win_on_top;
+static bool roster_win_on_right;
+static time_t LastActivity;
+
+static char inputLine[INPUTLINE_LENGTH+1];
+#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
+static char maskLine[INPUTLINE_LENGTH+1];
+#endif
+static char *ptr_inputline;
+static short int inputline_offset;
+static int completion_started;
+static GList *cmdhisto;
+static GList *cmdhisto_cur;
+static guint cmdhisto_nblines;
+static char cmdhisto_backup[INPUTLINE_LENGTH+1];
+
+static int chatstate; /* (0=active, 1=composing, 2=paused) */
+static bool lock_chatstate;
+static time_t chatstate_timestamp;
+static guint chatstate_timeout_id = 0;
+int chatstates_disabled;
+
+#define MAX_KEYSEQ_LENGTH 8
+
+typedef struct {
+ char *seqstr;
+ guint mkeycode;
+ gint value;
+} keyseq;
+
+#ifdef HAVE_GLIB_REGEX
+static GRegex *url_regex;
+#endif
+
+GSList *keyseqlist;
+static void add_keyseq(char *seqstr, guint mkeycode, gint value);
+
+void scr_WriteInWindow(const char *winId, const char *text, time_t timestamp,
+ unsigned int prefix_flags, int force_show,
+ unsigned mucnicklen, gpointer xep184);
+
+void scr_WriteMessage(const char *bjid, const char *text,
+ time_t timestamp, guint prefix_flags,
+ unsigned mucnicklen, gpointer xep184);
+
+inline void scr_UpdateBuddyWindow(void);
+inline void scr_set_chatmode(int enable);
+
+#define SPELLBADCHAR 5
+
+#ifdef WITH_ENCHANT
+EnchantBroker *spell_broker;
+EnchantDict *spell_checker;
+#endif
+
+#ifdef WITH_ASPELL
+AspellConfig *spell_config;
+AspellSpeller *spell_checker;
+#endif
+
+typedef struct {
+ int color_pair;
+ int color_attrib;
+} ccolor;
+
+typedef struct {
+ char *status, *wildcard;
+ ccolor *color;
+ GPatternSpec *compiled;
+} rostercolor;
+
+static GSList *rostercolrules = NULL;
+
+static GHashTable *muccolors = NULL, *nickcolors = NULL;
+
+typedef struct {
+ bool manual; // Manually set?
+ ccolor *color;
+} nickcolor;
+
+static int nickcolcount = 0;
+static ccolor ** nickcols = NULL;
+static muccoltype glob_muccol = MC_OFF;
+
+/* Functions */
+
+static int FindColor(const char *name)
+{
+ int result;
+
+ if (!strcmp(name, "default"))
+ return -1;
+ if (!strcmp(name, "black"))
+ return COLOR_BLACK;
+ if (!strcmp(name, "red"))
+ return COLOR_RED;
+ if (!strcmp(name, "green"))
+ return COLOR_GREEN;
+ if (!strcmp(name, "yellow"))
+ return COLOR_YELLOW;
+ if (!strcmp(name, "blue"))
+ return COLOR_BLUE;
+ if (!strcmp(name, "magenta"))
+ return COLOR_MAGENTA;
+ if (!strcmp(name, "cyan"))
+ return COLOR_CYAN;
+ if (!strcmp(name, "white"))
+ return COLOR_WHITE;
+
+ // Directly support 256-color values
+ result = atoi(name);
+ if (result > 0 && result < COLORS)
+ return result;
+
+ scr_LogPrint(LPRINT_LOGNORM, "ERROR: Wrong color: %s", name);
+ return -1;
+}
+
+static ccolor *get_user_color(const char *color)
+{
+ bool isbright = FALSE;
+ int cl;
+ ccolor *ccol;
+ if (!strncmp(color, "bright", 6)) {
+ isbright = TRUE;
+ color += 6;
+ }
+ cl = FindColor(color);
+ if (cl < 0)
+ return NULL;
+ ccol = g_new0(ccolor, 1);
+ ccol->color_attrib = isbright ? A_BOLD : A_NORMAL;
+ ccol->color_pair = cl + COLOR_max; //user colors come after the internal ones
+ return ccol;
+}
+
+static void ensure_string_htable(GHashTable **table,
+ GDestroyNotify value_destroy_func)
+{
+ if (*table)//Have it already
+ return;
+ *table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, value_destroy_func);
+}
+
+// Sets the coloring mode for given MUC
+// The MUC room does not need to be in the roster at that time
+// muc - the JID of room
+// type - the new type
+void scr_MucColor(const char *muc, muccoltype type)
+{
+ gchar *muclow = g_utf8_strdown(muc, -1);
+ if (type == MC_REMOVE) {//Remove it
+ if (strcmp(muc, "*")) {
+ if (muccolors && g_hash_table_lookup(muccolors, muclow))
+ g_hash_table_remove(muccolors, muclow);
+ } else {
+ scr_LogPrint(LPRINT_NORMAL, "Can not remove global coloring mode");
+ }
+ g_free(muclow);
+ } else {//Add or overwrite
+ if (strcmp(muc, "*")) {
+ muccoltype *value = g_new(muccoltype, 1);
+ *value = type;
+ ensure_string_htable(&muccolors, g_free);
+ g_hash_table_replace(muccolors, muclow, value);
+ } else {
+ glob_muccol = type;
+ g_free(muclow);
+ }
+ }
+ //Need to redraw?
+ if (chatmode &&
+ ((buddy_search_jid(muc) == current_buddy) || !strcmp(muc, "*")))
+ scr_UpdateBuddyWindow();
+}
+
+// Sets the color for nick in MUC
+// If color is "-", the color is marked as automaticly assigned and is
+// not used if the room is in the "preset" mode
+void scr_MucNickColor(const char *nick, const char *color)
+{
+ char *snick, *mnick;
+ bool need_update = FALSE;
+ snick = g_strdup_printf("<%s>", nick);
+ mnick = g_strdup_printf("*%s ", nick);
+ if (!strcmp(color, "-")) {//Remove the color
+ if (nickcolors) {
+ nickcolor *nc = g_hash_table_lookup(nickcolors, snick);
+ if (nc) {//Have this nick already
+ nc->manual = FALSE;
+ nc = g_hash_table_lookup(nickcolors, mnick);
+ assert(nc);//Must have both at the same time
+ nc->manual = FALSE;
+ }// Else -> no color saved, nothing to delete
+ }
+ g_free(snick);//They are not saved in the hash
+ g_free(mnick);
+ need_update = TRUE;
+ } else {
+ ccolor *cl = get_user_color(color);
+ if (!cl) {
+ scr_LogPrint(LPRINT_NORMAL, "No such color name");
+ g_free(snick);
+ g_free(mnick);
+ } else {
+ nickcolor *nc = g_new(nickcolor, 1);
+ ensure_string_htable(&nickcolors, NULL);
+ nc->manual = TRUE;
+ nc->color = cl;
+ //Free the struct, if any there already
+ g_free(g_hash_table_lookup(nickcolors, mnick));
+ //Save the new ones
+ g_hash_table_replace(nickcolors, mnick, nc);
+ g_hash_table_replace(nickcolors, snick, nc);
+ need_update = TRUE;
+ }
+ }
+ if (need_update && chatmode &&
+ (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_ROOM))
+ scr_UpdateBuddyWindow();
+}
+
+static void free_rostercolrule(rostercolor *col)
+{
+ g_free(col->status);
+ g_free(col->wildcard);
+ g_free(col->color);
+ g_pattern_spec_free(col->compiled);
+ g_free(col);
+}
+
+// Removes all roster coloring rules
+void scr_RosterClearColor(void)
+{
+ GSList *head;
+ for (head = rostercolrules; head; head = g_slist_next(head)) {
+ free_rostercolrule(head->data);
+ }
+ g_slist_free(rostercolrules);
+ rostercolrules = NULL;
+}
+
+// Adds, modifies or removes roster coloring rule
+// color set to "-" removes the rule,
+// otherwise it is modified (if exists) or added
+//
+// Returns weather it was successfull (therefore the roster should be
+// redrawed) or not. If it failed, for example because of invalid color
+// name, it also prints the error.
+bool scr_RosterColor(const char *status, const char *wildcard,
+ const char *color)
+{
+ GSList *head;
+ GSList *found = NULL;
+ for (head = rostercolrules; head; head = g_slist_next(head)) {
+ rostercolor *rc = head->data;
+ if ((!strcmp(status, rc->status)) && (!strcmp(wildcard, rc->wildcard))) {
+ found = head;
+ break;
+ }
+ }
+ if (!strcmp(color,"-")) {//Delete the rule
+ if (found) {
+ free_rostercolrule(found->data);
+ rostercolrules = g_slist_delete_link(rostercolrules, found);
+ return TRUE;
+ } else {
+ scr_LogPrint(LPRINT_NORMAL, "No such color rule, nothing removed");
+ return FALSE;
+ }
+ } else {
+ ccolor *cl = get_user_color(color);
+ if (!cl) {
+ scr_LogPrint(LPRINT_NORMAL, "No such color name");
+ return FALSE;
+ }
+ if (found) {
+ rostercolor *rc = found->data;
+ g_free(rc->color);
+ rc->color = cl;
+ } else {
+ rostercolor *rc = g_new(rostercolor, 1);
+ rc->status = g_strdup(status);
+ rc->wildcard = g_strdup(wildcard);
+ rc->compiled = g_pattern_spec_new(wildcard);
+ rc->color = cl;
+ rostercolrules = g_slist_prepend(rostercolrules, rc);
+ }
+ return TRUE;
+ }
+}
+
+static void ParseColors(void)
+{
+ const char *colors[] = {
+ "", "",
+ "general",
+ "msgout",
+ "msghl",
+ "status",
+ "roster",
+ "rostersel",
+ "rosterselmsg",
+ "rosternewmsg",
+ "info",
+ "msgin",
+ NULL
+ };
+
+ const char *color;
+ const char *background = settings_opt_get("color_background");
+ const char *backselected = settings_opt_get("color_bgrostersel");
+ const char *backstatus = settings_opt_get("color_bgstatus");
+ char *tmp;
+ int i;
+
+ // Initialize color attributes
+ memset(COLOR_ATTRIB, 0, sizeof(COLOR_ATTRIB));
+
+ // Default values
+ if (!background) background = "black";
+ if (!backselected) backselected = "cyan";
+ if (!backstatus) backstatus = "blue";
+
+ for (i=0; colors[i]; i++) {
+ tmp = g_strdup_printf("color_%s", colors[i]);
+ color = settings_opt_get(tmp);
+ g_free(tmp);
+
+ if (color) {
+ if (!strncmp(color, "bright", 6)) {
+ COLOR_ATTRIB[i+1] = A_BOLD;
+ color += 6;
+ }
+ }
+
+ switch (i + 1) {
+ case 1:
+ init_pair(1, COLOR_BLACK, COLOR_WHITE);
+ break;
+ case 2:
+ init_pair(2, COLOR_WHITE, COLOR_BLACK);
+ break;
+ case COLOR_GENERAL:
+ init_pair(i+1, ((color) ? FindColor(color) : COLOR_WHITE),
+ FindColor(background));
+ break;
+ case COLOR_MSGOUT:
+ init_pair(i+1, ((color) ? FindColor(color) : COLOR_CYAN),
+ FindColor(background));
+ break;
+ case COLOR_MSGHL:
+ init_pair(i+1, ((color) ? FindColor(color) : COLOR_YELLOW),
+ FindColor(background));
+ break;
+ case COLOR_STATUS:
+ init_pair(i+1, ((color) ? FindColor(color) : COLOR_WHITE),
+ FindColor(backstatus));
+ break;
+ case COLOR_ROSTER:
+ init_pair(i+1, ((color) ? FindColor(color) : COLOR_GREEN),
+ FindColor(background));
+ break;
+ case COLOR_ROSTERSEL:
+ init_pair(i+1, ((color) ? FindColor(color) : COLOR_BLUE),
+ FindColor(backselected));
+ break;
+ case COLOR_ROSTERSELNMSG:
+ init_pair(i+1, ((color) ? FindColor(color) : COLOR_RED),
+ FindColor(backselected));
+ break;
+ case COLOR_ROSTERNMSG:
+ init_pair(i+1, ((color) ? FindColor(color) : COLOR_RED),
+ FindColor(background));
+ break;
+ case COLOR_INFO:
+ init_pair(i+1, ((color) ? FindColor(color) : COLOR_WHITE),
+ FindColor(background));
+ break;
+ case COLOR_MSGIN:
+ init_pair(i+1, ((color) ? FindColor(color) : COLOR_WHITE),
+ FindColor(background));
+ break;
+ }
+ }
+ for (i = COLOR_max; i < (COLOR_max + COLORS); i++)
+ init_pair(i, i-COLOR_max, FindColor(background));
+
+ if (!nickcols) {
+ char *ncolors = g_strdup(settings_opt_get("nick_colors"));
+ if (ncolors) {
+ char *ncolor_start, *ncolor_end;
+ ncolor_start = ncolor_end = ncolors;
+
+ while (*ncolor_end)
+ ncolor_end++;
+
+ while (ncolors < ncolor_end && *ncolors) {
+ if ((*ncolors == ' ') || (*ncolors == '\t')) {
+ ncolors++;
+ } else {
+ char *end = ncolors;
+ ccolor *cl;
+ while (*end && (*end != ' ') && (*end != '\t'))
+ end++;
+ *end = '\0';
+ cl = get_user_color(ncolors);
+ if (!cl) {
+ scr_LogPrint(LPRINT_NORMAL, "Unknown color %s", ncolors);
+ } else {
+ nickcols = g_realloc(nickcols, (++nickcolcount) * sizeof *nickcols);
+ nickcols[nickcolcount-1] = cl;
+ }
+ ncolors = end+1;
+ }
+ }
+ g_free(ncolor_start);
+ }
+ if (!nickcols) {//Fallback to have something
+ nickcolcount = 1;
+ nickcols = g_new(ccolor*, 1);
+ *nickcols = g_new(ccolor, 1);
+ (*nickcols)->color_pair = COLOR_GENERAL;
+ (*nickcols)->color_attrib = A_NORMAL;
+ }
+ }
+}
+
+static void init_keycodes(void)
+{
+ add_keyseq("O5A", MKEY_EQUIV, 521); // Ctrl-Up
+ add_keyseq("O5B", MKEY_EQUIV, 514); // Ctrl-Down
+ add_keyseq("O5C", MKEY_EQUIV, 518); // Ctrl-Right
+ add_keyseq("O5D", MKEY_EQUIV, 516); // Ctrl-Left
+ add_keyseq("O6A", MKEY_EQUIV, 520); // Shift-Up
+ add_keyseq("O6B", MKEY_EQUIV, 513); // Shift-Down
+ add_keyseq("O6C", MKEY_EQUIV, 402); // Shift-Right
+ add_keyseq("O6D", MKEY_EQUIV, 393); // Shift-Left
+ add_keyseq("O2A", MKEY_EQUIV, 520); // Shift-Up
+ add_keyseq("O2B", MKEY_EQUIV, 513); // Shift-Down
+ add_keyseq("O2C", MKEY_EQUIV, 402); // Shift-Right
+ add_keyseq("O2D", MKEY_EQUIV, 393); // Shift-Left
+ add_keyseq("[5^", MKEY_CTRL_PGUP, 0); // Ctrl-PageUp
+ add_keyseq("[6^", MKEY_CTRL_PGDOWN, 0); // Ctrl-PageDown
+ add_keyseq("[5@", MKEY_CTRL_SHIFT_PGUP, 0); // Ctrl-Shift-PageUp
+ add_keyseq("[6@", MKEY_CTRL_SHIFT_PGDOWN, 0); // Ctrl-Shift-PageDown
+ add_keyseq("[7@", MKEY_CTRL_SHIFT_HOME, 0); // Ctrl-Shift-Home
+ add_keyseq("[8@", MKEY_CTRL_SHIFT_END, 0); // Ctrl-Shift-End
+ add_keyseq("[8^", MKEY_CTRL_END, 0); // Ctrl-End
+ add_keyseq("[7^", MKEY_CTRL_HOME, 0); // Ctrl-Home
+ add_keyseq("[2^", MKEY_CTRL_INS, 0); // Ctrl-Insert
+ add_keyseq("[3^", MKEY_CTRL_DEL, 0); // Ctrl-Delete
+
+ // Xterm
+ add_keyseq("[1;5A", MKEY_EQUIV, 521); // Ctrl-Up
+ add_keyseq("[1;5B", MKEY_EQUIV, 514); // Ctrl-Down
+ add_keyseq("[1;5C", MKEY_EQUIV, 518); // Ctrl-Right
+ add_keyseq("[1;5D", MKEY_EQUIV, 516); // Ctrl-Left
+ add_keyseq("[1;6A", MKEY_EQUIV, 520); // Ctrl-Shift-Up
+ add_keyseq("[1;6B", MKEY_EQUIV, 513); // Ctrl-Shift-Down
+ add_keyseq("[1;6C", MKEY_EQUIV, 402); // Ctrl-Shift-Right
+ add_keyseq("[1;6D", MKEY_EQUIV, 393); // Ctrl-Shift-Left
+ add_keyseq("[1;6H", MKEY_CTRL_SHIFT_HOME, 0); // Ctrl-Shift-Home
+ add_keyseq("[1;6F", MKEY_CTRL_SHIFT_END, 0); // Ctrl-Shift-End
+ add_keyseq("[1;2A", MKEY_EQUIV, 521); // Shift-Up
+ add_keyseq("[1;2B", MKEY_EQUIV, 514); // Shift-Down
+ add_keyseq("[5;5~", MKEY_CTRL_PGUP, 0); // Ctrl-PageUp
+ add_keyseq("[6;5~", MKEY_CTRL_PGDOWN, 0); // Ctrl-PageDown
+ add_keyseq("[1;5F", MKEY_CTRL_END, 0); // Ctrl-End
+ add_keyseq("[1;5H", MKEY_CTRL_HOME, 0); // Ctrl-Home
+ add_keyseq("[2;5~", MKEY_CTRL_INS, 0); // Ctrl-Insert
+ add_keyseq("[3;5~", MKEY_CTRL_DEL, 0); // Ctrl-Delete
+
+ // PuTTY
+ add_keyseq("[A", MKEY_EQUIV, 521); // Ctrl-Up
+ add_keyseq("[B", MKEY_EQUIV, 514); // Ctrl-Down
+ add_keyseq("[C", MKEY_EQUIV, 518); // Ctrl-Right
+ add_keyseq("[D", MKEY_EQUIV, 516); // Ctrl-Left
+
+ // screen
+ add_keyseq("Oa", MKEY_EQUIV, 521); // Ctrl-Up
+ add_keyseq("Ob", MKEY_EQUIV, 514); // Ctrl-Down
+ add_keyseq("Oc", MKEY_EQUIV, 518); // Ctrl-Right
+ add_keyseq("Od", MKEY_EQUIV, 516); // Ctrl-Left
+ add_keyseq("[a", MKEY_EQUIV, 520); // Shift-Up
+ add_keyseq("[b", MKEY_EQUIV, 513); // Shift-Down
+ add_keyseq("[c", MKEY_EQUIV, 402); // Shift-Right
+ add_keyseq("[d", MKEY_EQUIV, 393); // Shift-Left
+ add_keyseq("[5$", MKEY_SHIFT_PGUP, 0); // Shift-PageUp
+ add_keyseq("[6$", MKEY_SHIFT_PGDOWN, 0); // Shift-PageDown
+
+ // VT100
+ add_keyseq("[H", MKEY_EQUIV, KEY_HOME); // Home
+ add_keyseq("[F", MKEY_EQUIV, KEY_END); // End
+
+ // Konsole Linux
+ add_keyseq("[1~", MKEY_EQUIV, KEY_HOME); // Home
+ add_keyseq("[4~", MKEY_EQUIV, KEY_END); // End
+}
+
+// scr_init_bindings()
+// Create default key bindings
+// Return 0 if error and 1 if none
+void scr_init_bindings(void)
+{
+ GString *sbuf = g_string_new("");
+
+ // Common backspace key codes: 8, 127
+ settings_set(SETTINGS_TYPE_BINDING, "8", "iline char_bdel"); // Ctrl-h
+ settings_set(SETTINGS_TYPE_BINDING, "127", "iline char_bdel");
+ g_string_printf(sbuf, "%d", KEY_BACKSPACE);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline char_bdel");
+ g_string_printf(sbuf, "%d", KEY_DC);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline char_fdel");
+ g_string_printf(sbuf, "%d", KEY_LEFT);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline bchar");
+ g_string_printf(sbuf, "%d", KEY_RIGHT);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline fchar");
+ settings_set(SETTINGS_TYPE_BINDING, "7", "iline compl_cancel"); // Ctrl-g
+ g_string_printf(sbuf, "%d", KEY_UP);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str,
+ "iline hist_beginning_search_bwd");
+ g_string_printf(sbuf, "%d", KEY_DOWN);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str,
+ "iline hist_beginning_search_fwd");
+ g_string_printf(sbuf, "%d", KEY_PPAGE);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "roster up");
+ g_string_printf(sbuf, "%d", KEY_NPAGE);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "roster down");
+ g_string_printf(sbuf, "%d", KEY_HOME);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline iline_start");
+ settings_set(SETTINGS_TYPE_BINDING, "1", "iline iline_start"); // Ctrl-a
+ g_string_printf(sbuf, "%d", KEY_END);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline iline_end");
+ settings_set(SETTINGS_TYPE_BINDING, "5", "iline iline_end"); // Ctrl-e
+ // Ctrl-o (accept-line-and-down-history):
+ settings_set(SETTINGS_TYPE_BINDING, "15", "iline iline_accept_down_hist");
+ settings_set(SETTINGS_TYPE_BINDING, "21", "iline iline_bdel"); // Ctrl-u
+ g_string_printf(sbuf, "%d", KEY_EOL);
+ settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline iline_fdel");
+ settings_set(SETTINGS_TYPE_BINDING, "11", "iline iline_fdel"); // Ctrl-k
+ settings_set(SETTINGS_TYPE_BINDING, "16", "buffer up"); // Ctrl-p
+ settings_set(SETTINGS_TYPE_BINDING, "14", "buffer down"); // Ctrl-n
+ settings_set(SETTINGS_TYPE_BINDING, "20", "iline char_swap"); // Ctrl-t
+ settings_set(SETTINGS_TYPE_BINDING, "23", "iline word_bdel"); // Ctrl-w
+ settings_set(SETTINGS_TYPE_BINDING, "M98", "iline bword"); // Meta-b
+ settings_set(SETTINGS_TYPE_BINDING, "M102", "iline fword"); // Meta-f
+ settings_set(SETTINGS_TYPE_BINDING, "M100", "iline word_fdel"); // Meta-d
+ // Ctrl-Left (2 codes):
+ settings_set(SETTINGS_TYPE_BINDING, "515", "iline bword");
+ settings_set(SETTINGS_TYPE_BINDING, "516", "iline bword");
+ // Ctrl-Right (2 codes):
+ settings_set(SETTINGS_TYPE_BINDING, "517", "iline fword");
+ settings_set(SETTINGS_TYPE_BINDING, "518", "iline fword");
+ settings_set(SETTINGS_TYPE_BINDING, "12", "screen_refresh"); // Ctrl-l
+ settings_set(SETTINGS_TYPE_BINDING, "27", "chat_disable --show-roster");// Esc
+ settings_set(SETTINGS_TYPE_BINDING, "M27", "chat_disable"); // Esc-Esc
+ settings_set(SETTINGS_TYPE_BINDING, "4", "iline send_multiline"); // Ctrl-d
+ settings_set(SETTINGS_TYPE_BINDING, "M117", "iline word_upcase"); // Meta-u
+ settings_set(SETTINGS_TYPE_BINDING, "M108", "iline word_downcase"); // Meta-l
+ settings_set(SETTINGS_TYPE_BINDING, "M99", "iline word_capit"); // Meta-c
+
+ settings_set(SETTINGS_TYPE_BINDING, "265", "help"); // Bind F1 to help...
+
+ g_string_free(sbuf, TRUE);
+}
+
+// is_speckey(key)
+// Return TRUE if key is a special code, i.e. no char should be displayed on
+// the screen. It's not very nice, it's a workaround for the systems where
+// isprint(KEY_PPAGE) returns TRUE...
+static int is_speckey(int key)
+{
+ switch (key) {
+ case 127:
+ case 393:
+ case 402:
+ case KEY_BACKSPACE:
+ case KEY_DC:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_PPAGE:
+ case KEY_NPAGE:
+ case KEY_HOME:
+ case KEY_END:
+ case KEY_EOL:
+ return TRUE;
+ }
+
+ // Fn keys
+ if (key >= 265 && key < 265+12)
+ return TRUE;
+
+ // Special key combinations
+ if (key >= 513 && key <= 521)
+ return TRUE;
+
+ return FALSE;
+}
+
+void scr_InitLocaleCharSet(void)
+{
+ setlocale(LC_ALL, "");
+#ifdef HAVE_LOCALCHARSET_H
+ LocaleCharSet = locale_charset();
+#else
+ LocaleCharSet = nl_langinfo(CODESET);
+#endif
+ utf8_mode = (strcmp(LocaleCharSet, "UTF-8") == 0);
+}
+
+void scr_InitCurses(void)
+{
+ /* Key sequences initialization */
+ init_keycodes();
+
+ initscr();
+ raw();
+ noecho();
+ nonl();
+ intrflush(stdscr, FALSE);
+ start_color();
+ use_default_colors();
+#ifdef NCURSES_MOUSE_VERSION
+ if (settings_opt_get_int("use_mouse"))
+ mousemask(ALL_MOUSE_EVENTS, NULL);
+#endif
+
+ if (settings_opt_get("escdelay")) {
+#ifdef HAVE_ESCDELAY
+ ESCDELAY = (unsigned) settings_opt_get_int("escdelay");
+#else
+ scr_LogPrint(LPRINT_LOGNORM, "ERROR: no ESCDELAY support.");
+#endif
+ }
+
+ ParseColors();
+
+ getmaxyx(stdscr, maxY, maxX);
+ Log_Win_Height = DEFAULT_LOG_WIN_HEIGHT;
+ // Note scr_DrawMainWindow() should be called early after scr_InitCurses()
+ // to update Log_Win_Height and set max{X,Y}
+
+ inputLine[0] = 0;
+ ptr_inputline = inputLine;
+
+ if (settings_opt_get("url_regex")) {
+#ifdef HAVE_GLIB_REGEX
+ url_regex = g_regex_new(settings_opt_get("url_regex"),
+ G_REGEX_OPTIMIZE, 0, NULL);
+#else
+ scr_LogPrint(LPRINT_LOGNORM, "ERROR: Your glib version is too old, "
+ "cannot use url_regex.");
+#endif // HAVE_GLIB_REGEX
+ }
+
+ Curses = TRUE;
+ return;
+}
+
+void scr_TerminateCurses(void)
+{
+ if (!Curses) return;
+ clear();
+ refresh();
+ endwin();
+#ifdef HAVE_GLIB_REGEX
+ if (url_regex)
+ g_regex_unref(url_regex);
+#endif
+ Curses = FALSE;
+ return;
+}
+
+void scr_Beep(void)
+{
+ beep();
+}
+
+// This and following belongs to dynamic setting of time prefix
+static const char *timeprefixes[] = {
+ "%m-%d %H:%M ",
+ "%H:%M ",
+ " "
+};
+
+static const char *spectimeprefixes[] = {
+ "%m-%d %H:%M:%S ",
+ "%H:%M:%S ",
+ " "
+};
+
+static int timepreflengths[] = {
+ // (length of the corresponding timeprefix + 5)
+ 17,
+ 11,
+ 6
+};
+
+static const char *gettprefix(void)
+{
+ guint n = settings_opt_get_int("time_prefix");
+ return timeprefixes[(n < 3 ? n : 0)];
+}
+
+static const char *getspectprefix(void)
+{
+ guint n = settings_opt_get_int("time_prefix");
+ return spectimeprefixes[(n < 3 ? n : 0)];
+}
+
+guint scr_getprefixwidth(void)
+{
+ guint n = settings_opt_get_int("time_prefix");
+ return timepreflengths[(n < 3 ? n : 0)];
+}
+
+// scr_print_logwindow(string)
+// Display the string in the log window.
+// Note: The string must be in the user's locale!
+void scr_print_logwindow(const char *string)
+{
+ time_t timestamp;
+ char strtimestamp[64];
+
+ timestamp = time(NULL);
+ strftime(strtimestamp, 48, "[%H:%M:%S]", localtime(×tamp));
+ if (Curses) {
+ wprintw(logWnd, "\n%s %s", strtimestamp, string);
+ update_panels();
+ } else {
+ printf("%s %s\n", strtimestamp, string);
+ }
+}
+
+// scr_LogPrint(...)
+// Display a message in the log window and in the status buffer.
+// Add the message to the tracelog file if the log flag is set.
+// This function will convert from UTF-8 unless the LPRINT_NOTUTF8 flag is set.
+void scr_LogPrint(unsigned int flag, const char *fmt, ...)
+{
+ time_t timestamp;
+ char strtimestamp[64];
+ char *buffer, *btext;
+ char *convbuf1 = NULL, *convbuf2 = NULL;
+ va_list ap;
+
+ if (!(flag & ~LPRINT_NOTUTF8)) return; // Shouldn't happen
+
+ timestamp = time(NULL);
+ strftime(strtimestamp, 48, "[%H:%M:%S]", localtime(×tamp));
+ va_start(ap, fmt);
+ btext = g_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ if (flag & LPRINT_NORMAL) {
+ char *buffer_locale;
+ char *buf_specialwindow;
+
+ buffer = g_strdup_printf("%s %s", strtimestamp, btext);
+
+ // Convert buffer to current locale for wprintw()
+ if (!(flag & LPRINT_NOTUTF8))
+ buffer_locale = convbuf1 = from_utf8(buffer);
+ else
+ buffer_locale = buffer;
+
+ if (!buffer_locale) {
+ wprintw(logWnd,
+ "\n%s*Error: cannot convert string to locale.", strtimestamp);
+ update_panels();
+ g_free(buffer);
+ g_free(btext);
+ return;
+ }
+
+ // For the special status buffer, we need utf-8, but without the timestamp
+ if (flag & LPRINT_NOTUTF8)
+ buf_specialwindow = convbuf2 = to_utf8(btext);
+ else
+ buf_specialwindow = btext;
+
+ if (Curses) {
+ wprintw(logWnd, "\n%s", buffer_locale);
+ update_panels();
+ scr_WriteInWindow(NULL, buf_specialwindow, timestamp,
+ HBB_PREFIX_SPECIAL, FALSE, 0, NULL);
+ } else {
+ printf("%s\n", buffer_locale);
+ // ncurses are not initialized yet, so we call directly hbuf routine
+ hbuf_add_line(&statushbuf, buf_specialwindow, timestamp,
+ HBB_PREFIX_SPECIAL, 0, 0, 0, NULL);
+ }
+
+ g_free(convbuf1);
+ g_free(convbuf2);
+ g_free(buffer);
+ }
+
+ if (flag & (LPRINT_LOG|LPRINT_DEBUG)) {
+ strftime(strtimestamp, 23, "[%Y-%m-%d %H:%M:%S]", localtime(×tamp));
+ buffer = g_strdup_printf("%s %s\n", strtimestamp, btext);
+ ut_WriteLog(flag, buffer);
+ g_free(buffer);
+ }
+ g_free(btext);
+}
+
+static winbuf *scr_SearchWindow(const char *winId, int special)
+{
+ char *id;
+ winbuf *wbp;
+
+ if (special)
+ return statusWindow; // Only one special window atm.
+
+ if (!winId)
+ return NULL;
+
+ id = g_strdup(winId);
+ mc_strtolower(id);
+ wbp = g_hash_table_lookup(winbufhash, id);
+ g_free(id);
+ return wbp;
+}
+
+int scr_BuddyBufferExists(const char *bjid)
+{
+ return (scr_SearchWindow(bjid, FALSE) != NULL);
+}
+
+// scr_new_buddy(title, dontshow)
+// Note: title (aka winId/jid) can be NULL for special buffers
+static winbuf *scr_new_buddy(const char *title, int dont_show)
+{
+ winbuf *tmp;
+
+ tmp = g_new0(winbuf, 1);
+
+ tmp->win = activechatWnd;
+ tmp->panel = activechatPanel;
+
+ if (!dont_show) {
+ currentWindow = tmp;
+ } else {
+ if (currentWindow)
+ top_panel(currentWindow->panel);
+ else
+ top_panel(chatPanel);
+ }
+ update_panels();
+
+ // If title is NULL, this is a special buffer
+ if (title) {
+ char *id;
+ id = hlog_get_log_jid(title);
+ if (id) {
+ winbuf *wb = scr_SearchWindow(id, FALSE);
+ if (!wb)
+ wb = scr_new_buddy(id, TRUE);
+ tmp->bd=wb->bd;
+ g_free(id);
+ } else { // Load buddy history from file (if enabled)
+ tmp->bd = g_new0(buffdata, 1);
+ hlog_read_history(title, &tmp->bd->hbuf,
+ maxX - Roster_Width - scr_getprefixwidth());
+ }
+
+ id = g_strdup(title);
+ mc_strtolower(id);
+ g_hash_table_insert(winbufhash, id, tmp);
+ } else {
+ tmp->bd = g_new0(buffdata, 1);
+ }
+ return tmp;
+}
+
+// scr_line_prefix(line, pref, preflen)
+// Use data from the hbb_line structure and write the prefix
+// to pref (not exceeding preflen, trailing null byte included).
+void scr_line_prefix(hbb_line *line, char *pref, guint preflen)
+{
+ char date[64];
+
+ if (line->timestamp &&
+ !(line->flags & (HBB_PREFIX_SPECIAL|HBB_PREFIX_CONT))) {
+ strftime(date, 30, gettprefix(), localtime(&line->timestamp));
+ } else
+ strcpy(date, " ");
+
+ if (!(line->flags & HBB_PREFIX_CONT)) {
+ if (line->flags & HBB_PREFIX_INFO) {
+ char dir = '*';
+ if (line->flags & HBB_PREFIX_IN)
+ dir = '<';
+ else if (line->flags & HBB_PREFIX_OUT)
+ dir = '>';
+ g_snprintf(pref, preflen, "%s*%c* ", date, dir);
+ } else if (line->flags & HBB_PREFIX_ERR) {
+ char dir = '#';
+ if (line->flags & HBB_PREFIX_IN)
+ dir = '<';
+ else if (line->flags & HBB_PREFIX_OUT)
+ dir = '>';
+ g_snprintf(pref, preflen, "%s#%c# ", date, dir);
+ } else if (line->flags & HBB_PREFIX_IN) {
+ char cryptflag;
+ if (line->flags & HBB_PREFIX_PGPCRYPT)
+ cryptflag = '~';
+ else if (line->flags & HBB_PREFIX_OTRCRYPT)
+ cryptflag = 'O';
+ else
+ cryptflag = '=';
+ g_snprintf(pref, preflen, "%s<%c= ", date, cryptflag);
+ } else if (line->flags & HBB_PREFIX_OUT) {
+ char cryptflag, receiptflag;
+ if (line->flags & HBB_PREFIX_PGPCRYPT)
+ cryptflag = '~';
+ else if (line->flags & HBB_PREFIX_OTRCRYPT)
+ cryptflag = 'O';
+ else
+ cryptflag = '-';
+ if (line->flags & HBB_PREFIX_RECEIPT)
+ receiptflag = 'r';
+ else
+ receiptflag = '-';
+ g_snprintf(pref, preflen, "%s%c%c> ", date, receiptflag, cryptflag);
+ } else if (line->flags & HBB_PREFIX_SPECIAL) {
+ strftime(date, 30, getspectprefix(), localtime(&line->timestamp));
+ g_snprintf(pref, preflen, "%s ", date);
+ } else {
+ g_snprintf(pref, preflen, "%s ", date);
+ }
+ } else {
+ g_snprintf(pref, preflen, " ");
+ }
+}
+
+// scr_UpdateWindow()
+// (Re-)Display the given chat window.
+static void scr_UpdateWindow(winbuf *win_entry)
+{
+ int n;
+ guint prefixwidth;
+ char pref[96];
+ hbb_line **lines, *line;
+ GList *hbuf_head;
+ int color;
+
+ prefixwidth = scr_getprefixwidth();
+ prefixwidth = MIN(prefixwidth, sizeof pref);
+
+ // Should the window be empty?
+ if (win_entry->bd->cleared) {
+ werase(win_entry->win);
+ return;
+ }
+
+ // win_entry->bd->top is the top message of the screen. If it set to NULL,
+ // we are displaying the last messages.
+
+ // We will show the last CHAT_WIN_HEIGHT lines.
+ // Let's find out where it begins.
+ if (!win_entry->bd->top || (g_list_position(g_list_first(win_entry->bd->hbuf),
+ win_entry->bd->top) == -1)) {
+ // Move up CHAT_WIN_HEIGHT lines
+ win_entry->bd->hbuf = g_list_last(win_entry->bd->hbuf);
+ hbuf_head = win_entry->bd->hbuf;
+ win_entry->bd->top = NULL; // (Just to make sure)
+ n = 0;
+ while (hbuf_head && (n < CHAT_WIN_HEIGHT-1) && g_list_previous(hbuf_head)) {
+ hbuf_head = g_list_previous(hbuf_head);
+ n++;
+ }
+ // If the buffer is locked, remember current "top" line for the next time.
+ if (win_entry->bd->lock)
+ win_entry->bd->top = hbuf_head;
+ } else
+ hbuf_head = win_entry->bd->top;
+
+ // Get the last CHAT_WIN_HEIGHT lines.
+ lines = hbuf_get_lines(hbuf_head, CHAT_WIN_HEIGHT);
+
+ // Display these lines
+ for (n = 0; n < CHAT_WIN_HEIGHT; n++) {
+ wmove(win_entry->win, n, 0);
+ line = *(lines+n);
+ if (line) {
+ if (line->flags & HBB_PREFIX_HLIGHT_OUT)
+ color = COLOR_MSGOUT;
+ else if (line->flags & HBB_PREFIX_HLIGHT)
+ color = COLOR_MSGHL;
+ else if (line->flags & HBB_PREFIX_INFO)
+ color = COLOR_INFO;
+ else if (line->flags & HBB_PREFIX_IN)
+ color = COLOR_MSGIN;
+ else
+ color = COLOR_GENERAL;
+
+ if (color != COLOR_GENERAL)
+ wattrset(win_entry->win, get_color(color));
+
+ // Generate the prefix area and display it
+ scr_line_prefix(line, pref, prefixwidth);
+ wprintw(win_entry->win, pref);
+
+ // Make sure we are at the right position
+ wmove(win_entry->win, n, prefixwidth-1);
+
+ // The MUC nick - overwrite with proper color
+ if (line->mucnicklen) {
+ char *mucjid;
+ char tmp;
+ nickcolor *actual = NULL;
+ muccoltype type, *typetmp;
+
+ // Store the char after the nick
+ tmp = line->text[line->mucnicklen];
+ type = glob_muccol;
+ // Terminate the string after the nick
+ line->text[line->mucnicklen] = '\0';
+ mucjid = g_utf8_strdown(CURRENT_JID, -1);
+ if (muccolors) {
+ typetmp = g_hash_table_lookup(muccolors, mucjid);
+ if (typetmp)
+ type = *typetmp;
+ }
+ g_free(mucjid);
+ // Need to generate a color for the specified nick?
+ if ((type == MC_ALL) && (!nickcolors ||
+ !g_hash_table_lookup(nickcolors, line->text))) {
+ char *snick, *mnick;
+ nickcolor *nc;
+ const char *p = line->text;
+ unsigned int nicksum = 0;
+ snick = g_strdup(line->text);
+ mnick = g_strdup(line->text);
+ nc = g_new(nickcolor, 1);
+ ensure_string_htable(&nickcolors, NULL);
+ while (*p)
+ nicksum += *p++;
+ nc->color = nickcols[nicksum % nickcolcount];
+ nc->manual = FALSE;
+ *snick = '<';
+ snick[strlen(snick)-1] = '>';
+ *mnick = '*';
+ mnick[strlen(mnick)-1] = ' ';
+ // Insert them
+ g_hash_table_insert(nickcolors, snick, nc);
+ g_hash_table_insert(nickcolors, mnick, nc);
+ }
+ if (nickcolors)
+ actual = g_hash_table_lookup(nickcolors, line->text);
+ if (actual && ((type == MC_ALL) || (actual->manual))
+ && (line->flags & HBB_PREFIX_IN) &&
+ (!(line->flags & HBB_PREFIX_HLIGHT_OUT)))
+ wattrset(win_entry->win, compose_color(actual->color));
+ wprintw(win_entry->win, "%s", line->text);
+ // Return the char
+ line->text[line->mucnicklen] = tmp;
+ // Return the color back
+ wattrset(win_entry->win, get_color(color));
+ }
+
+ // Display text line
+ wprintw(win_entry->win, "%s", line->text+line->mucnicklen);
+ wclrtoeol(win_entry->win);
+
+ // Return the color back
+ if (color != COLOR_GENERAL)
+ wattrset(win_entry->win, get_color(COLOR_GENERAL));
+
+ g_free(line->text);
+ g_free(line);
+ } else {
+ wclrtobot(win_entry->win);
+ break;
+ }
+ }
+ g_free(lines);
+}
+
+static winbuf *scr_CreateWindow(const char *winId, int special, int dont_show)
+{
+ if (special) {
+ if (!statusWindow) {
+ statusWindow = scr_new_buddy(NULL, dont_show);
+ statusWindow->bd->hbuf = statushbuf;
+ }
+ return statusWindow;
+ } else {
+ return scr_new_buddy(winId, dont_show);
+ }
+}
+
+// scr_ShowWindow()
+// Display the chat window with the given identifier.
+// "special" must be true if this is a special buffer window.
+static void scr_ShowWindow(const char *winId, int special)
+{
+ winbuf *win_entry;
+
+ win_entry = scr_SearchWindow(winId, special);
+
+ if (!win_entry) {
+ win_entry = scr_CreateWindow(winId, special, FALSE);
+ }
+
+ top_panel(win_entry->panel);
+ currentWindow = win_entry;
+ chatmode = TRUE;
+ if (!win_entry->bd->lock)
+ roster_msg_setflag(winId, special, FALSE);
+ if (!special)
+ roster_setflags(winId, ROSTER_FLAG_LOCK, TRUE);
+ update_roster = TRUE;
+
+ // Refresh the window
+ scr_UpdateWindow(win_entry);
+
+ // Finished :)
+ update_panels();
+
+ top_panel(inputPanel);
+}
+
+// scr_ShowBuddyWindow()
+// Display the chat window buffer for the current buddy.
+void scr_ShowBuddyWindow(void)
+{
+ const gchar *bjid;
+
+ if (!current_buddy) {
+ bjid = NULL;
+ } else {
+ bjid = CURRENT_JID;
+ if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL) {
+ scr_ShowWindow(buddy_getname(BUDDATA(current_buddy)), TRUE);
+ return;
+ }
+ }
+
+ if (!bjid) {
+ top_panel(chatPanel);
+ top_panel(inputPanel);
+ currentWindow = NULL;
+ return;
+ }
+
+ scr_ShowWindow(bjid, FALSE);
+}
+
+// scr_UpdateBuddyWindow()
+// (Re)Display the current window.
+// If chatmode is enabled, call scr_ShowBuddyWindow(),
+// else display the chat window.
+inline void scr_UpdateBuddyWindow(void)
+{
+ if (chatmode) {
+ scr_ShowBuddyWindow();
+ return;
+ }
+
+ top_panel(chatPanel);
+ top_panel(inputPanel);
+}
+
+// scr_WriteInWindow()
+// Write some text in the winId window (this usually is a jid).
+// Use winId == NULL for the special status buffer.
+// Lines are splitted when they are too long to fit in the chat window.
+// If this window doesn't exist, it is created.
+void scr_WriteInWindow(const char *winId, const char *text, time_t timestamp,
+ unsigned int prefix_flags, int force_show,
+ unsigned mucnicklen, gpointer xep184)
+{
+ winbuf *win_entry;
+ char *text_locale;
+ int dont_show = FALSE;
+ int special;
+ guint num_history_blocks;
+ bool setmsgflg = FALSE;
+ char *nicktmp, *nicklocaltmp;
+
+ // Look for the window entry.
+ special = (winId == NULL);
+ win_entry = scr_SearchWindow(winId, special);
+
+ // Do we have to really show the window?
+ if (!chatmode)
+ dont_show = TRUE;
+ else if ((!force_show) && ((!currentWindow || (currentWindow != win_entry))))
+ dont_show = TRUE;
+
+ // If the window entry doesn't exist yet, let's create it.
+ if (!win_entry) {
+ win_entry = scr_CreateWindow(winId, special, dont_show);
+ }
+
+ // The message must be displayed -> update top pointer
+ if (win_entry->bd->cleared)
+ win_entry->bd->top = g_list_last(win_entry->bd->hbuf);
+
+ // Make sure we do not free the buffer while it's locked or when
+ // top is set.
+ if (win_entry->bd->lock || win_entry->bd->top)
+ num_history_blocks = 0U;
+ else
+ num_history_blocks = get_max_history_blocks();
+
+ text_locale = from_utf8(text);
+ //Convert the nick alone and compute its length
+ if (mucnicklen) {
+ nicktmp = g_strndup(text, mucnicklen);
+ nicklocaltmp = from_utf8(nicktmp);
+ mucnicklen = strlen(nicklocaltmp);
+ g_free(nicklocaltmp);
+ g_free(nicktmp);
+ }
+ hbuf_add_line(&win_entry->bd->hbuf, text_locale, timestamp, prefix_flags,
+ maxX - Roster_Width - scr_getprefixwidth(), num_history_blocks,
+ mucnicklen, xep184);
+ g_free(text_locale);
+
+ if (win_entry->bd->cleared) {
+ win_entry->bd->cleared = FALSE;
+ if (g_list_next(win_entry->bd->top))
+ win_entry->bd->top = g_list_next(win_entry->bd->top);
+ }
+
+ // Make sure the last line appears in the window; update top if necessary
+ if (!win_entry->bd->lock && win_entry->bd->top) {
+ int dist;
+ GList *first = g_list_first(win_entry->bd->hbuf);
+ dist = g_list_position(first, g_list_last(win_entry->bd->hbuf)) -
+ g_list_position(first, win_entry->bd->top);
+ if (dist >= CHAT_WIN_HEIGHT)
+ win_entry->bd->top = NULL;
+ }
+
+ if (!dont_show) {
+ if (win_entry->bd->lock)
+ setmsgflg = TRUE;
+ // Show and refresh the window
+ top_panel(win_entry->panel);
+ scr_UpdateWindow(win_entry);
+ top_panel(inputPanel);
+ update_panels();
+ } else if (!(prefix_flags & HBB_PREFIX_NOFLAG)) {
+ setmsgflg = TRUE;
+ }
+ if (setmsgflg && !special) {
+ if (special && !winId)
+ winId = SPECIAL_BUFFER_STATUS_ID;
+ roster_msg_setflag(winId, special, TRUE);
+ update_roster = TRUE;
+ }
+}
+
+// scr_UpdateMainStatus()
+// Redraw the main (bottom) status line.
+void scr_UpdateMainStatus(int forceupdate)
+{
+ char *sm = from_utf8(xmpp_getstatusmsg());
+ const char *info = settings_opt_get("info");
+
+ werase(mainstatusWnd);
+ if (info) {
+ char *info_locale = from_utf8(info);
+ mvwprintw(mainstatusWnd, 0, 0, "%c[%c] %s: %s",
+ (unread_msg(NULL) ? '#' : ' '),
+ imstatus2char[xmpp_getstatus()],
+ info_locale, (sm ? sm : ""));
+ g_free(info_locale);
+ } else
+ mvwprintw(mainstatusWnd, 0, 0, "%c[%c] %s",
+ (unread_msg(NULL) ? '#' : ' '),
+ imstatus2char[xmpp_getstatus()], (sm ? sm : ""));
+ if (forceupdate) {
+ top_panel(inputPanel);
+ update_panels();
+ }
+ g_free(sm);
+}
+
+// scr_DrawMainWindow()
+// Set fullinit to TRUE to also create panels. Set it to FALSE for a resize.
+//
+// I think it could be improved a _lot_ but I'm really not an ncurses
+// expert... :-\ Mikael.
+//
+void scr_DrawMainWindow(unsigned int fullinit)
+{
+ int requested_size;
+ gchar *ver, *message;
+ int chat_y_pos, chatstatus_y_pos, log_y_pos;
+ int roster_x_pos, chat_x_pos;
+
+ Log_Win_Height = DEFAULT_LOG_WIN_HEIGHT;
+ requested_size = settings_opt_get_int("log_win_height");
+ if (requested_size > 0) {
+ if (maxY > requested_size + 3)
+ Log_Win_Height = requested_size + 2;
+ else
+ Log_Win_Height = ((maxY > 5) ? (maxY - 2) : 3);
+ } else if (requested_size < 0) {
+ Log_Win_Height = 3;
+ }
+
+ if (maxY < Log_Win_Height+2) {
+ if (maxY < 5) {
+ Log_Win_Height = 3;
+ maxY = Log_Win_Height+2;
+ } else {
+ Log_Win_Height = maxY - 2;
+ }
+ }
+
+ if (roster_hidden) {
+ Roster_Width = 0;
+ } else {
+ requested_size = settings_opt_get_int("roster_width");
+ if (requested_size > 1)
+ Roster_Width = requested_size;
+ else if (requested_size == 1)
+ Roster_Width = 2;
+ else
+ Roster_Width = DEFAULT_ROSTER_WIDTH;
+ }
+
+ log_win_on_top = (settings_opt_get_int("log_win_on_top") == 1);
+ roster_win_on_right = (settings_opt_get_int("roster_win_on_right") == 1);
+
+ if (log_win_on_top) {
+ chat_y_pos = Log_Win_Height-1;
+ log_y_pos = 0;
+ chatstatus_y_pos = Log_Win_Height-2;
+ } else {
+ chat_y_pos = 0;
+ log_y_pos = CHAT_WIN_HEIGHT+1;
+ chatstatus_y_pos = CHAT_WIN_HEIGHT;
+ }
+
+ if (roster_win_on_right) {
+ roster_x_pos = maxX - Roster_Width;
+ chat_x_pos = 0;
+ } else {
+ roster_x_pos = 0;
+ chat_x_pos = Roster_Width;
+ }
+
+ if (fullinit) {
+ if (!winbufhash)
+ winbufhash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ /* Create windows */
+ rosterWnd = newwin(CHAT_WIN_HEIGHT, Roster_Width, chat_y_pos, roster_x_pos);
+ chatWnd = newwin(CHAT_WIN_HEIGHT, maxX - Roster_Width, chat_y_pos,
+ chat_x_pos);
+ activechatWnd = newwin(CHAT_WIN_HEIGHT, maxX - Roster_Width, chat_y_pos,
+ chat_x_pos);
+ logWnd = newwin(Log_Win_Height-2, maxX, log_y_pos, 0);
+ chatstatusWnd = newwin(1, maxX, chatstatus_y_pos, 0);
+ mainstatusWnd = newwin(1, maxX, maxY-2, 0);
+ inputWnd = newwin(1, maxX, maxY-1, 0);
+ if (!rosterWnd || !chatWnd || !logWnd || !inputWnd) {
+ scr_TerminateCurses();
+ fprintf(stderr, "Cannot create windows!\n");
+ exit(EXIT_FAILURE);
+ }
+ wbkgd(rosterWnd, get_color(COLOR_GENERAL));
+ wbkgd(chatWnd, get_color(COLOR_GENERAL));
+ wbkgd(activechatWnd, get_color(COLOR_GENERAL));
+ wbkgd(logWnd, get_color(COLOR_GENERAL));
+ wbkgd(chatstatusWnd, get_color(COLOR_STATUS));
+ wbkgd(mainstatusWnd, get_color(COLOR_STATUS));
+ } else {
+ /* Resize/move windows */
+ wresize(rosterWnd, CHAT_WIN_HEIGHT, Roster_Width);
+ wresize(chatWnd, CHAT_WIN_HEIGHT, maxX - Roster_Width);
+ wresize(logWnd, Log_Win_Height-2, maxX);
+
+ mvwin(chatWnd, chat_y_pos, chat_x_pos);
+ mvwin(rosterWnd, chat_y_pos, roster_x_pos);
+ mvwin(logWnd, log_y_pos, 0);
+
+ // Resize & move chat status window
+ wresize(chatstatusWnd, 1, maxX);
+ mvwin(chatstatusWnd, chatstatus_y_pos, 0);
+ // Resize & move main status window
+ wresize(mainstatusWnd, 1, maxX);
+ mvwin(mainstatusWnd, maxY-2, 0);
+ // Resize & move input line window
+ wresize(inputWnd, 1, maxX);
+ mvwin(inputWnd, maxY-1, 0);
+
+ werase(chatWnd);
+ }
+
+ /* Draw/init windows */
+
+ ver = mcabber_version();
+ message = g_strdup_printf("MCabber version %s.\n", ver);
+ mvwprintw(chatWnd, 0, 0, message);
+ mvwprintw(chatWnd, 1, 0, "http://mcabber.com/");
+ g_free(ver);
+ g_free(message);
+
+ // Auto-scrolling in log window
+ scrollok(logWnd, TRUE);
+
+
+ if (fullinit) {
+ // Enable keypad (+ special keys)
+ keypad(inputWnd, TRUE);
+#ifdef __MirBSD__
+ wtimeout(inputWnd, 50 /* ms */);
+#else
+ nodelay(inputWnd, TRUE);
+#endif
+
+ // Create panels
+ rosterPanel = new_panel(rosterWnd);
+ chatPanel = new_panel(chatWnd);
+ activechatPanel = new_panel(activechatWnd);
+ logPanel = new_panel(logWnd);
+ chatstatusPanel = new_panel(chatstatusWnd);
+ mainstatusPanel = new_panel(mainstatusWnd);
+ inputPanel = new_panel(inputWnd);
+
+ // Build the buddylist at least once, to make sure the special buffer
+ // is added
+ buddylist_build();
+
+ // Init prev_chatwidth; this variable will be used to prevent us
+ // from rewrapping buffers when the width doesn't change.
+ prev_chatwidth = maxX - Roster_Width - scr_getprefixwidth();
+ // Wrap existing status buffer lines
+ hbuf_rebuild(&statushbuf, prev_chatwidth);
+
+#ifndef UNICODE
+ if (utf8_mode)
+ scr_LogPrint(LPRINT_NORMAL,
+ "WARNING: Compiled without full UTF-8 support!");
+#endif
+ } else {
+ // Update panels
+ replace_panel(rosterPanel, rosterWnd);
+ replace_panel(chatPanel, chatWnd);
+ replace_panel(logPanel, logWnd);
+ replace_panel(chatstatusPanel, chatstatusWnd);
+ replace_panel(mainstatusPanel, mainstatusWnd);
+ replace_panel(inputPanel, inputWnd);
+ }
+
+ // We'll need to redraw the roster
+ update_roster = TRUE;
+ return;
+}
+
+static void resize_win_buffer(gpointer key, gpointer value, gpointer data)
+{
+ winbuf *wbp = value;
+ struct dimensions *dim = data;
+ int chat_x_pos, chat_y_pos;
+ int new_chatwidth;
+
+ if (!(wbp && wbp->win))
+ return;
+
+ if (log_win_on_top)
+ chat_y_pos = Log_Win_Height-1;
+ else
+ chat_y_pos = 0;
+
+ if (roster_win_on_right)
+ chat_x_pos = 0;
+ else
+ chat_x_pos = Roster_Width;
+
+ // Resize/move buddy window
+ wresize(wbp->win, dim->l, dim->c);
+ mvwin(wbp->win, chat_y_pos, chat_x_pos);
+ werase(wbp->win);
+ // If a panel exists, replace the old window with the new
+ if (wbp->panel)
+ replace_panel(wbp->panel, wbp->win);
+ // Redo line wrapping
+ wbp->bd->top = hbuf_previous_persistent(wbp->bd->top);
+
+ new_chatwidth = maxX - Roster_Width - scr_getprefixwidth();
+ if (new_chatwidth != prev_chatwidth)
+ hbuf_rebuild(&wbp->bd->hbuf, new_chatwidth);
+}
+
+// scr_Resize()
+// Function called when the window is resized.
+// - Resize windows
+// - Rewrap lines in each buddy buffer
+void scr_Resize(void)
+{
+ struct dimensions dim;
+
+ // First, update the global variables
+ getmaxyx(stdscr, maxY, maxX);
+ // scr_DrawMainWindow() will take care of maxY and Log_Win_Height
+
+ // Make sure the cursor stays inside the window
+ check_offset(0);
+
+ // Resize windows and update panels
+ scr_DrawMainWindow(FALSE);
+
+ // Resize all buddy windows
+ dim.l = CHAT_WIN_HEIGHT;
+ dim.c = maxX - Roster_Width;
+ if (dim.c < 1)
+ dim.c = 1;
+
+ // Resize all buffers
+ g_hash_table_foreach(winbufhash, resize_win_buffer, &dim);
+
+ // Resize/move special status buffer
+ if (statusWindow)
+ resize_win_buffer(NULL, statusWindow, &dim);
+
+ // Update prev_chatwidth, now that all buffers have been resized
+ prev_chatwidth = maxX - Roster_Width - scr_getprefixwidth();
+
+ // Refresh current buddy window
+ if (chatmode)
+ scr_ShowBuddyWindow();
+}
+
+// scr_UpdateChatStatus(forceupdate)
+// Redraw the buddy status bar.
+// Set forceupdate to TRUE if update_panels() must be called.
+void scr_UpdateChatStatus(int forceupdate)
+{
+ unsigned short btype, isgrp, ismuc, isspe;
+ const char *btypetext = "Unknown";
+ const char *fullname;
+ const char *msg = NULL;
+ char status;
+ char *buf, *buf_locale;
+
+ // Usually we need to update the bottom status line too,
+ // at least to refresh the pending message flag.
+ scr_UpdateMainStatus(FALSE);
+
+ // Clear the line
+ werase(chatstatusWnd);
+
+ if (!current_buddy) {
+ if (forceupdate) {
+ update_panels();
+ }
+ return;
+ }
+
+ fullname = buddy_getname(BUDDATA(current_buddy));
+ btype = buddy_gettype(BUDDATA(current_buddy));
+
+ isgrp = ismuc = isspe = 0;
+ if (btype & ROSTER_TYPE_USER) {
+ btypetext = "Buddy";
+ } else if (btype & ROSTER_TYPE_GROUP) {
+ btypetext = "Group";
+ isgrp = 1;
+ } else if (btype & ROSTER_TYPE_AGENT) {
+ btypetext = "Agent";
+ } else if (btype & ROSTER_TYPE_ROOM) {
+ btypetext = "Room";
+ ismuc = 1;
+ } else if (btype & ROSTER_TYPE_SPECIAL) {
+ btypetext = "Special buffer";
+ isspe = 1;
+ }
+
+ if (chatmode) {
+ wprintw(chatstatusWnd, "~");
+ } else {
+ unsigned short bflags = buddy_getflags(BUDDATA(current_buddy));
+ if (bflags & ROSTER_FLAG_MSG) {
+ // There is an unread message from the current buddy
+ wprintw(chatstatusWnd, "#");
+ }
+ }
+
+ if (chatmode && !isgrp) {
+ winbuf *win_entry;
+ win_entry = scr_SearchWindow(buddy_getjid(BUDDATA(current_buddy)), isspe);
+ if (win_entry && win_entry->bd->lock)
+ mvwprintw(chatstatusWnd, 0, 0, "*");
+ }
+
+ if (isgrp || isspe) {
+ buf_locale = from_utf8(fullname);
+ mvwprintw(chatstatusWnd, 0, 5, "%s: %s", btypetext, buf_locale);
+ g_free(buf_locale);
+ if (forceupdate) {
+ update_panels();
+ }
+ return;
+ }
+
+ status = '?';
+
+ if (ismuc) {
+ if (buddy_getinsideroom(BUDDATA(current_buddy)))
+ status = 'C';
+ else
+ status = 'x';
+ } else if (xmpp_getstatus() != offline) {
+ enum imstatus budstate;
+ budstate = buddy_getstatus(BUDDATA(current_buddy), NULL);
+ if (budstate < imstatus_size)
+ status = imstatus2char[budstate];
+ }
+
+ // No status message for MUC rooms
+ if (!ismuc) {
+ GSList *resources, *p_res, *p_next_res;
+ resources = buddy_getresources(BUDDATA(current_buddy));
+
+ for (p_res = resources ; p_res ; p_res = p_next_res) {
+ p_next_res = g_slist_next(p_res);
+ // Store the status message of the latest resource (highest priority)
+ if (!p_next_res)
+ msg = buddy_getstatusmsg(BUDDATA(current_buddy), p_res->data);
+ g_free(p_res->data);
+ }
+ g_slist_free(resources);
+ } else {
+ msg = buddy_gettopic(BUDDATA(current_buddy));
+ }
+
+ if (msg)
+ buf = g_strdup_printf("[%c] %s: %s -- %s", status, btypetext, fullname, msg);
+ else
+ buf = g_strdup_printf("[%c] %s: %s", status, btypetext, fullname);
+ replace_nl_with_dots(buf);
+ buf_locale = from_utf8(buf);
+ mvwprintw(chatstatusWnd, 0, 1, "%s", buf_locale);
+ g_free(buf_locale);
+ g_free(buf);
+
+ // Display chatstates of the contact, if available.
+ if (btype & ROSTER_TYPE_USER) {
+ char eventchar = 0;
+ guint event;
+
+ // We do not specify the resource here, so one of the resources with the
+ // highest priority will be used.
+ event = buddy_resource_getevents(BUDDATA(current_buddy), NULL);
+
+ if (event == ROSTER_EVENT_ACTIVE)
+ eventchar = 'A';
+ else if (event == ROSTER_EVENT_COMPOSING)
+ eventchar = 'C';
+ else if (event == ROSTER_EVENT_PAUSED)
+ eventchar = 'P';
+ else if (event == ROSTER_EVENT_INACTIVE)
+ eventchar = 'I';
+ else if (event == ROSTER_EVENT_GONE)
+ eventchar = 'G';
+
+ if (eventchar)
+ mvwprintw(chatstatusWnd, 0, maxX-3, "[%c]", eventchar);
+ }
+
+
+ if (forceupdate) {
+ update_panels();
+ }
+}
+
+void increment_if_buddy_not_filtered(gpointer rosterdata, void *param)
+{
+ int *p = param;
+ if (buddylist_is_status_filtered(buddy_getstatus(rosterdata, NULL)))
+ *p=*p+1;
+}
+
+// scr_DrawRoster()
+// Display the buddylist (not really the roster) on the screen
+void scr_DrawRoster(void)
+{
+ static int offset = 0;
+ char *name, *rline;
+ int maxx, maxy;
+ GList *buddy;
+ int i, n;
+ int rOffset;
+ int cursor_backup;
+ char status, pending;
+ enum imstatus currentstatus = xmpp_getstatus();
+ int x_pos;
+
+ // We can reset update_roster
+ update_roster = FALSE;
+
+ getmaxyx(rosterWnd, maxy, maxx);
+ maxx--; // Last char is for vertical border
+
+ cursor_backup = curs_set(0);
+
+ if (!buddylist)
+ offset = 0;
+ else
+ scr_UpdateChatStatus(FALSE);
+
+ // Cleanup of roster window
+ werase(rosterWnd);
+
+ if (Roster_Width) {
+ int line_x_pos = roster_win_on_right ? 0 : Roster_Width-1;
+ // Redraw the vertical line (not very good...)
+ wattrset(rosterWnd, get_color(COLOR_GENERAL));
+ for (i=0 ; i < CHAT_WIN_HEIGHT ; i++)
+ mvwaddch(rosterWnd, i, line_x_pos, ACS_VLINE);
+ }
+
+ // Leave now if buddylist is empty or the roster is hidden
+ if (!buddylist || !Roster_Width) {
+ update_panels();
+ curs_set(cursor_backup);
+ return;
+ }
+
+ // Update offset if necessary
+ // a) Try to show as many buddylist items as possible
+ i = g_list_length(buddylist) - maxy;
+ if (i < 0)
+ i = 0;
+ if (i < offset)
+ offset = i;
+ // b) Make sure the current_buddy is visible
+ i = g_list_position(buddylist, current_buddy);
+ if (i == -1) { // This is bad
+ scr_LogPrint(LPRINT_NORMAL, "Doh! Can't find current selected buddy!!");
+ curs_set(cursor_backup);
+ return;
+ } else if (i < offset) {
+ offset = i;
+ } else if (i+1 > offset + maxy) {
+ offset = i + 1 - maxy;
+ }
+
+ if (roster_win_on_right)
+ x_pos = 1; // 1 char offset (vertical line)
+ else
+ x_pos = 0;
+
+ name = g_new0(char, 4*Roster_Width);
+ rline = g_new0(char, 4*Roster_Width+1);
+
+ buddy = buddylist;
+ rOffset = offset;
+
+ for (i=0; i<maxy && buddy; buddy = g_list_next(buddy)) {
+ unsigned short bflags, btype, ismsg, isgrp, ismuc, ishid, isspe;
+ gchar *rline_locale;
+ GSList *resources, *p_res;
+
+ bflags = buddy_getflags(BUDDATA(buddy));
+ btype = buddy_gettype(BUDDATA(buddy));
+
+ ismsg = bflags & ROSTER_FLAG_MSG;
+ ishid = bflags & ROSTER_FLAG_HIDE;
+ isgrp = btype & ROSTER_TYPE_GROUP;
+ ismuc = btype & ROSTER_TYPE_ROOM;
+ isspe = btype & ROSTER_TYPE_SPECIAL;
+
+ if (rOffset > 0) {
+ rOffset--;
+ continue;
+ }
+
+ status = '?';
+ pending = ' ';
+
+ resources = buddy_getresources(BUDDATA(buddy));
+ for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
+ guint events = buddy_resource_getevents(BUDDATA(buddy),
+ p_res ? p_res->data : "");
+ if ((events & ROSTER_EVENT_PAUSED) && pending != '+')
+ pending = '.';
+ if (events & ROSTER_EVENT_COMPOSING)
+ pending = '+';
+ g_free(p_res->data);
+ }
+ g_slist_free(resources);
+
+ // Display message notice if there is a message flag, but not
+ // for unfolded groups.
+ if (ismsg && (!isgrp || ishid)) {
+ pending = '#';
+ }
+
+ if (ismuc) {
+ if (buddy_getinsideroom(BUDDATA(buddy)))
+ status = 'C';
+ else
+ status = 'x';
+ } else if (currentstatus != offline) {
+ enum imstatus budstate;
+ budstate = buddy_getstatus(BUDDATA(buddy), NULL);
+ if (budstate < imstatus_size)
+ status = imstatus2char[budstate];
+ }
+ if (buddy == current_buddy) {
+ if (pending == '#')
+ wattrset(rosterWnd, get_color(COLOR_ROSTERSELNMSG));
+ else
+ wattrset(rosterWnd, get_color(COLOR_ROSTERSEL));
+ // The 3 following lines aim at coloring the whole line
+ wmove(rosterWnd, i, x_pos);
+ for (n = 0; n < maxx; n++)
+ waddch(rosterWnd, ' ');
+ } else {
+ if (pending == '#')
+ wattrset(rosterWnd, get_color(COLOR_ROSTERNMSG));
+ else {
+ int color = get_color(COLOR_ROSTER);
+ if ((!isspe) && (!isgrp)) {//Look for color rules
+ GSList *head;
+ const char *jid = buddy_getjid(BUDDATA(buddy));
+ for (head = rostercolrules; head; head = g_slist_next(head)) {
+ rostercolor *rc = head->data;
+ if (g_pattern_match_string(rc->compiled, jid) &&
+ (!strcmp("*", rc->status) || strchr(rc->status, status))) {
+ color = compose_color(rc->color);
+ break;
+ }
+ }
+ }
+ wattrset(rosterWnd, color);
+ }
+ }
+
+ if (Roster_Width > 7)
+ g_utf8_strncpy(name, buddy_getname(BUDDATA(buddy)), Roster_Width-7);
+ else
+ name[0] = 0;
+
+ if (isgrp) {
+ if (ishid) {
+ int group_count = 0;
+ foreach_group_member(BUDDATA(buddy), increment_if_buddy_not_filtered,
+ &group_count);
+ snprintf(rline, 4*Roster_Width, " %c+++ %s (%i)", pending, name,
+ group_count);
+ /* Do not display the item count if there isn't enough space */
+ if (g_utf8_strlen(rline, 4*Roster_Width) >= Roster_Width)
+ snprintf(rline, 4*Roster_Width, " %c--- %s", pending, name);
+ }
+ else
+ snprintf(rline, 4*Roster_Width, " %c--- %s", pending, name);
+ } else if (isspe) {
+ snprintf(rline, 4*Roster_Width, " %c%s", pending, name);
+ } else {
+ char sepleft = '[';
+ char sepright = ']';
+ if (btype & ROSTER_TYPE_USER) {
+ guint subtype = buddy_getsubscription(BUDDATA(buddy));
+ if (status == '_' && !(subtype & sub_to))
+ status = '?';
+ if (!(subtype & sub_from)) {
+ sepleft = '{';
+ sepright = '}';
+ }
+ }
+
+ snprintf(rline, 4*Roster_Width,
+ " %c%c%c%c %s", pending, sepleft, status, sepright, name);
+ }
+
+ rline_locale = from_utf8(rline);
+ mvwprintw(rosterWnd, i, x_pos, "%s", rline_locale);
+ g_free(rline_locale);
+ i++;
+ }
+
+ g_free(rline);
+ g_free(name);
+ top_panel(inputPanel);
+ update_panels();
+ curs_set(cursor_backup);
+}
+
+// scr_RosterVisibility(status)
+// Set the roster visibility:
+// status=1 Show roster
+// status=0 Hide roster
+// status=-1 Toggle roster status
+void scr_RosterVisibility(int status)
+{
+ int old_roster_status = roster_hidden;
+
+ if (status > 0)
+ roster_hidden = FALSE;
+ else if (status == 0)
+ roster_hidden = TRUE;
+ else
+ roster_hidden = !roster_hidden;
+
+ if (roster_hidden != old_roster_status) {
+ // Recalculate windows size and redraw
+ scr_Resize();
+ redrawwin(stdscr);
+ }
+}
+
+#ifdef HAVE_GLIB_REGEX
+static inline void scr_LogUrls(const gchar *string)
+{
+ GMatchInfo *match_info;
+
+ g_regex_match_full(url_regex, string, -1, 0, 0, &match_info, NULL);
+ while (g_match_info_matches(match_info)) {
+ gchar *url = g_match_info_fetch(match_info, 0);
+ scr_print_logwindow(url);
+ g_free(url);
+ g_match_info_next(match_info, NULL);
+ }
+ g_match_info_free(match_info);
+}
+#endif
+
+void scr_WriteMessage(const char *bjid, const char *text,
+ time_t timestamp, guint prefix_flags,
+ unsigned mucnicklen, gpointer xep184)
+{
+ char *xtext;
+
+ if (!timestamp) timestamp = time(NULL);
+
+ xtext = ut_expand_tabs(text); // Expand tabs and filter out some chars
+
+ scr_WriteInWindow(bjid, xtext, timestamp, prefix_flags, FALSE, mucnicklen,
+ xep184);
+
+ if (xtext != (char*)text)
+ g_free(xtext);
+}
+
+// If prefix is NULL, HBB_PREFIX_IN is supposed.
+void scr_WriteIncomingMessage(const char *jidfrom, const char *text,
+ time_t timestamp, guint prefix, unsigned mucnicklen)
+{
+ if (!(prefix &
+ ~HBB_PREFIX_NOFLAG & ~HBB_PREFIX_HLIGHT & ~HBB_PREFIX_HLIGHT_OUT &
+ ~HBB_PREFIX_PGPCRYPT & ~HBB_PREFIX_OTRCRYPT))
+ prefix |= HBB_PREFIX_IN;
+
+#ifdef HAVE_GLIB_REGEX
+ if (url_regex)
+ scr_LogUrls(text);
+#endif
+ scr_WriteMessage(jidfrom, text, timestamp, prefix, mucnicklen, NULL);
+}
+
+void scr_WriteOutgoingMessage(const char *jidto, const char *text, guint prefix,
+ gpointer xep184)
+{
+ GSList *roster_elt;
+ roster_elt = roster_find(jidto, jidsearch,
+ ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM);
+
+ scr_WriteMessage(jidto, text,
+ 0, prefix|HBB_PREFIX_OUT|HBB_PREFIX_HLIGHT_OUT, 0, xep184);
+
+ // Show jidto's buffer unless the buddy is not in the buddylist
+ if (roster_elt && g_list_position(buddylist, roster_elt->data) != -1)
+ scr_ShowWindow(jidto, FALSE);
+}
+
+void scr_RemoveReceiptFlag(const char *bjid, gpointer xep184)
+{
+ winbuf *win_entry = scr_SearchWindow(bjid, FALSE);
+ if (win_entry) {
+ hbuf_remove_receipt(win_entry->bd->hbuf, xep184);
+ if (chatmode && (buddy_search_jid(bjid) == current_buddy))
+ scr_UpdateBuddyWindow();
+ }
+}
+
+static inline void set_autoaway(bool setaway)
+{
+ static enum imstatus oldstatus;
+ static char *oldmsg;
+ Autoaway = setaway;
+
+ if (setaway) {
+ const char *msg, *prevmsg;
+ oldstatus = xmpp_getstatus();
+ if (oldmsg) {
+ g_free(oldmsg);
+ oldmsg = NULL;
+ }
+ prevmsg = xmpp_getstatusmsg();
+ msg = settings_opt_get("message_autoaway");
+ if (!msg)
+ msg = prevmsg;
+ if (prevmsg)
+ oldmsg = g_strdup(prevmsg);
+ xmpp_setstatus(away, NULL, msg, FALSE);
+ } else {
+ // Back
+ xmpp_setstatus(oldstatus, NULL, (oldmsg ? oldmsg : ""), FALSE);
+ if (oldmsg) {
+ g_free(oldmsg);
+ oldmsg = NULL;
+ }
+ }
+}
+
+// set_chatstate(state)
+// Set the current chat state (0=active, 1=composing, 2=paused)
+// If the chat state has changed, call xmpp_send_chatstate()
+static inline void set_chatstate(int state)
+{
+#if defined JEP0022 || defined JEP0085
+ if (chatstates_disabled)
+ return;
+ if (!chatmode)
+ state = 0;
+ if (state != chatstate) {
+ chatstate = state;
+ if (current_buddy &&
+ buddy_gettype(BUDDATA(current_buddy)) == ROSTER_TYPE_USER) {
+ guint jep_state;
+ if (chatstate == 1) {
+ if (chatstate_timeout_id == 0)
+ chatstate_timeout_id = g_timeout_add_seconds(1,
+ scr_ChatStatesTimeout,
+ NULL);
+ jep_state = ROSTER_EVENT_COMPOSING;
+ }
+ else if (chatstate == 2)
+ jep_state = ROSTER_EVENT_PAUSED;
+ else
+ jep_state = ROSTER_EVENT_ACTIVE;
+ xmpp_send_chatstate(BUDDATA(current_buddy), jep_state);
+ }
+ if (!chatstate)
+ chatstate_timestamp = 0;
+ }
+#endif
+}
+
+#if defined JEP0022 || defined JEP0085
+gboolean scr_ChatStatesTimeout(void)
+{
+ time_t now;
+ time(&now);
+ // Check if we're currently composing...
+ if (chatstate != 1 || !chatstate_timestamp) {
+ chatstate_timeout_id = 0;
+ return FALSE;
+ }
+
+ // If the timeout is reached, let's change the state right now.
+ if (now >= chatstate_timestamp + COMPOSING_TIMEOUT) {
+ chatstate_timestamp = now;
+ set_chatstate(2);
+ chatstate_timeout_id = 0;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+// Check if we should enter/leave automatic away status
+void scr_CheckAutoAway(int activity)
+{
+ enum imstatus cur_st;
+ unsigned int autoaway_timeout = settings_opt_get_int("autoaway");
+
+ if (Autoaway && activity) set_autoaway(FALSE);
+ if (!autoaway_timeout) return;
+ if (!LastActivity || activity) time(&LastActivity);
+
+ cur_st = xmpp_getstatus();
+ // Auto-away is disabled for the following states
+ if ((cur_st != available) && (cur_st != freeforchat))
+ return;
+
+ if (!activity) {
+ time_t now;
+ time(&now);
+ if (!Autoaway && (now > LastActivity + (time_t)autoaway_timeout))
+ set_autoaway(TRUE);
+ }
+}
+
+// set_current_buddy(newbuddy)
+// Set the current_buddy to newbuddy (if not NULL)
+// Lock the newbuddy, and unlock the previous current_buddy
+static void set_current_buddy(GList *newbuddy)
+{
+ enum imstatus prev_st = imstatus_size;
+ /* prev_st initialized to imstatus_size, which is used as "undef" value.
+ * We are sure prev_st will get a different status value after the
+ * buddy_getstatus() call.
+ */
+
+ if (!current_buddy || !newbuddy) return;
+ if (newbuddy == current_buddy) return;
+
+ // We're moving to another buddy. We're thus inactive wrt current_buddy.
+ set_chatstate(0);
+ // We don't want the chatstate to be changed again right now.
+ lock_chatstate = TRUE;
+
+ prev_st = buddy_getstatus(BUDDATA(current_buddy), NULL);
+ buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
+ if (chatmode)
+ alternate_buddy = current_buddy;
+ current_buddy = newbuddy;
+ // Lock the buddy in the buddylist if we're in chat mode
+ if (chatmode)
+ buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE);
+ // We should rebuild the buddylist but not everytime
+ // Here we check if we were locking a buddy who is actually offline,
+ // and hide_offline_buddies is TRUE. In which case we need to rebuild.
+ if (!(buddylist_get_filter() & 1<<prev_st))
+ buddylist_build();
+ update_roster = TRUE;
+}
+
+// scr_RosterTop()
+// Go to the first buddy in the buddylist
+void scr_RosterTop(void)
+{
+ set_current_buddy(buddylist);
+ if (chatmode)
+ scr_ShowBuddyWindow();
+}
+
+// scr_RosterBottom()
+// Go to the last buddy in the buddylist
+void scr_RosterBottom(void)
+{
+ set_current_buddy(g_list_last(buddylist));
+ if (chatmode)
+ scr_ShowBuddyWindow();
+}
+
+// scr_RosterUpDown(updown, n)
+// Go to the nth next buddy in the buddylist
+// (up if updown == -1, down if updown == 1)
+void scr_RosterUpDown(int updown, unsigned int n)
+{
+ unsigned int i;
+
+ if (updown < 0) {
+ for (i = 0; i < n; i++)
+ set_current_buddy(g_list_previous(current_buddy));
+ } else {
+ for (i = 0; i < n; i++)
+ set_current_buddy(g_list_next(current_buddy));
+ }
+ if (chatmode)
+ scr_ShowBuddyWindow();
+}
+
+// scr_RosterPrevGroup()
+// Go to the previous group in the buddylist
+void scr_RosterPrevGroup(void)
+{
+ GList *bud;
+
+ for (bud = current_buddy ; bud ; ) {
+ bud = g_list_previous(bud);
+ if (!bud)
+ break;
+ if (buddy_gettype(BUDDATA(bud)) & ROSTER_TYPE_GROUP) {
+ set_current_buddy(bud);
+ if (chatmode)
+ scr_ShowBuddyWindow();
+ break;
+ }
+ }
+}
+
+// scr_RosterNextGroup()
+// Go to the next group in the buddylist
+void scr_RosterNextGroup(void)
+{
+ GList *bud;
+
+ for (bud = current_buddy ; bud ; ) {
+ bud = g_list_next(bud);
+ if (!bud)
+ break;
+ if (buddy_gettype(BUDDATA(bud)) & ROSTER_TYPE_GROUP) {
+ set_current_buddy(bud);
+ if (chatmode)
+ scr_ShowBuddyWindow();
+ break;
+ }
+ }
+}
+
+// scr_RosterSearch(str)
+// Look forward for a buddy with jid/name containing str.
+void scr_RosterSearch(char *str)
+{
+ set_current_buddy(buddy_search(str));
+ if (chatmode)
+ scr_ShowBuddyWindow();
+}
+
+// scr_RosterJumpJid(bjid)
+// Jump to buddy bjid.
+// NOTE: With this function, the buddy is added to the roster if doesn't exist.
+void scr_RosterJumpJid(char *barejid)
+{
+ GSList *roster_elt;
+ // Look for an existing buddy
+ roster_elt = roster_find(barejid, jidsearch,
+ ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM);
+ // Create it if necessary
+ if (!roster_elt)
+ roster_elt = roster_add_user(barejid, NULL, NULL, ROSTER_TYPE_USER,
+ sub_none, -1);
+ // Set a lock to see it in the buddylist
+ buddy_setflags(BUDDATA(roster_elt), ROSTER_FLAG_LOCK, TRUE);
+ buddylist_build();
+ // Jump to the buddy
+ set_current_buddy(buddy_search_jid(barejid));
+ if (chatmode)
+ scr_ShowBuddyWindow();
+}
+
+// scr_RosterUnreadMessage(next)
+// Go to a new message. If next is not null, try to go to the next new
+// message. If it is not possible or if next is NULL, go to the first new
+// message from unread_list.
+void scr_RosterUnreadMessage(int next)
+{
+ gpointer unread_ptr;
+ gpointer refbuddata;
+ GList *nbuddy;
+
+ if (!current_buddy) return;
+
+ if (next) refbuddata = BUDDATA(current_buddy);
+ else refbuddata = NULL;
+
+ unread_ptr = unread_msg(refbuddata);
+ if (!unread_ptr) return;
+
+ if (!(buddy_gettype(unread_ptr) & ROSTER_TYPE_SPECIAL)) {
+ gpointer ngroup;
+ // If buddy is in a folded group, we need to expand it
+ ngroup = buddy_getgroup(unread_ptr);
+ if (buddy_getflags(ngroup) & ROSTER_FLAG_HIDE) {
+ buddy_setflags(ngroup, ROSTER_FLAG_HIDE, FALSE);
+ buddylist_build();
+ }
+ }
+
+ nbuddy = g_list_find(buddylist, unread_ptr);
+ if (nbuddy) {
+ set_current_buddy(nbuddy);
+ if (chatmode) scr_ShowBuddyWindow();
+ } else
+ scr_LogPrint(LPRINT_LOGNORM, "Error: nbuddy == NULL"); // should not happen
+}
+
+// scr_RosterJumpAlternate()
+// Try to jump to alternate (== previous) buddy
+void scr_RosterJumpAlternate(void)
+{
+ if (!alternate_buddy || g_list_position(buddylist, alternate_buddy) == -1)
+ return;
+ set_current_buddy(alternate_buddy);
+ if (chatmode)
+ scr_ShowBuddyWindow();
+}
+
+// scr_RosterDisplay(filter)
+// Set the roster filter mask. If filter is null/empty, the current
+// mask is displayed.
+void scr_RosterDisplay(const char *filter)
+{
+ guchar status;
+ enum imstatus budstate;
+ char strfilter[imstatus_size+1];
+ char *psfilter;
+
+ if (filter && *filter) {
+ int show_all = (*filter == '*');
+ status = 0;
+ for (budstate = 0; budstate < imstatus_size-1; budstate++)
+ if (strchr(filter, imstatus2char[budstate]) || show_all)
+ status |= 1<<budstate;
+ buddylist_set_filter(status);
+ buddylist_build();
+ update_roster = TRUE;
+ return;
+ }
+
+ // Display current filter
+ psfilter = strfilter;
+ status = buddylist_get_filter();
+ for (budstate = 0; budstate < imstatus_size-1; budstate++)
+ if (status & 1<<budstate)
+ *psfilter++ = imstatus2char[budstate];
+ *psfilter = '\0';
+ scr_LogPrint(LPRINT_NORMAL, "Roster status filter: %s", strfilter);
+}
+
+// scr_BufferScrollUpDown()
+// Scroll up/down the current buddy window,
+// - half a screen if nblines is 0,
+// - up if updown == -1, down if updown == 1
+void scr_BufferScrollUpDown(int updown, unsigned int nblines)
+{
+ winbuf *win_entry;
+ int n, nbl;
+ GList *hbuf_top;
+ guint isspe;
+
+ // Get win_entry
+ if (!current_buddy) return;
+
+ isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
+ win_entry = scr_SearchWindow(CURRENT_JID, isspe);
+ if (!win_entry) return;
+
+ if (!nblines) {
+ // Scroll half a screen (or less)
+ nbl = CHAT_WIN_HEIGHT/2;
+ } else {
+ nbl = nblines;
+ }
+ hbuf_top = win_entry->bd->top;
+
+ if (updown == -1) { // UP
+ if (!hbuf_top) {
+ hbuf_top = g_list_last(win_entry->bd->hbuf);
+ if (!win_entry->bd->cleared) {
+ if (!nblines) nbl = nbl*3 - 1;
+ else nbl += CHAT_WIN_HEIGHT - 1;
+ } else {
+ win_entry->bd->cleared = FALSE;
+ }
+ }
+ for (n=0 ; hbuf_top && n < nbl && g_list_previous(hbuf_top) ; n++)
+ hbuf_top = g_list_previous(hbuf_top);
+ win_entry->bd->top = hbuf_top;
+ } else { // DOWN
+ for (n=0 ; hbuf_top && n < nbl ; n++)
+ hbuf_top = g_list_next(hbuf_top);
+ win_entry->bd->top = hbuf_top;
+ // Check if we are at the bottom
+ for (n=0 ; hbuf_top && n < CHAT_WIN_HEIGHT-1 ; n++)
+ hbuf_top = g_list_next(hbuf_top);
+ if (!hbuf_top)
+ win_entry->bd->top = NULL; // End reached
+ }
+
+ // Refresh the window
+ scr_UpdateWindow(win_entry);
+
+ // Finished :)
+ update_panels();
+}
+
+// scr_BufferClear()
+// Clear the current buddy window (used for the /clear command)
+void scr_BufferClear(void)
+{
+ winbuf *win_entry;
+ guint isspe;
+
+ // Get win_entry
+ if (!current_buddy) return;
+ isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
+ win_entry = scr_SearchWindow(CURRENT_JID, isspe);
+ if (!win_entry) return;
+
+ win_entry->bd->cleared = TRUE;
+ win_entry->bd->top = NULL;
+
+ // Refresh the window
+ scr_UpdateWindow(win_entry);
+
+ // Finished :)
+ update_panels();
+}
+
+// buffer_purge()
+// key: winId/jid
+// value: winbuf structure
+// data: int, set to 1 if the buffer should be closed.
+// NOTE: does not work for special buffers.
+static void buffer_purge(gpointer key, gpointer value, gpointer data)
+{
+ int *p_closebuf = data;
+ winbuf *win_entry = value;
+
+ // Delete the current hbuf
+ hbuf_free(&win_entry->bd->hbuf);
+
+ if (*p_closebuf) {
+ g_hash_table_remove(winbufhash, key);
+ } else {
+ win_entry->bd->cleared = FALSE;
+ win_entry->bd->top = NULL;
+ }
+}
+
+// scr_BufferPurge(closebuf, jid)
+// Purge/Drop the current buddy buffer or jid's buffer if jid != NULL.
+// If closebuf is 1, close the buffer.
+void scr_BufferPurge(int closebuf, const char *jid)
+{
+ winbuf *win_entry;
+ guint isspe;
+ guint *p_closebuf;
+ const char *cjid;
+ guint hold_chatmode = FALSE;
+
+ if (jid) {
+ cjid = jid;
+ isspe = FALSE;
+ // If closebuf is TRUE, it's probably better not to leave chat mode
+ // if the change isn't related to the current buffer.
+ if (closebuf && current_buddy) {
+ if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL ||
+ strcasecmp(jid, CURRENT_JID))
+ hold_chatmode = TRUE;
+ }
+ } else {
+ // Get win_entry
+ if (!current_buddy) return;
+ cjid = CURRENT_JID;
+ isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
+ }
+ win_entry = scr_SearchWindow(cjid, isspe);
+ if (!win_entry) return;
+
+ if (!isspe) {
+ p_closebuf = g_new(guint, 1);
+ *p_closebuf = closebuf;
+ buffer_purge((gpointer)cjid, win_entry, p_closebuf);
+ g_free(p_closebuf);
+ if (closebuf && !hold_chatmode) {
+ scr_set_chatmode(FALSE);
+ currentWindow = NULL;
+ }
+ } else {
+ // (Special buffer)
+ // Reset the current hbuf
+ hbuf_free(&win_entry->bd->hbuf);
+ // Currently it can only be the status buffer
+ statushbuf = NULL;
+
+ win_entry->bd->cleared = FALSE;
+ win_entry->bd->top = NULL;
+ }
+
+ // Refresh the window
+ scr_UpdateBuddyWindow();
+
+ // Finished :)
+ update_panels();
+}
+
+void scr_BufferPurgeAll(int closebuf)
+{
+ guint *p_closebuf;
+ p_closebuf = g_new(guint, 1);
+
+ *p_closebuf = closebuf;
+ g_hash_table_foreach(winbufhash, buffer_purge, p_closebuf);
+ g_free(p_closebuf);
+
+ if (closebuf) {
+ scr_set_chatmode(FALSE);
+ currentWindow = NULL;
+ }
+
+ // Refresh the window
+ scr_UpdateBuddyWindow();
+
+ // Finished :)
+ update_panels();
+}
+
+// scr_BufferScrollLock(lock)
+// Lock/unlock the current buddy buffer
+// lock = 1 : lock
+// lock = 0 : unlock
+// lock = -1: toggle lock status
+void scr_BufferScrollLock(int lock)
+{
+ winbuf *win_entry;
+ guint isspe;
+
+ // Get win_entry
+ if (!current_buddy) return;
+ isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
+ win_entry = scr_SearchWindow(CURRENT_JID, isspe);
+ if (!win_entry) return;
+
+ if (lock == -1)
+ lock = !win_entry->bd->lock;
+
+ if (lock) {
+ win_entry->bd->lock = TRUE;
+ } else {
+ win_entry->bd->lock = FALSE;
+ //win_entry->bd->cleared = FALSE;
+ if (isspe || (buddy_getflags(BUDDATA(current_buddy)) & ROSTER_FLAG_MSG))
+ win_entry->bd->top = NULL;
+ }
+
+ // If chatmode is disabled and we're at the bottom of the buffer,
+ // we need to set the "top" line, so we need to call scr_ShowBuddyWindow()
+ // at least once. (Maybe it will cause a double refresh...)
+ if (!chatmode && !win_entry->bd->top) {
+ chatmode = TRUE;
+ scr_ShowBuddyWindow();
+ chatmode = FALSE;
+ }
+
+ // Refresh the window
+ scr_UpdateBuddyWindow();
+
+ // Finished :)
+ update_panels();
+}
+
+// scr_BufferTopBottom()
+// Jump to the head/tail of the current buddy window
+// (top if topbottom == -1, bottom topbottom == 1)
+void scr_BufferTopBottom(int topbottom)
+{
+ winbuf *win_entry;
+ guint isspe;
+
+ // Get win_entry
+ if (!current_buddy) return;
+ isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
+ win_entry = scr_SearchWindow(CURRENT_JID, isspe);
+ if (!win_entry) return;
+
+ win_entry->bd->cleared = FALSE;
+ if (topbottom == 1)
+ win_entry->bd->top = NULL;
+ else
+ win_entry->bd->top = g_list_first(win_entry->bd->hbuf);
+
+ // Refresh the window
+ scr_UpdateWindow(win_entry);
+
+ // Finished :)
+ update_panels();
+}
+
+// scr_BufferSearch(direction, text)
+// Jump to the next line containing text
+// (backward search if direction == -1, forward if topbottom == 1)
+void scr_BufferSearch(int direction, const char *text)
+{
+ winbuf *win_entry;
+ GList *current_line, *search_res;
+ guint isspe;
+
+ // Get win_entry
+ if (!current_buddy) return;
+ isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
+ win_entry = scr_SearchWindow(CURRENT_JID, isspe);
+ if (!win_entry) return;
+
+ if (win_entry->bd->top)
+ current_line = win_entry->bd->top;
+ else
+ current_line = g_list_last(win_entry->bd->hbuf);
+
+ search_res = hbuf_search(current_line, direction, text);
+
+ if (search_res) {
+ win_entry->bd->cleared = FALSE;
+ win_entry->bd->top = search_res;
+
+ // Refresh the window
+ scr_UpdateWindow(win_entry);
+
+ // Finished :)
+ update_panels();
+ } else
+ scr_LogPrint(LPRINT_NORMAL, "Search string not found");
+}
+
+// scr_BufferPercent(n)
+// Jump to the specified position in the buffer, in %
+void scr_BufferPercent(int pc)
+{
+ winbuf *win_entry;
+ GList *search_res;
+ guint isspe;
+
+ // Get win_entry
+ if (!current_buddy) return;
+ isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
+ win_entry = scr_SearchWindow(CURRENT_JID, isspe);
+ if (!win_entry) return;
+
+ if (pc < 0 || pc > 100) {
+ scr_LogPrint(LPRINT_NORMAL, "Bad % value");
+ return;
+ }
+
+ search_res = hbuf_jump_percent(win_entry->bd->hbuf, pc);
+
+ win_entry->bd->cleared = FALSE;
+ win_entry->bd->top = search_res;
+
+ // Refresh the window
+ scr_UpdateWindow(win_entry);
+
+ // Finished :)
+ update_panels();
+}
+
+// scr_BufferDate(t)
+// Jump to the first line after date t in the buffer
+// t is a date in seconds since `00:00:00 1970-01-01 UTC'
+void scr_BufferDate(time_t t)
+{
+ winbuf *win_entry;
+ GList *search_res;
+ guint isspe;
+
+ // Get win_entry
+ if (!current_buddy) return;
+ isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
+ win_entry = scr_SearchWindow(CURRENT_JID, isspe);
+ if (!win_entry) return;
+
+ search_res = hbuf_jump_date(win_entry->bd->hbuf, t);
+
+ win_entry->bd->cleared = FALSE;
+ win_entry->bd->top = search_res;
+
+ // Refresh the window
+ scr_UpdateWindow(win_entry);
+
+ // Finished :)
+ update_panels();
+}
+
+void scr_BufferDump(const char *file)
+{
+ char *extfname;
+
+ if (!currentWindow) {
+ scr_LogPrint(LPRINT_NORMAL, "No current buffer!");
+ return;
+ }
+
+ if (!file || !*file) {
+ scr_LogPrint(LPRINT_NORMAL, "Missing parameter (file name)!");
+ return;
+ }
+
+ extfname = expand_filename(file);
+ hbuf_dump_to_file(currentWindow->bd->hbuf, extfname);
+ g_free(extfname);
+}
+
+// buffer_list()
+// key: winId/jid
+// value: winbuf structure
+// data: none.
+static void buffer_list(gpointer key, gpointer value, gpointer data)
+{
+ GList *head;
+ winbuf *win_entry = value;
+
+ head = g_list_first(win_entry->bd->hbuf);
+
+ scr_LogPrint(LPRINT_NORMAL, " %s (%u/%u)", key,
+ g_list_length(head), hbuf_get_blocks_number(head));
+}
+
+void scr_BufferList(void)
+{
+ scr_LogPrint(LPRINT_NORMAL, "Buffer list:");
+ buffer_list("[status]", statusWindow, NULL);
+ g_hash_table_foreach(winbufhash, buffer_list, NULL);
+ scr_LogPrint(LPRINT_NORMAL, "End of buffer list.");
+ scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
+ update_roster = TRUE;
+}
+
+// scr_set_chatmode()
+// Public function to (un)set chatmode...
+inline void scr_set_chatmode(int enable)
+{
+ chatmode = enable;
+ scr_UpdateChatStatus(TRUE);
+}
+
+// scr_get_chatmode()
+// Public function to get chatmode state.
+inline int scr_get_chatmode(void)
+{
+ return chatmode;
+}
+
+// scr_get_multimode()
+// Public function to get multimode status...
+inline int scr_get_multimode(void)
+{
+ return multimode;
+}
+
+// scr_setmsgflag_if_needed(jid)
+// Set the message flag unless we're already in the jid buffer window
+void scr_setmsgflag_if_needed(const char *bjid, int special)
+{
+ const char *current_id;
+ bool iscurrentlocked = FALSE;
+
+ if (!bjid)
+ return;
+
+ if (current_buddy) {
+ if (special)
+ current_id = buddy_getname(BUDDATA(current_buddy));
+ else
+ current_id = buddy_getjid(BUDDATA(current_buddy));
+ if (current_id) {
+ winbuf *win_entry = scr_SearchWindow(current_id, special);
+ if (!win_entry) return;
+ iscurrentlocked = win_entry->bd->lock;
+ }
+ } else {
+ current_id = NULL;
+ }
+ if (!chatmode || !current_id || strcmp(bjid, current_id) || iscurrentlocked)
+ roster_msg_setflag(bjid, special, TRUE);
+}
+
+// scr_set_multimode()
+// Public function to (un)set multimode...
+// Convention:
+// 0 = disabled / 1 = multimode / 2 = multimode verbatim (commands disabled)
+void scr_set_multimode(int enable, char *subject)
+{
+ g_free(multiline);
+ multiline = NULL;
+
+ g_free(multimode_subj);
+ if (enable && subject)
+ multimode_subj = g_strdup(subject);
+ else
+ multimode_subj = NULL;
+
+ multimode = enable;
+}
+
+// scr_get_multiline()
+// Public function to get the current multi-line.
+const char *scr_get_multiline(void)
+{
+ if (multimode && multiline)
+ return multiline;
+ return NULL;
+}
+
+// scr_get_multimode_subj()
+// Public function to get the multi-line subject, if any.
+const char *scr_get_multimode_subj(void)
+{
+ if (multimode)
+ return multimode_subj;
+ return NULL;
+}
+
+// scr_append_multiline(line)
+// Public function to append a line to the current multi-line message.
+// Skip empty leading lines.
+void scr_append_multiline(const char *line)
+{
+ static int num;
+
+ if (!multimode) {
+ scr_LogPrint(LPRINT_NORMAL, "Error: Not in multi-line message mode!");
+ return;
+ }
+ if (multiline) {
+ int len = strlen(multiline)+strlen(line)+2;
+ if (len >= HBB_BLOCKSIZE - 1) {
+ // We don't handle single messages with size > HBB_BLOCKSIZE
+ // (see hbuf)
+ scr_LogPrint(LPRINT_NORMAL, "Your multi-line message is too big, "
+ "this line has not been added.");
+ scr_LogPrint(LPRINT_NORMAL, "Please send this part now...");
+ return;
+ }
+ if (num >= MULTILINE_MAX_LINE_NUMBER) {
+ // We don't allow too many lines; however the maximum is arbitrary
+ // (It should be < 1000 yet)
+ scr_LogPrint(LPRINT_NORMAL, "Your message has too many lines, "
+ "this one has not been added.");
+ scr_LogPrint(LPRINT_NORMAL, "Please send this part now...");
+ return;
+ }
+ multiline = g_renew(char, multiline, len);
+ strcat(multiline, "\n");
+ strcat(multiline, line);
+ num++;
+ } else {
+ // First message line (we skip leading empty lines)
+ num = 0;
+ if (line[0]) {
+ multiline = g_strdup(line);
+ num++;
+ } else
+ return;
+ }
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+ "Multi-line mode: line #%d added [%.25s...", num, line);
+}
+
+// scr_cmdhisto_addline()
+// Add a line to the inputLine history
+static inline void scr_cmdhisto_addline(char *line)
+{
+ int max_histo_lines;
+
+ if (!line || !*line)
+ return;
+
+ max_histo_lines = settings_opt_get_int("cmdhistory_lines");
+
+ if (max_histo_lines < 0)
+ max_histo_lines = 1;
+
+ if (max_histo_lines)
+ while (cmdhisto_nblines >= (guint)max_histo_lines) {
+ if (cmdhisto_cur && cmdhisto_cur == cmdhisto)
+ break;
+ g_free(cmdhisto->data);
+ cmdhisto = g_list_delete_link(cmdhisto, cmdhisto);
+ cmdhisto_nblines--;
+ }
+
+ cmdhisto = g_list_append(cmdhisto, g_strdup(line));
+ cmdhisto_nblines++;
+}
+
+// scr_cmdhisto_prev()
+// Look for previous line beginning w/ the given mask in the inputLine history
+// Returns NULL if none found
+static const char *scr_cmdhisto_prev(char *mask, guint len)
+{
+ GList *hl;
+ if (!cmdhisto_cur) {
+ hl = g_list_last(cmdhisto);
+ if (hl) { // backup current line
+ strncpy(cmdhisto_backup, mask, INPUTLINE_LENGTH);
+ }
+ } else {
+ hl = g_list_previous(cmdhisto_cur);
+ }
+ while (hl) {
+ if (!strncmp((char*)hl->data, mask, len)) {
+ // Found a match
+ cmdhisto_cur = hl;
+ return (const char*)hl->data;
+ }
+ hl = g_list_previous(hl);
+ }
+ return NULL;
+}
+
+// scr_cmdhisto_next()
+// Look for next line beginning w/ the given mask in the inputLine history
+// Returns NULL if none found
+static const char *scr_cmdhisto_next(char *mask, guint len)
+{
+ GList *hl;
+ if (!cmdhisto_cur) return NULL;
+ hl = cmdhisto_cur;
+ while ((hl = g_list_next(hl)) != NULL)
+ if (!strncmp((char*)hl->data, mask, len)) {
+ // Found a match
+ cmdhisto_cur = hl;
+ return (const char*)hl->data;
+ }
+ // If the "backuped" line matches, we'll use it
+ if (strncmp(cmdhisto_backup, mask, len)) return NULL; // No match
+ cmdhisto_cur = NULL;
+ return cmdhisto_backup;
+}
+
+// readline_transpose_chars()
+// Drag the character before point forward over the character at
+// point, moving point forward as well. If point is at the end of
+// the line, then this transposes the two characters before point.
+void readline_transpose_chars(void)
+{
+ char *c1, *c2;
+ unsigned a, b;
+
+ if (ptr_inputline == inputLine) return;
+
+ if (!*ptr_inputline) { // We're at EOL
+ // If line is only 1 char long, nothing to do...
+ if (ptr_inputline == prev_char(ptr_inputline, inputLine)) return;
+ // Transpose the two previous characters
+ c2 = prev_char(ptr_inputline, inputLine);
+ c1 = prev_char(c2, inputLine);
+ a = get_char(c1);
+ b = get_char(c2);
+ put_char(put_char(c1, b), a);
+ } else {
+ // Swap the two characters before the cursor and move right.
+ c2 = ptr_inputline;
+ c1 = prev_char(c2, inputLine);
+ a = get_char(c1);
+ b = get_char(c2);
+ put_char(put_char(c1, b), a);
+ check_offset(1);
+ }
+}
+
+void readline_forward_kill_word(void)
+{
+ char *c, *old = ptr_inputline;
+ int spaceallowed = 1;
+
+ if (! *ptr_inputline) return;
+
+ for (c = ptr_inputline ; *c ; c = next_char(c)) {
+ if (!iswalnum(get_char(c))) {
+ if (iswblank(get_char(c))) {
+ if (!spaceallowed) break;
+ } else spaceallowed = 0;
+ } else spaceallowed = 0;
+ }
+
+ // Modify the line
+ for (;;) {
+ *old = *c++;
+ if (!*old++) break;
+ }
+}
+
+// readline_backward_kill_word()
+// Kill the word before the cursor, in input line
+void readline_backward_kill_word(void)
+{
+ char *c, *old = ptr_inputline;
+ int spaceallowed = 1;
+
+ if (ptr_inputline == inputLine) return;
+
+ c = prev_char(ptr_inputline, inputLine);
+ for ( ; c > inputLine ; c = prev_char(c, inputLine)) {
+ if (!iswalnum(get_char(c))) {
+ if (iswblank(get_char(c))) {
+ if (!spaceallowed) break;
+ } else spaceallowed = 0;
+ } else spaceallowed = 0;
+ }
+
+ if (c == inputLine && *c == COMMAND_CHAR && old != c+1) {
+ c = next_char(c);
+ } else if (c != inputLine || iswblank(get_char(c))) {
+ if ((c < prev_char(ptr_inputline, inputLine)) && (!iswalnum(get_char(c))))
+ c = next_char(c);
+ }
+
+ // Modify the line
+ ptr_inputline = c;
+ for (;;) {
+ *c = *old++;
+ if (!*c++) break;
+ }
+ check_offset(-1);
+}
+
+// readline_backward_word()
+// Move back to the start of the current or previous word
+void readline_backward_word(void)
+{
+ int i = 0;
+
+ if (ptr_inputline == inputLine) return;
+
+ if (iswalnum(get_char(ptr_inputline)) &&
+ !iswalnum(get_char(prev_char(ptr_inputline, inputLine))))
+ i--;
+
+ for ( ;
+ ptr_inputline > inputLine;
+ ptr_inputline = prev_char(ptr_inputline, inputLine)) {
+ if (!iswalnum(get_char(ptr_inputline))) {
+ if (i) {
+ ptr_inputline = next_char(ptr_inputline);
+ break;
+ }
+ } else i++;
+ }
+
+ check_offset(-1);
+}
+
+// readline_forward_word()
+// Move forward to the end of the next word
+void readline_forward_word(void)
+{
+ int stopsymbol_allowed = 1;
+
+ while (*ptr_inputline) {
+ if (!iswalnum(get_char(ptr_inputline))) {
+ if (!stopsymbol_allowed) break;
+ } else stopsymbol_allowed = 0;
+ ptr_inputline = next_char(ptr_inputline);
+ }
+
+ check_offset(1);
+}
+
+void readline_updowncase_word(int upcase)
+{
+ int stopsymbol_allowed = 1;
+
+ while (*ptr_inputline) {
+ if (!iswalnum(get_char(ptr_inputline))) {
+ if (!stopsymbol_allowed) break;
+ } else {
+ stopsymbol_allowed = 0;
+ if (upcase)
+ *ptr_inputline = towupper(get_char(ptr_inputline));
+ else
+ *ptr_inputline = towlower(get_char(ptr_inputline));
+ }
+ ptr_inputline = next_char(ptr_inputline);
+ }
+
+ check_offset(1);
+}
+
+void readline_capitalize_word(void)
+{
+ int stopsymbol_allowed = 1;
+ int upcased = 0;
+
+ while (*ptr_inputline) {
+ if (!iswalnum(get_char(ptr_inputline))) {
+ if (!stopsymbol_allowed) break;
+ } else {
+ stopsymbol_allowed = 0;
+ if (!upcased) {
+ *ptr_inputline = towupper(get_char(ptr_inputline));
+ upcased = 1;
+ } else *ptr_inputline = towlower(get_char(ptr_inputline));
+ }
+ ptr_inputline = next_char(ptr_inputline);
+ }
+
+ check_offset(1);
+}
+
+void readline_backward_char(void)
+{
+ if (ptr_inputline == (char*)&inputLine) return;
+
+ ptr_inputline = prev_char(ptr_inputline, inputLine);
+ check_offset(-1);
+}
+
+void readline_forward_char(void)
+{
+ if (!*ptr_inputline) return;
+
+ ptr_inputline = next_char(ptr_inputline);
+ check_offset(1);
+}
+
+// readline_accept_line(down_history)
+// Validate current command line.
+// If down_history is true, load the next history line.
+int readline_accept_line(int down_history)
+{
+ scr_CheckAutoAway(TRUE);
+ if (process_line(inputLine))
+ return 255;
+ // Add line to history
+ scr_cmdhisto_addline(inputLine);
+ // Reset the line
+ ptr_inputline = inputLine;
+ *ptr_inputline = 0;
+ inputline_offset = 0;
+
+ if (down_history) {
+ // Use next history line instead of a blank line
+ const char *l = scr_cmdhisto_next("", 0);
+ if (l) strcpy(inputLine, l);
+ // Reset backup history line
+ cmdhisto_backup[0] = 0;
+ } else {
+ // Reset history line pointer
+ cmdhisto_cur = NULL;
+ }
+ return 0;
+}
+
+void readline_cancel_completion(void)
+{
+ scr_cancel_current_completion();
+ scr_end_current_completion();
+ check_offset(-1);
+}
+
+void readline_do_completion(void)
+{
+ int i, n;
+
+ if (multimode != 2) {
+ // Not in verbatim multi-line mode
+ scr_handle_tab();
+ } else {
+ // Verbatim multi-line mode: expand tab
+ char tabstr[9];
+ n = 8 - (ptr_inputline - inputLine) % 8;
+ for (i = 0; i < n; i++)
+ tabstr[i] = ' ';
+ tabstr[i] = '\0';
+ scr_insert_text(tabstr);
+ }
+ check_offset(0);
+}
+
+void readline_refresh_screen(void)
+{
+ scr_CheckAutoAway(TRUE);
+ ParseColors();
+ scr_Resize();
+ redrawwin(stdscr);
+}
+
+void readline_disable_chat_mode(guint show_roster)
+{
+ scr_CheckAutoAway(TRUE);
+ currentWindow = NULL;
+ chatmode = FALSE;
+ if (current_buddy)
+ buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
+ if (show_roster)
+ scr_RosterVisibility(1);
+ scr_UpdateChatStatus(FALSE);
+ top_panel(chatPanel);
+ top_panel(inputPanel);
+ update_panels();
+}
+
+void readline_hist_beginning_search_bwd(void)
+{
+ const char *l = scr_cmdhisto_prev(inputLine, ptr_inputline-inputLine);
+ if (l) strcpy(inputLine, l);
+}
+
+void readline_hist_beginning_search_fwd(void)
+{
+ const char *l = scr_cmdhisto_next(inputLine, ptr_inputline-inputLine);
+ if (l) strcpy(inputLine, l);
+}
+
+void readline_hist_prev(void)
+{
+ const char *l = scr_cmdhisto_prev(inputLine, 0);
+ if (l) {
+ strcpy(inputLine, l);
+ // Set the pointer at the EOL.
+ // We have to move it to BOL first, because we could be too far already.
+ readline_iline_start();
+ readline_iline_end();
+ }
+}
+
+void readline_hist_next(void)
+{
+ const char *l = scr_cmdhisto_next(inputLine, 0);
+ if (l) {
+ strcpy(inputLine, l);
+ // Set the pointer at the EOL.
+ // We have to move it to BOL first, because we could be too far already.
+ readline_iline_start();
+ readline_iline_end();
+ }
+}
+
+void readline_backward_kill_char(void)
+{
+ char *src, *c;
+
+ if (ptr_inputline == (char*)&inputLine)
+ return;
+
+ src = ptr_inputline;
+ c = prev_char(ptr_inputline, inputLine);
+ ptr_inputline = c;
+ for ( ; *src ; )
+ *c++ = *src++;
+ *c = 0;
+ check_offset(-1);
+}
+
+void readline_forward_kill_char(void)
+{
+ if (!*ptr_inputline)
+ return;
+
+ strcpy(ptr_inputline, next_char(ptr_inputline));
+}
+
+void readline_iline_start(void)
+{
+ ptr_inputline = inputLine;
+ inputline_offset = 0;
+}
+
+void readline_iline_end(void)
+{
+ for (; *ptr_inputline; ptr_inputline++) ;
+ check_offset(1);
+}
+
+void readline_backward_kill_iline(void)
+{
+ strcpy(inputLine, ptr_inputline);
+ ptr_inputline = inputLine;
+ inputline_offset = 0;
+}
+
+void readline_forward_kill_iline(void)
+{
+ *ptr_inputline = 0;
+}
+
+void readline_send_multiline(void)
+{
+ // Validate current multi-line
+ if (multimode)
+ process_command(mkcmdstr("msay send"), TRUE);
+}
+
+// which_row()
+// Tells which row our cursor is in, in the command line.
+// -2 -> normal text
+// -1 -> room: nickname completion
+// 0 -> command
+// 1 -> parameter 1 (etc.)
+// If > 0, then *p_row is set to the beginning of the row
+static int which_row(const char **p_row)
+{
+ int row = -1;
+ char *p;
+ int quote = FALSE;
+
+ // Not a command?
+ if ((ptr_inputline == inputLine) || (inputLine[0] != COMMAND_CHAR)) {
+ if (!current_buddy) return -2;
+ if (buddy_gettype(BUDDATA(current_buddy)) == ROSTER_TYPE_ROOM) {
+ *p_row = inputLine;
+ return -1;
+ }
+ return -2;
+ }
+
+ // This is a command
+ row = 0;
+ for (p = inputLine ; p < ptr_inputline ; p = next_char(p)) {
+ if (quote) {
+ if (*p == '"' && *(p-1) != '\\')
+ quote = FALSE;
+ continue;
+ }
+ if (*p == '"' && *(p-1) != '\\') {
+ quote = TRUE;
+ } else if (*p == ' ') {
+ if (*(p-1) != ' ')
+ row++;
+ *p_row = p+1;
+ }
+ }
+ return row;
+}
+
+// scr_insert_text()
+// Insert the given text at the current cursor position.
+// The cursor is moved. We don't check if the cursor still is in the screen
+// after, the caller should do that.
+static void scr_insert_text(const char *text)
+{
+ char tmpLine[INPUTLINE_LENGTH+1];
+ int len = strlen(text);
+ // Check the line isn't too long
+ if (strlen(inputLine) + len >= INPUTLINE_LENGTH) {
+ scr_LogPrint(LPRINT_LOGNORM, "Cannot insert text, line too long.");
+ return;
+ }
+
+ strcpy(tmpLine, ptr_inputline);
+ strcpy(ptr_inputline, text);
+ ptr_inputline += len;
+ strcpy(ptr_inputline, tmpLine);
+}
+
+static void scr_cancel_current_completion(void);
+
+// scr_handle_tab()
+// Function called when tab is pressed.
+// Initiate or continue a completion...
+static void scr_handle_tab(void)
+{
+ int nrow;
+ const char *row;
+ const char *cchar;
+ guint compl_categ;
+
+ row = inputLine; // (Kills a GCC warning)
+ nrow = which_row(&row);
+
+ // a) No completion if no leading slash ('cause not a command),
+ // unless this is a room (then, it is a nickname completion)
+ // b) We can't have more than 2 parameters (we use 2 flags)
+ if ((nrow == -2) || (nrow == 3 && !completion_started) || nrow > 3)
+ return;
+
+ if (nrow == 0) { // Command completion
+ row = next_char(inputLine);
+ compl_categ = COMPL_CMD;
+ } else if (nrow == -1) { // Nickname completion
+ compl_categ = COMPL_RESOURCE;
+ } else { // Other completion, depending on the command
+ int alias = FALSE;
+ cmd *com;
+ char *xpline = expandalias(inputLine);
+ com = cmd_get(xpline);
+ if (xpline != inputLine) {
+ // This is an alias, so we can't complete rows > 0
+ alias = TRUE;
+ g_free(xpline);
+ }
+ if ((!com && (!alias || !completion_started)) || !row) {
+ scr_LogPrint(LPRINT_NORMAL, "I cannot complete that...");
+ return;
+ }
+ if (!alias)
+ compl_categ = com->completion_flags[nrow-1];
+ else
+ compl_categ = 0;
+ }
+
+ if (!completion_started) {
+ guint dynlist;
+ GSList *list = compl_get_category_list(compl_categ, &dynlist);
+ if (list) {
+ guint n;
+ char *prefix = g_strndup(row, ptr_inputline-row);
+ // Init completion
+ n = new_completion(prefix, list);
+ g_free(prefix);
+ if (n == 0 && nrow == -1) {
+ // This is a MUC room and we can't complete from the beginning of the
+ // line. Let's try a bit harder and complete the current word.
+ row = prev_char(ptr_inputline, inputLine);
+ while (row >= inputLine) {
+ if (iswspace(get_char(row)) || get_char(row) == '(') {
+ row = next_char((char*)row);
+ break;
+ }
+ if (row == inputLine)
+ break;
+ row = prev_char((char*)row, inputLine);
+ }
+ // There's no need to try again if row == inputLine
+ if (row > inputLine) {
+ prefix = g_strndup(row, ptr_inputline-row);
+ new_completion(prefix, list);
+ g_free(prefix);
+ }
+ }
+ // Free the list if it's a dynamic one
+ if (dynlist) {
+ GSList *slp;
+ for (slp = list; slp; slp = g_slist_next(slp))
+ g_free(slp->data);
+ g_slist_free(list);
+ }
+ // Now complete
+ cchar = complete();
+ if (cchar)
+ scr_insert_text(cchar);
+ completion_started = TRUE;
+ }
+ } else { // Completion already initialized
+ scr_cancel_current_completion();
+ // Now complete again
+ cchar = complete();
+ if (cchar)
+ scr_insert_text(cchar);
+ }
+}
+
+static void scr_cancel_current_completion(void)
+{
+ char *c;
+ char *src = ptr_inputline;
+ guint back = cancel_completion();
+ guint i;
+ // Remove $back chars
+ for (i = 0; i < back; i++)
+ ptr_inputline = prev_char(ptr_inputline, inputLine);
+ c = ptr_inputline;
+ for ( ; *src ; )
+ *c++ = *src++;
+ *c = 0;
+}
+
+static void scr_end_current_completion(void)
+{
+ done_completion();
+ completion_started = FALSE;
+}
+
+// check_offset(int direction)
+// Check inputline_offset value, and make sure the cursor is inside the
+// screen.
+static inline void check_offset(int direction)
+{
+ int i;
+ char *c = &inputLine[inputline_offset];
+ // Left side
+ if (inputline_offset && direction <= 0) {
+ while (ptr_inputline <= c) {
+ for (i = 0; i < 5; i++)
+ c = prev_char(c, inputLine);
+ if (c == inputLine)
+ break;
+ }
+ }
+ // Right side
+ if (direction >= 0) {
+ int delta = get_char_width(c);
+ while (ptr_inputline > c) {
+ c = next_char(c);
+ delta += get_char_width(c);
+ }
+ c = &inputLine[inputline_offset];
+ while (delta >= maxX) {
+ for (i = 0; i < 5; i++) {
+ delta -= get_char_width(c);
+ c = next_char(c);
+ }
+ }
+ }
+ inputline_offset = c - inputLine;
+}
+
+#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
+// prints inputLine with underlined words when misspelled
+static inline void print_checked_line(void)
+{
+ char *wprint_char_fmt = "%c";
+ int point;
+ int nrchar = maxX;
+ char *ptrCur = inputLine + inputline_offset;
+
+#ifdef UNICODE
+ // We need this to display a single UTF-8 char... Any better solution?
+ if (utf8_mode)
+ wprint_char_fmt = "%lc";
+#endif
+
+ wmove(inputWnd, 0, 0); // problem with backspace
+
+ while (*ptrCur && nrchar-- > 0) {
+ point = ptrCur - inputLine;
+ if (maskLine[point])
+ wattrset(inputWnd, A_UNDERLINE);
+ wprintw(inputWnd, wprint_char_fmt, get_char(ptrCur));
+ wattrset(inputWnd, A_NORMAL);
+ ptrCur = next_char(ptrCur);
+ }
+}
+#endif
+
+static inline void refresh_inputline(void)
+{
+#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
+ if (settings_opt_get_int("spell_enable")) {
+ memset(maskLine, 0, INPUTLINE_LENGTH+1);
+ spellcheck(inputLine, maskLine);
+ }
+ print_checked_line();
+ wclrtoeol(inputWnd);
+ if (*ptr_inputline) {
+ // hack to set cursor pos. Characters can have different width,
+ // so I know of no better way.
+ char c = *ptr_inputline;
+ *ptr_inputline = 0;
+ print_checked_line();
+ *ptr_inputline = c;
+ }
+#else
+ mvwprintw(inputWnd, 0, 0, "%s", inputLine + inputline_offset);
+ wclrtoeol(inputWnd);
+ if (*ptr_inputline) {
+ // hack to set cursor pos. Characters can have different width,
+ // so I know of no better way.
+ char c = *ptr_inputline;
+ *ptr_inputline = 0;
+ mvwprintw(inputWnd, 0, 0, "%s", inputLine + inputline_offset);
+ *ptr_inputline = c;
+ }
+#endif
+}
+
+void scr_handle_CtrlC(void)
+{
+ if (!Curses) return;
+ // Leave multi-line mode
+ process_command(mkcmdstr("msay abort"), TRUE);
+ // Same as Ctrl-g, now
+ scr_cancel_current_completion();
+ scr_end_current_completion();
+ check_offset(-1);
+ refresh_inputline();
+}
+
+static void add_keyseq(char *seqstr, guint mkeycode, gint value)
+{
+ keyseq *ks;
+
+ // Let's make sure the length is correct
+ if (strlen(seqstr) > MAX_KEYSEQ_LENGTH) {
+ scr_LogPrint(LPRINT_LOGNORM, "add_keyseq(): key sequence is too long!");
+ return;
+ }
+
+ ks = g_new0(keyseq, 1);
+ ks->seqstr = g_strdup(seqstr);
+ ks->mkeycode = mkeycode;
+ ks->value = value;
+ keyseqlist = g_slist_append(keyseqlist, ks);
+}
+
+// match_keyseq(iseq, &ret)
+// Check if "iseq" is a known key escape sequence.
+// Return value:
+// -1 if "seq" matches no known sequence
+// 0 if "seq" could match 1 or more known sequences
+// >0 if "seq" matches a key sequence; the mkey code is returned
+// and *ret is set to the matching keyseq structure.
+static inline gint match_keyseq(int *iseq, keyseq **ret)
+{
+ GSList *ksl;
+ keyseq *ksp;
+ char *p, c;
+ int *i;
+ int needmore = FALSE;
+
+ for (ksl = keyseqlist; ksl; ksl = g_slist_next(ksl)) {
+ ksp = ksl->data;
+ p = ksp->seqstr;
+ i = iseq;
+ while (1) {
+ c = (unsigned char)*i;
+ if (!*p && !c) { // Match
+ (*ret) = ksp;
+ return ksp->mkeycode;
+ }
+ if (!c) {
+ // iseq is too short
+ needmore = TRUE;
+ break;
+ } else if (!*p || c != *p) {
+ // This isn't a match
+ break;
+ }
+ p++; i++;
+ }
+ }
+
+ if (needmore)
+ return 0;
+ return -1;
+}
+
+static inline int match_utf8_keyseq(int *iseq)
+{
+ int *strp = iseq;
+ unsigned c = *strp++;
+ unsigned mask = 0x80;
+ int len = -1;
+ while (c & mask) {
+ mask >>= 1;
+ len++;
+ }
+ if (len <= 0 || len > 4)
+ return -1;
+ c &= mask - 1;
+ while ((*strp & 0xc0) == 0x80) {
+ if (len-- <= 0) // can't happen
+ return -1;
+ c = (c << 6) | (*strp++ & 0x3f);
+ }
+ if (len)
+ return 0;
+ return c;
+}
+
+void scr_Getch(keycode *kcode)
+{
+ keyseq *mks = NULL;
+ int ks[MAX_KEYSEQ_LENGTH+1];
+ int i;
+
+ memset(kcode, 0, sizeof(keycode));
+ memset(ks, 0, sizeof(ks));
+
+ kcode->value = wgetch(inputWnd);
+ if (utf8_mode) {
+ bool ismeta = (kcode->value == 27);
+#ifdef NCURSES_MOUSE_VERSION
+ bool ismouse = (kcode->value == KEY_MOUSE);
+
+ if (ismouse) {
+ MEVENT mouse;
+ getmouse(&mouse);
+ kcode->value = mouse.bstate;
+ kcode->mcode = MKEY_MOUSE;
+ return;
+ } else if (ismeta)
+#else
+ if (ismeta)
+#endif
+ ks[0] = wgetch(inputWnd);
+ else
+ ks[0] = kcode->value;
+
+ for (i = 0; i < MAX_KEYSEQ_LENGTH - 1; i++) {
+ int match = match_utf8_keyseq(ks);
+ if (match == -1)
+ break;
+ if (match > 0) {
+ kcode->value = match;
+ kcode->utf8 = 1;
+ if (ismeta)
+ kcode->mcode = MKEY_META;
+ return;
+ }
+ ks[i + 1] = wgetch(inputWnd);
+ if (ks[i + 1] == ERR)
+ break;
+ }
+ while (i > 0)
+ ungetch(ks[i--]);
+ if (ismeta)
+ ungetch(ks[0]);
+ memset(ks, 0, sizeof(ks));
+ }
+ if (kcode->value != 27)
+ return;
+
+ // Check for escape key sequence
+ for (i=0; i < MAX_KEYSEQ_LENGTH; i++) {
+ int match;
+ ks[i] = wgetch(inputWnd);
+ if (ks[i] == ERR) break;
+ match = match_keyseq(ks, &mks);
+ if (match == -1) {
+ // No such key sequence. Let's increment i as it is a valid key.
+ i++;
+ break;
+ }
+ if (match > 0) {
+ // We have a matching sequence
+ kcode->mcode = mks->mkeycode;
+ kcode->value = mks->value;
+ return;
+ }
+ }
+
+ // No match. Let's return a meta-key.
+ if (i > 0) {
+ kcode->mcode = MKEY_META;
+ kcode->value = ks[0];
+ }
+ if (i > 1) {
+ // We need to push some keys back to the keyboard buffer
+ while (i-- > 1)
+ ungetch(ks[i]);
+ }
+ return;
+}
+
+void scr_DoUpdate(void)
+{
+ doupdate();
+}
+
+static int bindcommand(keycode kcode)
+{
+ gchar asciikey[16], asciicode[16];
+ const gchar *boundcmd;
+
+ if (kcode.utf8)
+ g_snprintf(asciicode, 15, "U%d", kcode.value);
+ else
+ g_snprintf(asciicode, 15, "%d", kcode.value);
+
+ if (!kcode.mcode || kcode.mcode == MKEY_EQUIV)
+ g_snprintf(asciikey, 15, "%s", asciicode);
+ else if (kcode.mcode == MKEY_META)
+ g_snprintf(asciikey, 15, "M%s", asciicode);
+ else if (kcode.mcode == MKEY_MOUSE)
+ g_snprintf(asciikey, 15, "p%s", asciicode);
+ else
+ g_snprintf(asciikey, 15, "MK%d", kcode.mcode);
+
+ boundcmd = settings_get(SETTINGS_TYPE_BINDING, asciikey);
+
+ if (boundcmd) {
+ gchar *cmdline = from_utf8(boundcmd);
+ scr_CheckAutoAway(TRUE);
+ if (process_command(cmdline, TRUE))
+ return 255; // Quit
+ g_free(cmdline);
+ return 0;
+ }
+
+ scr_LogPrint(LPRINT_NORMAL, "Unknown key=%s", asciikey);
+#ifndef UNICODE
+ if (utf8_mode)
+ scr_LogPrint(LPRINT_NORMAL,
+ "WARNING: Compiled without full UTF-8 support!");
+#endif
+ return -1;
+}
+
+// process_key(key)
+// Handle the pressed key, in the command line (bottom).
+void process_key(keycode kcode)
+{
+ int key = kcode.value;
+ int display_char = FALSE;
+
+ lock_chatstate = FALSE;
+
+ switch (kcode.mcode) {
+ case 0:
+ break;
+ case MKEY_EQUIV:
+ key = kcode.value;
+ break;
+ case MKEY_META:
+ default:
+ if (bindcommand(kcode) == 255) {
+ mcabber_set_terminate_ui();
+ return;
+ }
+ key = ERR; // Do not process any further
+ }
+
+ if (kcode.utf8) {
+ if (key != ERR && !kcode.mcode)
+ display_char = TRUE;
+ goto display;
+ }
+
+ switch (key) {
+ case 0:
+ case ERR:
+ break;
+ case 9: // Tab
+ readline_do_completion();
+ break;
+ case 13: // Enter
+ if (readline_accept_line(FALSE) == 255) {
+ mcabber_set_terminate_ui();
+ return;
+ }
+ break;
+ case 3: // Ctrl-C
+ scr_handle_CtrlC();
+ break;
+ case KEY_RESIZE:
+#ifdef USE_SIGWINCH
+ {
+ struct winsize size;
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &size) != -1)
+ resizeterm(size.ws_row, size.ws_col);
+ }
+#endif
+ scr_Resize();
+ break;
+ default:
+ display_char = TRUE;
+ } // switch
+
+display:
+ if (display_char) {
+ guint printable;
+
+ if (kcode.utf8) {
+ printable = iswprint(key);
+ } else {
+#ifdef __CYGWIN__
+ printable = (isprint(key) || (key >= 161 && key <= 255))
+ && !is_speckey(key);
+#else
+ printable = isprint(key) && !is_speckey(key);
+#endif
+ }
+ if (printable) {
+ char tmpLine[INPUTLINE_LENGTH+1];
+
+ // Check the line isn't too long
+ if (strlen(inputLine) + 4 > INPUTLINE_LENGTH)
+ return;
+
+ // Insert char
+ strcpy(tmpLine, ptr_inputline);
+ ptr_inputline = put_char(ptr_inputline, key);
+ strcpy(ptr_inputline, tmpLine);
+ check_offset(1);
+ } else {
+ // Look for a key binding.
+ if (!kcode.utf8 && (bindcommand(kcode) == 255)) {
+ mcabber_set_terminate_ui();
+ return;
+ }
+ }
+ }
+
+ if (completion_started && key != 9 && key != KEY_RESIZE)
+ scr_end_current_completion();
+ refresh_inputline();
+
+ if (!lock_chatstate) {
+ // Set chat state to composing (1) if the user is currently composing,
+ // i.e. not an empty line and not a command line.
+ if (inputLine[0] == 0 || inputLine[0] == COMMAND_CHAR)
+ set_chatstate(0);
+ else
+ set_chatstate(1);
+ if (chatstate)
+ time(&chatstate_timestamp);
+ }
+ return;
+}
+
+#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
+// initialization
+void spellcheck_init(void)
+{
+ int spell_enable = settings_opt_get_int("spell_enable");
+ const char *spell_lang = settings_opt_get("spell_lang");
+#ifdef WITH_ASPELL
+ const char *spell_encoding = settings_opt_get("spell_encoding");
+ AspellCanHaveError *possible_err;
+#endif
+
+ if (!spell_enable)
+ return;
+
+#ifdef WITH_ENCHANT
+ if (spell_checker) {
+ enchant_broker_free_dict(spell_broker, spell_checker);
+ enchant_broker_free(spell_broker);
+ spell_checker = NULL;
+ spell_broker = NULL;
+ }
+
+ spell_broker = enchant_broker_init();
+ spell_checker = enchant_broker_request_dict(spell_broker, spell_lang);
+#endif
+#ifdef WITH_ASPELL
+ if (spell_checker) {
+ delete_aspell_speller(spell_checker);
+ delete_aspell_config(spell_config);
+ spell_checker = NULL;
+ spell_config = NULL;
+ }
+
+ spell_config = new_aspell_config();
+ aspell_config_replace(spell_config, "encoding", spell_encoding);
+ aspell_config_replace(spell_config, "lang", spell_lang);
+ possible_err = new_aspell_speller(spell_config);
+
+ if (aspell_error_number(possible_err) != 0) {
+ spell_checker = NULL;
+ delete_aspell_config(spell_config);
+ spell_config = NULL;
+ } else {
+ spell_checker = to_aspell_speller(possible_err);
+ }
+#endif
+}
+
+// Deinitialization of spellchecker
+void spellcheck_deinit(void)
+{
+ if (spell_checker) {
+#ifdef WITH_ENCHANT
+ enchant_broker_free_dict(spell_broker, spell_checker);
+#endif
+#ifdef WITH_ASPELL
+ delete_aspell_speller(spell_checker);
+#endif
+ spell_checker = NULL;
+ }
+
+#ifdef WITH_ENCHANT
+ if (spell_broker) {
+ enchant_broker_free(spell_broker);
+ spell_broker = NULL;
+ }
+#endif
+#ifdef WITH_ASPELL
+ if (spell_config) {
+ delete_aspell_config(spell_config);
+ spell_config = NULL;
+ }
+#endif
+}
+
+#define spell_isalpha(c) (utf8_mode ? iswalpha(get_char(c)) : isalpha(*c))
+
+// Spell checking function
+static void spellcheck(char *line, char *checked)
+{
+ const char *start, *line_start;
+
+ if (inputLine[0] == 0 || inputLine[0] == COMMAND_CHAR)
+ return;
+
+ line_start = line;
+
+ while (*line) {
+
+ if (!spell_isalpha(line)) {
+ line = next_char(line);
+ continue;
+ }
+
+ if (!strncmp(line, "http://", 7)) {
+ line += 7; // : and / characters are 1 byte long in utf8, right?
+
+ while (!strchr(" \t\r\n", *line))
+ line = next_char(line); // i think line++ would be fine here?
+
+ continue;
+ }
+
+ if (!strncmp(line, "ftp://", 6)) {
+ line += 6;
+
+ while (!strchr(" \t\r\n", *line))
+ line = next_char(line);
+
+ continue;
+ }
+
+ start = line;
+
+ while (spell_isalpha(line))
+ line = next_char(line);
+
+ if (spell_checker &&
+#ifdef WITH_ENCHANT
+ enchant_dict_check(spell_checker, start, line - start) != 0
+#endif
+#ifdef WITH_ASPELL
+ aspell_speller_check(spell_checker, start, line - start) == 0
+#endif
+ )
+ memset(&checked[start - line_start], SPELLBADCHAR, line - start);
+ }
+}
+#endif
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/screen.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,189 @@
+#ifndef __MCABBER_SCREEN_H__
+#define __MCABBER_SCREEN_H__ 1
+
+#include <glib.h>
+
+#include <mcabber/config.h>
+
+#if HAVE_NCURSESW_NCURSES_H
+# include <ncursesw/ncurses.h>
+# include <ncursesw/panel.h>
+#elif HAVE_NCURSES_NCURSES_H
+# include <ncurses/ncurses.h>
+# include <ncurses/panel.h>
+#else
+# include <ncurses.h>
+# include <panel.h>
+#endif
+
+#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
+void spellcheck_init(void);
+void spellcheck_deinit(void);
+//static void spellcheck(char*, char*);
+#endif
+
+#include <mcabber/hbuf.h>
+#include <mcabber/logprint.h>
+#include <mcabber/roster.h>
+
+#define INPUTLINE_LENGTH 1024
+
+// Only used in screen.c; this is the maximum line number
+// in a multi-line message. Should be < 1000
+// Note: message length is limited by the HBB_BLOCKSIZE size too
+#define MULTILINE_MAX_LINE_NUMBER 299
+
+// When chatstates are enabled, timeout (in seconds) before "composing"
+// becomes "paused" because of user inactivity.
+// Warning: setting this very low will cause more network traffic.
+#define COMPOSING_TIMEOUT 6L
+
+enum colors {
+ COLOR_GENERAL = 3,
+ COLOR_MSGOUT,
+ COLOR_MSGHL,
+ COLOR_STATUS,
+ COLOR_ROSTER,
+ COLOR_ROSTERSEL,
+ COLOR_ROSTERSELNMSG,
+ COLOR_ROSTERNMSG,
+ COLOR_INFO,
+ COLOR_MSGIN,
+ COLOR_max
+};
+
+int COLOR_ATTRIB[COLOR_max];
+
+extern int update_roster;
+
+typedef struct {
+ int value;
+ int utf8;
+ enum {
+ MKEY_META = 1,
+ MKEY_EQUIV,
+ MKEY_CTRL_PGUP,
+ MKEY_CTRL_PGDOWN,
+ MKEY_SHIFT_PGUP,
+ MKEY_SHIFT_PGDOWN,
+ MKEY_CTRL_SHIFT_PGUP,
+ MKEY_CTRL_SHIFT_PGDOWN,
+ MKEY_CTRL_HOME,
+ MKEY_CTRL_END,
+ MKEY_CTRL_INS,
+ MKEY_CTRL_DEL,
+ MKEY_CTRL_SHIFT_HOME,
+ MKEY_CTRL_SHIFT_END,
+ MKEY_MOUSE
+ } mcode;
+} keycode;
+
+typedef enum {
+ MC_ALL,
+ MC_PRESET,
+ MC_OFF,
+ MC_REMOVE
+} muccoltype;
+
+void scr_init_bindings(void);
+
+void scr_Getch(keycode *kcode);
+void process_key(keycode kcode);
+
+void scr_InitLocaleCharSet(void);
+void scr_InitCurses(void);
+void scr_TerminateCurses(void);
+void scr_DrawMainWindow(unsigned int fullinit);
+void scr_DrawRoster(void);
+void scr_UpdateMainStatus(int forceupdate);
+void scr_UpdateChatStatus(int forceupdate);
+void scr_RosterVisibility(int status);
+void scr_WriteIncomingMessage(const char *jidfrom, const char *text,
+ time_t timestamp, guint prefix,
+ unsigned mucnicklen);
+void scr_WriteOutgoingMessage(const char *jidto, const char *text,
+ guint prefix, gpointer xep184);
+void scr_RemoveReceiptFlag(const char *jidto, gpointer xep184);
+void scr_ShowBuddyWindow(void);
+int scr_BuddyBufferExists(const char *jid);
+void scr_UpdateBuddyWindow(void);
+void scr_set_chatmode(int enable);
+int scr_get_chatmode(void);
+void scr_set_multimode(int enable, char *subject);
+int scr_get_multimode(void);
+void scr_setmsgflag_if_needed(const char *jid, int special);
+void scr_append_multiline(const char *line);
+const char *scr_get_multiline(void);
+const char *scr_get_multimode_subj(void);
+
+guint scr_getprefixwidth(void);
+void scr_line_prefix(hbb_line *line, char *prefix, guint preflen);
+
+void scr_Beep(void);
+
+bool Autoaway;
+
+void scr_CheckAutoAway(int activity);
+
+#if defined JEP0022 || defined JEP0085
+gboolean scr_ChatStatesTimeout();
+#endif
+int chatstates_disabled;
+
+// For commands...
+void scr_RosterTop(void);
+void scr_RosterBottom(void);
+void scr_RosterUpDown(int updown, unsigned int n);
+void scr_RosterPrevGroup(void);
+void scr_RosterNextGroup(void);
+void scr_RosterSearch(char *);
+void scr_RosterJumpJid(char *);
+void scr_RosterDisplay(const char *);
+void scr_BufferTopBottom(int topbottom);
+void scr_BufferClear(void);
+void scr_BufferScrollLock(int lock);
+void scr_BufferPurge(int, const char*);
+void scr_BufferPurgeAll(int);
+void scr_BufferSearch(int direction, const char *text);
+void scr_BufferPercent(int pc);
+void scr_BufferDate(time_t t);
+void scr_BufferDump(const char *file);
+void scr_RosterUnreadMessage(int);
+void scr_RosterJumpAlternate(void);
+void scr_BufferScrollUpDown(int updown, unsigned int nblines);
+bool scr_RosterColor(const char *status, const char *wildcard,
+ const char *color);
+void scr_RosterClearColor(void);
+void scr_MucColor(const char *muc, muccoltype type);
+void scr_MucNickColor(const char *nick, const char *color);
+void scr_BufferList(void);
+
+void readline_transpose_chars(void);
+void readline_forward_kill_word(void);
+void readline_backward_kill_word(void);
+void readline_backward_word(void);
+void readline_forward_word(void);
+void readline_updowncase_word(int);
+void readline_capitalize_word(void);
+void readline_backward_char(void);
+void readline_forward_char(void);
+int readline_accept_line(int down_history);
+void readline_cancel_completion(void);
+void readline_do_completion(void);
+void readline_refresh_screen(void);
+void readline_disable_chat_mode(guint show_roster);
+void readline_hist_beginning_search_bwd(void);
+void readline_hist_beginning_search_fwd(void);
+void readline_hist_prev(void);
+void readline_hist_next(void);
+void readline_backward_kill_char(void);
+void readline_forward_kill_char(void);
+void readline_iline_start(void);
+void readline_iline_end(void);
+void readline_backward_kill_iline(void);
+void readline_forward_kill_iline(void);
+void readline_send_multiline(void);
+
+#endif
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/settings.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,609 @@
+/*
+ * settings.c -- Configuration stuff
+ *
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "settings.h"
+#include "commands.h"
+#include "logprint.h"
+#include "otr.h"
+#include "utils.h"
+#include "xmpp.h"
+#include "main.h"
+
+// Maximum line length
+// (probably best to use the same value as INPUTLINE_LENGTH)
+#define CONFLINE_LENGTH 1024
+
+static GHashTable *option;
+static GHashTable *alias;
+static GHashTable *binding;
+
+#ifdef HAVE_GPGME /* PGP settings */
+static GHashTable *pgpopt;
+
+typedef struct {
+ gchar *pgp_keyid; /* KeyId the contact is supposed to use */
+ guint pgp_disabled; /* If TRUE, PGP is disabled for outgoing messages */
+ guint pgp_force; /* If TRUE, PGP is used w/o negotiation */
+} T_pgpopt;
+#endif
+
+#ifdef HAVE_LIBOTR
+static GHashTable *otrpolicy;
+static enum otr_policy default_policy;
+#endif
+
+static inline GHashTable *get_hash(guint type)
+{
+ if (type == SETTINGS_TYPE_OPTION) return option;
+ else if (type == SETTINGS_TYPE_ALIAS) return alias;
+ else if (type == SETTINGS_TYPE_BINDING) return binding;
+#ifdef HAVE_LIBOTR
+ else if (type == SETTINGS_TYPE_OTR) return otrpolicy;
+#endif
+ return NULL;
+}
+
+/* -- */
+
+void settings_init(void)
+{
+ option = g_hash_table_new_full(&g_str_hash, &g_str_equal, &g_free, &g_free);
+ alias = g_hash_table_new_full(&g_str_hash, &g_str_equal, &g_free, &g_free);
+ binding = g_hash_table_new_full(&g_str_hash, &g_str_equal, &g_free, &g_free);
+#ifdef HAVE_GPGME
+ pgpopt = g_hash_table_new(&g_str_hash, &g_str_equal);
+#endif
+#ifdef HAVE_LIBOTR
+ otrpolicy = g_hash_table_new(&g_str_hash, &g_str_equal);
+#endif
+}
+
+// cfg_read_file(filename, mainfile)
+// Read and parse config file "filename". If filename is NULL,
+// try to open the configuration file at the default locations.
+// mainfile must be set to TRUE for the startup config file.
+// If mainfile is TRUE, the permissions of the configuration file will
+// be fixed if they're insecure.
+//
+int cfg_read_file(char *filename, guint mainfile)
+{
+ static unsigned int runtime;
+ FILE *fp;
+ char *buf;
+ char *line, *eol;
+ unsigned int ln = 0;
+ int err = 0;
+
+ if (!filename) {
+ // Use default config file locations
+ char *home;
+ GString *sfilename;
+
+ if (!mainfile) {
+ scr_LogPrint(LPRINT_LOGNORM, "No file name provided");
+ return -1;
+ }
+
+ home = getenv("HOME");
+ if (!home) {
+ scr_LogPrint(LPRINT_LOG, "Can't find home dir!");
+ fprintf(stderr, "Can't find home dir!\n");
+ err = -1;
+ goto cfg_read_file_return;
+ }
+ sfilename = g_string_new("");
+ g_string_printf(sfilename, "%s/.mcabber/mcabberrc", home);
+ if ((fp = fopen(sfilename->str, "r")) == NULL) {
+ // 2nd try...
+ g_string_printf(sfilename, "%s/.mcabberrc", home);
+ if ((fp = fopen(sfilename->str, "r")) == NULL) {
+ fprintf(stderr, "Cannot open config file!\n");
+ g_string_free(sfilename, TRUE);
+ err = -1;
+ goto cfg_read_file_return;
+ }
+ }
+ // Check configuration file permissions
+ // As it could contain sensitive data, we make it user-readable only.
+ checkset_perm(sfilename->str, TRUE);
+ scr_LogPrint(LPRINT_LOGNORM, "Reading %s", sfilename->str);
+ // Check mcabber dir. Here we just warn, we don't change the modes.
+ g_string_printf(sfilename, "%s/.mcabber/", home);
+ checkset_perm(sfilename->str, FALSE);
+ g_string_free(sfilename, TRUE);
+ } else {
+ // filename was specified
+ if ((fp = fopen(filename, "r")) == NULL) {
+ const char *msg = "Cannot open configuration file";
+ if (mainfile)
+ perror(msg);
+ else
+ scr_LogPrint(LPRINT_LOGNORM, "%s (%s).", msg, filename);
+ err = -2;
+ goto cfg_read_file_return;
+ }
+ // Check configuration file permissions (see above)
+ // We don't change the permissions if that's not the main file.
+ if (mainfile)
+ checkset_perm(filename, TRUE);
+ scr_LogPrint(LPRINT_LOGNORM, "Reading %s", filename);
+ }
+
+ buf = g_new(char, CONFLINE_LENGTH+1);
+
+ while (fgets(buf+1, CONFLINE_LENGTH, fp) != NULL) {
+ // The first char is reserved to add a '/', to make a command line
+ line = buf+1;
+ ln++;
+
+ // Strip leading spaces
+ while (isspace(*line))
+ line++;
+
+ // Make eol point to the last char of the line
+ for (eol = line ; *eol ; eol++)
+ ;
+ if (eol > line)
+ eol--;
+
+ // Strip trailing spaces
+ while (eol > line && isspace(*eol))
+ *eol-- = 0;
+
+ // Ignore empty lines and comments
+ if ((*line == '\n') || (*line == '\0') || (*line == '#'))
+ continue;
+
+ // We only allow assignments line, except for commands "pgp", "source",
+ // "color", "load" and "otrpolicy", unless we're in runtime (i.e. not startup).
+ if (runtime ||
+ (strchr(line, '=') != NULL) ||
+ startswith(line, "pgp ", FALSE) ||
+ startswith(line, "source ", FALSE) ||
+ startswith(line, "color ", FALSE) ||
+#ifdef MODULES_ENABLE
+ startswith(line, "load ", FALSE) ||
+#endif
+ startswith(line, "otrpolicy", FALSE)) {
+ // Only accept a few "safe" commands
+ if (!runtime &&
+ !startswith(line, "set ", FALSE) &&
+ !startswith(line, "bind ", FALSE) &&
+ !startswith(line, "alias ", FALSE) &&
+ !startswith(line, "pgp ", FALSE) &&
+ !startswith(line, "source ", FALSE) &&
+ !startswith(line, "color ", FALSE) &&
+#ifdef MODULES_ENABLE
+ !startswith(line, "load ", FALSE) &&
+#endif
+ !startswith(line, "otrpolicy ", FALSE)) {
+ scr_LogPrint(LPRINT_LOGNORM, "Error in configuration file (l. %d): "
+ "this command can't be used here", ln);
+ err++;
+ continue;
+ }
+ // Set the leading COMMAND_CHAR to build a command line
+ // and process the command
+ *(--line) = COMMAND_CHAR;
+ if (process_command(line, TRUE) == 255)
+ mcabber_set_terminate_ui();
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "Error in configuration file (l. %d): "
+ "this is not an assignment", ln);
+ err++;
+ }
+ }
+ g_free(buf);
+ fclose(fp);
+
+ if (filename)
+ scr_LogPrint(LPRINT_LOGNORM, "Loaded %s.", filename);
+
+cfg_read_file_return:
+ // If we're done with the main file parsing, we can assume that
+ // the next time this function is called will be at run time.
+ if (mainfile)
+ runtime = TRUE;
+ return err;
+}
+
+// parse_assigment(assignment, pkey, pval)
+// Read assignment and split it to key, value
+//
+// If this is an assignment, the function will return TRUE and
+// set *pkey and *pval (*pval is set to NULL if value field is empty).
+//
+// If this isn't a assignment (no = char), the function will set *pval
+// to NULL and return FALSE.
+//
+// The caller should g_free() *pkey and *pval (if not NULL) after use.
+guint parse_assigment(gchar *assignment, gchar **pkey, gchar **pval)
+{
+ char *key, *val, *t, *p;
+
+ *pkey = *pval = NULL;
+
+ key = assignment;
+ // Remove leading spaces in option name
+ while ((!isalnum(*key)) && (*key != '=') && *key) {
+ //if (!isblank(*key))
+ // scr_LogPrint("Error in assignment parsing!");
+ key++;
+ }
+ if (!*key) return FALSE; // Empty assignment
+
+ if (*key == '=') {
+ //scr_LogPrint("Cannot parse assignment!");
+ return FALSE;
+ }
+ // Ok, key points to the option name
+
+ for (val = key+1 ; *val && (*val != '=') ; val++)
+ if (!isalnum(*val) && !isblank(*val) && (*val != '_') && (*val != '-')) {
+ // Key should only have alnum chars...
+ //scr_LogPrint("Error in assignment parsing!");
+ return FALSE;
+ }
+ // Remove trailing spaces in option name:
+ for (t = val-1 ; t > key && isblank(*t) ; t--)
+ ;
+ // Check for embedded whitespace characters
+ for (p = key; p < t; p++) {
+ if (isblank(*p)) {
+ //scr_LogPrint("Error in assignment parsing!"
+ // " (Name should not contain space chars)");
+ return FALSE;
+ }
+ }
+
+ *pkey = g_strndup(key, t+1-key);
+
+ if (!*val) return FALSE; // Not an assignment
+
+ // Remove leading and trailing spaces in option value:
+ for (val++; *val && isblank(*val) ; val++) ;
+ for (t = val ; *t ; t++) ;
+ for (t-- ; t >= val && isblank(*t) ; t--) ;
+
+ if (t < val) return TRUE; // no value (variable reset for example)
+
+ // If the value begins and ends with quotes ("), these quotes are
+ // removed and whitespace is not stripped
+ if ((t>val) && (*val == '"' && *t == '"')) {
+ val++;
+ t--;
+ }
+ *pval = g_strndup(val, t+1-val);
+ return TRUE;
+}
+
+void settings_set(guint type, const gchar *key, const gchar *value)
+{
+ GHashTable *hash;
+
+ hash = get_hash(type);
+ if (!hash)
+ return;
+
+ if (!value) {
+ g_hash_table_remove(hash, key);
+ } else {
+ g_hash_table_insert(hash, g_strdup(key), g_strdup(value));
+ }
+}
+
+void settings_del(guint type, const gchar *key)
+{
+ settings_set(type, key, NULL);
+}
+
+const gchar *settings_get(guint type, const gchar *key)
+{
+ GHashTable *hash;
+
+ hash = get_hash(type);
+ if (!hash)
+ return NULL;
+
+ return g_hash_table_lookup(hash, key);
+}
+
+int settings_get_int(guint type, const gchar *key)
+{
+ const gchar *setval = settings_get(type, key);
+
+ if (setval) return atoi(setval);
+ return 0;
+}
+
+// settings_get_status_msg(status)
+// Return a string with the current status message:
+// - if there is a user-defined message ("message" option),
+// return this message
+// - if there is a user-defined message for the given status (and no
+// generic user message), it is returned
+// - if no message is found, return NULL
+const gchar *settings_get_status_msg(enum imstatus status)
+{
+ const gchar *rstatus = settings_opt_get("message");
+
+ if (rstatus) return rstatus;
+
+ switch(status) {
+ case available:
+ rstatus = settings_opt_get("message_avail");
+ break;
+
+ case freeforchat:
+ rstatus = settings_opt_get("message_free");
+ break;
+
+ case dontdisturb:
+ rstatus = settings_opt_get("message_dnd");
+ break;
+
+ case notavail:
+ rstatus = settings_opt_get("message_notavail");
+ break;
+
+ case away:
+ rstatus = settings_opt_get("message_away");
+ break;
+
+ default: // offline, invisible
+ break;
+ }
+ return rstatus;
+}
+
+// settings_foreach(type, pfunction, param)
+// Call pfunction(key, value, param) for each setting with requested type.
+void settings_foreach(guint type, void (*pfunc)(char *k, char *v, void *param),
+ void *param)
+{
+ GHashTable *hash;
+
+ hash = get_hash(type);
+ if (!hash)
+ return;
+
+ g_hash_table_foreach(hash, (GHFunc)pfunc, param);
+}
+
+
+// default_muc_nickname()
+// Return the user's default nickname
+// The caller should free the string after use
+char *default_muc_nickname(const char *roomid)
+{
+ char *nick;
+
+ nick = (char*)xmpp_get_bookmark_nick(roomid);
+ if (nick)
+ return g_strdup(nick);
+
+ // We try the "nickname" option, then the username part of the jid.
+ nick = (char*)settings_opt_get("nickname");
+ if (nick)
+ return g_strdup(nick);
+
+ nick = jid_get_username(settings_opt_get("jid"));
+ return nick;
+}
+
+
+/* PGP settings */
+
+// settings_pgp_setdisabled(jid, value)
+// Enable/disable PGP encryption for jid.
+// (Set value to TRUE to disable encryption)
+void settings_pgp_setdisabled(const char *bjid, guint value)
+{
+#ifdef HAVE_GPGME
+ T_pgpopt *pgpdata;
+ pgpdata = g_hash_table_lookup(pgpopt, bjid);
+ if (!pgpdata) {
+ // If value is 0, we do not need to create a structure (that's
+ // the default value).
+ if (value) {
+ pgpdata = g_new0(T_pgpopt, 1);
+ pgpdata->pgp_disabled = value;
+ g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
+ }
+ } else {
+ pgpdata->pgp_disabled = value;
+ // We could remove the key/value if pgp_disabled is 0 and
+ // pgp_keyid is NULL, actually.
+ }
+#endif
+}
+
+// settings_pgp_getdisabled(jid)
+// Return TRUE if PGP encryption should be disabled for jid.
+guint settings_pgp_getdisabled(const char *bjid)
+{
+#ifdef HAVE_GPGME
+ T_pgpopt *pgpdata;
+ pgpdata = g_hash_table_lookup(pgpopt, bjid);
+ if (pgpdata)
+ return pgpdata->pgp_disabled;
+ else
+ return FALSE; // Default: not disabled
+#else
+ return TRUE; // No PGP support, let's say it's disabled.
+#endif
+}
+
+// settings_pgp_setforce(jid, value)
+// Force (or not) PGP encryption for jid.
+// When value is TRUE, PGP support will be assumed for the remote client.
+void settings_pgp_setforce(const char *bjid, guint value)
+{
+#ifdef HAVE_GPGME
+ T_pgpopt *pgpdata;
+ pgpdata = g_hash_table_lookup(pgpopt, bjid);
+ if (!pgpdata) {
+ // If value is 0, we do not need to create a structure (that's
+ // the default value).
+ if (value) {
+ pgpdata = g_new0(T_pgpopt, 1);
+ pgpdata->pgp_force = value;
+ g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
+ }
+ } else {
+ pgpdata->pgp_force = value;
+ }
+ if (value && pgpdata && !pgpdata->pgp_keyid)
+ scr_LogPrint(LPRINT_NORMAL, "Warning: the Key Id is not set!");
+#endif
+}
+
+// settings_pgp_getforce(jid)
+// Return TRUE if PGP enforcement is set for jid.
+guint settings_pgp_getforce(const char *bjid)
+{
+#ifdef HAVE_GPGME
+ T_pgpopt *pgpdata;
+ pgpdata = g_hash_table_lookup(pgpopt, bjid);
+ if (pgpdata)
+ return pgpdata->pgp_force;
+ else
+ return FALSE; // Default
+#else
+ return FALSE; // No PGP support
+#endif
+}
+
+// settings_pgp_setkeyid(jid, keyid)
+// Set the PGP KeyId for user jid.
+// Use keyid = NULL to erase the previous KeyId.
+void settings_pgp_setkeyid(const char *bjid, const char *keyid)
+{
+#ifdef HAVE_GPGME
+ T_pgpopt *pgpdata;
+ pgpdata = g_hash_table_lookup(pgpopt, bjid);
+ if (!pgpdata) {
+ // If keyid is NULL, we do not need to create a structure (that's
+ // the default value).
+ if (keyid) {
+ pgpdata = g_new0(T_pgpopt, 1);
+ pgpdata->pgp_keyid = g_strdup(keyid);
+ g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
+ }
+ } else {
+ g_free(pgpdata->pgp_keyid);
+ if (keyid)
+ pgpdata->pgp_keyid = g_strdup(keyid);
+ else
+ pgpdata->pgp_keyid = NULL;
+ // We could remove the key/value if pgp_disabled is 0 and
+ // pgp_keyid is NULL, actually.
+ }
+#endif
+}
+
+// settings_pgp_getkeyid(jid)
+// Get the PGP KeyId for user jid.
+const char *settings_pgp_getkeyid(const char *bjid)
+{
+#ifdef HAVE_GPGME
+ T_pgpopt *pgpdata;
+ pgpdata = g_hash_table_lookup(pgpopt, bjid);
+ if (pgpdata)
+ return pgpdata->pgp_keyid;
+#endif
+ return NULL;
+}
+
+/* otr settings */
+
+#ifdef HAVE_LIBOTR
+static void remove_default_policies(char *k, char *policy, void *defaultp)
+{
+ if (*(enum otr_policy *)policy == *(enum otr_policy *)defaultp) {
+ g_free((enum otr_policy *) policy);
+ g_hash_table_remove(otrpolicy, k);
+ }
+}
+#endif
+
+void settings_otr_setpolicy(const char *bjid, guint value)
+{
+#ifdef HAVE_LIBOTR
+ enum otr_policy *otrdata;
+
+ if (!bjid) {
+ default_policy = value;
+ /* refresh hash */
+ settings_foreach(SETTINGS_TYPE_OTR, &remove_default_policies, &value);
+ return;
+ }
+
+ otrdata = g_hash_table_lookup(otrpolicy, bjid);
+
+ if (value == default_policy) {
+ if (otrdata) {
+ g_free(otrdata);
+ g_hash_table_remove(otrpolicy, bjid);
+ }
+ } else if (otrdata) {
+ *otrdata = value;
+ } else {
+ otrdata = g_new(enum otr_policy, 1);
+ *otrdata = value;
+ g_hash_table_insert(otrpolicy, g_strdup(bjid), otrdata);
+ }
+#endif
+}
+
+guint settings_otr_getpolicy(const char *bjid)
+{
+#ifdef HAVE_LIBOTR
+ enum otr_policy *otrdata;
+ if (!bjid)
+ return default_policy;
+
+ otrdata = g_hash_table_lookup(otrpolicy, bjid);
+ if (otrdata)
+ return *otrdata;
+ else
+ return default_policy;
+#else
+ return 0;
+#endif
+}
+
+guint get_max_history_blocks(void)
+{
+ int max_num_of_blocks = settings_opt_get_int("max_history_blocks");
+ if (max_num_of_blocks < 0)
+ max_num_of_blocks = 0;
+ else if (max_num_of_blocks == 1)
+ max_num_of_blocks = 2;
+ return (guint)max_num_of_blocks;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/settings.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,62 @@
+#ifndef __MCABBER_SETTINGS_H__
+#define __MCABBER_SETTINGS_H__ 1
+
+#include <ctype.h>
+#include <glib.h>
+
+#include <mcabber/roster.h>
+#include <mcabber/config.h>
+
+#ifndef isblank
+# define isblank(c) ((c) == 0x20 || (c) == 0x09)
+#endif
+
+
+#define SETTINGS_TYPE_OPTION 1
+#define SETTINGS_TYPE_ALIAS 2
+#define SETTINGS_TYPE_BINDING 3
+#ifdef HAVE_LIBOTR
+#define SETTINGS_TYPE_OTR 4
+#endif
+
+#define COMMAND_CHAR '/'
+#define COMMAND_CHARSTR "/"
+
+#define settings_opt_get(k) settings_get(SETTINGS_TYPE_OPTION, k)
+#define settings_opt_get_int(k) settings_get_int(SETTINGS_TYPE_OPTION, k)
+
+#define mkcmdstr(cmd) COMMAND_CHARSTR cmd
+
+void settings_init(void);
+int cfg_read_file(char *filename, guint mainfile);
+guint parse_assigment(gchar *assignment, gchar **pkey, gchar **pval);
+void settings_set(guint type, const gchar *key, const gchar *value);
+void settings_del(guint type, const gchar *key);
+const gchar *settings_get(guint type, const gchar *key);
+int settings_get_int(guint type, const gchar *key);
+const gchar *settings_get_status_msg(enum imstatus status);
+void settings_foreach(guint type,
+ void (*pfunc)(char *k, char *v, void *param),
+ void *param);
+
+void settings_pgp_setdisabled(const char *bjid, guint value);
+guint settings_pgp_getdisabled(const char *bjid);
+void settings_pgp_setforce(const char *bjid, guint value);
+guint settings_pgp_getforce(const char *bjid);
+void settings_pgp_setkeyid(const char *bjid, const char *keyid);
+const char *settings_pgp_getkeyid(const char *bjid);
+
+#ifdef HAVE_LIBOTR
+guint settings_otr_getpolicy(const char *bjid);
+void settings_otr_setpolicy(const char *bjid, guint value);
+#endif
+
+guint get_max_history_blocks(void);
+
+char *default_muc_nickname(const char *roomid);
+
+const gchar *isbound(int key);
+
+#endif /* __MCABBER_SETTINGS_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/utf8.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,98 @@
+/*
+ * utf8.c -- UTF-8 routines
+ *
+ * Copyright (C) 2006 Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>
+ *
+ * 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 "utf8.h"
+
+char *prev_char(char *str, const char *limit)
+{
+ if (str <= limit)
+ return str;
+ str--;
+ if (utf8_mode)
+ while ((str > limit) && ((*str & 0xc0) == 0x80))
+ str--;
+ return str;
+}
+
+char *next_char(char *str)
+{
+ if (!*str)
+ return str;
+ str++;
+ if (utf8_mode)
+ while ((*str & 0xc0) == 0x80)
+ str++;
+ return str;
+}
+
+unsigned get_char(const char *str)
+{
+ unsigned char *strp = (unsigned char *)str;
+ unsigned c = *strp++;
+ unsigned mask = 0x80;
+ int len = -1;
+ if (!utf8_mode)
+ return c;
+ while (c & mask) {
+ mask >>= 1;
+ len++;
+ }
+ if (len <= 0 || len > 4)
+ goto no_utf8;
+ c &= mask - 1;
+ while ((*strp & 0xc0) == 0x80) {
+ if (len-- <= 0)
+ goto no_utf8;
+ c = (c << 6) | (*strp++ & 0x3f);
+ }
+ if (len)
+ goto no_utf8;
+ return c;
+
+no_utf8:
+ return *str;
+}
+
+char *put_char(char *str, unsigned c)
+{
+ int mask = 0xffffffc0;
+ int i = 4;
+ char code[5];
+ if (!utf8_mode || c < 128) {
+ *str++ = c;
+ return str;
+ }
+ while (c & mask) {
+ code[i--] = 0x80 | (c & 0x3f);
+ c >>= 6;
+ mask >>= 1;
+ if (i < 0) {
+ *str++ = '?';
+ return str;
+ }
+ }
+ code[i] = (mask << 1) | c;
+ for (; i < 5; i++)
+ *str++ = code[i];
+ return str;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/utf8.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,49 @@
+#ifndef __MCABBER_UTF8_H__
+#define __MCABBER_UTF8_H__ 1
+
+#include <mcabber/config.h>
+
+#if defined HAVE_UNICODE && defined HAVE_WCHAR_H && defined HAVE_WCTYPE_H
+# define UNICODE
+#endif
+
+#ifdef HAVE_WCHAR_H
+# include <wchar.h>
+# define get_char_width(c) (utf8_mode ? wcwidth(get_char(c)) : 1)
+#else
+# define wcwidth(c) 1
+# define get_char_width(c) 1
+#endif
+
+#ifdef HAVE_WCTYPE_H
+# include <wctype.h>
+
+/* The following bit is a hack for Solaris 8&9 systems that don't have
+ * iswblank().
+ * For now i made sure it comes after wctype.h so it doesn't create
+ * problems (wctype.h has calls to iswblank() before wctype() is declared).
+ * (Sebastian Kayser)
+ */
+# ifndef HAVE_ISWBLANK
+# define iswblank(wc) iswctype(wc, wctype("blank"))
+# endif
+
+#else
+# define iswblank(c) (c == ' ')
+# define iswalnum(c) isalnum(c)
+# define iswprint(c) isprint(c)
+# define towupper(c) toupper(c)
+# define towlower(c) tolower(c)
+# define iswalpha(c) isalpha(c)
+#endif
+
+extern int utf8_mode;
+
+char *prev_char(char *str, const char *limit);
+char *next_char(char *str);
+unsigned get_char(const char *str);
+char *put_char(char *str, unsigned c);
+
+#endif /* __MCABBER_UTF8_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/utils.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,789 @@
+/*
+ * utils.c -- Various utility functions
+ *
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ * Some of the ut_* functions are derived from Cabber debug/log code.
+ * from_iso8601() comes from the Pidgin (libpurple) 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 <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#ifdef HAVE_LIBIDN
+#include <idna.h>
+#include <stringprep.h>
+static char idnprep[1024];
+#endif
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+/* For Cygwin (thanks go to Yitzchak Scott-Thoennes) */
+#ifdef __CYGWIN__
+# define timezonevar
+ extern long timezone;
+#endif
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#include "utils.h"
+#include "logprint.h"
+
+static int DebugEnabled;
+static char *FName;
+
+// jidtodisp(jid)
+// Strips the resource part from the jid
+// The caller should g_free the result after use.
+char *jidtodisp(const char *fjid)
+{
+ char *ptr;
+ char *alias;
+
+ alias = g_strdup(fjid);
+
+ if ((ptr = strchr(alias, JID_RESOURCE_SEPARATOR)) != NULL) {
+ *ptr = 0;
+ }
+ return alias;
+}
+
+char *jid_get_username(const char *fjid)
+{
+ char *ptr;
+ char *username;
+
+ username = g_strdup(fjid);
+ if ((ptr = strchr(username, JID_DOMAIN_SEPARATOR)) != NULL) {
+ *ptr = 0;
+ }
+ return username;
+}
+
+char *compose_jid(const char *username, const char *servername,
+ const char *resource)
+{
+ char *fjid;
+
+ if (!strchr(username, JID_DOMAIN_SEPARATOR)) {
+ fjid = g_strdup_printf("%s%c%s%c%s", username,
+ JID_DOMAIN_SEPARATOR, servername,
+ JID_RESOURCE_SEPARATOR, resource);
+ } else {
+ fjid = g_strdup_printf("%s%c%s", username,
+ JID_RESOURCE_SEPARATOR, resource);
+ }
+ return fjid;
+}
+
+gboolean jid_equal(const char *jid1, const char *jid2)
+{
+ char *a,*b;
+ int ret;
+ if (!jid1 && !jid2)
+ return TRUE;
+ if (!jid1 || !jid2)
+ return FALSE;
+
+ a = jidtodisp(jid1);
+ b = jidtodisp(jid2);
+ ret = strcasecmp(a, b);
+ g_free(a);
+ g_free(b);
+ return (ret == 0) ? TRUE : FALSE;
+}
+
+// expand_filename(filename)
+// Expand "~/" with the $HOME env. variable in a file name.
+// The caller must free the string after use.
+char *expand_filename(const char *fname)
+{
+ if (!fname)
+ return NULL;
+ if (!strncmp(fname, "~/", 2)) {
+ char *homedir = getenv("HOME");
+ if (homedir)
+ return g_strdup_printf("%s%s", homedir, fname+1);
+ }
+ return g_strdup(fname);
+}
+
+void fingerprint_to_hex(const unsigned char *fpr, char hex[49])
+{
+ int i;
+ char *p;
+
+ for (p = hex, i = 0; i < 15; i++, p+=3)
+ g_sprintf(p, "%02X:", fpr[i]);
+ g_sprintf(p, "%02X", fpr[i]);
+ hex[48] = '\0';
+}
+
+gboolean hex_to_fingerprint(const char *hex, char fpr[16])
+{
+ int i;
+ char *p;
+
+ if (strlen(hex) != 47)
+ return FALSE;
+ for (i = 0, p = (char*)hex; *p && *(p+1); i++, p += 3)
+ fpr[i] = (char) g_ascii_strtoull (p, NULL, 16);
+ return TRUE;
+}
+
+void ut_InitDebug(int level, const char *filename)
+{
+ FILE *fp;
+ struct stat buf;
+ int err;
+
+ if (level < 1) {
+ DebugEnabled = 0;
+ FName = NULL;
+ return;
+ }
+
+ if (filename)
+ FName = expand_filename(filename);
+ else {
+ FName = getenv("HOME");
+ if (!FName)
+ FName = g_strdup("/tmp/mcabberlog");
+ else {
+ FName = g_strdup_printf("%s/mcabberlog", FName);
+ }
+ }
+
+ DebugEnabled = level;
+
+ fp = fopen(FName, "a");
+ if (!fp) {
+ fprintf(stderr, "ERROR: Cannot open tracelog file\n");
+ return;
+ }
+
+ err = fstat(fileno(fp), &buf);
+ if (err || buf.st_uid != geteuid()) {
+ fclose(fp);
+ DebugEnabled = 0;
+ FName = NULL;
+ if (err) {
+ fprintf(stderr, "ERROR: cannot stat the tracelog file!\n");
+ } else {
+ fprintf(stderr, "ERROR: tracelog file does not belong to you!\n");
+ }
+ return;
+ }
+ fchmod(fileno(fp), S_IRUSR|S_IWUSR);
+
+ fprintf(fp, "New trace log started.\n----------------------\n");
+ fclose(fp);
+}
+
+void ut_WriteLog(unsigned int flag, const char *data)
+{
+ if (!DebugEnabled || !FName) return;
+
+ if (((DebugEnabled >= 2) && (flag & (LPRINT_LOG|LPRINT_DEBUG))) ||
+ ((DebugEnabled == 1) && (flag & LPRINT_LOG))) {
+ FILE *fp = fopen(FName, "a+");
+ if (!fp) {
+ scr_LogPrint(LPRINT_NORMAL, "ERROR: Cannot open tracelog file");
+ return;
+ }
+ if (fputs(data, fp) == EOF)
+ scr_LogPrint(LPRINT_NORMAL, "ERROR: Cannot write to tracelog file");
+ fclose(fp);
+ }
+}
+
+// checkset_perm(name, setmode)
+// Check the permissions of the "name" file/dir
+// If setmode is true, correct the permissions if they are wrong
+// Return values: -1 == bad file/dir, 0 == success, 1 == cannot correct
+int checkset_perm(const char *name, unsigned int setmode)
+{
+ int fd;
+ struct stat buf;
+
+#ifdef __CYGWIN__
+ // Permission checking isn't efficient on Cygwin
+ return 0;
+#endif
+
+ fd = stat(name, &buf);
+ if (fd == -1) return -1;
+
+ if (buf.st_uid != geteuid()) {
+ scr_LogPrint(LPRINT_LOGNORM, "Wrong file owner [%s]", name);
+ return 1;
+ }
+
+ if (buf.st_mode & (S_IRGRP | S_IWGRP | S_IXGRP) ||
+ buf.st_mode & (S_IROTH | S_IWOTH | S_IXOTH)) {
+ if (setmode) {
+ mode_t newmode = 0;
+ scr_LogPrint(LPRINT_LOGNORM, "Bad permissions [%s]", name);
+ if (S_ISDIR(buf.st_mode))
+ newmode |= S_IXUSR;
+ newmode |= S_IRUSR | S_IWUSR;
+ if (chmod(name, newmode)) {
+ scr_LogPrint(LPRINT_LOGNORM, "WARNING: Failed to correct permissions!");
+ return 1;
+ }
+ scr_LogPrint(LPRINT_LOGNORM, "Permissions have been corrected");
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "WARNING: Bad permissions [%s]", name);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+const char *ut_get_tmpdir(void)
+{
+ static const char *tmpdir;
+ const char *tmpvars[] = { "MCABBERTMPDIR", "TMP", "TMPDIR", "TEMP" };
+ unsigned int i;
+
+ if (tmpdir)
+ return tmpdir;
+
+ for (i = 0; i < (sizeof(tmpvars) / sizeof(const char *)); i++) {
+ tmpdir = getenv(tmpvars[i]);
+ if (tmpdir && tmpdir[0] && tmpdir[0] == '/' && tmpdir[1]) {
+ // Looks ok.
+ return tmpdir;
+ }
+ }
+
+ // Default temporary directory
+ tmpdir = "/tmp";
+ return tmpdir;
+}
+
+// to_iso8601(dststr, timestamp)
+// Convert timestamp to iso8601 format, and store it in dststr.
+// NOTE: dststr should be at last 19 chars long.
+// Return should be 0
+int to_iso8601(char *dststr, time_t timestamp)
+{
+ struct tm *tm_time;
+ int ret;
+
+ tm_time = gmtime(×tamp);
+
+ ret = snprintf(dststr, 19, "%.4d%02d%02dT%02d:%02d:%02dZ",
+ (int)(1900+tm_time->tm_year), tm_time->tm_mon+1, tm_time->tm_mday,
+ tm_time->tm_hour, tm_time->tm_min, tm_time->tm_sec);
+
+ return ((ret == -1) ? -1 : 0);
+}
+
+// from_iso8601(timestamp, utc)
+// This function came from the Pidgin project, gaim_str_to_time().
+// (Actually date may not be pure iso-8601)
+// Thanks, guys!
+// ** Modified by somian 10 Apr 2006 with advice from ysth.
+time_t from_iso8601(const char *timestamp, int utc)
+{
+ struct tm t;
+ time_t retval = 0;
+ char buf[32];
+ char *c;
+ int tzoff = 0;
+ int hms_succ = 0;
+ int tmpyear;
+
+ time(&retval);
+ localtime_r(&retval, &t);
+
+ /* Reset time to midnight (00:00:00) */
+ t.tm_hour = t.tm_min = t.tm_sec = 0;
+
+ snprintf(buf, sizeof(buf), "%s", timestamp);
+ c = buf;
+
+ /* 4 digit year */
+ if (!sscanf(c, "%04d", &tmpyear)) return 0;
+ t.tm_year = tmpyear;
+ c+=4;
+ if (*c == '-')
+ c++;
+
+ t.tm_year -= 1900;
+
+ /* 2 digit month */
+ if (!sscanf(c, "%02d", &t.tm_mon)) return 0;
+ c+=2;
+ if (*c == '-')
+ c++;
+
+ t.tm_mon -= 1;
+
+ /* 2 digit day */
+ if (!sscanf(c, "%02d", &t.tm_mday)) return 0;
+ c+=2;
+ if (*c == 'T' || *c == '.') { /* we have more than a date, keep going */
+ c++; /* skip the "T" */
+
+ /* 2 digit hour */
+ if (sscanf(c, "%02d:%02d:%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3)
+ {
+ hms_succ = 1;
+ c += 8;
+ }
+ else if (sscanf(c, "%02d%02d%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3)
+ {
+ hms_succ = 1;
+ c += 6;
+ }
+
+ if (hms_succ) {
+ int tzhrs, tzmins;
+
+ if (*c == '.') /* dealing with precision we don't care about */
+ c += 4;
+
+ if ((*c == '+' || *c == '-') &&
+ sscanf(c+1, "%02d:%02d", &tzhrs, &tzmins)) {
+ tzoff = tzhrs*60*60 + tzmins*60;
+ if (*c == '+')
+ tzoff *= -1;
+ }
+
+ if (tzoff || utc) {
+#ifdef HAVE_TM_GMTOFF
+ tzoff += t.tm_gmtoff;
+#else
+# ifdef HAVE_TIMEZONE
+ tzset(); /* making sure */
+ tzoff -= timezone;
+# endif
+#endif
+ }
+ }
+ }
+
+ t.tm_isdst = -1;
+
+ retval = mktime(&t);
+
+ retval += tzoff;
+
+ return retval;
+}
+
+/**
+ * Derived from libjabber/jid.c, because the libjabber version is not
+ * really convenient for our usage.
+ *
+ * Check if the full JID is valid
+ * Return 0 if it is valid, non zero otherwise
+ */
+int check_jid_syntax(const char *fjid)
+{
+ const char *str;
+ const char *domain, *resource;
+ int domlen;
+#ifdef HAVE_LIBIDN
+ char *idnpp;
+ int r;
+#endif
+
+ if (!fjid) return 1;
+
+ domain = strchr(fjid, JID_DOMAIN_SEPARATOR);
+
+ /* the username is optional */
+ if (!domain) {
+ domain = fjid;
+ } else {
+ /* node identifiers may not be longer than 1023 bytes */
+ if ((domain == fjid) || (domain-fjid > 1023))
+ return 1;
+ domain++;
+
+#ifdef HAVE_LIBIDN
+ idnpp = idnprep;
+ str = fjid;
+ while (*str != JID_DOMAIN_SEPARATOR)
+ *idnpp++ = *str++;
+ *idnpp = 0;
+
+ r = stringprep(idnprep, 1023, 0, stringprep_xmpp_nodeprep);
+ if (r != STRINGPREP_OK || !idnprep[0])
+ return 1;
+ /* the username looks okay */
+#else
+ /* check for low and invalid ascii characters in the username */
+ for (str = fjid; *str != JID_DOMAIN_SEPARATOR; str++) {
+ if (*str <= ' ' || *str == ':' || *str == JID_DOMAIN_SEPARATOR ||
+ *str == '<' || *str == '>' || *str == '\'' ||
+ *str == '"' || *str == '&') {
+ return 1;
+ }
+ }
+ /* the username is okay as far as we can tell without LIBIDN */
+#endif
+ }
+
+ resource = strchr(domain, JID_RESOURCE_SEPARATOR);
+
+ /* the resource is optional */
+ if (resource) {
+ domlen = resource - domain;
+ resource++;
+ /* resources may not be longer than 1023 bytes */
+ if ((*resource == '\0') || strlen(resource) > 1023)
+ return 1;
+#ifdef HAVE_LIBIDN
+ strncpy(idnprep, resource, sizeof(idnprep));
+ r = stringprep(idnprep, 1023, 0, stringprep_xmpp_resourceprep);
+ if (r != STRINGPREP_OK || !idnprep[0])
+ return 1;
+#endif
+ } else {
+ domlen = strlen(domain);
+ }
+
+ /* there must be a domain identifier */
+ if (domlen == 0) return 1;
+
+ /* and it must not be longer than 1023 bytes */
+ if (domlen > 1023) return 1;
+
+#ifdef HAVE_LIBIDN
+ idnpp = idnprep;
+ str = domain;
+ while (*str != '\0' && *str != JID_RESOURCE_SEPARATOR)
+ *idnpp++ = *str++;
+ *idnpp = 0;
+
+ r = stringprep_nameprep(idnprep, 1023);
+ if (r != STRINGPREP_OK || !idnprep[0])
+ return 1;
+
+ if (idna_to_ascii_8z(idnprep, &idnpp, IDNA_USE_STD3_ASCII_RULES) !=
+ IDNA_SUCCESS)
+ return 1;
+ else
+ free(idnpp);
+#else
+ /* make sure the hostname is valid characters */
+ for (str = domain; *str != '\0' && *str != JID_RESOURCE_SEPARATOR; str++) {
+ if (!(isalnum(*str) || *str == '.' || *str == '-' || *str == '_'))
+ return 1;
+ }
+#endif
+
+ /* it's okay as far as we can tell */
+ return 0;
+}
+
+
+inline void mc_strtolower(char *str)
+{
+ if (!str) return;
+ for ( ; *str; str++)
+ *str = tolower(*str);
+}
+
+// strip_arg_special_chars(string)
+// Remove quotes and backslashes before an escaped quote
+// Only quotes need a backslash
+// Ex.: ["a b"] -> [a b]; [a\"b] -> [a"b]
+void strip_arg_special_chars(char *s)
+{
+ int instring = FALSE;
+ int escape = FALSE;
+ char *p;
+
+ if (!s) return;
+
+ for (p = s; *p; p++) {
+ if (*p == '"') {
+ if (!escape) {
+ instring = !instring;
+ strcpy(p, p+1);
+ p--;
+ } else
+ escape = FALSE;
+ } else if (*p == '\\') {
+ if (!escape) {
+ strcpy(p, p+1);
+ p--;
+ }
+ escape = !escape;
+ } else
+ escape = FALSE;
+ }
+}
+
+// split_arg(arg, n, preservelast)
+// Split the string arg into a maximum of n pieces, taking care of
+// double quotes.
+// Return a null-terminated array of strings. This array should be freed
+// by the caller after use, for example with free_arg_lst().
+// If dontstriplast is true, the Nth argument isn't stripped (i.e. no
+// processing of quote chars)
+char **split_arg(const char *arg, unsigned int n, int dontstriplast)
+{
+ char **arglst;
+ const char *p, *start, *end;
+ unsigned int i = 0;
+ int instring = FALSE;
+ int escape = FALSE;
+
+ arglst = g_new0(char*, n+1);
+
+ if (!arg || !n) return arglst;
+
+ // Skip leading space
+ for (start = arg; *start && *start == ' '; start++) ;
+ // End of string pointer
+ for (end = start; *end; end++) ;
+ // Skip trailing space
+ while (end > start+1 && *(end-1) == ' ')
+ end--;
+
+ for (p = start; p < end; p++) {
+ if (*p == '"' && !escape)
+ instring = !instring;
+ if (*p == '\\' && !escape)
+ escape = TRUE;
+ else if (escape)
+ escape = FALSE;
+ if (*p == ' ' && !instring && i+1 < n) {
+ // end of parameter
+ *(arglst+i) = g_strndup(start, p-start);
+ strip_arg_special_chars(*(arglst+i));
+ for (start = p+1; *start && *start == ' '; start++) ;
+ p = start-1;
+ i++;
+ }
+ }
+
+ if (start < end) {
+ *(arglst+i) = g_strndup(start, end-start);
+ if (!dontstriplast || i+1 < n)
+ strip_arg_special_chars(*(arglst+i));
+ }
+
+ return arglst;
+}
+
+// free_arg_lst(arglst)
+// Free an array allocated by split_arg()
+void free_arg_lst(char **arglst)
+{
+ char **arg_elt;
+
+ for (arg_elt = arglst; *arg_elt; arg_elt++)
+ g_free(*arg_elt);
+ g_free(arglst);
+}
+
+// replace_nl_with_dots(bufstr)
+// Replace '\n' with "(...)" (or with a NUL if the string is too short)
+void replace_nl_with_dots(char *bufstr)
+{
+ char *p = strchr(bufstr, '\n');
+ if (p) {
+ if (strlen(p) >= 5)
+ strcpy(p, "(...)");
+ else
+ *p = 0;
+ }
+}
+
+// ut_expand_tabs(text)
+// Expand tabs and filter out some bad chars in string text.
+// If there is no tab and no bad chars in the string, a pointer to text
+// is returned (be careful _not_ to free the pointer in this case).
+// If there are some tabs or bad chars, a new string with expanded chars
+// and no bad chars is returned; this is up to the caller to free this
+// string after use.
+char *ut_expand_tabs(const char *text)
+{
+ char *xtext, *linestart;
+ char *p, *q;
+ guint n = 0, bc = 0;
+
+ xtext = (char*)text;
+ for (p=xtext; *p; p++)
+ if (*p == '\t')
+ n++;
+ else if (*p == '\x0d')
+ bc++;
+ // XXX Are there other special chars we should filter out?
+
+ if (!n && !bc)
+ return (char*)text;
+
+ xtext = g_new(char, strlen(text) + 1 + 8*n);
+ p = (char*)text;
+ q = linestart = xtext;
+ do {
+ if (*p == '\t') {
+ do { *q++ = ' '; } while ((q-linestart)%8);
+ } else if (*p != '\x0d') {
+ *q++ = *p;
+ if (*p =='\n')
+ linestart = q;
+ }
+ } while (*p++);
+
+ return xtext;
+}
+
+
+/* Cygwin's newlib does not have strcasestr() */
+/* The author of the code before the endif is
+ * Jeffrey Stedfast <fejj@ximian.com>
+ * and this code is reusable in compliance with the GPL v2. -- somian */
+
+#if !defined(HAVE_STRCASESTR)
+
+# define lowercase(c) (isupper ((int) (c)) ? tolower ((int) (c)) : (int) (c))
+# define bm_index(c, icase) ((icase) ? lowercase (c) : (int) (c))
+# define bm_equal(c1, c2, icase) ((icase) ? lowercase (c1) == lowercase (c2) : (c1) == (c2))
+
+/* FIXME: this is just a guess... should really do some performace tests to get an accurate measure */
+# define bm_optimal(hlen, nlen) (((hlen) ? (hlen) > 20 : 1) && (nlen) > 10 ? 1 : 0)
+
+static unsigned char *
+__boyer_moore (const unsigned char *haystack, size_t haystacklen,
+ const unsigned char *needle, size_t needlelen, int icase)
+{
+ register unsigned char *hc_ptr, *nc_ptr;
+ unsigned char *he_ptr, *ne_ptr, *h_ptr;
+ size_t skiptable[256], n;
+ register int i;
+
+#ifdef BOYER_MOORE_CHECKS
+ /* we don't need to do these checks since memmem/strstr/etc do it already */
+ /* if the haystack is shorter than the needle then we can't possibly match */
+ if (haystacklen < needlelen)
+ return NULL;
+
+ /* instant match if the pattern buffer is 0-length */
+ if (needlelen == 0)
+ return (unsigned char *) haystack;
+#endif /* BOYER_MOORE_CHECKS */
+
+ /* set a pointer at the end of each string */
+ ne_ptr = (unsigned char *) needle + needlelen - 1;
+ he_ptr = (unsigned char *) haystack + haystacklen - 1;
+
+ /* create our skip table */
+ for (i = 0; i < 256; i++)
+ skiptable[i] = needlelen;
+ for (nc_ptr = (unsigned char *) needle; nc_ptr < ne_ptr; nc_ptr++)
+ skiptable[bm_index (*nc_ptr, icase)] = (size_t) (ne_ptr - nc_ptr);
+
+ h_ptr = (unsigned char *) haystack;
+ while (haystacklen >= needlelen) {
+ hc_ptr = h_ptr + needlelen - 1; /* set the haystack compare pointer */
+ nc_ptr = ne_ptr; /* set the needle compare pointer */
+
+ /* work our way backwards till they don't match */
+ for (i = 0; nc_ptr > (unsigned char *) needle; nc_ptr--, hc_ptr--, i++)
+ if (!bm_equal (*nc_ptr, *hc_ptr, icase))
+ break;
+
+ if (!bm_equal (*nc_ptr, *hc_ptr, icase)) {
+ n = skiptable[bm_index (*hc_ptr, icase)];
+ if (n == needlelen && i)
+ if (bm_equal (*ne_ptr, ((unsigned char *) needle)[0], icase))
+ n--;
+ h_ptr += n;
+ haystacklen -= n;
+ } else
+ return (unsigned char *) h_ptr;
+ }
+
+ return NULL;
+}
+
+/*
+ * strcasestr:
+ * @haystack: string to search
+ * @needle: substring to search for
+ *
+ * Finds the first occurence of the substring @needle within the
+ * string @haystack ignoring case.
+ *
+ * Returns a pointer to the beginning of the substring match within
+ * @haystack, or NULL if the substring is not found.
+ **/
+char *
+strcasestr (const char *haystack, const char *needle)
+{
+ register unsigned char *h, *n, *hc, *nc;
+ size_t needlelen;
+
+ needlelen = strlen (needle);
+
+ if (needlelen == 0) {
+ return (char *) haystack;
+ } else if (bm_optimal (0, needlelen)) {
+ return (char *) __boyer_moore ((const unsigned char *) haystack,
+ strlen (haystack),
+ (const unsigned char *) needle,
+ needlelen, 1);
+ }
+
+ h = (unsigned char *) haystack;
+ n = (unsigned char *) needle;
+
+ while (*(h + needlelen - 1)) {
+ if (lowercase (*h) == lowercase (*n)) {
+ for (hc = h + 1, nc = n + 1; *hc && *nc; hc++, nc++)
+ if (lowercase (*hc) != lowercase (*nc))
+ break;
+
+ if (!*nc)
+ return (char *) h;
+ }
+ h++;
+ }
+ return NULL;
+}
+#endif /* !HAVE_STRCASESTR */
+
+// startswith(str, word, ignore_case)
+// Returns TRUE if string str starts with word.
+int startswith(const char *str, const char *word, guint ignore_case)
+{
+ if (ignore_case && !strncasecmp(str, word, strlen(word)))
+ return TRUE;
+ else if (!ignore_case && !strncmp(str, word, strlen(word)))
+ return TRUE;
+ return FALSE;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/utils.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,57 @@
+#ifndef __MCABBER_UTILS_H__
+#define __MCABBER_UTILS_H__ 1
+
+#include <mcabber/config.h>
+
+extern const char *LocaleCharSet;
+
+#define to_utf8(s) ((s) ? g_locale_to_utf8((s), -1, NULL,NULL,NULL) : NULL)
+#define from_utf8(s) ((s) ? g_convert_with_fallback((s), -1, LocaleCharSet, \
+ "UTF-8", NULL,NULL,NULL,NULL) : NULL)
+
+#define JID_RESOURCE_SEPARATOR '/'
+#define JID_RESOURCE_SEPARATORSTR "/"
+#define JID_DOMAIN_SEPARATOR '@'
+#define JID_DOMAIN_SEPARATORSTR "@"
+
+char *jidtodisp(const char *fjid);
+char *jid_get_username(const char *fjid);
+char *compose_jid(const char *username, const char *servername,
+ const char *resource);
+gboolean jid_equal(const char *jid1, const char *jid2);
+
+void fingerprint_to_hex(const unsigned char *fpr, char hex[49]);
+gboolean hex_to_fingerprint(const char * hex, char fpr[16]);
+
+void ut_InitDebug(int level, const char *file);
+void ut_WriteLog(unsigned int flag, const char *data);
+
+char *expand_filename(const char *fname);
+
+int checkset_perm(const char *name, unsigned int setmode);
+
+const char *ut_get_tmpdir(void);
+
+int to_iso8601(char *dststr, time_t timestamp);
+time_t from_iso8601(const char *timestamp, int utc);
+
+int check_jid_syntax(const char *fjid);
+
+void mc_strtolower(char *str);
+
+void strip_arg_special_chars(char *s);
+char **split_arg(const char *arg, unsigned int n, int dontstriplast);
+void free_arg_lst(char **arglst);
+
+void replace_nl_with_dots(char *bufstr);
+char *ut_expand_tabs(const char *text);
+
+#if !defined (HAVE_STRCASESTR)
+char *strcasestr(const char *haystack, const char *needle);
+#endif
+
+int startswith(const char *str, const char *word, guint ignore_case);
+
+#endif // __MCABBER_UTILS_H__
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,2200 @@
+/*
+ * xmpp.c -- Jabber protocol handling
+ *
+ * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ * Parts come from the centericq project:
+ * Copyright (C) 2002-2005 by Konstantin Klyagin <konst@konst.org.ua>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "xmpp.h"
+#include "xmpp_helper.h"
+#include "xmpp_iq.h"
+#include "xmpp_iqrequest.h"
+#include "xmpp_muc.h"
+#include "xmpp_s10n.h"
+#include "caps.h"
+#include "events.h"
+#include "histolog.h"
+#include "hooks.h"
+#include "otr.h"
+#include "roster.h"
+#include "screen.h"
+#include "settings.h"
+#include "utils.h"
+#include "main.h"
+
+#define RECONNECTION_TIMEOUT 60L
+
+LmConnection* lconnection;
+static guint AutoConnection;
+
+inline void update_last_use(void);
+inline gboolean xmpp_reconnect();
+
+enum imstatus mystatus = offline;
+static enum imstatus mywantedstatus = available;
+gchar *mystatusmsg;
+
+char imstatus2char[imstatus_size+1] = {
+ '_', 'o', 'f', 'd', 'n', 'a', 'i', '\0'
+};
+
+char *imstatus_showmap[] = {
+ "",
+ "",
+ "chat",
+ "dnd",
+ "xa",
+ "away",
+ ""
+};
+
+LmMessageNode *bookmarks = NULL;
+LmMessageNode *rosternotes = NULL;
+
+static struct IqHandlers
+{
+ const gchar *xmlns;
+ LmHandleMessageFunction handler;
+} iq_handlers[] = {
+ {NS_PING, &handle_iq_ping},
+ {NS_VERSION, &handle_iq_version},
+ {NS_TIME, &handle_iq_time},
+ {NS_ROSTER, &handle_iq_roster},
+ {NS_XMPP_TIME, &handle_iq_time202},
+ {NS_LAST, &handle_iq_last},
+ {NS_DISCO_INFO, &handle_iq_disco_info},
+ {NS_DISCO_ITEMS,&handle_iq_disco_items},
+ {NS_COMMANDS, &handle_iq_commands},
+ {NS_VCARD, &handle_iq_vcard},
+ {NULL, NULL}
+};
+
+void update_last_use(void)
+{
+ iqlast = time(NULL);
+}
+
+// Note: the caller should check the jid is correct
+void xmpp_addbuddy(const char *bjid, const char *name, const char *group)
+{
+ LmMessageNode *query, *y;
+ LmMessage *iq;
+ char *cleanjid;
+
+ if (!lm_connection_is_authenticated(lconnection)) return;
+
+ cleanjid = jidtodisp(bjid); // Stripping resource, just in case...
+
+ // We don't check if the jabber user already exists in the roster,
+ // because it allows to re-ask for notification.
+
+ iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_SET);
+ query = lm_message_node_add_child(iq->node, "query", NULL);
+ lm_message_node_set_attribute(query, "xmlns", NS_ROSTER);
+ y = lm_message_node_add_child(query, "item", NULL);
+ lm_message_node_set_attribute(y, "jid", cleanjid);
+
+ if (name)
+ lm_message_node_set_attribute(y, "name", name);
+
+ if (group)
+ lm_message_node_add_child(y, "group", group);
+
+ lm_connection_send(lconnection, iq, NULL);
+ lm_message_unref(iq);
+
+ xmpp_send_s10n(cleanjid, LM_MESSAGE_SUB_TYPE_SUBSCRIBE);
+
+ roster_add_user(cleanjid, name, group, ROSTER_TYPE_USER, sub_pending, -1);
+ g_free(cleanjid);
+ buddylist_build();
+
+ update_roster = TRUE;
+}
+
+void xmpp_updatebuddy(const char *bjid, const char *name, const char *group)
+{
+ LmMessage *iq;
+ LmMessageNode *x;
+ char *cleanjid;
+
+ if (!lm_connection_is_authenticated(lconnection)) return;
+
+ // XXX We should check name's and group's correctness
+
+ cleanjid = jidtodisp(bjid); // Stripping resource, just in case...
+
+ iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_SET);
+ x = lm_message_node_add_child(iq->node, "query", NULL);
+ lm_message_node_set_attribute(x, "xmlns", NS_ROSTER);
+ x = lm_message_node_add_child(x, "item", NULL);
+ lm_message_node_set_attributes(x,
+ "jid", cleanjid,
+ "name", name,
+ NULL);
+
+ if (group)
+ lm_message_node_add_child(x, "group", group);
+
+ lm_connection_send(lconnection, iq, NULL);
+ lm_message_unref(iq);
+ g_free(cleanjid);
+}
+
+void xmpp_delbuddy(const char *bjid)
+{
+ LmMessageNode *y, *z;
+ LmMessage *iq;
+ char *cleanjid;
+
+ if (!lm_connection_is_authenticated(lconnection)) return;
+
+ cleanjid = jidtodisp(bjid); // Stripping resource, just in case...
+
+ // If the current buddy is an agent, unsubscribe from it
+ if (roster_gettype(cleanjid) == ROSTER_TYPE_AGENT) {
+ scr_LogPrint(LPRINT_LOGNORM, "Unregistering from the %s agent", cleanjid);
+
+ iq = lm_message_new_with_sub_type(cleanjid, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_SET);
+ y = lm_message_node_add_child(iq->node, "query", NULL);
+ lm_message_node_set_attribute(y, "xmlns", NS_REGISTER);
+ lm_message_node_add_child(y, "remove", NULL);
+ lm_connection_send(lconnection, iq, NULL);
+ lm_message_unref(iq);
+ }
+
+ // Cancel the subscriptions
+ xmpp_send_s10n(cleanjid, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED); //cancel "from"
+ xmpp_send_s10n(cleanjid, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE); //cancel "to"
+
+ // Ask for removal from roster
+ iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_SET);
+
+ y = lm_message_node_add_child(iq->node, "query", NULL);
+ lm_message_node_set_attribute(y, "xmlns", NS_ROSTER);
+ z = lm_message_node_add_child(y, "item", NULL);
+ lm_message_node_set_attributes(z,
+ "jid", cleanjid,
+ "subscription", "remove",
+ NULL);
+ lm_connection_send(lconnection, iq, NULL);
+ lm_message_unref(iq);
+
+ roster_del_user(cleanjid);
+ g_free(cleanjid);
+ buddylist_build();
+
+ update_roster = TRUE;
+}
+
+void xmpp_request(const char *fjid, enum iqreq_type reqtype)
+{
+ GSList *resources, *p_res;
+ GSList *roster_elt;
+ const char *strreqtype, *xmlns;
+
+ if (reqtype == iqreq_version) {
+ xmlns = NS_VERSION;
+ strreqtype = "version";
+ } else if (reqtype == iqreq_time) {
+ xmlns = NS_TIME;
+ strreqtype = "time";
+ } else if (reqtype == iqreq_last) {
+ xmlns = NS_LAST;
+ strreqtype = "last";
+ } else if (reqtype == iqreq_vcard) {
+ xmlns = NS_VCARD;
+ strreqtype = "vCard";
+ // Special case
+ } else
+ return;
+
+ if (strchr(fjid, JID_RESOURCE_SEPARATOR)) {
+ // This is a full JID
+ xmpp_iq_request(fjid, xmlns);
+ scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fjid);
+ return;
+ }
+
+ // The resource has not been specified
+ roster_elt = roster_find(fjid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_ROOM);
+ if (!roster_elt) {
+ scr_LogPrint(LPRINT_NORMAL, "No known resource for <%s>...", fjid);
+ xmpp_iq_request(fjid, xmlns); // Let's send a request anyway...
+ scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fjid);
+ return;
+ }
+
+ // Send a request to each resource
+ resources = buddy_getresources(roster_elt->data);
+ if (!resources) {
+ scr_LogPrint(LPRINT_NORMAL, "No known resource for <%s>...", fjid);
+ xmpp_iq_request(fjid, xmlns); // Let's send a request anyway...
+ scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fjid);
+ }
+ for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
+ gchar *fulljid;
+ fulljid = g_strdup_printf("%s/%s", fjid, (char*)p_res->data);
+ xmpp_iq_request(fulljid, xmlns);
+ scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fulljid);
+ g_free(fulljid);
+ g_free(p_res->data);
+ }
+ g_slist_free(resources);
+}
+
+static LmHandlerResult cb_xep184(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data)
+{
+ char *from = jidtodisp(lm_message_get_from(m));
+ scr_RemoveReceiptFlag(from, h);
+ g_free(from);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+// xmpp_send_msg(jid, text, type, subject,
+// otrinject, *encrypted, type_overwrite)
+// When encrypted is not NULL, the function set *encrypted to 1 if the
+// message has been PGP-encrypted. If encryption enforcement is set and
+// encryption fails, *encrypted is set to -1.
+void xmpp_send_msg(const char *fjid, const char *text, int type,
+ const char *subject, gboolean otrinject, gint *encrypted,
+ LmMessageSubType type_overwrite, gpointer *xep184)
+{
+ LmMessage *x;
+ LmMessageSubType subtype;
+#ifdef HAVE_LIBOTR
+ int otr_msg = 0;
+#endif
+#if defined HAVE_GPGME || defined JEP0022 || defined JEP0085
+ char *rname, *barejid;
+ GSList *sl_buddy;
+#endif
+#if defined JEP0022 || defined JEP0085
+ LmMessageNode *event;
+ guint use_jep85 = 0;
+ struct jep0085 *jep85 = NULL;
+#endif
+ gchar *enc = NULL;
+
+ if (encrypted)
+ *encrypted = 0;
+
+ if (!lm_connection_is_authenticated(lconnection))
+ return;
+
+ if (!text && type == ROSTER_TYPE_USER)
+ return;
+
+ if (type_overwrite != LM_MESSAGE_SUB_TYPE_NOT_SET)
+ subtype = type_overwrite;
+ else {
+ if (type == ROSTER_TYPE_ROOM)
+ subtype = LM_MESSAGE_SUB_TYPE_GROUPCHAT;
+ else
+ subtype = LM_MESSAGE_SUB_TYPE_CHAT;
+ }
+
+#if defined HAVE_GPGME || defined HAVE_LIBOTR || \
+ defined JEP0022 || defined JEP0085
+ rname = strchr(fjid, JID_RESOURCE_SEPARATOR);
+ barejid = jidtodisp(fjid);
+ sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
+
+ // If we can get a resource name, we use it. Else we use NULL,
+ // which hopefully will give us the most likely resource.
+ if (rname)
+ rname++;
+
+#ifdef HAVE_LIBOTR
+ if (otr_enabled() && !otrinject) {
+ if (type == ROSTER_TYPE_USER) {
+ otr_msg = otr_send((char **)&text, barejid);
+ if (!text) {
+ g_free(barejid);
+ if (encrypted)
+ *encrypted = -1;
+ return;
+ }
+ }
+ if (otr_msg && encrypted)
+ *encrypted = ENCRYPTED_OTR;
+ }
+#endif
+
+#ifdef HAVE_GPGME
+ if (type == ROSTER_TYPE_USER && sl_buddy && gpg_enabled()) {
+ if (!settings_pgp_getdisabled(barejid)) { // not disabled for this contact?
+ guint force;
+ struct pgp_data *res_pgpdata;
+ force = settings_pgp_getforce(barejid);
+ res_pgpdata = buddy_resource_pgp(sl_buddy->data, rname);
+ if (force || (res_pgpdata && res_pgpdata->sign_keyid)) {
+ /* Remote client has PGP support (we have a signature)
+ * OR encryption is enforced (force = TRUE).
+ * If the contact has a specific KeyId, we'll use it;
+ * if not, we'll use the key used for the signature.
+ * Both keys should match, in theory (cf. XEP-0027). */
+ const char *key;
+ key = settings_pgp_getkeyid(barejid);
+ if (!key && res_pgpdata)
+ key = res_pgpdata->sign_keyid;
+ if (key)
+ enc = gpg_encrypt(text, key);
+ if (!enc && force) {
+ if (encrypted)
+ *encrypted = -1;
+ g_free(barejid);
+ return;
+ }
+ }
+ }
+ }
+#endif // HAVE_GPGME
+
+ g_free(barejid);
+#endif // HAVE_GPGME || defined JEP0022 || defined JEP0085
+
+ x = lm_message_new_with_sub_type(fjid, LM_MESSAGE_TYPE_MESSAGE, subtype);
+ lm_message_node_add_child(x->node, "body",
+ enc ? "This message is PGP-encrypted." : text);
+
+ if (subject)
+ lm_message_node_add_child(x->node, "subject", subject);
+
+ if (enc) {
+ LmMessageNode *y;
+ y = lm_message_node_add_child(x->node, "x", enc);
+ lm_message_node_set_attribute(y, "xmlns", NS_ENCRYPTED);
+ if (encrypted)
+ *encrypted = ENCRYPTED_PGP;
+ g_free(enc);
+ }
+
+ //XEP-0184: Message Receipts
+ if (sl_buddy && rname && xep184 &&
+ caps_has_feature(buddy_resource_getcaps(sl_buddy->data, rname),
+ NS_RECEIPTS)) {
+ lm_message_node_set_attribute
+ (lm_message_node_add_child(x->node, "request", NULL),
+ "xmlns", NS_RECEIPTS);
+ *xep184 = lm_message_handler_new(cb_xep184, NULL, NULL);
+ }
+
+#if defined JEP0022 || defined JEP0085
+ // If typing notifications are disabled, we can skip all this stuff...
+ if (chatstates_disabled || type == ROSTER_TYPE_ROOM)
+ goto xmpp_send_msg_no_chatstates;
+
+ if (sl_buddy)
+ jep85 = buddy_resource_jep85(sl_buddy->data, rname);
+#endif
+
+#ifdef JEP0085
+ /* JEP-0085 5.1
+ * "Until receiving a reply to the initial content message (or a standalone
+ * notification) from the Contact, the User MUST NOT send subsequent chat
+ * state notifications to the Contact."
+ * In our implementation support is initially "unknown", then it's "probed"
+ * and can become "ok".
+ */
+ if (jep85 && (jep85->support == CHATSTATES_SUPPORT_OK ||
+ jep85->support == CHATSTATES_SUPPORT_UNKNOWN)) {
+ event = lm_message_node_add_child(x->node, "active", NULL);
+ lm_message_node_set_attribute(event, "xmlns", NS_CHATSTATES);
+ if (jep85->support == CHATSTATES_SUPPORT_UNKNOWN)
+ jep85->support = CHATSTATES_SUPPORT_PROBED;
+ else
+ use_jep85 = 1;
+ jep85->last_state_sent = ROSTER_EVENT_ACTIVE;
+ }
+#endif
+#ifdef JEP0022
+ /* JEP-22
+ * If the Contact supports JEP-0085, we do not use JEP-0022.
+ * If not, we try to fall back to JEP-0022.
+ */
+ if (!use_jep85) {
+ struct jep0022 *jep22 = NULL;
+ event = lm_message_node_add_child(x->node, "x", NULL);
+ lm_message_node_set_attribute(event, "xmlns", NS_EVENT);
+ lm_message_node_add_child(event, "composing", NULL);
+
+ if (sl_buddy)
+ jep22 = buddy_resource_jep22(sl_buddy->data, rname);
+ if (jep22)
+ jep22->last_state_sent = ROSTER_EVENT_ACTIVE;
+
+ // An id is mandatory when using JEP-0022.
+ if (text || subject) {
+ const gchar *msgid = lm_message_get_id(x);
+ // Let's update last_msgid_sent
+ if (jep22) {
+ g_free(jep22->last_msgid_sent);
+ jep22->last_msgid_sent = g_strdup(msgid);
+ }
+ }
+ }
+#endif
+
+xmpp_send_msg_no_chatstates:
+ if (mystatus != invisible)
+ update_last_use();
+ if (xep184 && *xep184) {
+ lm_connection_send_with_reply(lconnection, x, *xep184, NULL);
+ lm_message_handler_unref(*xep184);
+ } else
+ lm_connection_send(lconnection, x, NULL);
+ lm_message_unref(x);
+}
+
+#ifdef JEP0085
+// xmpp_send_jep85_chatstate()
+// Send a JEP-85 chatstate.
+static void xmpp_send_jep85_chatstate(const char *bjid, const char *resname,
+ guint state)
+{
+ LmMessage *m;
+ LmMessageNode *event;
+ GSList *sl_buddy;
+ const char *chattag;
+ char *rjid, *fjid = NULL;
+ struct jep0085 *jep85 = NULL;
+
+ if (!lm_connection_is_authenticated(lconnection)) return;
+
+ sl_buddy = roster_find(bjid, jidsearch, ROSTER_TYPE_USER);
+
+ // If we have a resource name, we use it. Else we use NULL,
+ // which hopefully will give us the most likely resource.
+ if (sl_buddy)
+ jep85 = buddy_resource_jep85(sl_buddy->data, resname);
+
+ if (!jep85 || (jep85->support != CHATSTATES_SUPPORT_OK))
+ return;
+
+ if (state == jep85->last_state_sent)
+ return;
+
+ if (state == ROSTER_EVENT_ACTIVE)
+ chattag = "active";
+ else if (state == ROSTER_EVENT_COMPOSING)
+ chattag = "composing";
+ else if (state == ROSTER_EVENT_PAUSED)
+ chattag = "paused";
+ else {
+ scr_LogPrint(LPRINT_LOGNORM, "Error: unsupported JEP-85 state (%d)", state);
+ return;
+ }
+
+ jep85->last_state_sent = state;
+
+ if (resname)
+ fjid = g_strdup_printf("%s/%s", bjid, resname);
+
+ rjid = resname ? fjid : (char*)bjid;
+ m = lm_message_new_with_sub_type(rjid, LM_MESSAGE_TYPE_MESSAGE,
+ LM_MESSAGE_SUB_TYPE_CHAT);
+
+ event = lm_message_node_add_child(m->node, chattag, NULL);
+ lm_message_node_set_attribute(event, "xmlns", NS_CHATSTATES);
+
+ lm_connection_send(lconnection, m, NULL);
+ lm_message_unref(m);
+
+ g_free(fjid);
+}
+#endif
+
+#ifdef JEP0022
+// xmpp_send_jep22_event()
+// Send a JEP-22 message event (delivered, composing...).
+static void xmpp_send_jep22_event(const char *fjid, guint type)
+{
+ LmMessage *x;
+ LmMessageNode *event;
+ const char *msgid;
+ char *rname, *barejid;
+ GSList *sl_buddy;
+ struct jep0022 *jep22 = NULL;
+ guint jep22_state;
+
+ if (!lm_connection_is_authenticated(lconnection)) return;
+
+ rname = strchr(fjid, JID_RESOURCE_SEPARATOR);
+ barejid = jidtodisp(fjid);
+ sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
+ g_free(barejid);
+
+ // If we can get a resource name, we use it. Else we use NULL,
+ // which hopefully will give us the most likely resource.
+ if (rname)
+ rname++;
+ if (sl_buddy)
+ jep22 = buddy_resource_jep22(sl_buddy->data, rname);
+
+ if (!jep22)
+ return; // XXX Maybe we could try harder (other resources?)
+
+ msgid = jep22->last_msgid_rcvd;
+
+ // For composing events (composing, active, inactive, paused...),
+ // JEP22 only has 2 states; we'll use composing and active.
+ if (type == ROSTER_EVENT_COMPOSING)
+ jep22_state = ROSTER_EVENT_COMPOSING;
+ else if (type == ROSTER_EVENT_ACTIVE ||
+ type == ROSTER_EVENT_PAUSED)
+ jep22_state = ROSTER_EVENT_ACTIVE;
+ else
+ jep22_state = 0; // ROSTER_EVENT_NONE
+
+ if (jep22_state) {
+ // Do not re-send a same event
+ if (jep22_state == jep22->last_state_sent)
+ return;
+ jep22->last_state_sent = jep22_state;
+ }
+
+ x = lm_message_new_with_sub_type(fjid, LM_MESSAGE_TYPE_MESSAGE,
+ LM_MESSAGE_SUB_TYPE_CHAT);
+
+ event = lm_message_node_add_child(x->node, "x", NULL);
+ lm_message_node_set_attribute(event, "xmlns", NS_EVENT);
+ if (type == ROSTER_EVENT_DELIVERED)
+ lm_message_node_add_child(event, "delivered", NULL);
+ else if (type == ROSTER_EVENT_COMPOSING)
+ lm_message_node_add_child(event, "composing", NULL);
+ lm_message_node_add_child(event, "id", msgid);
+
+ lm_connection_send(lconnection, x, NULL);
+ lm_message_unref(x);
+}
+#endif
+
+// xmpp_send_chatstate(buddy, state)
+// Send a chatstate or event (JEP-22/85) according to the buddy's capabilities.
+// The message is sent to one of the resources with the highest priority.
+#if defined JEP0022 || defined JEP0085
+void xmpp_send_chatstate(gpointer buddy, guint chatstate)
+{
+ const char *bjid;
+#ifdef JEP0085
+ GSList *resources, *p_res, *p_next;
+ struct jep0085 *jep85 = NULL;
+#endif
+#ifdef JEP0022
+ struct jep0022 *jep22;
+#endif
+
+ bjid = buddy_getjid(buddy);
+ if (!bjid) return;
+
+#ifdef JEP0085
+ /* Send the chatstate to the last resource (which should have the highest
+ priority).
+ If chatstate is "active", send an "active" state to all resources
+ which do not curently have this state.
+ */
+ resources = buddy_getresources(buddy);
+ for (p_res = resources ; p_res ; p_res = p_next) {
+ p_next = g_slist_next(p_res);
+ jep85 = buddy_resource_jep85(buddy, p_res->data);
+ if (jep85 && jep85->support == CHATSTATES_SUPPORT_OK) {
+ // If p_next is NULL, this is the highest (prio) resource, i.e.
+ // the one we are probably writing to.
+ if (!p_next || (jep85->last_state_sent != ROSTER_EVENT_ACTIVE &&
+ chatstate == ROSTER_EVENT_ACTIVE))
+ xmpp_send_jep85_chatstate(bjid, p_res->data, chatstate);
+ }
+ g_free(p_res->data);
+ }
+ g_slist_free(resources);
+ // If the last resource had chatstates support when can return now,
+ // we don't want to send a JEP22 event.
+ if (jep85 && jep85->support == CHATSTATES_SUPPORT_OK)
+ return;
+#endif
+#ifdef JEP0022
+ jep22 = buddy_resource_jep22(buddy, NULL);
+ if (jep22 && jep22->support == CHATSTATES_SUPPORT_OK) {
+ xmpp_send_jep22_event(bjid, chatstate);
+ }
+#endif
+}
+#endif
+
+
+// chatstates_reset_probed(fulljid)
+// If the JEP has been probed for this contact, set it back to unknown so
+// that we probe it again. The parameter must be a full jid (w/ resource).
+#if defined JEP0022 || defined JEP0085
+static void chatstates_reset_probed(const char *fulljid)
+{
+ char *rname, *barejid;
+ GSList *sl_buddy;
+ struct jep0085 *jep85;
+ struct jep0022 *jep22;
+
+ rname = strchr(fulljid, JID_RESOURCE_SEPARATOR);
+ if (!rname++)
+ return;
+
+ barejid = jidtodisp(fulljid);
+ sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
+ g_free(barejid);
+
+ if (!sl_buddy)
+ return;
+
+ jep85 = buddy_resource_jep85(sl_buddy->data, rname);
+ jep22 = buddy_resource_jep22(sl_buddy->data, rname);
+
+ if (jep85 && jep85->support == CHATSTATES_SUPPORT_PROBED)
+ jep85->support = CHATSTATES_SUPPORT_UNKNOWN;
+ if (jep22 && jep22->support == CHATSTATES_SUPPORT_PROBED)
+ jep22->support = CHATSTATES_SUPPORT_UNKNOWN;
+}
+#endif
+
+#ifdef HAVE_GPGME
+// keys_mismatch(key, expectedkey)
+// Return TRUE if both keys are non-null and "expectedkey" doesn't match
+// the end of "key".
+// If one of the keys is null, return FALSE.
+// If expectedkey is less than 8 bytes long, return TRUE.
+//
+// Example: keys_mismatch("C9940A9BB0B92210", "B0B92210") will return FALSE.
+static bool keys_mismatch(const char *key, const char *expectedkey)
+{
+ int lk, lek;
+
+ if (!expectedkey || !key)
+ return FALSE;
+
+ lk = strlen(key);
+ lek = strlen(expectedkey);
+
+ // If the expectedkey is less than 8 bytes long, this is probably a
+ // user mistake so we consider it's a mismatch.
+ if (lek < 8)
+ return TRUE;
+
+ if (lek < lk)
+ key += lk - lek;
+
+ return strcasecmp(key, expectedkey);
+}
+#endif
+
+// check_signature(barejid, resourcename, xmldata, text)
+// Verify the signature (in xmldata) of "text" for the contact
+// barejid/resourcename.
+// xmldata is the 'jabber:x:signed' stanza.
+// If the key id is found, the contact's PGP data are updated.
+static void check_signature(const char *barejid, const char *rname,
+ LmMessageNode *node, const char *text)
+{
+#ifdef HAVE_GPGME
+ const char *p, *key;
+ GSList *sl_buddy;
+ struct pgp_data *res_pgpdata;
+ gpgme_sigsum_t sigsum;
+
+ // All parameters must be valid
+ if (!(node && barejid && rname && text))
+ return;
+
+ if (!gpg_enabled())
+ return;
+
+ // Get the resource PGP data structure
+ sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
+ if (!sl_buddy)
+ return;
+ res_pgpdata = buddy_resource_pgp(sl_buddy->data, rname);
+ if (!res_pgpdata)
+ return;
+
+ if (!node->name || strcmp(node->name, "x")) //XXX: probably useless
+ return; // We expect "<x xmlns='jabber:x:signed'>"
+
+ // Get signature
+ p = lm_message_node_get_value(node);
+ if (!p)
+ return;
+
+ key = gpg_verify(p, text, &sigsum);
+ if (key) {
+ const char *expectedkey;
+ char *buf;
+ g_free(res_pgpdata->sign_keyid);
+ res_pgpdata->sign_keyid = (char *)key;
+ res_pgpdata->last_sigsum = sigsum;
+ if (sigsum & GPGME_SIGSUM_RED) {
+ buf = g_strdup_printf("Bad signature from <%s/%s>", barejid, rname);
+ scr_WriteIncomingMessage(barejid, buf, 0, HBB_PREFIX_INFO, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+ g_free(buf);
+ }
+ // Verify that the key id is the one we expect.
+ expectedkey = settings_pgp_getkeyid(barejid);
+ if (keys_mismatch(key, expectedkey)) {
+ buf = g_strdup_printf("Warning: The KeyId from <%s/%s> doesn't match "
+ "the key you set up", barejid, rname);
+ scr_WriteIncomingMessage(barejid, buf, 0, HBB_PREFIX_INFO, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+ g_free(buf);
+ }
+ }
+#endif
+}
+
+static LmSSLResponse ssl_cb(LmSSL *ssl, LmSSLStatus status, gpointer ud)
+{
+ scr_LogPrint(LPRINT_LOGNORM, "SSL status:%d", status);
+
+ switch (status) {
+ case LM_SSL_STATUS_NO_CERT_FOUND:
+ scr_LogPrint(LPRINT_LOGNORM, "No certificate found!");
+ break;
+ case LM_SSL_STATUS_UNTRUSTED_CERT:
+ scr_LogPrint(LPRINT_LOGNORM, "Certificate is not trusted!");
+ break;
+ case LM_SSL_STATUS_CERT_EXPIRED:
+ scr_LogPrint(LPRINT_LOGNORM, "Certificate has expired!");
+ break;
+ case LM_SSL_STATUS_CERT_NOT_ACTIVATED:
+ scr_LogPrint(LPRINT_LOGNORM, "Certificate has not been activated!");
+ break;
+ case LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH:
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Certificate hostname does not match expected hostname!");
+ break;
+ case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH: {
+ char fpr[49];
+ fingerprint_to_hex((const unsigned char*)lm_ssl_get_fingerprint(ssl),
+ fpr);
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Certificate fingerprint does not match expected fingerprint!");
+ scr_LogPrint(LPRINT_LOGNORM, "Remote fingerprint: %s", fpr);
+
+ scr_LogPrint(LPRINT_LOGNORM, "Expected fingerprint: %s",
+ settings_opt_get("ssl_fingerprint"));
+
+ return LM_SSL_RESPONSE_STOP;
+ break;
+ }
+ case LM_SSL_STATUS_GENERIC_ERROR:
+ scr_LogPrint(LPRINT_LOGNORM, "Generic SSL error!");
+ break;
+ }
+
+ if (settings_opt_get_int("ssl_ignore_checks"))
+ return LM_SSL_RESPONSE_CONTINUE;
+ return LM_SSL_RESPONSE_STOP;
+}
+
+static void connection_auth_cb(LmConnection *connection, gboolean success,
+ gpointer user_data)
+{
+ if (success) {
+ LmMessage *m;
+
+ m = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_PRESENCE,
+ LM_MESSAGE_SUB_TYPE_AVAILABLE);
+ lm_connection_send(connection, m, NULL);
+
+ lm_message_unref(m);
+ xmpp_setprevstatus();
+ xmpp_iq_request(NULL, NS_ROSTER);
+ xmpp_request_storage("storage:bookmarks");
+ xmpp_request_storage("storage:rosternotes");
+
+ AutoConnection = TRUE;
+ } else
+ scr_LogPrint(LPRINT_LOGNORM, "Authentication failed");
+}
+
+gboolean xmpp_reconnect()
+{
+ if (!lm_connection_is_authenticated(lconnection))
+ xmpp_connect();
+ return FALSE;
+}
+
+static void _try_to_reconnect(void)
+{
+ if (AutoConnection)
+ g_timeout_add_seconds(RECONNECTION_TIMEOUT, xmpp_reconnect, NULL);
+}
+
+static void connection_open_cb(LmConnection *connection, gboolean success,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ if (success) {
+ const char *password, *resource;
+ char *username;
+ username = jid_get_username(settings_opt_get("jid"));
+ password = settings_opt_get("password");
+ resource = strchr(lm_connection_get_jid(connection),
+ JID_RESOURCE_SEPARATOR);
+ if (resource)
+ resource++;
+
+ if (!lm_connection_authenticate(lconnection, username, password, resource,
+ connection_auth_cb, NULL, FALSE, &error)) {
+ scr_LogPrint(LPRINT_LOGNORM, "Failed to authenticate: %s\n",
+ error->message);
+ g_error_free (error);
+ _try_to_reconnect();
+ }
+ g_free(username);
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "There was an error while connecting.");
+ _try_to_reconnect();
+ }
+}
+
+static void connection_close_cb(LmConnection *connection,
+ LmDisconnectReason reason,
+ gpointer user_data)
+{
+ const char *str;
+
+ switch (reason) {
+ case LM_DISCONNECT_REASON_OK:
+ str = "LM_DISCONNECT_REASON_OK";
+ break;
+ case LM_DISCONNECT_REASON_PING_TIME_OUT:
+ str = "LM_DISCONNECT_REASON_PING_TIME_OUT";
+ break;
+ case LM_DISCONNECT_REASON_HUP:
+ str = "LM_DISCONNECT_REASON_HUP";
+ break;
+ case LM_DISCONNECT_REASON_ERROR:
+ str = "LM_DISCONNECT_REASON_ERROR";
+ break;
+ case LM_DISCONNECT_REASON_UNKNOWN:
+ default:
+ str = "LM_DISCONNECT_REASON_UNKNOWN";
+ break;
+ }
+
+ if (reason != LM_DISCONNECT_REASON_OK)
+ _try_to_reconnect();
+
+ // Free bookmarks
+ if (bookmarks)
+ lm_message_node_unref(bookmarks);
+ bookmarks = NULL;
+ // Free roster
+ roster_free();
+ if (rosternotes)
+ lm_message_node_unref(rosternotes);
+ rosternotes = NULL;
+ // Update display
+ update_roster = TRUE;
+ scr_UpdateBuddyWindow();
+
+ scr_LogPrint(LPRINT_NORMAL, "Disconnected, reason:%d->'%s'\n", reason, str);
+}
+
+static void handle_state_events(const char *from, LmMessageNode *node)
+{
+#if defined JEP0022 || defined JEP0085
+ LmMessageNode *state_ns = NULL;
+ const char *body;
+ char *rname, *bjid;
+ GSList *sl_buddy;
+ guint events;
+ struct jep0022 *jep22 = NULL;
+ struct jep0085 *jep85 = NULL;
+ enum {
+ JEP_none,
+ JEP_85,
+ JEP_22
+ } which_jep = JEP_none;
+
+ rname = strchr(from, JID_RESOURCE_SEPARATOR);
+ if (rname)
+ ++rname;
+ else
+ rname = (char *)from + strlen(from);
+ bjid = jidtodisp(from);
+ sl_buddy = roster_find(bjid, jidsearch, ROSTER_TYPE_USER);
+ g_free(bjid);
+
+ /* XXX Actually that's wrong, since it filters out server "offline"
+ messages (for JEP-0022). This JEP is (almost) deprecated so
+ we don't really care. */
+ if (!sl_buddy) {
+ return;
+ }
+
+ /* Let's see chich JEP the contact uses. If possible, we'll use
+ JEP-85, if not we'll look for JEP-22 support. */
+ events = buddy_resource_getevents(sl_buddy->data, rname);
+
+ jep85 = buddy_resource_jep85(sl_buddy->data, rname);
+ if (jep85) {
+ state_ns = lm_message_node_find_xmlns(node, NS_CHATSTATES);
+ if (state_ns)
+ which_jep = JEP_85;
+ }
+
+ if (which_jep != JEP_85) { /* Fall back to JEP-0022 */
+ jep22 = buddy_resource_jep22(sl_buddy->data, rname);
+ if (jep22) {
+ state_ns = lm_message_node_find_xmlns(node, NS_EVENT);
+ if (state_ns)
+ which_jep = JEP_22;
+ }
+ }
+
+ if (!which_jep) { /* Sender does not use chat states */
+ return;
+ }
+
+ body = lm_message_node_get_child_value(node, "body");
+
+ if (which_jep == JEP_85) { /* JEP-0085 */
+ jep85->support = CHATSTATES_SUPPORT_OK;
+
+ if (!strcmp(state_ns->name, "composing")) {
+ jep85->last_state_rcvd = ROSTER_EVENT_COMPOSING;
+ } else if (!strcmp(state_ns->name, "active")) {
+ jep85->last_state_rcvd = ROSTER_EVENT_ACTIVE;
+ } else if (!strcmp(state_ns->name, "paused")) {
+ jep85->last_state_rcvd = ROSTER_EVENT_PAUSED;
+ } else if (!strcmp(state_ns->name, "inactive")) {
+ jep85->last_state_rcvd = ROSTER_EVENT_INACTIVE;
+ } else if (!strcmp(state_ns->name, "gone")) {
+ jep85->last_state_rcvd = ROSTER_EVENT_GONE;
+ }
+ events = jep85->last_state_rcvd;
+ } else { /* JEP-0022 */
+#ifdef JEP0022
+ const char *msgid;
+ jep22->support = CHATSTATES_SUPPORT_OK;
+ jep22->last_state_rcvd = ROSTER_EVENT_NONE;
+
+ msgid = lm_message_node_get_attribute(node, "id");
+
+ if (lm_message_node_get_child(state_ns, "composing")) {
+ // Clear composing if the message contains a body
+ if (body)
+ events &= ~ROSTER_EVENT_COMPOSING;
+ else
+ events |= ROSTER_EVENT_COMPOSING;
+ jep22->last_state_rcvd |= ROSTER_EVENT_COMPOSING;
+
+ } else {
+ events &= ~ROSTER_EVENT_COMPOSING;
+ }
+
+ // Cache the message id
+ g_free(jep22->last_msgid_rcvd);
+ if (msgid)
+ jep22->last_msgid_rcvd = g_strdup(msgid);
+ else
+ jep22->last_msgid_rcvd = NULL;
+
+ if (lm_message_node_get_child(state_ns, "delivered")) {
+ jep22->last_state_rcvd |= ROSTER_EVENT_DELIVERED;
+
+ // Do we have to send back an ACK?
+ if (body)
+ xmpp_send_jep22_event(from, ROSTER_EVENT_DELIVERED);
+ }
+#endif
+ }
+
+ buddy_resource_setevents(sl_buddy->data, rname, events);
+
+ update_roster = TRUE;
+#endif
+}
+
+static void gotmessage(LmMessageSubType type, const char *from,
+ const char *body, const char *enc, const char *subject,
+ time_t timestamp, LmMessageNode *node_signed)
+{
+ char *bjid;
+ const char *rname, *s;
+ char *decrypted_pgp = NULL;
+ char *decrypted_otr = NULL;
+ int otr_msg = 0, free_msg = 0;
+
+ bjid = jidtodisp(from);
+
+ rname = strchr(from, JID_RESOURCE_SEPARATOR);
+ if (rname) rname++;
+
+#ifdef HAVE_GPGME
+ if (enc && gpg_enabled()) {
+ decrypted_pgp = gpg_decrypt(enc);
+ if (decrypted_pgp) {
+ body = decrypted_pgp;
+ }
+ }
+ // Check signature of an unencrypted message
+ if (node_signed && gpg_enabled())
+ check_signature(bjid, rname, node_signed, decrypted_pgp);
+#endif
+
+#ifdef HAVE_LIBOTR
+ if (otr_enabled()) {
+ decrypted_otr = (char*)body;
+ otr_msg = otr_receive(&decrypted_otr, bjid, &free_msg);
+ if (!decrypted_otr) {
+ goto gotmessage_return;
+ }
+ body = decrypted_otr;
+ }
+#endif
+
+ // Check for unexpected groupchat messages
+ // If we receive a groupchat message from a room we're not a member of,
+ // this is probably a server issue and the best we can do is to send
+ // a type unavailable.
+ if (type == LM_MESSAGE_SUB_TYPE_GROUPCHAT && !roster_getnickname(bjid)) {
+ // It shouldn't happen, probably a server issue
+ GSList *room_elt;
+ char *mbuf;
+
+ mbuf = g_strdup_printf("Unexpected groupchat packet!");
+ scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
+ scr_WriteIncomingMessage(bjid, mbuf, 0, HBB_PREFIX_INFO, 0);
+ g_free(mbuf);
+
+ // Send back an unavailable packet
+ xmpp_setstatus(offline, bjid, "", TRUE);
+
+ // MUC
+ // Make sure this is a room (it can be a conversion user->room)
+ room_elt = roster_find(bjid, jidsearch, 0);
+ if (!room_elt) {
+ roster_add_user(bjid, NULL, NULL, ROSTER_TYPE_ROOM, sub_none, -1);
+ } else {
+ buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
+ }
+
+ buddylist_build();
+ scr_DrawRoster();
+ goto gotmessage_return;
+ }
+
+ // We don't call the message_in hook if 'block_unsubscribed' is true and
+ // this is a regular message from an unsubscribed user.
+ // System messages (from our server) are allowed.
+ if ((!settings_opt_get_int("block_unsubscribed") ||
+ (roster_getsubscription(bjid) & sub_from) ||
+ (type == LM_MESSAGE_SUB_TYPE_CHAT)) ||
+ ((s = settings_opt_get("server")) != NULL && !strcasecmp(bjid, s))) {
+ gchar *fullbody = NULL;
+ guint encrypted;
+
+ if (decrypted_pgp)
+ encrypted = ENCRYPTED_PGP;
+ else if (otr_msg)
+ encrypted = ENCRYPTED_OTR;
+ else
+ encrypted = 0;
+
+ if (subject) {
+ if (body)
+ fullbody = g_strdup_printf("[%s]\n%s", subject, body);
+ else
+ fullbody = g_strdup_printf("[%s]\n", subject);
+ body = fullbody;
+ }
+ hk_message_in(bjid, rname, timestamp, body, type, encrypted);
+ g_free(fullbody);
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "Blocked a message from <%s>", bjid);
+ }
+
+gotmessage_return:
+ // Clean up and exit
+ g_free(bjid);
+ g_free(decrypted_pgp);
+ if (free_msg)
+ g_free(decrypted_otr);
+}
+
+
+static LmHandlerResult handle_messages(LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *m, gpointer user_data)
+{
+ const char *p, *from=lm_message_get_from(m);
+ char *r, *s;
+ LmMessageNode *x;
+ const char *body = NULL;
+ const char *enc = NULL;
+ const char *subject = NULL;
+ time_t timestamp = 0L;
+ LmMessageSubType mstype;
+
+ mstype = lm_message_get_sub_type(m);
+
+ body = lm_message_node_get_child_value(m->node, "body");
+
+ x = lm_message_node_find_xmlns(m->node, NS_ENCRYPTED);
+ if (x && (p = lm_message_node_get_value(x)) != NULL)
+ enc = p;
+
+ p = lm_message_node_get_child_value(m->node, "subject");
+ if (p != NULL) {
+ if (mstype != LM_MESSAGE_SUB_TYPE_GROUPCHAT) {
+ // Chat message
+ subject = p;
+ } else { // Room topic
+ GSList *roombuddy;
+ gchar *mbuf;
+ const gchar *subj = p;
+ // Get the room (s) and the nickname (r)
+ s = g_strdup(lm_message_get_from(m));
+ r = strchr(s, JID_RESOURCE_SEPARATOR);
+ if (r) *r++ = 0;
+ else r = s;
+ // Set the new topic
+ roombuddy = roster_find(s, jidsearch, 0);
+ if (roombuddy)
+ buddy_settopic(roombuddy->data, subj);
+ // Display inside the room window
+ if (r == s) {
+ // No specific resource (this is certainly history)
+ mbuf = g_strdup_printf("The topic has been set to: %s", subj);
+ } else {
+ mbuf = g_strdup_printf("%s has set the topic to: %s", r, subj);
+ }
+ scr_WriteIncomingMessage(s, mbuf, 0,
+ HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+ if (settings_opt_get_int("log_muc_conf"))
+ hlog_write_message(s, 0, -1, mbuf);
+ g_free(s);
+ g_free(mbuf);
+ // The topic is displayed in the chat status line, so refresh now.
+ scr_UpdateChatStatus(TRUE);
+ }
+ }
+
+ // Timestamp?
+ timestamp = lm_message_node_get_timestamp(m->node);
+
+ if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
+ x = lm_message_node_get_child(m->node, "error");
+ display_server_error(x);
+#if defined JEP0022 || defined JEP0085
+ // If the JEP85/22 support is probed, set it back to unknown so that
+ // we probe it again.
+ chatstates_reset_probed(from);
+#endif
+ } else {
+ handle_state_events(from, m->node);
+ }
+ if (from && (body || subject))
+ gotmessage(mstype, from, body, enc, subject, timestamp,
+ lm_message_node_find_xmlns(m->node, NS_SIGNED));
+ //report received message if message receipt was requested
+ if (lm_message_node_get_child(m->node, "request")) {
+ LmMessage *rcvd = lm_message_new(from, LM_MESSAGE_TYPE_MESSAGE);
+ lm_message_node_set_attribute(rcvd->node, "id", lm_message_get_id(m));
+ lm_message_node_set_attribute
+ (lm_message_node_add_child(rcvd->node, "received", NULL),
+ "xmlns", NS_RECEIPTS);
+ lm_connection_send(connection, rcvd, NULL);
+ lm_message_unref(rcvd);
+ }
+
+ if (from) {
+ x = lm_message_node_find_xmlns(m->node,
+ "http://jabber.org/protocol/muc#user");
+ if (x && !strcmp(x->name, "x"))
+ got_muc_message(from, x);
+ }
+
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult cb_caps(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data)
+{
+ char *ver = user_data;
+ LmMessageSubType mstype = lm_message_get_sub_type(m);
+
+ caps_add(ver);
+ if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
+ display_server_error(lm_message_node_get_child(m->node, "error"));
+ } else if (mstype == LM_MESSAGE_SUB_TYPE_RESULT) {
+ LmMessageNode *info;
+ LmMessageNode *query = lm_message_node_get_child(m->node, "query");
+
+ info = lm_message_node_get_child(query, "identity");
+ if (info)
+ caps_set_identity(ver, lm_message_node_get_attribute(info, "category"),
+ lm_message_node_get_attribute(info, "name"),
+ lm_message_node_get_attribute(info, "type"));
+ info = lm_message_node_get_child(query, "feature");
+ while (info) {
+ if (!g_strcmp0(info->name, "feature"))
+ caps_add_feature(ver, lm_message_node_get_attribute(info, "var"));
+ info = info->next;
+ }
+ }
+ g_free(ver);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult handle_presence(LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *m, gpointer user_data)
+{
+ char *r;
+ const char *from, *rname, *p=NULL, *ustmsg=NULL;
+ enum imstatus ust;
+ char bpprio;
+ time_t timestamp = 0L;
+ LmMessageNode *muc_packet, *caps;
+ LmMessageSubType mstype;
+
+ // Check for MUC presence packet
+ muc_packet = lm_message_node_find_xmlns
+ (m->node, "http://jabber.org/protocol/muc#user");
+
+ from = lm_message_get_from(m);
+
+ rname = strchr(from, JID_RESOURCE_SEPARATOR);
+ if (rname) rname++;
+
+ if (settings_opt_get_int("ignore_self_presence")) {
+ const char *self_fjid = lm_connection_get_jid(connection);
+ if (self_fjid && !strcasecmp(self_fjid, from)) {
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Ignoring self presence
+ }
+ }
+
+ r = jidtodisp(from);
+ mstype = lm_message_get_sub_type(m);
+
+ if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
+ LmMessageNode *x;
+ scr_LogPrint(LPRINT_LOGNORM, "Error presence packet from <%s>", r);
+ x = lm_message_node_find_child(m->node, "error");
+ display_server_error(x);
+ // Let's check it isn't a nickname conflict.
+ // XXX Note: We should handle the <conflict/> string condition.
+ if ((p = lm_message_node_get_attribute(x, "code")) != NULL) {
+ if (atoi(p) == 409) {
+ // 409 = conflict (nickname is in use or registered by another user)
+ // If we are not inside this room, we should reset the nickname
+ GSList *room_elt = roster_find(r, jidsearch, 0);
+ if (room_elt && !buddy_getinsideroom(room_elt->data))
+ buddy_setnickname(room_elt->data, NULL);
+ }
+ }
+
+ g_free(r);
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+
+ p = lm_message_node_get_child_value(m->node, "priority");
+ if (p && *p) bpprio = (gchar)atoi(p);
+ else bpprio = 0;
+
+ ust = available;
+
+ p = lm_message_node_get_child_value(m->node, "show");
+ if (p) {
+ if (!strcmp(p, "away")) ust = away;
+ else if (!strcmp(p, "dnd")) ust = dontdisturb;
+ else if (!strcmp(p, "xa")) ust = notavail;
+ else if (!strcmp(p, "chat")) ust = freeforchat;
+ }
+
+ if (mstype == LM_MESSAGE_SUB_TYPE_UNAVAILABLE)
+ ust = offline;
+
+ ustmsg = lm_message_node_get_child_value(m->node, "status");
+
+ // Timestamp?
+ timestamp = lm_message_node_get_timestamp(m->node);
+
+ if (muc_packet) {
+ // This is a MUC presence message
+ handle_muc_presence(from, muc_packet, r, rname,
+ ust, ustmsg, timestamp, bpprio);
+ } else {
+ // Not a MUC message, so this is a regular buddy...
+ // Call hk_statuschange() if status has changed or if the
+ // status message is different
+ const char *msg;
+ msg = roster_getstatusmsg(r, rname);
+ if ((ust != roster_getstatus(r, rname)) ||
+ (!ustmsg && msg && msg[0]) || (ustmsg && (!msg || strcmp(ustmsg, msg))))
+ hk_statuschange(r, rname, bpprio, timestamp, ust, ustmsg);
+ // Presence signature processing
+ if (!ustmsg)
+ ustmsg = ""; // Some clients omit the <status/> element :-(
+ check_signature(r, rname, lm_message_node_find_xmlns(m->node, NS_SIGNED),
+ ustmsg);
+ }
+
+ // XEP-0115 Entity Capabilities
+ caps = lm_message_node_find_xmlns(m->node, NS_CAPS);
+ if (caps && ust != offline) {
+ const char *ver = lm_message_node_get_attribute(caps, "ver");
+ GSList *sl_buddy = NULL;
+ if (rname)
+ sl_buddy = roster_find(r, jidsearch, ROSTER_TYPE_USER);
+ // Only cache the caps if the user is on the roster
+ if (sl_buddy && buddy_getonserverflag(sl_buddy->data)) {
+ buddy_resource_setcaps(sl_buddy->data, rname, ver);
+
+ if (!caps_has_hash(ver)) {
+ char *node;
+ LmMessageHandler *handler;
+ LmMessage *iq = lm_message_new_with_sub_type(from, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_GET);
+ node = g_strdup_printf("%s#%s",
+ lm_message_node_get_attribute(caps, "node"),
+ ver);
+ lm_message_node_set_attributes
+ (lm_message_node_add_child(iq->node, "query", NULL),
+ "xmlns", NS_DISCO_INFO,
+ "node", node,
+ NULL);
+ g_free(node);
+ handler = lm_message_handler_new(cb_caps, g_strdup(ver), NULL);
+ lm_connection_send_with_reply(connection, iq, handler, NULL);
+ lm_message_unref(iq);
+ lm_message_handler_unref(handler);
+ }
+ }
+ }
+
+ g_free(r);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+
+static LmHandlerResult handle_iq(LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *m, gpointer user_data)
+{
+ int i;
+ guint dbgflg;
+ const char *xmlns = NULL;
+ LmMessageNode *x;
+ LmMessageSubType mstype = lm_message_get_sub_type(m);
+
+ if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
+ display_server_error(lm_message_node_get_child(m->node, "error"));
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+
+ for (x = m->node->children; x; x=x->next) {
+ xmlns = lm_message_node_get_attribute(x, "xmlns");
+ if (xmlns)
+ for (i=0; iq_handlers[i].xmlns; ++i)
+ if (!strcmp(iq_handlers[i].xmlns, xmlns))
+ return iq_handlers[i].handler(NULL, connection, m, user_data);
+ xmlns = NULL;
+ }
+
+ if ((mstype == LM_MESSAGE_SUB_TYPE_SET) ||
+ (mstype == LM_MESSAGE_SUB_TYPE_GET))
+ send_iq_error(connection, m, XMPP_ERROR_NOT_IMPLEMENTED);
+
+ if (mstype == LM_MESSAGE_SUB_TYPE_RESULT)
+ dbgflg = LPRINT_DEBUG;
+ else
+ dbgflg = LPRINT_NORMAL|LPRINT_DEBUG;
+
+ scr_LogPrint(dbgflg, "Unhandled IQ: %s", lm_message_node_to_string(m->node));
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult handle_s10n(LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *m, gpointer user_data)
+{
+ char *r;
+ char *buf;
+ int newbuddy;
+ const char *from = lm_message_get_from(m);
+ LmMessageSubType mstype;
+
+ r = jidtodisp(from);
+
+ newbuddy = !roster_find(r, jidsearch, 0);
+ mstype = lm_message_get_sub_type(m);
+
+ if (mstype == LM_MESSAGE_SUB_TYPE_SUBSCRIBE) {
+ /* The sender wishes to subscribe to our presence */
+ const char *msg;
+ eviqs *evn;
+
+ msg = lm_message_node_get_child_value(m->node, "status");
+
+ buf = g_strdup_printf("<%s> wants to subscribe to your presence updates",
+ from);
+ scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+ g_free(buf);
+
+ if (msg) {
+ buf = g_strdup_printf("<%s> said: %s", from, msg);
+ scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
+ replace_nl_with_dots(buf);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+ g_free(buf);
+ }
+
+ // Create a new event item
+ evn = evs_new(EVS_TYPE_SUBSCRIPTION, EVS_MAX_TIMEOUT);
+ if (evn) {
+ evn->callback = &evscallback_subscription;
+ evn->data = g_strdup(r);
+ evn->desc = g_strdup_printf("<%s> wants to subscribe to your "
+ "presence updates", r);
+ buf = g_strdup_printf("Please use /event %s accept|reject", evn->id);
+ } else {
+ buf = g_strdup_printf("Unable to create a new event!");
+ }
+ scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+ g_free(buf);
+ } else if (mstype == LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE) {
+ /* The sender is unsubscribing from our presence */
+ xmpp_send_s10n(from, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED);
+ buf = g_strdup_printf("<%s> is unsubscribing from your "
+ "presence updates", from);
+ scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+ g_free(buf);
+ } else if (mstype == LM_MESSAGE_SUB_TYPE_SUBSCRIBED) {
+ /* The sender has allowed us to receive their presence */
+ buf = g_strdup_printf("<%s> has allowed you to receive their "
+ "presence updates", from);
+ scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+ g_free(buf);
+ } else if (mstype == LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED) {
+ /* The subscription request has been denied or a previously-granted
+ subscription has been cancelled */
+ roster_unsubscribed(from);
+ update_roster = TRUE;
+ buf = g_strdup_printf("<%s> has cancelled your subscription to "
+ "their presence updates", from);
+ scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+ g_free(buf);
+ } else {
+ g_free(r);
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+
+ if (newbuddy)
+ update_roster = TRUE;
+ g_free(r);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+//TODO: Use the enum of loudmouth, when it's included in the header...
+typedef enum {
+ LM_LOG_LEVEL_VERBOSE = 1 << (G_LOG_LEVEL_USER_SHIFT),
+ LM_LOG_LEVEL_NET = 1 << (G_LOG_LEVEL_USER_SHIFT + 1),
+ LM_LOG_LEVEL_PARSER = 1 << (G_LOG_LEVEL_USER_SHIFT + 2),
+ LM_LOG_LEVEL_SSL = 1 << (G_LOG_LEVEL_USER_SHIFT + 3),
+ LM_LOG_LEVEL_SASL = 1 << (G_LOG_LEVEL_USER_SHIFT + 4),
+ LM_LOG_LEVEL_ALL = (LM_LOG_LEVEL_NET |
+ LM_LOG_LEVEL_VERBOSE |
+ LM_LOG_LEVEL_PARSER |
+ LM_LOG_LEVEL_SSL |
+ LM_LOG_LEVEL_SASL)
+} LmLogLevelFlags;
+
+static void lm_debug_handler (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data)
+{
+ if (message && *message) {
+ char *msg;
+ int mcabber_loglevel = settings_opt_get_int("tracelog_level");
+
+ if (mcabber_loglevel < 2)
+ return;
+
+ if (message[0] == '\n')
+ msg = g_strdup(&message[1]);
+ else
+ msg = g_strdup(message);
+
+ if (msg[strlen(msg)-1] == '\n')
+ msg[strlen(msg)-1] = '\0';
+
+ if (log_level & LM_LOG_LEVEL_VERBOSE) {
+ scr_LogPrint(LPRINT_DEBUG, "LM-VERBOSE: %s", msg);
+ }
+ if (log_level & LM_LOG_LEVEL_NET) {
+ if (mcabber_loglevel > 2)
+ scr_LogPrint(LPRINT_DEBUG, "LM-NET: %s", msg);
+ } else if (log_level & LM_LOG_LEVEL_PARSER) {
+ if (mcabber_loglevel > 3)
+ scr_LogPrint(LPRINT_DEBUG, "LM-PARSER: %s", msg);
+ } else if (log_level & LM_LOG_LEVEL_SASL) {
+ scr_LogPrint(LPRINT_DEBUG, "LM-SASL: %s", msg);
+ } else if (log_level & LM_LOG_LEVEL_SSL) {
+ scr_LogPrint(LPRINT_DEBUG, "LM-SSL: %s", msg);
+ }
+ g_free(msg);
+ }
+}
+
+
+void xmpp_connect(void)
+{
+ const char *userjid, *password, *resource, *servername, *ssl_fpr;
+ char *dynresource = NULL;
+ char fpr[16];
+ const char *proxy_host;
+ const char *resource_prefix = PACKAGE_NAME;
+ char *fjid;
+ int ssl, tls;
+ LmSSL *lssl;
+ unsigned int port;
+ unsigned int ping;
+ LmMessageHandler *handler;
+ GError *error = NULL;
+
+ if (lconnection && lm_connection_is_open(lconnection))
+ xmpp_disconnect();
+
+ servername = settings_opt_get("server");
+ userjid = settings_opt_get("jid");
+ password = settings_opt_get("password");
+ resource = settings_opt_get("resource");
+ proxy_host = settings_opt_get("proxy_host");
+ ssl_fpr = settings_opt_get("ssl_fingerprint");
+
+ if (!userjid) {
+ scr_LogPrint(LPRINT_LOGNORM, "Your JID has not been specified!");
+ return;
+ }
+ if (!password) {
+ scr_LogPrint(LPRINT_LOGNORM, "Your password has not been specified!");
+ return;
+ }
+
+ lconnection = lm_connection_new_with_context(NULL, main_context);
+
+ g_log_set_handler("LM", LM_LOG_LEVEL_ALL, lm_debug_handler, NULL);
+
+ ping = 40;
+ if (settings_opt_get("pinginterval"))
+ ping = (unsigned int) settings_opt_get_int("pinginterval");
+ lm_connection_set_keep_alive_rate(lconnection, ping);
+ scr_LogPrint(LPRINT_DEBUG, "Ping interval established: %d secs", ping);
+
+ lm_connection_set_disconnect_function(lconnection, connection_close_cb,
+ NULL, NULL);
+
+ handler = lm_message_handler_new(handle_messages, NULL, NULL);
+ lm_connection_register_message_handler(lconnection, handler,
+ LM_MESSAGE_TYPE_MESSAGE,
+ LM_HANDLER_PRIORITY_NORMAL);
+ lm_message_handler_unref(handler);
+
+ handler = lm_message_handler_new(handle_iq, NULL, NULL);
+ lm_connection_register_message_handler(lconnection, handler,
+ LM_MESSAGE_TYPE_IQ,
+ LM_HANDLER_PRIORITY_NORMAL);
+ lm_message_handler_unref(handler);
+
+ handler = lm_message_handler_new(handle_presence, NULL, NULL);
+ lm_connection_register_message_handler(lconnection, handler,
+ LM_MESSAGE_TYPE_PRESENCE,
+ LM_HANDLER_PRIORITY_LAST);
+ lm_message_handler_unref(handler);
+
+ handler = lm_message_handler_new(handle_s10n, NULL, NULL);
+ lm_connection_register_message_handler(lconnection, handler,
+ LM_MESSAGE_TYPE_PRESENCE,
+ LM_HANDLER_PRIORITY_NORMAL);
+ lm_message_handler_unref(handler);
+
+ /* Connect to server */
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, "Connecting to server: %s",
+ servername ? servername : "...");
+ if (!resource)
+ resource = resource_prefix;
+
+ if (!settings_opt_get("disable_random_resource")) {
+#if HAVE_ARC4RANDOM
+ dynresource = g_strdup_printf("%s.%08x", resource, arc4random());
+#else
+ unsigned int tab[2];
+ srand(time(NULL));
+ tab[0] = (unsigned int) (0xffff * (rand() / (RAND_MAX + 1.0)));
+ tab[1] = (unsigned int) (0xffff * (rand() / (RAND_MAX + 1.0)));
+ dynresource = g_strdup_printf("%s.%04x%04x", resource, tab[0], tab[1]);
+#endif
+ resource = dynresource;
+ }
+
+ port = (unsigned int) settings_opt_get_int("port");
+
+ if (port)
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, " using port %d", port);
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, " resource %s", resource);
+
+ if (proxy_host) {
+ int proxy_port = settings_opt_get_int("proxy_port");
+ if (proxy_port <= 0 || proxy_port > 65535) {
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, "Invalid proxy port: %d",
+ proxy_port);
+ } else {
+ const char *proxy_user, *proxy_pass;
+ LmProxy *lproxy;
+ proxy_user = settings_opt_get("proxy_user");
+ proxy_pass = settings_opt_get("proxy_pass");
+ // Proxy initialization
+ lproxy = lm_proxy_new_with_server(LM_PROXY_TYPE_HTTP,
+ proxy_host, proxy_port);
+ lm_proxy_set_username(lproxy, proxy_user);
+ lm_proxy_set_password(lproxy, proxy_pass);
+ lm_connection_set_proxy(lconnection, lproxy);
+ lm_proxy_unref(lproxy);
+ scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, " using proxy %s:%d",
+ proxy_host, proxy_port);
+ }
+ }
+
+ fjid = compose_jid(userjid, servername, resource);
+ lm_connection_set_jid(lconnection, fjid);
+ if (servername)
+ lm_connection_set_server(lconnection, servername);
+#if defined(HAVE_LIBOTR)
+ otr_init(fjid);
+#endif
+ g_free(fjid);
+ g_free(dynresource);
+
+ ssl = settings_opt_get_int("ssl");
+ tls = settings_opt_get_int("tls");
+
+ if (!lm_ssl_is_supported()) {
+ if (ssl || tls) {
+ scr_LogPrint(LPRINT_LOGNORM, "** Error: SSL is NOT available, "
+ "please recompile loudmouth with SSL enabled.");
+ return;
+ }
+ }
+
+ if (ssl && tls) {
+ scr_LogPrint(LPRINT_LOGNORM, "You can only set ssl or tls, not both.");
+ return;
+ }
+
+ if (!port)
+ port = (ssl ? LM_CONNECTION_DEFAULT_PORT_SSL : LM_CONNECTION_DEFAULT_PORT);
+ lm_connection_set_port(lconnection, port);
+
+ if (ssl_fpr && (!hex_to_fingerprint(ssl_fpr, fpr))) {
+ scr_LogPrint(LPRINT_LOGNORM, "** Plese set the fingerprint in the format "
+ "97:5C:00:3F:1D:77:45:25:E2:C5:70:EC:83:C8:87:EE");
+ return;
+ }
+
+ lssl = lm_ssl_new((ssl_fpr ? fpr : NULL), ssl_cb, NULL, NULL);
+ if (lssl) {
+ lm_ssl_use_starttls(lssl, !ssl, tls);
+ lm_connection_set_ssl(lconnection, lssl);
+ lm_ssl_unref(lssl);
+ } else if (ssl || tls) {
+ scr_LogPrint(LPRINT_LOGNORM, "** Error: Couldn't create SSL struct.");
+ return;
+ }
+
+ if (!lm_connection_open(lconnection, connection_open_cb,
+ NULL, FALSE, &error)) {
+ _try_to_reconnect();
+ scr_LogPrint(LPRINT_LOGNORM, "Failed to open: %s\n", error->message);
+ g_error_free (error);
+ }
+}
+
+// insert_entity_capabilities(presence_stanza)
+// Entity Capabilities (XEP-0115)
+static void insert_entity_capabilities(LmMessageNode *x, enum imstatus status)
+{
+ LmMessageNode *y;
+ const char *ver = entity_version(status);
+
+ y = lm_message_node_add_child(x, "c", NULL);
+ lm_message_node_set_attribute(y, "xmlns", NS_CAPS);
+ lm_message_node_set_attribute(y, "hash", "sha-1");
+ lm_message_node_set_attribute(y, "node", MCABBER_CAPS_NODE);
+ lm_message_node_set_attribute(y, "ver", ver);
+}
+
+void xmpp_disconnect(void)
+{
+ if (!lconnection || !lm_connection_is_authenticated(lconnection))
+ return;
+
+ // Launch pre-disconnect internal hook
+ hook_execute_internal("hook-pre-disconnect");
+ // Announce it to everyone else
+ xmpp_setstatus(offline, NULL, "", FALSE);
+ lm_connection_close(lconnection, NULL);
+}
+
+void xmpp_setstatus(enum imstatus st, const char *recipient, const char *msg,
+ int do_not_sign)
+{
+ LmMessage *m;
+
+ if (msg) {
+ // The status message has been specified. We'll use it, unless it is
+ // "-" which is a special case (option meaning "no status message").
+ if (!strcmp(msg, "-"))
+ msg = "";
+ } else {
+ // No status message specified; we'll use:
+ // a) the default status message (if provided by the user);
+ // b) the current status message;
+ // c) no status message (i.e. an empty one).
+ msg = settings_get_status_msg(st);
+ if (!msg) {
+ if (mystatusmsg)
+ msg = mystatusmsg;
+ else
+ msg = "";
+ }
+ }
+
+ // Only send the packet if we're online.
+ // (But we want to update internal status even when disconnected,
+ // in order to avoid some problems during network failures)
+ if (lm_connection_is_authenticated(lconnection)) {
+ const char *s_msg = (st != invisible ? msg : NULL);
+ m = lm_message_new_presence(st, recipient, s_msg);
+ insert_entity_capabilities(m->node, st); // Entity Capabilities (XEP-0115)
+#ifdef HAVE_GPGME
+ if (!do_not_sign && gpg_enabled()) {
+ char *signature;
+ signature = gpg_sign(s_msg ? s_msg : "");
+ if (signature) {
+ LmMessageNode *y;
+ y = lm_message_node_add_child(m->node, "x", signature);
+ lm_message_node_set_attribute(y, "xmlns", NS_SIGNED);
+ g_free(signature);
+ }
+ }
+#endif
+ lm_connection_send(lconnection, m, NULL);
+ lm_message_unref(m);
+ }
+
+ // If we didn't change our _global_ status, we are done
+ if (recipient) return;
+
+ if (lm_connection_is_authenticated(lconnection)) {
+ // Send presence to chatrooms
+ if (st != invisible) {
+ struct T_presence room_presence;
+ room_presence.st = st;
+ room_presence.msg = msg;
+ foreach_buddy(ROSTER_TYPE_ROOM, &roompresence, &room_presence);
+ }
+
+ // We'll have to update the roster if we switch to/from offline because
+ // we don't know the presences of buddies when offline...
+ if (mystatus == offline || st == offline)
+ update_roster = TRUE;
+
+ hk_mystatuschange(0, mystatus, st, (st != invisible ? msg : ""));
+ mystatus = st;
+ }
+
+ if (st)
+ mywantedstatus = st;
+
+ if (msg != mystatusmsg) {
+ g_free(mystatusmsg);
+ if (*msg)
+ mystatusmsg = g_strdup(msg);
+ else
+ mystatusmsg = NULL;
+ }
+
+ if (!Autoaway)
+ update_last_use();
+
+ // Update status line
+ scr_UpdateMainStatus(TRUE);
+}
+
+
+enum imstatus xmpp_getstatus(void)
+{
+ return mystatus;
+}
+
+const char *xmpp_getstatusmsg(void)
+{
+ return mystatusmsg;
+}
+
+// xmpp_setprevstatus()
+// Set previous status. This wrapper function is used after a disconnection.
+void xmpp_setprevstatus(void)
+{
+ xmpp_setstatus(mywantedstatus, NULL, mystatusmsg, FALSE);
+}
+
+// send_storage(store)
+// Send the node "store" to update the server.
+// Note: the sender should check we're online.
+void send_storage(LmMessageNode *store)
+{
+ LmMessage *iq;
+ LmMessageNode *query;
+
+ if (!rosternotes) return;
+
+ iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_SET);
+ query = lm_message_node_add_child(iq->node, "query", NULL);
+ lm_message_node_set_attribute(query, "xmlns", NS_PRIVATE);
+ lm_message_node_insert_childnode(query, store);
+
+ lm_connection_send(lconnection, iq, NULL);
+ lm_message_unref(iq);
+}
+
+
+// xmpp_is_bookmarked(roomjid)
+// Return TRUE if there's a bookmark for the given jid.
+guint xmpp_is_bookmarked(const char *bjid)
+{
+ LmMessageNode *x;
+
+ if (!bookmarks)
+ return FALSE;
+
+ // Walk through the storage bookmark tags
+ for (x = bookmarks->children ; x; x = x->next) {
+ // If the node is a conference item, check the jid.
+ if (x->name && !strcmp(x->name, "conference")) {
+ const char *fjid = lm_message_node_get_attribute(x, "jid");
+ if (fjid && !strcasecmp(bjid, fjid))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// xmpp_get_bookmark_nick(roomjid)
+// Return the room nickname if it is present in a bookmark.
+const char *xmpp_get_bookmark_nick(const char *bjid)
+{
+ LmMessageNode *x;
+
+ if (!bookmarks || !bjid)
+ return NULL;
+
+ // Walk through the storage bookmark tags
+ for (x = bookmarks->children ; x; x = x->next) {
+ // If the node is a conference item, check the jid.
+ if (x->name && !strcmp(x->name, "conference")) {
+ const char *fjid = lm_message_node_get_attribute(x, "jid");
+ if (fjid && !strcasecmp(bjid, fjid))
+ return lm_message_node_get_child_value(x, "nick");
+ }
+ }
+ return NULL;
+}
+
+
+// xmpp_get_all_storage_bookmarks()
+// Return a GSList with all storage bookmarks.
+// The caller should g_free the list (not the MUC jids).
+GSList *xmpp_get_all_storage_bookmarks(void)
+{
+ LmMessageNode *x;
+ GSList *sl_bookmarks = NULL;
+
+ // If we have no bookmarks, probably the server doesn't support them.
+ if (!bookmarks)
+ return NULL;
+
+ // Walk through the storage bookmark tags
+ for (x = bookmarks->children ; x; x = x->next) {
+ // If the node is a conference item, let's add the note to our list.
+ if (x->name && !strcmp(x->name, "conference")) {
+ struct bookmark *bm_elt;
+ const char *autojoin, *name, *nick;
+ const char *fjid = lm_message_node_get_attribute(x, "jid");
+ if (!fjid)
+ continue;
+ bm_elt = g_new0(struct bookmark, 1);
+ bm_elt->roomjid = g_strdup(fjid);
+ autojoin = lm_message_node_get_attribute(x, "autojoin");
+ nick = lm_message_node_get_attribute(x, "nick");
+ name = lm_message_node_get_attribute(x, "name");
+ if (autojoin && !strcmp(autojoin, "1"))
+ bm_elt->autojoin = 1;
+ if (nick)
+ bm_elt->nick = g_strdup(nick);
+ if (name)
+ bm_elt->name = g_strdup(name);
+ sl_bookmarks = g_slist_append(sl_bookmarks, bm_elt);
+ }
+ }
+ return sl_bookmarks;
+}
+
+// xmpp_set_storage_bookmark(roomid, name, nick, passwd, autojoin,
+// printstatus, autowhois)
+// Update the private storage bookmarks: add a conference room.
+// If name is nil, we remove the bookmark.
+void xmpp_set_storage_bookmark(const char *roomid, const char *name,
+ const char *nick, const char *passwd,
+ int autojoin, enum room_printstatus pstatus,
+ enum room_autowhois awhois)
+{
+ LmMessageNode *x;
+ bool changed = FALSE;
+
+ if (!roomid)
+ return;
+
+ // If we have no bookmarks, probably the server doesn't support them.
+ if (!bookmarks) {
+ scr_LogPrint(LPRINT_NORMAL,
+ "Sorry, your server doesn't seem to support private storage.");
+ return;
+ }
+
+ // Walk through the storage tags
+ for (x = bookmarks->children ; x; x = x->next) {
+ // If the current node is a conference item, see if we have to replace it.
+ if (x->name && !strcmp(x->name, "conference")) {
+ const char *fjid = lm_message_node_get_attribute(x, "jid");
+ if (!fjid)
+ continue;
+ if (!strcmp(fjid, roomid)) {
+ // We've found a bookmark for this room. Let's hide it and we'll
+ // create a new one.
+ lm_message_node_hide(x);
+ changed = TRUE;
+ if (!name)
+ scr_LogPrint(LPRINT_LOGNORM, "Deleting bookmark...");
+ }
+ }
+ }
+
+ // Let's create a node/bookmark for this roomid, if the name is not NULL.
+ if (name) {
+ x = lm_message_node_add_child(bookmarks, "conference", NULL);
+ lm_message_node_set_attributes(x,
+ "jid", roomid,
+ "name", name,
+ "autojoin", autojoin ? "1" : "0",
+ NULL);
+ if (nick)
+ lm_message_node_add_child(x, "nick", nick);
+ if (passwd)
+ lm_message_node_add_child(x, "password", passwd);
+ if (pstatus)
+ lm_message_node_add_child(x, "print_status", strprintstatus[pstatus]);
+ if (awhois)
+ lm_message_node_set_attributes(x, "autowhois",
+ (awhois == autowhois_on) ? "1" : "0",
+ NULL);
+ changed = TRUE;
+ scr_LogPrint(LPRINT_LOGNORM, "Updating bookmarks...");
+ }
+
+ if (!changed)
+ return;
+
+ if (lm_connection_is_authenticated(lconnection))
+ send_storage(bookmarks);
+ else
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Warning: you're not connected to the server.");
+}
+
+static struct annotation *parse_storage_rosternote(LmMessageNode *notenode)
+{
+ const char *p;
+ struct annotation *note = g_new0(struct annotation, 1);
+ p = lm_message_node_get_attribute(notenode, "cdate");
+ if (p)
+ note->cdate = from_iso8601(p, 1);
+ p = lm_message_node_get_attribute(notenode, "mdate");
+ if (p)
+ note->mdate = from_iso8601(p, 1);
+ note->text = g_strdup(lm_message_node_get_value(notenode));
+ note->jid = g_strdup(lm_message_node_get_attribute(notenode, "jid"));
+ return note;
+}
+
+// xmpp_get_all_storage_rosternotes()
+// Return a GSList with all storage annotations.
+// The caller should g_free the list and its contents.
+GSList *xmpp_get_all_storage_rosternotes(void)
+{
+ LmMessageNode *x;
+ GSList *sl_notes = NULL;
+
+ // If we have no rosternotes, probably the server doesn't support them.
+ if (!rosternotes)
+ return NULL;
+
+ // Walk through the storage rosternotes tags
+ for (x = rosternotes->children ; x; x = x->next) {
+ struct annotation *note;
+
+ // We want a note item
+ if (!x->name || strcmp(x->name, "note"))
+ continue;
+ // Just in case, check the jid...
+ if (!lm_message_node_get_attribute(x, "jid"))
+ continue;
+ // Ok, let's add the note to our list
+ note = parse_storage_rosternote(x);
+ sl_notes = g_slist_append(sl_notes, note);
+ }
+ return sl_notes;
+}
+
+// xmpp_get_storage_rosternotes(barejid, silent)
+// Return the annotation associated with this jid.
+// If silent is TRUE, no warning is displayed when rosternotes is disabled
+// The caller should g_free the string and structure after use.
+struct annotation *xmpp_get_storage_rosternotes(const char *barejid, int silent)
+{
+ LmMessageNode *x;
+
+ if (!barejid)
+ return NULL;
+
+ // If we have no rosternotes, probably the server doesn't support them.
+ if (!rosternotes) {
+ if (!silent)
+ scr_LogPrint(LPRINT_NORMAL, "Sorry, "
+ "your server doesn't seem to support private storage.");
+ return NULL;
+ }
+
+ // Walk through the storage rosternotes tags
+ for (x = rosternotes->children ; x; x = x->next) {
+ const char *fjid;
+ // We want a note item
+ if (!x->name || strcmp(x->name, "note"))
+ continue;
+ // Just in case, check the jid...
+ fjid = lm_message_node_get_attribute(x, "jid");
+ if (fjid && !strcmp(fjid, barejid)) // We've found a note for this contact.
+ return parse_storage_rosternote(x);
+ }
+ return NULL; // No note found
+}
+
+// xmpp_set_storage_rosternotes(barejid, note)
+// Update the private storage rosternotes: add/delete a note.
+// If note is nil, we remove the existing note.
+void xmpp_set_storage_rosternotes(const char *barejid, const char *note)
+{
+ LmMessageNode *x;
+ bool changed = FALSE;
+ const char *cdate = NULL;
+
+ if (!barejid)
+ return;
+
+ // If we have no rosternotes, probably the server doesn't support them.
+ if (!rosternotes) {
+ scr_LogPrint(LPRINT_NORMAL,
+ "Sorry, your server doesn't seem to support private storage.");
+ return;
+ }
+
+ // Walk through the storage tags
+ for (x = rosternotes->children ; x; x = x->next) {
+ // If the current node is a conference item, see if we have to replace it.
+ if (x->name && !strcmp(x->name, "note")) {
+ const char *fjid = lm_message_node_get_attribute(x, "jid");
+ if (!fjid)
+ continue;
+ if (!strcmp(fjid, barejid)) {
+ // We've found a note for this jid. Let's hide it and we'll
+ // create a new one.
+ cdate = lm_message_node_get_attribute(x, "cdate");
+ lm_message_node_hide(x);
+ changed = TRUE;
+ break;
+ }
+ }
+ }
+
+ // Let's create a node for this jid, if the note is not NULL.
+ if (note) {
+ char mdate[20];
+ time_t now;
+ time(&now);
+ to_iso8601(mdate, now);
+ if (!cdate)
+ cdate = mdate;
+ x = lm_message_node_add_child(rosternotes, "note", note);
+ lm_message_node_set_attributes(x,
+ "jid", barejid,
+ "cdate", cdate,
+ "mdate", mdate,
+ NULL);
+ changed = TRUE;
+ }
+
+ if (!changed)
+ return;
+
+ if (lm_connection_is_authenticated(lconnection))
+ send_storage(rosternotes);
+ else
+ scr_LogPrint(LPRINT_LOGNORM,
+ "Warning: you're not connected to the server.");
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,82 @@
+#ifndef __MCABBER_XMPP_H__
+#define __MCABBER_XMPP_H__ 1
+
+#include <loudmouth/loudmouth.h>
+#include <mcabber/roster.h>
+
+enum iqreq_type {
+ iqreq_none,
+ iqreq_version,
+ iqreq_time,
+ iqreq_last,
+ iqreq_vcard
+};
+
+struct annotation {
+ time_t cdate;
+ time_t mdate;
+ gchar *jid;
+ gchar *text;
+};
+
+struct bookmark {
+ gchar *roomjid;
+ gchar *name;
+ gchar *nick;
+ guint autojoin;
+ /* enum room_printstatus pstatus; */
+ /* enum room_autowhois awhois; */
+};
+
+extern LmConnection* lconnection;
+extern LmSSL* lssl;
+
+void xmpp_connect(void);
+void xmpp_disconnect(void);
+
+void xmpp_room_join(const char *room, const char *nickname, const char *passwd);
+int xmpp_room_setattrib(const char *roomid, const char *fjid,
+ const char *nick, struct role_affil ra,
+ const char *reason);
+void xmpp_room_invite(const char *room, const char *fjid, const char *reason);
+void xmpp_room_unlock(const char *room);
+void xmpp_room_destroy(const char *room, const char *venue, const char *reason);
+
+void xmpp_addbuddy(const char *bjid, const char *name, const char *group);
+void xmpp_updatebuddy(const char *bjid, const char *name, const char *group);
+void xmpp_delbuddy(const char *bjid);
+
+void xmpp_send_msg(const char *fjid, const char *text, int type,
+ const char *subject, gboolean otrinject, gint *encrypted,
+ LmMessageSubType type_overwrite, gpointer *xep184);
+
+void xmpp_send_s10n(const char *bjid, LmMessageSubType type);
+
+enum imstatus xmpp_getstatus(void);
+const char *xmpp_getstatusmsg(void);
+void xmpp_setprevstatus(void);
+
+void xmpp_setstatus(enum imstatus st, const char *recipient,
+ const char *msg, int do_not_sign);
+
+void xmpp_send_chatstate(gpointer buddy, guint chatstate);
+
+GSList *xmpp_get_all_storage_bookmarks(void);
+GSList *xmpp_get_all_storage_rosternotes(void);
+void xmpp_set_storage_bookmark(const char *roomid, const char *name,
+ const char *nick, const char *passwd,
+ int autojoin, enum room_printstatus pstatus,
+ enum room_autowhois awhois);
+struct annotation *xmpp_get_storage_rosternotes(const char *barejid,
+ int silent);
+void xmpp_set_storage_rosternotes(const char *barejid, const char *note);
+guint xmpp_is_bookmarked(const char *bjid);
+const char *xmpp_get_bookmark_nick(const char *bjid);
+
+void xmpp_request(const char *fjid, enum iqreq_type reqtype);
+void request_vcard(const char *bjid);
+void xmpp_request_storage(const gchar *storage);
+
+#endif /* __MCABBER_XMPP_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_defines.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,82 @@
+#ifndef __MCABBER_XMPP_DEFINES_H__
+#define __MCABBER_XMPP_DEFINES_H__ 1
+
+#define MCABBER_CAPS_NODE "http://mcabber.com/caps"
+
+#define NS_CLIENT "jabber:client"
+#define NS_SERVER "jabber:server"
+#define NS_DIALBACK "jabber:server:dialback"
+#define NS_AUTH "jabber:iq:auth"
+#define NS_AUTH_CRYPT "jabber:iq:auth:crypt"
+#define NS_REGISTER "jabber:iq:register"
+#define NS_ROSTER "jabber:iq:roster"
+#define NS_OFFLINE "jabber:x:offline"
+#define NS_AGENT "jabber:iq:agent"
+#define NS_AGENTS "jabber:iq:agents"
+#define NS_DELAY "jabber:x:delay"
+#define NS_VERSION "jabber:iq:version"
+#define NS_TIME "jabber:iq:time"
+#define NS_VCARD "vcard-temp"
+#define NS_PRIVATE "jabber:iq:private"
+#define NS_SEARCH "jabber:iq:search"
+#define NS_OOB "jabber:iq:oob"
+#define NS_XOOB "jabber:x:oob"
+#define NS_ADMIN "jabber:iq:admin"
+#define NS_FILTER "jabber:iq:filter"
+#define NS_AUTH_0K "jabber:iq:auth:0k"
+#define NS_BROWSE "jabber:iq:browse"
+#define NS_EVENT "jabber:x:event"
+#define NS_CONFERENCE "jabber:iq:conference"
+#define NS_SIGNED "jabber:x:signed"
+#define NS_ENCRYPTED "jabber:x:encrypted"
+#define NS_GATEWAY "jabber:iq:gateway"
+#define NS_LAST "jabber:iq:last"
+#define NS_ENVELOPE "jabber:x:envelope"
+#define NS_EXPIRE "jabber:x:expire"
+#define NS_XHTML "http://www.w3.org/1999/xhtml"
+#define NS_DISCO_INFO "http://jabber.org/protocol/disco#info"
+#define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items"
+#define NS_IQ_AUTH "http://jabber.org/features/iq-auth"
+#define NS_REGISTER_FEATURE "http://jabber.org/features/iq-register"
+
+#define NS_CAPS "http://jabber.org/protocol/caps"
+#define NS_CHATSTATES "http://jabber.org/protocol/chatstates"
+#define NS_COMMANDS "http://jabber.org/protocol/commands"
+#define NS_MUC "http://jabber.org/protocol/muc"
+
+#define NS_XDBGINSERT "jabber:xdb:ginsert"
+#define NS_XDBNSLIST "jabber:xdb:nslist"
+
+#define NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
+#define NS_XMPP_TLS "urn:ietf:params:xml:ns:xmpp-tls"
+#define NS_XMPP_STREAMS "urn:ietf:params:xml:ns:xmpp-streams"
+
+#define NS_XMPP_DELAY "urn:xmpp:delay"
+#define NS_XMPP_TIME "urn:xmpp:time"
+#define NS_PING "urn:xmpp:ping"
+#define NS_RECEIPTS "urn:xmpp:receipts"
+
+#define NS_JABBERD_STOREDPRESENCE "http://jabberd.org/ns/storedpresence"
+#define NS_JABBERD_HISTORY "http://jabberd.org/ns/history"
+
+#define XMPP_ERROR_REDIRECT 302
+#define XMPP_ERROR_BAD_REQUEST 400
+#define XMPP_ERROR_NOT_AUTHORIZED 401
+#define XMPP_ERROR_PAYMENT_REQUIRED 402
+#define XMPP_ERROR_FORBIDDEN 403
+#define XMPP_ERROR_NOT_FOUND 404
+#define XMPP_ERROR_NOT_ALLOWED 405
+#define XMPP_ERROR_NOT_ACCEPTABLE 406
+#define XMPP_ERROR_REGISTRATION_REQUIRED 407
+#define XMPP_ERROR_REQUEST_TIMEOUT 408
+#define XMPP_ERROR_CONFLICT 409
+#define XMPP_ERROR_INTERNAL_SERVER_ERROR 500
+#define XMPP_ERROR_NOT_IMPLEMENTED 501
+#define XMPP_ERROR_REMOTE_SERVER_ERROR 502
+#define XMPP_ERROR_SERVICE_UNAVAILABLE 503
+#define XMPP_ERROR_REMOTE_SERVER_TIMEOUT 504
+#define XMPP_ERROR_DISCONNECTED 510
+
+#endif
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_helper.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,400 @@
+/*
+ * xmpp_helper.c -- Jabber protocol helper functions
+ *
+ * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ * Some parts initially came from the centericq project:
+ * Copyright (C) 2002-2005 by Konstantin Klyagin <konst@konst.org.ua>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+
+#include "xmpp_helper.h"
+#include "settings.h"
+#include "utils.h"
+#include "caps.h"
+#include "logprint.h"
+#include "config.h"
+
+time_t iqlast; // last message/status change time
+
+extern char *imstatus_showmap[];
+
+struct xmpp_error xmpp_errors[] = {
+ {XMPP_ERROR_REDIRECT, "302",
+ "Redirect", "redirect", "modify"},
+ {XMPP_ERROR_BAD_REQUEST, "400",
+ "Bad Request", "bad-request", "modify"},
+ {XMPP_ERROR_NOT_AUTHORIZED, "401",
+ "Not Authorized", "not-authorized", "auth"},
+ {XMPP_ERROR_PAYMENT_REQUIRED, "402",
+ "Payment Required", "payment-required", "auth"},
+ {XMPP_ERROR_FORBIDDEN, "403",
+ "Forbidden", "forbidden", "auth"},
+ {XMPP_ERROR_NOT_FOUND, "404",
+ "Not Found", "item-not-found", "cancel"},
+ {XMPP_ERROR_NOT_ALLOWED, "405",
+ "Not Allowed", "not-allowed", "cancel"},
+ {XMPP_ERROR_NOT_ACCEPTABLE, "406",
+ "Not Acceptable", "not-acceptable", "modify"},
+ {XMPP_ERROR_REGISTRATION_REQUIRED, "407",
+ "Registration required", "registration-required", "auth"},
+ {XMPP_ERROR_REQUEST_TIMEOUT, "408",
+ "Request Timeout", "remote-server-timeout", "wait"},
+ {XMPP_ERROR_CONFLICT, "409",
+ "Conflict", "conflict", "cancel"},
+ {XMPP_ERROR_INTERNAL_SERVER_ERROR, "500",
+ "Internal Server Error", "internal-server-error", "wait"},
+ {XMPP_ERROR_NOT_IMPLEMENTED, "501",
+ "Not Implemented", "feature-not-implemented", "cancel"},
+ {XMPP_ERROR_REMOTE_SERVER_ERROR, "502",
+ "Remote Server Error", "service-unavailable", "wait"},
+ {XMPP_ERROR_SERVICE_UNAVAILABLE, "503",
+ "Service Unavailable", "service-unavailable", "cancel"},
+ {XMPP_ERROR_REMOTE_SERVER_TIMEOUT, "504",
+ "Remote Server Timeout", "remote-server-timeout", "wait"},
+ {XMPP_ERROR_DISCONNECTED, "510",
+ "Disconnected", "service-unavailable", "cancel"},
+ {0, NULL, NULL, NULL, NULL}
+};
+
+
+#ifdef MODULES_ENABLE
+static GSList *xmpp_additional_features = NULL;
+static char *ver, *ver_notavail;
+
+void xmpp_add_feature (const char *xmlns)
+{
+ if (xmlns) {
+ ver = NULL;
+ ver_notavail = NULL;
+ xmpp_additional_features = g_slist_append(xmpp_additional_features,
+ g_strdup (xmlns));
+ }
+}
+
+void xmpp_del_feature (const char *xmlns)
+{
+ GSList *feature = xmpp_additional_features;
+ while (feature) {
+ if (!strcmp(feature->data, xmlns)) {
+ ver = NULL;
+ ver_notavail = NULL;
+ g_free (feature->data);
+ xmpp_additional_features = g_slist_delete_link(xmpp_additional_features,
+ feature);
+ return;
+ }
+ feature = g_slist_next (feature);
+ }
+}
+#endif
+
+const gchar* lm_message_node_get_child_value(LmMessageNode *node,
+ const gchar *child)
+{
+ LmMessageNode *tmp;
+ tmp = lm_message_node_find_child(node, child);
+ if (tmp)
+ return lm_message_node_get_value(tmp);
+ else return NULL;
+}
+
+static LmMessageNode *hidden = NULL;
+
+void lm_message_node_hide(LmMessageNode *node)
+{
+ LmMessageNode *parent = node->parent, *prev_sibling = node->prev;
+
+ if (hidden) {
+ hidden->children = hidden->next = hidden->prev = hidden->parent = NULL;
+ lm_message_node_unref(hidden);
+ }
+
+ if (parent->children == node)
+ parent->children = node->next;
+ if (prev_sibling)
+ prev_sibling->next = node->next;
+ if (node->next)
+ node->next->prev = prev_sibling;
+}
+
+//maybe not a good idea, because it uses internals of loudmouth...
+//it's used for rosternotes/bookmarks
+LmMessageNode *lm_message_node_new(const gchar *name, const gchar *xmlns)
+{
+ LmMessageNode *node;
+
+ node = g_new0 (LmMessageNode, 1);
+ node->name = g_strdup (name);
+ node->value = NULL;
+ node->raw_mode = FALSE;
+ node->attributes = NULL;
+ node->next = NULL;
+ node->prev = NULL;
+ node->parent = NULL;
+ node->children = NULL;
+
+ node->ref_count = 1;
+ lm_message_node_set_attribute(node, "xmlns", xmlns);
+ return node;
+}
+
+void lm_message_node_insert_childnode(LmMessageNode *node,
+ LmMessageNode *child)
+{
+ LmMessageNode *x;
+ lm_message_node_deep_ref(child);
+
+ if (node->children == NULL)
+ node->children = child;
+ else {
+ for (x = node->children; x->next; x = x->next)
+ ;
+ x->next = child;
+ }
+}
+
+void lm_message_node_deep_ref(LmMessageNode *node)
+{
+ if (node == NULL)
+ return;
+ lm_message_node_ref(node);
+ lm_message_node_deep_ref(node->next);
+ lm_message_node_deep_ref(node->children);
+}
+
+const gchar* lm_message_get_from(LmMessage *m)
+{
+ return lm_message_node_get_attribute(m->node, "from");
+}
+
+const gchar* lm_message_get_id(LmMessage *m)
+{
+ return lm_message_node_get_attribute(m->node, "id");
+}
+
+LmMessage *lm_message_new_iq_from_query(LmMessage *m,
+ LmMessageSubType type)
+{
+ LmMessage *new;
+ const char *from = lm_message_node_get_attribute(m->node, "from");
+ const char *id = lm_message_node_get_attribute(m->node, "id");
+
+ new = lm_message_new_with_sub_type(from, LM_MESSAGE_TYPE_IQ,
+ type);
+ if (id)
+ lm_message_node_set_attribute(new->node, "id", id);
+
+ return new;
+}
+
+// entity_version(enum imstatus status)
+// Return a static version string for Entity Capabilities.
+// It should be specific to the client version, please change the id
+// if you alter mcabber's disco support (or add something to the version
+// number) so that it doesn't conflict with the official client.
+const char *entity_version(enum imstatus status)
+{
+#ifndef MODULES_ENABLE
+ static char *ver, *ver_notavail;
+#endif
+
+ if (ver && (status != notavail))
+ return ver;
+ if (ver_notavail)
+ return ver_notavail;
+
+ caps_add("");
+ caps_set_identity("", "client", PACKAGE_STRING, "pc");
+ caps_add_feature("", NS_DISCO_INFO);
+ caps_add_feature("", NS_MUC);
+ // advertise ChatStates only if they aren't disabled
+ if (!settings_opt_get_int("disable_chatstates"))
+ caps_add_feature("", NS_CHATSTATES);
+ caps_add_feature("", NS_TIME);
+ caps_add_feature("", NS_XMPP_TIME);
+ caps_add_feature("", NS_VERSION);
+ caps_add_feature("", NS_PING);
+ caps_add_feature("", NS_COMMANDS);
+ caps_add_feature("", NS_RECEIPTS);
+ if (!settings_opt_get_int("iq_last_disable") &&
+ (!settings_opt_get_int("iq_last_disable_when_notavail") ||
+ status != notavail))
+ caps_add_feature("", NS_LAST);
+#ifdef MODULES_ENABLE
+ {
+ GSList *el = xmpp_additional_features;
+ while (el) {
+ caps_add_feature("", el->data);
+ el = g_slist_next (el);
+ }
+ }
+#endif
+
+ if (status == notavail) {
+ ver_notavail = caps_generate();
+ return ver_notavail;
+ }
+
+ ver = caps_generate();
+ return ver;
+}
+
+LmMessageNode *lm_message_node_find_xmlns(LmMessageNode *node,
+ const char *xmlns)
+{
+ LmMessageNode *x;
+ const char *p;
+
+ for (x = node->children ; x; x = x->next) {
+ if ((p = lm_message_node_get_attribute(x, "xmlns")) && !strcmp(p, xmlns))
+ break;
+ }
+ return x;
+}
+
+time_t lm_message_node_get_timestamp(LmMessageNode *node)
+{
+ LmMessageNode *x;
+ const char *p;
+
+ x = lm_message_node_find_xmlns(node, NS_XMPP_DELAY);
+ if (x && (!strcmp(x->name, "delay")) &&
+ (p = lm_message_node_get_attribute(x, "stamp")) != NULL)
+ return from_iso8601(p, 1);
+ x = lm_message_node_find_xmlns(node, NS_DELAY);
+ if (x && (p = lm_message_node_get_attribute(x, "stamp")) != NULL)
+ return from_iso8601(p, 1);
+ return 0;
+}
+
+// lm_message_new_presence(status, recipient, message)
+// Create an xmlnode with default presence attributes
+// Note: the caller must free the node after use
+LmMessage *lm_message_new_presence(enum imstatus st,
+ const char *recipient,
+ const char *msg)
+{
+ unsigned int prio;
+ LmMessage *x = lm_message_new(recipient, LM_MESSAGE_TYPE_PRESENCE);
+
+ switch(st) {
+ case away:
+ case notavail:
+ case dontdisturb:
+ case freeforchat:
+ lm_message_node_add_child(x->node, "show", imstatus_showmap[st]);
+ break;
+
+ case invisible:
+ lm_message_node_set_attribute(x->node, "type", "invisible");
+ break;
+
+ case offline:
+ lm_message_node_set_attribute(x->node, "type", "unavailable");
+ break;
+
+ default:
+ break;
+ }
+
+ if (st == away || st == notavail)
+ prio = settings_opt_get_int("priority_away");
+ else
+ prio = settings_opt_get_int("priority");
+
+ if (prio) {
+ char strprio[8];
+ snprintf(strprio, 8, "%d", (int)prio);
+ lm_message_node_add_child(x->node, "priority", strprio);
+ }
+
+ if (msg)
+ lm_message_node_add_child(x->node, "status", msg);
+
+ return x;
+}
+
+static const char *defaulterrormsg(guint code)
+{
+ int i;
+
+ for (i = 0; xmpp_errors[i].code; ++i) {
+ if (xmpp_errors[i].code == code)
+ return xmpp_errors[i].meaning;
+ }
+ return NULL;
+}
+
+// display_server_error(x)
+// Display the error to the user
+// x: error tag xmlnode pointer
+void display_server_error(LmMessageNode *x)
+{
+ const char *desc = NULL, *p=NULL, *s;
+ char *sdesc, *tmp;
+ int code = 0;
+
+ if (!x) return;
+
+ /* RFC3920:
+ * The <error/> element:
+ * o MUST contain a child element corresponding to one of the defined
+ * stanza error conditions specified below; this element MUST be
+ * qualified by the 'urn:ietf:params:xml:ns:xmpp-stanzas' namespace.
+ */
+ if (x->children)
+ p = x->children->name;
+ if (p)
+ scr_LogPrint(LPRINT_LOGNORM, "Received error packet [%s]", p);
+
+ // For backward compatibility
+ if ((s = lm_message_node_get_attribute(x, "code")) != NULL) {
+ code = atoi(s);
+ // Default message
+ desc = defaulterrormsg(code);
+ }
+
+ // Error tag data is better, if available
+ s = lm_message_node_get_value(x);
+ if (s && *s) desc = s;
+
+ // And sometimes there is a text message
+ s = lm_message_node_get_child_value(x, "text");
+
+ if (s && *s) desc = s;
+
+ // If we still have no description, let's give up
+ if (!desc)
+ return;
+
+ // Strip trailing newlines
+ sdesc = g_strdup(desc);
+ for (tmp = sdesc; *tmp; tmp++) ;
+ if (tmp > sdesc)
+ tmp--;
+ while (tmp >= sdesc && (*tmp == '\n' || *tmp == '\r'))
+ *tmp-- = '\0';
+
+ scr_LogPrint(LPRINT_LOGNORM, "Error code from server: %d %s", code, sdesc);
+ g_free(sdesc);
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_helper.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,58 @@
+#ifndef __MCABBER_XMPPHELPER_H__
+#define __MCABBER_XMPPHELPER_H__ 1
+
+#include <time.h>
+#include <loudmouth/loudmouth.h>
+
+#include <mcabber/xmpp.h>
+#include <mcabber/xmpp_defines.h>
+#include <mcabber/config.h>
+
+extern time_t iqlast; /* last message/status change time */
+
+struct T_presence {
+ enum imstatus st;
+ const char *msg;
+};
+
+struct xmpp_error {
+ guint code;
+ const char *code_str;
+ const char *meaning;
+ const char *condition;
+ const char *type;
+};
+
+
+#ifdef MODULES_ENABLE
+void xmpp_add_feature (const char *xmlns);
+void xmpp_del_feature (const char *xmlns);
+#endif
+
+LmMessageNode *lm_message_node_new(const gchar *name, const gchar *xmlns);
+LmMessageNode *lm_message_node_find_xmlns(LmMessageNode *node,
+ const char *xmlns);
+const gchar* lm_message_node_get_child_value(LmMessageNode *node,
+ const gchar *child);
+void lm_message_node_hide(LmMessageNode *node);
+void lm_message_node_insert_childnode(LmMessageNode *node,
+ LmMessageNode *child);
+void lm_message_node_deep_ref(LmMessageNode *node);
+time_t lm_message_node_get_timestamp(LmMessageNode *node);
+
+LmMessage *lm_message_new_iq_from_query(LmMessage *m, LmMessageSubType type);
+
+LmMessage *lm_message_new_presence(enum imstatus st,
+ const char *recipient, const char *msg);
+
+const gchar* lm_message_get_from(LmMessage *m);
+const gchar* lm_message_get_id(LmMessage *m);
+
+void display_server_error(LmMessageNode *x);
+
+/* XEP-0115 (Entity Capabilities) node */
+const char *entity_version(enum imstatus status);
+
+#endif
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_iq.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,836 @@
+/*
+ * xmpp_iq.c -- Jabber protocol IQ-related stuff
+ *
+ * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ * Parts come from the centericq project:
+ * Copyright (C) 2002-2005 by Konstantin Klyagin <konst@konst.org.ua>
+ * Some small parts come from the Pidgin project <http://pidgin.im/>
+ *
+ * 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 <string.h>
+#include <sys/utsname.h>
+
+#include "xmpp_helper.h"
+#include "commands.h"
+#include "screen.h"
+#include "utils.h"
+#include "logprint.h"
+#include "settings.h"
+#include "caps.h"
+#include "main.h"
+
+extern struct xmpp_error xmpp_errors[];
+
+static LmHandlerResult handle_iq_command_set_status(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m,
+ gpointer ud);
+
+static LmHandlerResult handle_iq_command_leave_groupchats(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m,
+ gpointer ud);
+
+inline double seconds_since_last_use(void);
+
+struct adhoc_command {
+ char *name;
+ char *description;
+ bool only_for_self;
+ LmHandleMessageFunction callback;
+};
+
+const struct adhoc_command adhoc_command_list[] = {
+ { "http://jabber.org/protocol/rc#set-status",
+ "Change client status",
+ 1,
+ &handle_iq_command_set_status },
+ { "http://jabber.org/protocol/rc#leave-groupchats",
+ "Leave groupchat(s)",
+ 1,
+ &handle_iq_command_leave_groupchats },
+ { NULL, NULL, 0, NULL },
+};
+
+struct adhoc_status {
+ char *name; // the name used by adhoc
+ char *description;
+ char *status; // the string, used by setstus
+};
+// It has to match imstatus of roster.h!
+const struct adhoc_status adhoc_status_list[] = {
+ {"offline", "Offline", "offline"},
+ {"online", "Online", "avail"},
+ {"chat", "Chat", "free"},
+ {"dnd", "Do not disturb", "dnd"},
+ {"xd", "Extended away", "notavail"},
+ {"away", "Away", "away"},
+ {"invisible", "Invisible", "invisible"},
+ {NULL, NULL, NULL},
+};
+
+static char *generate_session_id(char *prefix)
+{
+ char *result;
+ static int counter = 0;
+ counter++;
+ // TODO better use timestamp?
+ result = g_strdup_printf("%s-%i", prefix, counter);
+ return result;
+}
+
+static LmMessage *lm_message_new_iq_error(LmMessage *m, guint error)
+{
+ LmMessage *r;
+ LmMessageNode *err;
+ int i;
+
+ for (i = 0; xmpp_errors[i].code; ++i)
+ if (xmpp_errors[i].code == error)
+ break;
+ g_return_val_if_fail(xmpp_errors[i].code > 0, NULL);
+
+ r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_ERROR);
+ err = lm_message_node_add_child(r->node, "error", NULL);
+ lm_message_node_set_attribute(err, "code", xmpp_errors[i].code_str);
+ lm_message_node_set_attribute(err, "type", xmpp_errors[i].type);
+ lm_message_node_set_attribute
+ (lm_message_node_add_child(err,
+ xmpp_errors[i].condition, NULL),
+ "xmlns", NS_XMPP_STANZAS);
+
+ return r;
+}
+
+void send_iq_error(LmConnection *c, LmMessage *m, guint error)
+{
+ LmMessage *r;
+ r = lm_message_new_iq_error(m, error);
+ lm_connection_send(c, r, NULL);
+ lm_message_unref(r);
+}
+
+static void lm_message_node_add_dataform_result(LmMessageNode *node,
+ const char *message)
+{
+ LmMessageNode *x, *field;
+
+ x = lm_message_node_add_child(node, "x", NULL);
+ lm_message_node_set_attributes(x,
+ "type", "result",
+ "xmlns", "jabber:x:data",
+ NULL);
+ field = lm_message_node_add_child(x, "field", NULL);
+ lm_message_node_set_attributes(field,
+ "type", "text-single",
+ "var", "message",
+ NULL);
+ lm_message_node_add_child(field, "value", message);
+}
+
+static LmHandlerResult handle_iq_commands_list(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ LmMessage *iq;
+ LmMessageNode *query;
+ const char *requester_jid;
+ const struct adhoc_command *command;
+ const char *node;
+ gboolean from_self;
+
+ iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+ query = lm_message_node_add_child(iq->node, "query", NULL);
+ lm_message_node_set_attribute(query, "xmlns", NS_COMMANDS);
+ node = lm_message_node_get_attribute
+ (lm_message_node_get_child(m->node, "query"),
+ "node");
+ if (node)
+ lm_message_node_set_attribute(query, "node", node);
+
+ requester_jid = lm_message_get_from(m);
+ from_self = jid_equal(lm_connection_get_jid(c), requester_jid);
+
+ for (command = adhoc_command_list ; command->name ; command++) {
+ if (!command->only_for_self || from_self) {
+ lm_message_node_set_attributes
+ (lm_message_node_add_child(query, "item", NULL),
+ "node", command->name,
+ "name", command->description,
+ "jid", lm_connection_get_jid(c),
+ NULL);
+ }
+ }
+
+ lm_connection_send(c, iq, NULL);
+ lm_message_unref(iq);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult handle_iq_command_set_status(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ const char *action, *node;
+ char *sessionid;
+ LmMessage *iq;
+ LmMessageNode *command, *x, *y;
+ const struct adhoc_status *s;
+
+ x = lm_message_node_get_child(m->node, "command");
+ action = lm_message_node_get_attribute(x, "action");
+ node = lm_message_node_get_attribute(x, "node");
+ sessionid = (char *)lm_message_node_get_attribute(x, "sessionid");
+
+ iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+ command = lm_message_node_add_child(iq->node, "command", NULL);
+ lm_message_node_set_attribute(command, "node", node);
+ lm_message_node_set_attribute(command, "xmlns", NS_COMMANDS);
+
+ if (!sessionid) {
+ sessionid = generate_session_id("set-status");
+ lm_message_node_set_attribute(command, "sessionid", sessionid);
+ g_free(sessionid);
+ sessionid = NULL;
+ lm_message_node_set_attribute(command, "status", "executing");
+
+ x = lm_message_node_add_child(command, "x", NULL);
+ lm_message_node_set_attribute(x, "type", "form");
+ lm_message_node_set_attribute(x, "xmlns", "jabber:x:data");
+
+ lm_message_node_add_child(x, "title", "Change Status");
+
+ lm_message_node_add_child(x, "instructions",
+ "Choose the status and status message");
+
+ // TODO see if factorisation is possible
+ y = lm_message_node_add_child(x, "field", NULL);
+ lm_message_node_set_attribute(y, "type", "hidden");
+ lm_message_node_set_attribute(y, "var", "FORM_TYPE");
+
+ lm_message_node_add_child(y, "value", "http://jabber.org/protocol/rc");
+
+ y = lm_message_node_add_child(x, "field", NULL);
+ lm_message_node_set_attributes(y,
+ "type", "list-single",
+ "var", "status",
+ "label", "Status",
+ NULL);
+ lm_message_node_add_child(y, "required", NULL);
+
+ // XXX: ugly
+ lm_message_node_add_child(y, "value",
+ adhoc_status_list[xmpp_getstatus()].name);
+ for (s = adhoc_status_list; s->name; s++) {
+ LmMessageNode *option = lm_message_node_add_child(y, "option", NULL);
+ lm_message_node_add_child(option, "value", s->name);
+ lm_message_node_set_attribute(option, "label", s->description);
+ }
+ // TODO add priority ?
+ // I do not think this is useful, user should not have to care of the
+ // priority like gossip and gajim do (misc)
+ lm_message_node_set_attributes
+ (lm_message_node_add_child(x, "field", NULL),
+ "type", "text-multi",
+ "var", "status-message",
+ "label", "Message",
+ NULL);
+ } else if (action && !strcmp(action, "cancel")) {
+ lm_message_node_set_attribute(command, "status", "canceled");
+ } else { // (if sessionid and not canceled)
+ y = lm_message_node_find_xmlns(x, "jabber:x:data"); //x?xmlns=jabber:x:data
+ if (y) {
+ const char *value=NULL, *message=NULL;
+ LmMessageNode *fields, *field;
+ field = fields = lm_message_node_get_child(y, "field"); //field?var=status
+ while (field && strcmp("status",
+ lm_message_node_get_attribute(field, "var")))
+ field = field->next;
+ field = lm_message_node_get_child(field, "value");
+ if (field)
+ value = lm_message_node_get_value(field);
+ field = fields; //field?var=status-message
+ while (field && strcmp("status-message",
+ lm_message_node_get_attribute(field, "var")))
+ field = field->next;
+ field = lm_message_node_get_child(field, "value");
+ if (field)
+ message = lm_message_node_get_value(field);
+ if (value) {
+ for (s = adhoc_status_list; !s->name || strcmp(s->name, value); s++);
+ if (s->name) {
+ char *status = g_strdup_printf("%s %s", s->status,
+ message ? message : "");
+ cmd_setstatus(NULL, status);
+ g_free(status);
+ lm_message_node_set_attribute(command, "status", "completed");
+ lm_message_node_add_dataform_result(command,
+ "Status has been changed");
+ }
+ }
+ }
+ }
+ if (sessionid)
+ lm_message_node_set_attribute(command, "sessionid", sessionid);
+ lm_connection_send(c, iq, NULL);
+ lm_message_unref(iq);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static void _callback_foreach_buddy_groupchat(gpointer rosterdata, void *param)
+{
+ LmMessageNode *field, *option;
+ const char *room_jid, *nickname;
+ char *desc;
+
+ room_jid = buddy_getjid(rosterdata);
+ if (!room_jid) return;
+ nickname = buddy_getnickname(rosterdata);
+ if (!nickname) return;
+ field = param;
+
+ option = lm_message_node_add_child(field, "option", NULL);
+ lm_message_node_add_child(option, "value", room_jid);
+ desc = g_strdup_printf("%s on %s", nickname, room_jid);
+ lm_message_node_set_attribute(option, "label", desc);
+ g_free(desc);
+}
+
+static LmHandlerResult handle_iq_command_leave_groupchats(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m,
+ gpointer ud)
+{
+ const char *action, *node;
+ char *sessionid;
+ LmMessage *iq;
+ LmMessageNode *command, *x;
+
+ x = lm_message_node_get_child(m->node, "command");
+ action = lm_message_node_get_attribute(x, "action");
+ node = lm_message_node_get_attribute(x, "node");
+ sessionid = (char*)lm_message_node_get_attribute(x, "sessionid");
+
+ iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+ command = lm_message_node_add_child(iq->node, "command", NULL);
+ lm_message_node_set_attributes(command,
+ "node", node,
+ "xmlns", NS_COMMANDS,
+ NULL);
+
+ if (!sessionid) {
+ LmMessageNode *field;
+
+ sessionid = generate_session_id("leave-groupchats");
+ lm_message_node_set_attribute(command, "sessionid", sessionid);
+ g_free(sessionid);
+ sessionid = NULL;
+ lm_message_node_set_attribute(command, "status", "executing");
+
+ x = lm_message_node_add_child(command, "x", NULL);
+ lm_message_node_set_attributes(x,
+ "type", "form",
+ "xmlns", "jabber:x:data",
+ NULL);
+
+ lm_message_node_add_child(x, "title", "Leave groupchat(s)");
+
+ lm_message_node_add_child(x, "instructions",
+ "What groupchats do you want to leave?");
+
+ field = lm_message_node_add_child(x, "field", NULL);
+ lm_message_node_set_attributes(field,
+ "type", "hidden",
+ "var", "FORM_TYPE",
+ NULL);
+
+ lm_message_node_add_child(field, "value",
+ "http://jabber.org/protocol/rc");
+
+ field = lm_message_node_add_child(x, "field", NULL);
+ lm_message_node_set_attributes(field,
+ "type", "list-multi",
+ "var", "groupchats",
+ "label", "Groupchats: ",
+ NULL);
+ lm_message_node_add_child(field, "required", NULL);
+
+ foreach_buddy(ROSTER_TYPE_ROOM, &_callback_foreach_buddy_groupchat, field);
+ //TODO: return an error if we are not connected to groupchats
+ } else if (action && !strcmp(action, "cancel")) {
+ lm_message_node_set_attribute(command, "status", "canceled");
+ } else { // (if sessionid and not canceled)
+ LmMessageNode *form = lm_message_node_find_xmlns(x, "jabber:x:data");//TODO
+ if (form) {
+ LmMessageNode *field;
+
+ lm_message_node_set_attribute(command, "status", "completed");
+ //TODO: implement sth. like "field?var=groupchats" in xmlnode...
+ field = lm_message_node_get_child(form, "field");
+ while (field && strcmp("groupchats",
+ lm_message_node_get_attribute(field, "var")))
+ field = field->next;
+
+ if (field)
+ for (x = field->children ; x ; x = x->next)
+ {
+ if (!strcmp (x->name, "value")) {
+ GList* b = buddy_search_jid(lm_message_node_get_value(x));
+ if (b)
+ cmd_room_leave(b->data, "Requested by remote command");
+ }
+ }
+ lm_message_node_add_dataform_result(command,
+ "Groupchats have been left");
+ }
+ }
+ if (sessionid)
+ lm_message_node_set_attribute(command, "sessionid", sessionid);
+ lm_connection_send(c, iq, NULL);
+ lm_message_unref(iq);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+LmHandlerResult handle_iq_commands(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ const char *requester_jid = NULL;
+ LmMessageNode *cmd;
+ const struct adhoc_command *command;
+
+ // mcabber has only partial XEP-0146 support...
+ if (LM_MESSAGE_SUB_TYPE_SET != lm_message_get_sub_type(m))
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+
+ requester_jid = lm_message_get_from(m);
+
+ cmd = lm_message_node_get_child(m->node, "command");
+ if (jid_equal(lm_connection_get_jid(c), requester_jid)) {
+ const char *action, *node;
+ action = lm_message_node_get_attribute(cmd, "action");
+ node = lm_message_node_get_attribute(cmd, "node");
+ // action can be NULL, in which case it seems to take the default,
+ // ie execute
+ if (!action || !strcmp(action, "execute") || !strcmp(action, "cancel")
+ || !strcmp(action, "next") || !strcmp(action, "complete")) {
+ for (command = adhoc_command_list; command->name; command++) {
+ if (!strcmp(node, command->name))
+ command->callback(h, c, m, ud);
+ }
+ // "prev" action will get there, as we do not implement it,
+ // and do not authorize it
+ } else {
+ LmMessage *r;
+ LmMessageNode *err;
+ r = lm_message_new_iq_error(m, XMPP_ERROR_BAD_REQUEST);
+ err = lm_message_node_get_child(r->node, "error");
+ lm_message_node_set_attribute
+ (lm_message_node_add_child(err, "malformed-action", NULL),
+ "xmlns", NS_COMMANDS);
+ lm_connection_send(c, r, NULL);
+ lm_message_unref(r);
+ }
+ } else {
+ send_iq_error(c, m, XMPP_ERROR_FORBIDDEN);
+ }
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+
+LmHandlerResult handle_iq_disco_items(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ LmMessageNode *query;
+ const char *node;
+ query = lm_message_node_get_child(m->node, "query");
+ node = lm_message_node_get_attribute(query, "node");
+ if (node) {
+ if (!strcmp(node, NS_COMMANDS)) {
+ return handle_iq_commands_list(NULL, c, m, ud);
+ } else {
+ send_iq_error(c, m, XMPP_ERROR_NOT_IMPLEMENTED);
+ }
+ } else {
+ // not sure about this one
+ send_iq_error(c, m, XMPP_ERROR_NOT_IMPLEMENTED);
+ }
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+
+void _disco_add_feature_helper(gpointer data, gpointer user_data)
+{
+ LmMessageNode *node = user_data;
+ lm_message_node_set_attribute
+ (lm_message_node_add_child(node, "feature", NULL), "var", data);
+}
+
+// disco_info_set_caps(ansquery, entitycaps)
+// Add features attributes to ansquery. entitycaps should either be a
+// valid capabilities hash or NULL. If it is NULL, the node attribute won't
+// be added to the query child and Entity Capabilities will be announced
+// as a feature.
+// Please change the entity version string if you modify mcabber disco
+// source code, so that it doesn't conflict with the upstream client.
+static void disco_info_set_caps(LmMessageNode *ansquery,
+ const char *entitycaps)
+{
+ if (entitycaps) {
+ char *eversion;
+ eversion = g_strdup_printf("%s#%s", MCABBER_CAPS_NODE, entitycaps);
+ lm_message_node_set_attribute(ansquery, "node", eversion);
+ g_free(eversion);
+ }
+
+ lm_message_node_set_attributes
+ (lm_message_node_add_child(ansquery, "identity", NULL),
+ "category", "client",
+ "name", PACKAGE_STRING,
+ "type", "pc",
+ NULL);
+
+ if (entitycaps)
+ caps_foreach_feature(entitycaps, _disco_add_feature_helper, ansquery);
+ else {
+ caps_foreach_feature(entity_version(xmpp_getstatus()),
+ _disco_add_feature_helper,
+ ansquery);
+ lm_message_node_set_attribute
+ (lm_message_node_add_child(ansquery, "feature", NULL),
+ "var", NS_CAPS);
+ }
+}
+
+LmHandlerResult handle_iq_disco_info(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ LmMessage *r;
+ LmMessageNode *query, *tmp;
+ const char *node = NULL;
+ const char *param = NULL;
+
+ if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_RESULT)
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+
+ r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+ query = lm_message_node_add_child(r->node, "query", NULL);
+ lm_message_node_set_attribute(query, "xmlns", NS_DISCO_INFO);
+ tmp = lm_message_node_find_child(m->node, "query");
+ if (tmp) {
+ node = lm_message_node_get_attribute(tmp, "node");
+ param = node+strlen(MCABBER_CAPS_NODE)+1;
+ }
+ if (node && startswith(node, MCABBER_CAPS_NODE "#", FALSE))
+ disco_info_set_caps(query, param); // client#version
+ else
+ // Basic discovery request
+ disco_info_set_caps(query, NULL);
+
+ lm_connection_send(c, r, NULL);
+ lm_message_unref(r);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+LmHandlerResult handle_iq_roster(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ LmMessageNode *y;
+ const char *fjid, *name, *group, *sub, *ask;
+ char *cleanalias;
+ enum subscr esub;
+ int need_refresh = FALSE;
+ guint roster_type;
+
+ for (y = lm_message_node_find_child(lm_message_node_find_xmlns
+ (m->node, NS_ROSTER),
+ "item");
+ y;
+ y = y->next) {
+ char *name_tmp = NULL;
+
+ fjid = lm_message_node_get_attribute(y, "jid");
+ name = lm_message_node_get_attribute(y, "name");
+ sub = lm_message_node_get_attribute(y, "subscription");
+ ask = lm_message_node_get_attribute(y, "ask");
+
+ if (lm_message_node_find_child(y, "group"))
+ group = lm_message_node_get_value(lm_message_node_find_child(y, "group"));
+ else
+ group = NULL;
+
+ if (!fjid)
+ continue;
+
+ cleanalias = jidtodisp(fjid);
+
+ esub = sub_none;
+ if (sub) {
+ if (!strcmp(sub, "to")) esub = sub_to;
+ else if (!strcmp(sub, "from")) esub = sub_from;
+ else if (!strcmp(sub, "both")) esub = sub_both;
+ else if (!strcmp(sub, "remove")) esub = sub_remove;
+ }
+
+ if (esub == sub_remove) {
+ roster_del_user(cleanalias);
+ scr_LogPrint(LPRINT_LOGNORM, "Buddy <%s> has been removed "
+ "from the roster", cleanalias);
+ g_free(cleanalias);
+ need_refresh = TRUE;
+ continue;
+ }
+
+ if (ask && !strcmp(ask, "subscribe"))
+ esub |= sub_pending;
+
+ if (!name) {
+ if (!settings_opt_get_int("roster_hide_domain")) {
+ name = cleanalias;
+ } else {
+ char *p;
+ name = name_tmp = g_strdup(cleanalias);
+ p = strchr(name_tmp, JID_DOMAIN_SEPARATOR);
+ if (p) *p = '\0';
+ }
+ }
+
+ // Tricky... :-\ My guess is that if there is no JID_DOMAIN_SEPARATOR,
+ // this is an agent.
+ if (strchr(cleanalias, JID_DOMAIN_SEPARATOR))
+ roster_type = ROSTER_TYPE_USER;
+ else
+ roster_type = ROSTER_TYPE_AGENT;
+
+ roster_add_user(cleanalias, name, group, roster_type, esub, 1);
+
+ g_free(name_tmp);
+ g_free(cleanalias);
+ }
+
+ buddylist_build();
+ update_roster = TRUE;
+ if (need_refresh)
+ scr_UpdateBuddyWindow();
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+LmHandlerResult handle_iq_ping(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ LmMessage *r;
+
+ r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+ lm_connection_send(c, r, NULL);
+ lm_message_unref(r);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+double seconds_since_last_use(void)
+{
+ return difftime(time(NULL), iqlast);
+}
+
+LmHandlerResult handle_iq_last(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ LmMessage *r;
+ LmMessageNode *query;
+ char *seconds;
+
+ if (!settings_opt_get_int("iq_hide_requests")) {
+ scr_LogPrint(LPRINT_LOGNORM, "Received an IQ last time request from <%s>",
+ lm_message_get_from(m));
+ }
+
+ if (settings_opt_get_int("iq_last_disable") ||
+ (settings_opt_get_int("iq_last_disable_when_notavail") &&
+ xmpp_getstatus() == notavail))
+ {
+ send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+
+ r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+ query = lm_message_node_add_child(r->node, "query", NULL);
+ lm_message_node_set_attribute(query, "xmlns", NS_LAST);
+ seconds = g_strdup_printf("%.0f", seconds_since_last_use());
+ lm_message_node_set_attribute(query, "seconds", seconds);
+ g_free(seconds);
+
+ lm_connection_send(c, r, NULL);
+ lm_message_unref(r);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+LmHandlerResult handle_iq_version(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ LmMessage *r;
+ LmMessageNode *query;
+ char *os = NULL;
+ char *ver = mcabber_version();
+
+ if (!settings_opt_get_int("iq_hide_requests")) {
+ scr_LogPrint(LPRINT_LOGNORM, "Received an IQ version request from <%s>",
+ lm_message_get_from(m));
+ }
+ if (!settings_opt_get_int("iq_version_hide_os")) {
+ struct utsname osinfo;
+ uname(&osinfo);
+ os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release,
+ osinfo.machine);
+ }
+
+ r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+
+ query = lm_message_node_add_child(r->node, "query", NULL);
+ lm_message_node_set_attribute(query, "xmlns", NS_VERSION);
+
+ lm_message_node_add_child(query, "name", PACKAGE_NAME);
+ lm_message_node_add_child(query, "version", ver);
+ if (os) {
+ lm_message_node_add_child(query, "os", os);
+ g_free(os);
+ }
+
+ g_free(ver);
+ lm_connection_send(c, r, NULL);
+ lm_message_unref(r);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+// This function borrows some code from the Pidgin project
+LmHandlerResult handle_iq_time(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ LmMessage *r;
+ LmMessageNode *query;
+ char *buf, *utf8_buf;
+ time_t now_t;
+ struct tm *now;
+
+ time(&now_t);
+
+ if (!settings_opt_get_int("iq_hide_requests")) {
+ scr_LogPrint(LPRINT_LOGNORM, "Received an IQ time request from <%s>",
+ lm_message_get_from(m));
+ }
+
+ buf = g_new0(char, 512);
+
+ r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+ query = lm_message_node_add_child(r->node, "query", NULL);
+ lm_message_node_set_attribute(query, "xmlns", NS_TIME);
+
+ now = gmtime(&now_t);
+
+ strftime(buf, 512, "%Y%m%dT%T", now);
+ lm_message_node_add_child(query, "utc", buf);
+
+ now = localtime(&now_t);
+
+ strftime(buf, 512, "%Z", now);
+ if ((utf8_buf = to_utf8(buf))) {
+ lm_message_node_add_child(query, "tz", utf8_buf);
+ g_free(utf8_buf);
+ }
+
+ strftime(buf, 512, "%d %b %Y %T", now);
+ if ((utf8_buf = to_utf8(buf))) {
+ lm_message_node_add_child(query, "display", utf8_buf);
+ g_free(utf8_buf);
+ }
+
+ lm_connection_send(c, r, NULL);
+ lm_message_unref(r);
+ g_free(buf);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+// This function borrows some code from the Pidgin project
+LmHandlerResult handle_iq_time202(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ LmMessage *r;
+ LmMessageNode *query;
+ char *buf, *utf8_buf;
+ time_t now_t;
+ struct tm *now;
+ char const *sign;
+ int diff = 0;
+
+ time(&now_t);
+
+ if (!settings_opt_get_int("iq_hide_requests")) {
+ scr_LogPrint(LPRINT_LOGNORM, "Received an IQ time request from <%s>",
+ lm_message_get_from(m));
+ }
+
+ buf = g_new0(char, 512);
+
+ r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+ query = lm_message_node_add_child(r->node, "time", NULL);
+ lm_message_node_set_attribute(query, "xmlns", NS_XMPP_TIME);
+
+ now = localtime(&now_t);
+
+ if (now->tm_isdst >= 0) {
+#if defined HAVE_TM_GMTOFF
+ diff = now->tm_gmtoff;
+#elif defined HAVE_TIMEZONE
+ tzset();
+ diff = -timezone;
+#endif
+ }
+
+ if (diff < 0) {
+ sign = "-";
+ diff = -diff;
+ } else {
+ sign = "+";
+ }
+ diff /= 60;
+ snprintf(buf, 512, "%c%02d:%02d", *sign, diff / 60, diff % 60);
+ if ((utf8_buf = to_utf8(buf))) {
+ lm_message_node_add_child(query, "tzo", utf8_buf);
+ g_free(utf8_buf);
+ }
+
+ now = gmtime(&now_t);
+
+ strftime(buf, 512, "%Y-%m-%dT%TZ", now);
+ lm_message_node_add_child(query, "utc", buf);
+
+ lm_connection_send(c, r, NULL);
+ lm_message_unref(r);
+ g_free(buf);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+LmHandlerResult handle_iq_vcard(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud)
+{
+ send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_iq.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,32 @@
+#ifndef __MCABBER_XMPP_IQ_H__
+#define __MCABBER_XMPP_IQ_H__ 1
+
+LmHandlerResult handle_iq_commands(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m, gpointer ud);
+LmHandlerResult handle_iq_disco_items(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m, gpointer ud);
+LmHandlerResult handle_iq_disco_info(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m, gpointer ud);
+LmHandlerResult handle_iq_roster(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud);
+LmHandlerResult handle_iq_ping(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud);
+LmHandlerResult handle_iq_last(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud);
+LmHandlerResult handle_iq_version(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud);
+LmHandlerResult handle_iq_time(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud);
+LmHandlerResult handle_iq_time202(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud);
+LmHandlerResult handle_iq_vcard(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer ud);
+
+void send_iq_error(LmConnection *c, LmMessage *m, guint error);
+
+#endif /* __MCABBER_XMPP_IQ_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_iqrequest.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,636 @@
+/*
+ * xmpp_iqrequest.c -- Jabber IQ request handling
+ *
+ * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+
+#include "xmpp_helper.h"
+#include "xmpp_iq.h"
+#include "screen.h"
+#include "utils.h"
+#include "settings.h"
+#include "hooks.h"
+#include "hbuf.h"
+
+extern LmMessageNode *bookmarks;
+extern LmMessageNode *rosternotes;
+
+static LmHandlerResult cb_roster(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data);
+static LmHandlerResult cb_version(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data);
+static LmHandlerResult cb_time(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data);
+static LmHandlerResult cb_last(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data);
+static LmHandlerResult cb_vcard(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data);
+
+static struct IqRequestHandlers
+{
+ const gchar *xmlns;
+ const gchar *querytag;
+ LmHandleMessageFunction handler;
+} iq_request_handlers[] = {
+ {NS_ROSTER, "query", &cb_roster},
+ {NS_VERSION,"query", &cb_version},
+ {NS_TIME, "query", &cb_time},
+ {NS_LAST, "query", &cb_last},
+ {NS_VCARD, "vCard", &cb_vcard},
+ {NULL, NULL, NULL}
+};
+
+// Enum for vCard attributes
+enum vcard_attr {
+ vcard_home = 1<<0,
+ vcard_work = 1<<1,
+ vcard_postal = 1<<2,
+ vcard_voice = 1<<3,
+ vcard_fax = 1<<4,
+ vcard_cell = 1<<5,
+ vcard_inet = 1<<6,
+ vcard_pref = 1<<7,
+};
+
+// xmlns has to be a namespace from iq_request_handlers[].xmlns
+void xmpp_iq_request(const char *fulljid, const char *xmlns)
+{
+ LmMessage *iq;
+ LmMessageNode *query;
+ LmMessageHandler *handler;
+ int i;
+
+ iq = lm_message_new_with_sub_type(fulljid, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_GET);
+ for (i = 0; strcmp(iq_request_handlers[i].xmlns, xmlns) != 0 ; ++i)
+ ;
+ query = lm_message_node_add_child(iq->node,
+ iq_request_handlers[i].querytag,
+ NULL);
+ lm_message_node_set_attribute(query, "xmlns", xmlns);
+ handler = lm_message_handler_new(iq_request_handlers[i].handler,
+ NULL, FALSE);
+ lm_connection_send_with_reply(lconnection, iq, handler, NULL);
+ lm_message_handler_unref(handler);
+ lm_message_unref(iq);
+}
+
+// This callback is reached when mcabber receives the first roster update
+// after the connection.
+static LmHandlerResult cb_roster(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data)
+{
+ LmMessageNode *x;
+ const char *ns;
+
+ // Only execute the hook if the roster has been successfully retrieved
+ if (lm_message_get_sub_type(m) != LM_MESSAGE_SUB_TYPE_RESULT)
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+
+ x = lm_message_node_find_child(m->node, "query");
+ if (!x)
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+
+ ns = lm_message_node_get_attribute(x, "xmlns");
+ if (ns && !strcmp(ns, NS_ROSTER))
+ handle_iq_roster(NULL, c, m, user_data);
+
+ // Post-login stuff
+ hook_execute_internal("hook-post-connect");
+
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult cb_version(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data)
+{
+ LmMessageNode *ansqry;
+ const char *p, *bjid;
+ char *tmp;
+ char *buf;
+
+ ansqry = lm_message_node_get_child(m->node, "query");
+ if (!ansqry) {
+ scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result!");
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+
+ // Display IQ result sender...
+ p = lm_message_get_from(m);
+ if (!p) {
+ scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result (no sender name).");
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+ bjid = p;
+
+ buf = g_strdup_printf("Received IQ:version result from <%s>", bjid);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+
+ // bjid should now really be the "bare JID", let's strip the resource
+ tmp = strchr(bjid, JID_RESOURCE_SEPARATOR);
+ if (tmp) *tmp = '\0';
+
+ scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0);
+ g_free(buf);
+
+ // Get result data...
+ p = lm_message_node_get_child_value(ansqry, "name");
+ if (p) {
+ buf = g_strdup_printf("Name: %s", p);
+ scr_WriteIncomingMessage(bjid, buf,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ g_free(buf);
+ }
+ p = lm_message_node_get_child_value(ansqry, "version");
+ if (p) {
+ buf = g_strdup_printf("Version: %s", p);
+ scr_WriteIncomingMessage(bjid, buf,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ g_free(buf);
+ }
+ p = lm_message_node_get_child_value(ansqry, "os");
+ if (p) {
+ buf = g_strdup_printf("OS: %s", p);
+ scr_WriteIncomingMessage(bjid, buf,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ g_free(buf);
+ }
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult cb_time(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data)
+{
+ LmMessageNode *ansqry;
+ const char *p, *bjid;
+ char *tmp;
+ char *buf;
+
+ ansqry = lm_message_node_get_child(m->node, "query");
+ if (!ansqry) {
+ scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:time result!");
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+ // Display IQ result sender...
+ p = lm_message_get_from(m);
+ if (!p) {
+ scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:time result (no sender name).");
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+ bjid = p;
+
+ buf = g_strdup_printf("Received IQ:time result from <%s>", bjid);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+
+ // bjid should now really be the "bare JID", let's strip the resource
+ tmp = strchr(bjid, JID_RESOURCE_SEPARATOR);
+ if (tmp) *tmp = '\0';
+
+ scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0);
+ g_free(buf);
+
+ // Get result data...
+ p = lm_message_node_get_child_value(ansqry, "utc");
+ if (p) {
+ buf = g_strdup_printf("UTC: %s", p);
+ scr_WriteIncomingMessage(bjid, buf,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ g_free(buf);
+ }
+ p = lm_message_node_get_child_value(ansqry, "tz");
+ if (p) {
+ buf = g_strdup_printf("TZ: %s", p);
+ scr_WriteIncomingMessage(bjid, buf,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ g_free(buf);
+ }
+ p = lm_message_node_get_child_value(ansqry, "display");
+ if (p) {
+ buf = g_strdup_printf("Time: %s", p);
+ scr_WriteIncomingMessage(bjid, buf,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ g_free(buf);
+ }
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult cb_last(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data)
+{
+ LmMessageNode *ansqry;
+ const char *p, *bjid;
+ char *buf, *tmp;
+
+ ansqry = lm_message_node_get_child(m->node, "query");
+ if (!ansqry) {
+ scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:last result!");
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+ // Display IQ result sender...
+ p = lm_message_get_from(m);
+ if (!p) {
+ scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:last result (no sender name).");
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+ bjid = p;
+
+ buf = g_strdup_printf("Received IQ:last result from <%s>", bjid);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+
+ // bjid should now really be the "bare JID", let's strip the resource
+ tmp = strchr(bjid, JID_RESOURCE_SEPARATOR);
+ if (tmp) *tmp = '\0';
+
+ scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0);
+ g_free(buf);
+
+ // Get result data...
+ p = lm_message_node_get_attribute(ansqry, "seconds");
+ if (p) {
+ long int s;
+ GString *sbuf;
+ sbuf = g_string_new("Idle time: ");
+ s = atol(p);
+ // Days
+ if (s > 86400L) {
+ g_string_append_printf(sbuf, "%ldd ", s/86400L);
+ s %= 86400L;
+ }
+ // hh:mm:ss
+ g_string_append_printf(sbuf, "%02ld:", s/3600L);
+ s %= 3600L;
+ g_string_append_printf(sbuf, "%02ld:%02ld", s/60L, s%60L);
+ scr_WriteIncomingMessage(bjid, sbuf->str,
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ g_string_free(sbuf, TRUE);
+ } else {
+ scr_WriteIncomingMessage(bjid, "No idle time reported.",
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ }
+ p = lm_message_node_get_value(ansqry);
+ if (p) {
+ buf = g_strdup_printf("Status message: %s", p);
+ scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0);
+ g_free(buf);
+ }
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static void display_vcard_item(const char *bjid, const char *label,
+ enum vcard_attr vcard_attrib, const char *text)
+{
+ char *buf;
+
+ if (!text || !bjid || !label)
+ return;
+
+ buf = g_strdup_printf("%s: %s%s%s%s%s%s%s%s%s%s", label,
+ (vcard_attrib & vcard_home ? "[home]" : ""),
+ (vcard_attrib & vcard_work ? "[work]" : ""),
+ (vcard_attrib & vcard_postal ? "[postal]" : ""),
+ (vcard_attrib & vcard_voice ? "[voice]" : ""),
+ (vcard_attrib & vcard_fax ? "[fax]" : ""),
+ (vcard_attrib & vcard_cell ? "[cell]" : ""),
+ (vcard_attrib & vcard_inet ? "[inet]" : ""),
+ (vcard_attrib & vcard_pref ? "[pref]" : ""),
+ (vcard_attrib ? " " : ""),
+ text);
+ scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ g_free(buf);
+}
+
+static void handle_vcard_node(const char *barejid, LmMessageNode *vcardnode)
+{
+ LmMessageNode *x;
+ const char *p;
+
+ for (x = vcardnode->children ; x; x = x->next) {
+ const char *data;
+ enum vcard_attr vcard_attrib = 0;
+
+ p = x->name;
+ data = lm_message_node_get_value(x);
+ if (!p || !data)
+ continue;
+
+ if (!strcmp(p, "FN"))
+ display_vcard_item(barejid, "Name", vcard_attrib, data);
+ else if (!strcmp(p, "NICKNAME"))
+ display_vcard_item(barejid, "Nickname", vcard_attrib, data);
+ else if (!strcmp(p, "URL"))
+ display_vcard_item(barejid, "URL", vcard_attrib, data);
+ else if (!strcmp(p, "BDAY"))
+ display_vcard_item(barejid, "Birthday", vcard_attrib, data);
+ else if (!strcmp(p, "TZ"))
+ display_vcard_item(barejid, "Timezone", vcard_attrib, data);
+ else if (!strcmp(p, "TITLE"))
+ display_vcard_item(barejid, "Title", vcard_attrib, data);
+ else if (!strcmp(p, "ROLE"))
+ display_vcard_item(barejid, "Role", vcard_attrib, data);
+ else if (!strcmp(p, "DESC"))
+ display_vcard_item(barejid, "Comment", vcard_attrib, data);
+ else if (!strcmp(p, "N")) {
+ data = lm_message_node_get_child_value(x, "FAMILY");
+ display_vcard_item(barejid, "Family Name", vcard_attrib, data);
+ data = lm_message_node_get_child_value(x, "GIVEN");
+ display_vcard_item(barejid, "Given Name", vcard_attrib, data);
+ data = lm_message_node_get_child_value(x, "MIDDLE");
+ display_vcard_item(barejid, "Middle Name", vcard_attrib, data);
+ } else if (!strcmp(p, "ORG")) {
+ data = lm_message_node_get_child_value(x, "ORGNAME");
+ display_vcard_item(barejid, "Organisation name", vcard_attrib, data);
+ data = lm_message_node_get_child_value(x, "ORGUNIT");
+ display_vcard_item(barejid, "Organisation unit", vcard_attrib, data);
+ } else {
+ // The HOME, WORK and PREF attributes are common to the remaining fields
+ // (ADR, TEL & EMAIL)
+ if (lm_message_node_get_child(x, "HOME"))
+ vcard_attrib |= vcard_home;
+ if (lm_message_node_get_child(x, "WORK"))
+ vcard_attrib |= vcard_work;
+ if (lm_message_node_get_child(x, "PREF"))
+ vcard_attrib |= vcard_pref;
+ if (!strcmp(p, "ADR")) { // Address
+ if (lm_message_node_get_child(x, "POSTAL"))
+ vcard_attrib |= vcard_postal;
+ data = lm_message_node_get_child_value(x, "EXTADD");
+ display_vcard_item(barejid, "Addr (ext)", vcard_attrib, data);
+ data = lm_message_node_get_child_value(x, "STREET");
+ display_vcard_item(barejid, "Street", vcard_attrib, data);
+ data = lm_message_node_get_child_value(x, "LOCALITY");
+ display_vcard_item(barejid, "Locality", vcard_attrib, data);
+ data = lm_message_node_get_child_value(x, "REGION");
+ display_vcard_item(barejid, "Region", vcard_attrib, data);
+ data = lm_message_node_get_child_value(x, "PCODE");
+ display_vcard_item(barejid, "Postal code", vcard_attrib, data);
+ data = lm_message_node_get_child_value(x, "CTRY");
+ display_vcard_item(barejid, "Country", vcard_attrib, data);
+ } else if (!strcmp(p, "TEL")) { // Telephone
+ data = lm_message_node_get_child_value(x, "NUMBER");
+ if (data) {
+ if (lm_message_node_get_child(x, "VOICE"))
+ vcard_attrib |= vcard_voice;
+ if (lm_message_node_get_child(x, "FAX"))
+ vcard_attrib |= vcard_fax;
+ if (lm_message_node_get_child(x, "CELL"))
+ vcard_attrib |= vcard_cell;
+ display_vcard_item(barejid, "Phone", vcard_attrib, data);
+ }
+ } else if (!strcmp(p, "EMAIL")) { // Email
+ if (lm_message_node_get_child(x, "INTERNET"))
+ vcard_attrib |= vcard_inet;
+ data = lm_message_node_get_child_value(x, "USERID");
+ display_vcard_item(barejid, "Email", vcard_attrib, data);
+ }
+ }
+ }
+}
+
+static LmHandlerResult cb_vcard(LmMessageHandler *h, LmConnection *c,
+ LmMessage *m, gpointer user_data)
+{
+ LmMessageNode *ansqry;
+ const char *p, *bjid;
+ char *buf, *tmp;
+
+ // Display IQ result sender...
+ p = lm_message_get_from(m);
+ if (!p) {
+ scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:vCard result (no sender name).");
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+ bjid = p;
+
+ buf = g_strdup_printf("Received IQ:vCard result from <%s>", bjid);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+
+ // Get the vCard node
+ ansqry = lm_message_node_get_child(m->node, "vCard");
+ if (!ansqry) {
+ scr_LogPrint(LPRINT_LOGNORM, "Empty IQ:vCard result!");
+ g_free(buf);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+
+ // bjid should really be the "bare JID", let's strip the resource
+ tmp = strchr(bjid, JID_RESOURCE_SEPARATOR);
+ if (tmp) *tmp = '\0';
+
+ scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0);
+ g_free(buf);
+
+ // Get result data...
+ handle_vcard_node(bjid, ansqry);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static void storage_bookmarks_parse_conference(LmMessageNode *node)
+{
+ const char *fjid, *name, *autojoin;
+ const char *pstatus, *awhois;
+ char *bjid;
+ GSList *room_elt;
+
+ fjid = lm_message_node_get_attribute(node, "jid");
+ if (!fjid)
+ return;
+ name = lm_message_node_get_attribute(node, "name");
+ autojoin = lm_message_node_get_attribute(node, "autojoin");
+ awhois = lm_message_node_get_attribute(node, "autowhois");
+ pstatus = lm_message_node_get_child_value(node, "print_status");
+
+ bjid = jidtodisp(fjid); // Bare jid
+
+ // Make sure this is a room (it can be a conversion user->room)
+ room_elt = roster_find(bjid, jidsearch, 0);
+ if (!room_elt) {
+ room_elt = roster_add_user(bjid, name, NULL, ROSTER_TYPE_ROOM,
+ sub_none, -1);
+ } else {
+ buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
+ /*
+ // If the name is available, should we use it?
+ // I don't think so, it would be confusing because this item is already
+ // in the roster.
+ if (name)
+ buddy_setname(room_elt->data, name);
+ */
+ }
+
+ // Set the print_status and auto_whois values
+ if (pstatus) {
+ enum room_printstatus i;
+ for (i = status_none; i <= status_all; i++)
+ if (!strcasecmp(pstatus, strprintstatus[i]))
+ break;
+ if (i <= status_all)
+ buddy_setprintstatus(room_elt->data, i);
+ }
+ if (awhois) {
+ enum room_autowhois i = autowhois_default;
+ if (!strcmp(awhois, "1"))
+ i = autowhois_on;
+ else if (!strcmp(awhois, "0"))
+ i = autowhois_off;
+ if (i != autowhois_default)
+ buddy_setautowhois(room_elt->data, i);
+ }
+
+ // Is autojoin set?
+ // If it is, we'll look up for more information (nick? password?) and
+ // try to join the room.
+ if (autojoin && !strcmp(autojoin, "1")) {
+ const char *nick, *passwd;
+ char *tmpnick = NULL;
+ nick = lm_message_node_get_child_value(node, "nick");
+ passwd = lm_message_node_get_child_value(node, "password");
+ if (!nick || !*nick)
+ nick = tmpnick = default_muc_nickname(NULL);
+ // Let's join now
+ scr_LogPrint(LPRINT_LOGNORM, "Auto-join bookmark <%s>", bjid);
+ xmpp_room_join(bjid, nick, passwd);
+ g_free(tmpnick);
+ }
+ g_free(bjid);
+}
+
+static LmHandlerResult cb_storage_bookmarks(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m, gpointer user_data)
+{
+ LmMessageNode *x, *ansqry;
+ char *p;
+
+ if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) {
+ // No server support, or no bookmarks?
+ p = m->node->children->name;
+ if (p && !strcmp(p, "item-not-found")) {
+ // item-no-found means the server has Private Storage, but it's
+ // currently empty.
+ if (bookmarks)
+ lm_message_node_unref(bookmarks);
+ bookmarks = lm_message_node_new("storage", "storage:bookmarks");
+ // We return 0 so that the IQ error message be
+ // not displayed, as it isn't a real error.
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Unhandled error
+ }
+
+ ansqry = lm_message_node_get_child(m->node, "query");
+ ansqry = lm_message_node_get_child(ansqry, "storage");
+ if (!ansqry) {
+ scr_LogPrint(LPRINT_LOG, "Invalid IQ:private result! (storage:bookmarks)");
+ return 0;
+ }
+
+ // Walk through the storage tags
+ for (x = ansqry->children ; x; x = x->next) {
+ // If the current node is a conference item, parse it and update the roster
+ if (x->name && !strcmp(x->name, "conference"))
+ storage_bookmarks_parse_conference(x);
+ }
+ // "Copy" the bookmarks node
+ if (bookmarks)
+ lm_message_node_unref(bookmarks);
+ lm_message_node_deep_ref(ansqry);
+ bookmarks = ansqry;
+ return 0;
+}
+
+
+static LmHandlerResult cb_storage_rosternotes(LmMessageHandler *h,
+ LmConnection *c,
+ LmMessage *m, gpointer user_data)
+{
+ LmMessageNode *ansqry;
+
+ if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) {
+ const char *p;
+ // No server support, or no roster notes?
+ p = m->node->children->name;
+ if (p && !strcmp(p, "item-not-found")) {
+ // item-no-found means the server has Private Storage, but it's
+ // currently empty.
+ if (rosternotes)
+ lm_message_node_unref(rosternotes);
+ rosternotes = lm_message_node_new("storage", "storage:rosternotes");
+ // We return 0 so that the IQ error message be
+ // not displayed, as it isn't a real error.
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Unhandled error
+ }
+
+ ansqry = lm_message_node_get_child(m->node, "query");
+ ansqry = lm_message_node_get_child(ansqry, "storage");
+ if (!ansqry) {
+ scr_LogPrint(LPRINT_LOG, "Invalid IQ:private result! "
+ "(storage:rosternotes)");
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+ // Copy the rosternotes node
+ if (rosternotes)
+ lm_message_node_unref(rosternotes);
+ lm_message_node_deep_ref(ansqry);
+ rosternotes = ansqry;
+ return 0;
+}
+
+
+static struct IqRequestStorageHandlers
+{
+ const gchar *storagens;
+ LmHandleMessageFunction handler;
+} iq_request_storage_handlers[] = {
+ {"storage:rosternotes", &cb_storage_rosternotes},
+ {"storage:bookmarks", &cb_storage_bookmarks},
+ {NULL, NULL}
+};
+
+void xmpp_request_storage(const gchar *storage)
+{
+ LmMessage *iq;
+ LmMessageNode *query;
+ LmMessageHandler *handler;
+ int i;
+
+ iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_GET);
+ query = lm_message_node_add_child(iq->node, "query", NULL);
+ lm_message_node_set_attribute(query, "xmlns", NS_PRIVATE);
+ lm_message_node_set_attribute(lm_message_node_add_child
+ (query, "storage", NULL),
+ "xmlns", storage);
+
+ for (i = 0;
+ strcmp(iq_request_storage_handlers[i].storagens, storage) != 0;
+ ++i) ;
+
+ handler = lm_message_handler_new(iq_request_storage_handlers[i].handler,
+ NULL, FALSE);
+ lm_connection_send_with_reply(lconnection, iq, handler, NULL);
+ lm_message_handler_unref(handler);
+ lm_message_unref(iq);
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_iqrequest.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,8 @@
+#ifndef __MCABBER_XMPP_IQREQUEST_H__
+#define __MCABBER_XMPP_IQREQUEST_H__ 1
+
+void xmpp_iq_request(const char *fulljid, const char *xmlns);
+
+#endif /* __MCABBER_XMPP_IQREQUEST_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_muc.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,715 @@
+/*
+ * xmpp_muc.c -- Jabber MUC protocol handling
+ *
+ * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+
+#include "xmpp_helper.h"
+#include "events.h"
+#include "hooks.h"
+#include "screen.h"
+#include "hbuf.h"
+#include "roster.h"
+#include "commands.h"
+#include "settings.h"
+#include "utils.h"
+#include "histolog.h"
+
+extern enum imstatus mystatus;
+extern gchar *mystatusmsg;
+
+static void decline_invitation(event_muc_invitation *invitation, char *reason)
+{
+ // cut and paste from xmpp_room_invite
+ LmMessage *m;
+ LmMessageNode *x, *y;
+
+ if (!invitation) return;
+ if (!invitation->to || !invitation->from) return;
+
+ m = lm_message_new(invitation->to, LM_MESSAGE_TYPE_MESSAGE);
+
+ x = lm_message_node_add_child(m->node, "x", NULL);
+ lm_message_node_set_attribute(x, "xmlns",
+ "http://jabber.org/protocol/muc#user");
+
+ y = lm_message_node_add_child(x, "decline", NULL);
+ lm_message_node_set_attribute(y, "to", invitation->from);
+
+ if (reason)
+ lm_message_node_add_child(y, "reason", reason);
+
+ lm_connection_send(lconnection, m, NULL);
+ lm_message_unref(m);
+}
+
+static int evscallback_invitation(eviqs *evp, guint evcontext)
+{
+ event_muc_invitation *invitation = evp->data;
+
+ // Sanity check
+ if (!invitation) {
+ // Shouldn't happen.
+ scr_LogPrint(LPRINT_LOGNORM, "Error in evs callback.");
+ return 0;
+ }
+
+ if (evcontext == EVS_CONTEXT_TIMEOUT) {
+ scr_LogPrint(LPRINT_LOGNORM, "Event %s timed out, cancelled.", evp->id);
+ goto evscallback_invitation_free;
+ }
+ if (evcontext == EVS_CONTEXT_CANCEL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Event %s cancelled.", evp->id);
+ goto evscallback_invitation_free;
+ }
+ if (!(evcontext & EVS_CONTEXT_USER))
+ goto evscallback_invitation_free;
+ // Ok, let's work now.
+ // evcontext: 0, 1 == reject, accept
+
+ if (evcontext & ~EVS_CONTEXT_USER) {
+ char *nickname = default_muc_nickname(invitation->to);
+ xmpp_room_join(invitation->to, nickname, invitation->passwd);
+ g_free(nickname);
+ } else {
+ scr_LogPrint(LPRINT_LOGNORM, "Invitation to %s refused.", invitation->to);
+ decline_invitation(invitation, NULL);
+ }
+
+evscallback_invitation_free:
+ g_free(invitation->to);
+ g_free(invitation->from);
+ g_free(invitation->passwd);
+ g_free(invitation->reason);
+ g_free(invitation);
+ evp->data = NULL;
+ return 0;
+}
+
+// Join a MUC room
+void xmpp_room_join(const char *room, const char *nickname, const char *passwd)
+{
+ LmMessage *x;
+ LmMessageNode *y;
+ gchar *roomid;
+ GSList *room_elt;
+
+ if (!lm_connection_is_authenticated(lconnection) || !room) return;
+ if (!nickname) return;
+
+ roomid = g_strdup_printf("%s/%s", room, nickname);
+ if (check_jid_syntax(roomid)) {
+ scr_LogPrint(LPRINT_NORMAL, "<%s/%s> is not a valid Jabber room", room,
+ nickname);
+ g_free(roomid);
+ return;
+ }
+
+ room_elt = roster_find(room, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_ROOM);
+ // Add room if it doesn't already exist
+ if (!room_elt) {
+ room_elt = roster_add_user(room, NULL, NULL, ROSTER_TYPE_ROOM,
+ sub_none, -1);
+ } else {
+ // Make sure this is a room (it can be a conversion user->room)
+ buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
+ }
+ // If insideroom is TRUE, this is a nickname change and we don't care here
+ if (!buddy_getinsideroom(room_elt->data)) {
+ // We're trying to enter a room
+ buddy_setnickname(room_elt->data, nickname);
+ }
+
+ // Send the XML request
+ lm_message_new(roomid, LM_MESSAGE_TYPE_PRESENCE);
+
+ x = lm_message_new_presence(mystatus, roomid, mystatusmsg);
+ y = lm_message_node_add_child(x->node, "x", NULL);
+ lm_message_node_set_attribute(y, "xmlns", "http://jabber.org/protocol/muc");
+ if (passwd)
+ lm_message_node_add_child(y, "password", passwd);
+
+ lm_connection_send(lconnection, x, NULL);
+ lm_message_unref(x);
+ g_free(roomid);
+}
+
+// Invite a user to a MUC room
+// room syntax: "room@server"
+// reason can be null.
+void xmpp_room_invite(const char *room, const char *fjid, const char *reason)
+{
+ LmMessage *msg;
+ LmMessageNode *x, *y;
+
+ if (!lm_connection_is_authenticated(lconnection) || !room || !fjid) return;
+
+ msg = lm_message_new(room, LM_MESSAGE_TYPE_MESSAGE);
+
+ x = lm_message_node_add_child(msg->node, "x", NULL);
+ lm_message_node_set_attribute(x, "xmlns",
+ "http://jabber.org/protocol/muc#user");
+
+ y = lm_message_node_add_child(x, "invite", NULL);
+ lm_message_node_set_attribute(y, "to", fjid);
+
+ if (reason)
+ lm_message_node_add_child(y, "reason", reason);
+
+ lm_connection_send(lconnection, msg, NULL);
+ lm_message_unref(msg);
+}
+
+int xmpp_room_setattrib(const char *roomid, const char *fjid,
+ const char *nick, struct role_affil ra,
+ const char *reason)
+{
+ LmMessage *iq;
+ LmMessageNode *query, *x;
+
+ if (!lm_connection_is_authenticated(lconnection) || !roomid) return 1;
+ if (!fjid && !nick) return 1;
+
+ if (check_jid_syntax((char*)roomid)) {
+ scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber id", roomid);
+ return 1;
+ }
+ if (fjid && check_jid_syntax((char*)fjid)) {
+ scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber id", fjid);
+ return 1;
+ }
+
+ if (ra.type == type_affil && ra.val.affil == affil_outcast && !fjid)
+ return 1; // Shouldn't happen (jid mandatory when banning)
+
+ iq = lm_message_new_with_sub_type(roomid, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_SET);
+ query = lm_message_node_add_child(iq->node, "query", NULL);
+ lm_message_node_set_attribute(query, "xmlns",
+ "http://jabber.org/protocol/muc#admin");
+ x = lm_message_node_add_child(query, "item", NULL);
+
+ if (fjid) {
+ lm_message_node_set_attribute(x, "jid", fjid);
+ } else { // nickname
+ lm_message_node_set_attribute(x, "nick", nick);
+ }
+
+ if (ra.type == type_affil)
+ lm_message_node_set_attribute(x, "affiliation", straffil[ra.val.affil]);
+ else if (ra.type == type_role)
+ lm_message_node_set_attribute(x, "role", strrole[ra.val.role]);
+
+ if (reason)
+ lm_message_node_add_child(x, "reason", reason);
+
+ lm_connection_send(lconnection, iq, NULL);
+ lm_message_unref(iq);
+
+ return 0;
+}
+
+// Unlock a MUC room
+// room syntax: "room@server"
+void xmpp_room_unlock(const char *room)
+{
+ LmMessageNode *node;
+ LmMessage *iq;
+
+ if (!lm_connection_is_authenticated(lconnection) || !room) return;
+
+ iq = lm_message_new_with_sub_type(room, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_SET);
+
+ node = lm_message_node_add_child(iq->node, "query", NULL);
+ lm_message_node_set_attribute(node, "xmlns",
+ "http://jabber.org/protocol/muc#owner");
+ node = lm_message_node_add_child(node, "x", NULL);
+ lm_message_node_set_attributes(node, "xmlns", "jabber:x:data",
+ "type", "submit", NULL);
+
+ lm_connection_send(lconnection, iq, NULL);
+ lm_message_unref(iq);
+}
+
+// Destroy a MUC room
+// room syntax: "room@server"
+void xmpp_room_destroy(const char *room, const char *venue, const char *reason)
+{
+ LmMessage *iq;
+ LmMessageNode *query, *x;
+
+ if (!lm_connection_is_authenticated(lconnection) || !room) return;
+
+ iq = lm_message_new_with_sub_type(room, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_SET);
+ query = lm_message_node_add_child(iq->node, "query", NULL);
+ lm_message_node_set_attribute(query, "xmlns",
+ "http://jabber.org/protocol/muc#owner");
+ x = lm_message_node_add_child(query, "destroy", NULL);
+
+ if (venue && *venue)
+ lm_message_node_set_attribute(x, "jid", venue);
+
+ if (reason)
+ lm_message_node_add_child(x, "reason", reason);
+
+ lm_connection_send(lconnection, iq, NULL);
+ lm_message_unref(iq);
+}
+
+// muc_get_item_info(...)
+// Get room member's information from xmlndata.
+// The variables must be initialized before calling this function,
+// because they are not touched if the relevant information is missing.
+static void muc_get_item_info(const char *from, LmMessageNode *xmldata,
+ enum imrole *mbrole, enum imaffiliation *mbaffil,
+ const char **mbjid, const char **mbnick,
+ const char **actorjid, const char **reason)
+{
+ LmMessageNode *y, *z;
+ const char *p;
+
+ y = lm_message_node_find_child(xmldata, "item");
+ if (!y)
+ return;
+
+ p = lm_message_node_get_attribute(y, "affiliation");
+ if (p) {
+ if (!strcmp(p, "owner")) *mbaffil = affil_owner;
+ else if (!strcmp(p, "admin")) *mbaffil = affil_admin;
+ else if (!strcmp(p, "member")) *mbaffil = affil_member;
+ else if (!strcmp(p, "outcast")) *mbaffil = affil_outcast;
+ else if (!strcmp(p, "none")) *mbaffil = affil_none;
+ else scr_LogPrint(LPRINT_LOGNORM, "<%s>: Unknown affiliation \"%s\"",
+ from, p);
+ }
+ p = lm_message_node_get_attribute(y, "role");
+ if (p) {
+ if (!strcmp(p, "moderator")) *mbrole = role_moderator;
+ else if (!strcmp(p, "participant")) *mbrole = role_participant;
+ else if (!strcmp(p, "visitor")) *mbrole = role_visitor;
+ else if (!strcmp(p, "none")) *mbrole = role_none;
+ else scr_LogPrint(LPRINT_LOGNORM, "<%s>: Unknown role \"%s\"",
+ from, p);
+ }
+ *mbjid = lm_message_node_get_attribute(y, "jid");
+ *mbnick = lm_message_node_get_attribute(y, "nick");
+ // For kick/ban, there can be actor and reason tags
+ *reason = lm_message_node_get_child_value(y, "reason");
+ z = lm_message_node_find_child(y, "actor");
+ if (z)
+ *actorjid = lm_message_node_get_attribute(z, "jid");
+}
+
+// muc_handle_join(...)
+// Handle a join event in a MUC room.
+// This function will return the new_member value TRUE if somebody else joins
+// the room (and FALSE if _we_ are joining the room).
+static bool muc_handle_join(const GSList *room_elt, const char *rname,
+ const char *roomjid, const char *ournick,
+ enum room_printstatus printstatus,
+ time_t usttime, int log_muc_conf)
+{
+ bool new_member = FALSE; // True if somebody else joins the room (not us)
+ gchar *mbuf;
+
+ if (!buddy_getinsideroom(room_elt->data)) {
+ // We weren't inside the room yet. Now we are.
+ // However, this could be a presence packet from another room member
+
+ buddy_setinsideroom(room_elt->data, TRUE);
+ // Set the message flag unless we're already in the room buffer window
+ scr_setmsgflag_if_needed(roomjid, FALSE);
+ // Add a message to the tracelog file
+ mbuf = g_strdup_printf("You have joined %s as \"%s\"", roomjid, ournick);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
+ g_free(mbuf);
+ mbuf = g_strdup_printf("You have joined as \"%s\"", ournick);
+
+ // The 1st presence message could be for another room member
+ if (strcmp(ournick, rname)) {
+ // Display current mbuf and create a new message for the member
+ // Note: the usttime timestamp is related to the other member,
+ // so we use 0 here.
+ scr_WriteIncomingMessage(roomjid, mbuf, 0,
+ HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+ if (log_muc_conf)
+ hlog_write_message(roomjid, 0, -1, mbuf);
+ g_free(mbuf);
+ if (printstatus != status_none)
+ mbuf = g_strdup_printf("%s has joined", rname);
+ else
+ mbuf = NULL;
+ new_member = TRUE;
+ }
+ } else {
+ mbuf = NULL;
+ if (strcmp(ournick, rname)) {
+ if (printstatus != status_none)
+ mbuf = g_strdup_printf("%s has joined", rname);
+ new_member = TRUE;
+ }
+ }
+
+ if (mbuf) {
+ guint msgflags = HBB_PREFIX_INFO;
+ if (!settings_opt_get_int("muc_flag_joins"))
+ msgflags |= HBB_PREFIX_NOFLAG;
+ scr_WriteIncomingMessage(roomjid, mbuf, usttime, msgflags, 0);
+ if (log_muc_conf)
+ hlog_write_message(roomjid, 0, -1, mbuf);
+ g_free(mbuf);
+ }
+
+ return new_member;
+}
+
+void handle_muc_presence(const char *from, LmMessageNode *xmldata,
+ const char *roomjid, const char *rname,
+ enum imstatus ust, const char *ustmsg,
+ time_t usttime, char bpprio)
+{
+ LmMessageNode *y;
+ const char *p;
+ char *mbuf;
+ const char *ournick;
+ enum imrole mbrole = role_none;
+ enum imaffiliation mbaffil = affil_none;
+ enum room_printstatus printstatus;
+ enum room_autowhois autowhois;
+ const char *mbjid = NULL, *mbnick = NULL;
+ const char *actorjid = NULL, *reason = NULL;
+ bool new_member = FALSE; // True if somebody else joins the room (not us)
+ guint statuscode = 0;
+ guint nickchange = 0;
+ GSList *room_elt;
+ int log_muc_conf;
+ guint msgflags;
+
+ log_muc_conf = settings_opt_get_int("log_muc_conf");
+
+ room_elt = roster_find(roomjid, jidsearch, 0);
+ if (!room_elt) {
+ // Add room if it doesn't already exist
+ // It shouldn't happen, there is probably something wrong (server or
+ // network issue?)
+ room_elt = roster_add_user(roomjid, NULL, NULL, ROSTER_TYPE_ROOM,
+ sub_none, -1);
+ scr_LogPrint(LPRINT_LOGNORM, "Strange MUC presence message");
+ } else {
+ // Make sure this is a room (it can be a conversion user->room)
+ buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
+ }
+
+ // Get room member's information
+ muc_get_item_info(from, xmldata, &mbrole, &mbaffil, &mbjid, &mbnick,
+ &actorjid, &reason);
+
+ // Get our room nickname
+ ournick = buddy_getnickname(room_elt->data);
+
+ if (!ournick) {
+ // It shouldn't happen, probably a server issue
+ mbuf = g_strdup_printf("Unexpected groupchat packet!");
+
+ scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
+ scr_WriteIncomingMessage(roomjid, mbuf, 0, HBB_PREFIX_INFO, 0);
+ g_free(mbuf);
+ // Send back an unavailable packet
+ xmpp_setstatus(offline, roomjid, "", TRUE);
+ scr_DrawRoster();
+ return;
+ }
+
+ // Get the status code
+ // 201: a room has been created
+ // 301: the user has been banned from the room
+ // 303: new room nickname
+ // 307: the user has been kicked from the room
+ // 321,322,332: the user has been removed from the room
+ y = lm_message_node_find_child(xmldata, "status");
+ if (y) {
+ p = lm_message_node_get_attribute(y, "code");
+ if (p)
+ statuscode = atoi(p);
+ }
+
+ // Get the room's "print_status" settings
+ printstatus = buddy_getprintstatus(room_elt->data);
+ if (printstatus == status_default) {
+ printstatus = (guint) settings_opt_get_int("muc_print_status");
+ if (printstatus > 3)
+ printstatus = status_default;
+ }
+
+ // A new room has been created; accept MUC default config
+ if (statuscode == 201)
+ xmpp_room_unlock(roomjid);
+
+ // Check for nickname change
+ if (statuscode == 303 && mbnick) {
+ mbuf = g_strdup_printf("%s is now known as %s", rname, mbnick);
+ scr_WriteIncomingMessage(roomjid, mbuf, usttime,
+ HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+ if (log_muc_conf)
+ hlog_write_message(roomjid, 0, -1, mbuf);
+ g_free(mbuf);
+ buddy_resource_setname(room_elt->data, rname, mbnick);
+ // Maybe it's _our_ nickname...
+ if (ournick && !strcmp(rname, ournick))
+ buddy_setnickname(room_elt->data, mbnick);
+ nickchange = TRUE;
+ }
+
+ // Check for departure/arrival
+ if (!mbnick && ust == offline) {
+ // Somebody is leaving
+ enum { leave=0, kick, ban } how = leave;
+ bool we_left = FALSE;
+
+ if (statuscode == 307)
+ how = kick;
+ else if (statuscode == 301)
+ how = ban;
+
+ // If this is a leave, check if it is ourself
+ if (ournick && !strcmp(rname, ournick)) {
+ we_left = TRUE; // _We_ have left! (kicked, banned, etc.)
+ buddy_setinsideroom(room_elt->data, FALSE);
+ buddy_setnickname(room_elt->data, NULL);
+ buddy_del_all_resources(room_elt->data);
+ buddy_settopic(room_elt->data, NULL);
+ scr_UpdateChatStatus(FALSE);
+ update_roster = TRUE;
+ }
+
+ // The message depends on _who_ left, and _how_
+ if (how) {
+ gchar *mbuf_end;
+ // Forced leave
+ if (actorjid) {
+ mbuf_end = g_strdup_printf("%s from %s by <%s>.\nReason: %s",
+ (how == ban ? "banned" : "kicked"),
+ roomjid, actorjid, reason);
+ } else {
+ mbuf_end = g_strdup_printf("%s from %s.",
+ (how == ban ? "banned" : "kicked"),
+ roomjid);
+ }
+ if (we_left)
+ mbuf = g_strdup_printf("You have been %s", mbuf_end);
+ else
+ mbuf = g_strdup_printf("%s has been %s", rname, mbuf_end);
+
+ g_free(mbuf_end);
+ } else {
+ // Natural leave
+ if (we_left) {
+ LmMessageNode *destroynode = lm_message_node_find_child(xmldata,
+ "destroy");
+ if (destroynode) {
+ if ((reason = lm_message_node_get_child_value(destroynode,
+ "reason"))) {
+ mbuf = g_strdup_printf("You have left %s, "
+ "the room has been destroyed: %s",
+ roomjid, reason);
+ } else {
+ mbuf = g_strdup_printf("You have left %s, "
+ "the room has been destroyed", roomjid);
+ }
+ } else {
+ mbuf = g_strdup_printf("You have left %s", roomjid);
+ }
+ } else {
+ if (ust != offline) {
+ // This can happen when a network failure occurs,
+ // this isn't an official leave but the user isn't there anymore.
+ mbuf = g_strdup_printf("%s has disappeared!", rname);
+ ust = offline;
+ } else {
+ if (ustmsg)
+ mbuf = g_strdup_printf("%s has left: %s", rname, ustmsg);
+ else
+ mbuf = g_strdup_printf("%s has left", rname);
+ }
+ }
+ }
+
+ // Display the mbuf message if we're concerned
+ // or if the print_status isn't set to none.
+ if (we_left || printstatus != status_none) {
+ msgflags = HBB_PREFIX_INFO;
+ if (!we_left && settings_opt_get_int("muc_flag_joins") != 2)
+ msgflags |= HBB_PREFIX_NOFLAG;
+ scr_WriteIncomingMessage(roomjid, mbuf, usttime, msgflags, 0);
+ }
+
+ if (log_muc_conf)
+ hlog_write_message(roomjid, 0, -1, mbuf);
+
+ if (we_left) {
+ scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
+ g_free(mbuf);
+ return;
+ }
+ g_free(mbuf);
+ } else if (buddy_getstatus(room_elt->data, rname) == offline &&
+ ust != offline) {
+ // Somebody is joining
+ new_member = muc_handle_join(room_elt, rname, roomjid, ournick,
+ printstatus, usttime, log_muc_conf);
+ } else {
+ // This is a simple member status change
+
+ if (printstatus == status_all && !nickchange) {
+ mbuf = g_strdup_printf("Member status has changed: %s [%c] %s", rname,
+ imstatus2char[ust], ((ustmsg) ? ustmsg : ""));
+ scr_WriteIncomingMessage(roomjid, mbuf, usttime,
+ HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+ g_free(mbuf);
+ }
+ }
+
+ // Sanity check, shouldn't happen...
+ if (!rname)
+ return;
+
+ // Update room member status
+ roster_setstatus(roomjid, rname, bpprio, ust, ustmsg, usttime,
+ mbrole, mbaffil, mbjid);
+
+ autowhois = buddy_getautowhois(room_elt->data);
+ if (autowhois == autowhois_default)
+ autowhois = (settings_opt_get_int("muc_auto_whois") ?
+ autowhois_on : autowhois_off);
+
+ if (new_member && autowhois == autowhois_on) {
+ // FIXME: This will fail for some UTF-8 nicknames.
+ gchar *joiner_nick = from_utf8(rname);
+ cmd_room_whois(room_elt->data, joiner_nick, FALSE);
+ g_free(joiner_nick);
+ }
+
+ scr_DrawRoster();
+}
+
+void roompresence(gpointer room, void *presencedata)
+{
+ const char *bjid;
+ const char *nickname;
+ char *to;
+ struct T_presence *pres = presencedata;
+
+ if (!buddy_getinsideroom(room))
+ return;
+
+ bjid = buddy_getjid(room);
+ if (!bjid) return;
+ nickname = buddy_getnickname(room);
+ if (!nickname) return;
+
+ to = g_strdup_printf("%s/%s", bjid, nickname);
+ xmpp_setstatus(pres->st, to, pres->msg, TRUE);
+ g_free(to);
+}
+
+// got_invite(from, to, reason, passwd)
+// This function should be called when receiving an invitation from user
+// "from", to enter the room "to". Optional reason and room password can
+// be provided.
+static void got_invite(const char* from, const char *to, const char* reason,
+ const char* passwd)
+{
+ eviqs *evn;
+ event_muc_invitation *invitation;
+ GString *sbuf;
+ char *barejid;
+ GSList *room_elt;
+
+ sbuf = g_string_new("");
+ if (reason) {
+ g_string_printf(sbuf,
+ "Received an invitation to <%s>, from <%s>, reason: %s",
+ to, from, reason);
+ } else {
+ g_string_printf(sbuf, "Received an invitation to <%s>, from <%s>",
+ to, from);
+ }
+
+ barejid = jidtodisp(from);
+ scr_WriteIncomingMessage(barejid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", sbuf->str);
+
+ evn = evs_new(EVS_TYPE_INVITATION, EVS_MAX_TIMEOUT);
+ if (evn) {
+ evn->callback = &evscallback_invitation;
+ invitation = g_new(event_muc_invitation, 1);
+ invitation->to = g_strdup(to);
+ invitation->from = g_strdup(from);
+ invitation->passwd = g_strdup(passwd);
+ invitation->reason = g_strdup(reason);
+ evn->data = invitation;
+ evn->desc = g_strdup_printf("<%s> invites you to %s ", from, to);
+ g_string_printf(sbuf, "Please use /event %s accept|reject", evn->id);
+ } else {
+ g_string_printf(sbuf, "Unable to create a new event!");
+ }
+ scr_WriteIncomingMessage(barejid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", sbuf->str);
+ g_string_free(sbuf, TRUE);
+ g_free(barejid);
+
+ // Make sure the MUC room barejid is a room in the roster
+ barejid = jidtodisp(to);
+ room_elt = roster_find(barejid, jidsearch, 0);
+ if (room_elt)
+ buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
+
+ g_free(barejid);
+}
+
+
+// Specific MUC message handling (for example invitation processing)
+void got_muc_message(const char *from, LmMessageNode *x)
+{
+ LmMessageNode *invite = lm_message_node_get_child(x, "invite");
+ if (invite)
+ {
+ const char *invite_from;
+ const char *reason = NULL;
+ const char *password = NULL;
+
+ invite_from = lm_message_node_get_attribute(invite, "from");
+ reason = lm_message_node_get_child_value(invite, "reason");
+ password = lm_message_node_get_child_value(invite, "password");
+ if (invite_from)
+ got_invite(invite_from, from, reason, password);
+ }
+ // TODO
+ // handle status code = 100 ( not anonymous )
+ // handle status code = 170 ( changement de config )
+ // 10.2.1 Notification of Configuration Changes
+ // declined invitation
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_muc.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,13 @@
+#ifndef __MCABBER_XMPP_MUC_H__
+#define __MCABBER_XMPP_MUC_H__ 1
+
+void roompresence(gpointer room, void *presencedata);
+void got_muc_message(const char *from, LmMessageNode *x);
+void handle_muc_presence(const char *from, LmMessageNode * xmldata,
+ const char *roomjid, const char *rname,
+ enum imstatus ust, const char *ustmsg,
+ time_t usttime, char bpprio);
+
+#endif /* __MCABBER_XMPP_MUC_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_s10n.c Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,90 @@
+/*
+ * xmpp_s10n.c -- Jabber presence subscription handling
+ *
+ * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
+ * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
+ *
+ * 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 "xmpp_helper.h"
+#include "events.h"
+#include "screen.h"
+#include "hbuf.h"
+#include "settings.h"
+
+// xmpp_send_s10n(jid, subtype)
+// Send a s10n message with the passed subtype
+void xmpp_send_s10n(const char *bjid, LmMessageSubType type)
+{
+ LmMessage *x = lm_message_new_with_sub_type(bjid,
+ LM_MESSAGE_TYPE_PRESENCE,
+ type);
+ lm_connection_send(lconnection, x, NULL);
+ lm_message_unref(x);
+}
+
+int evscallback_subscription(eviqs *evp, guint evcontext)
+{
+ char *barejid;
+ char *buf;
+
+ if (evcontext == EVS_CONTEXT_TIMEOUT) {
+ scr_LogPrint(LPRINT_LOGNORM, "Event %s timed out, cancelled.",
+ evp->id);
+ return 0;
+ }
+ if (evcontext == EVS_CONTEXT_CANCEL) {
+ scr_LogPrint(LPRINT_LOGNORM, "Event %s cancelled.", evp->id);
+ return 0;
+ }
+ if (!(evcontext & EVS_CONTEXT_USER))
+ return 0;
+
+ // Sanity check
+ if (!evp->data) {
+ // Shouldn't happen, data should be set to the barejid.
+ scr_LogPrint(LPRINT_LOGNORM, "Error in evs callback.");
+ return 0;
+ }
+
+ // Ok, let's work now.
+ // evcontext: 0, 1 == reject, accept
+
+ barejid = evp->data;
+
+ if (evcontext & ~EVS_CONTEXT_USER) {
+ // Accept subscription request
+ xmpp_send_s10n(barejid, LM_MESSAGE_SUB_TYPE_SUBSCRIBED);
+ buf = g_strdup_printf("<%s> is allowed to receive your presence updates",
+ barejid);
+ } else {
+ // Reject subscription request
+ xmpp_send_s10n(barejid, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED);
+ buf = g_strdup_printf("<%s> won't receive your presence updates", barejid);
+ if (settings_opt_get_int("delete_on_reject")) {
+ // Remove the buddy from the roster if there is no current subscription
+ if (roster_getsubscription(barejid) == sub_none)
+ xmpp_delbuddy(barejid);
+ }
+ }
+ scr_WriteIncomingMessage(barejid, buf, 0, HBB_PREFIX_INFO, 0);
+ scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
+ g_free(buf);
+ return 0;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/xmpp_s10n.h Mon Jan 18 15:36:19 2010 +0200
@@ -0,0 +1,10 @@
+#ifndef __MCABBER_XMPP_S10N_H__
+#define __MCABBER_XMPP_S10N_H__ 1
+
+#include <mcabber/events.h>
+
+int evscallback_subscription(eviqs *evp, guint evcontext);
+
+#endif /* __MCABBER_XMPP_S10N_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/COPYING Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,344 +0,0 @@
-Specific permission is granted for the GPLed code in this distribution
-to be linked to OpenSSL without invoking GPL clause 2(b).
-----------------------------------------------------------------------
-
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- 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
-
-
-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.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
--- a/mcabber/src/Makefile.am Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-bin_PROGRAMS = mcabber
-mcabber_SOURCES = main.c main.h roster.c roster.h events.c events.h \
- commands.c commands.h compl.c compl.h \
- hbuf.c hbuf.h screen.c screen.h logprint.h \
- settings.c settings.h hooks.c hooks.h utf8.c utf8.h \
- histolog.c histolog.h utils.c utils.h pgp.c pgp.h \
- xmpp.c xmpp.h xmpp_helper.c xmpp_helper.h xmpp_defines.h \
- xmpp_iq.c xmpp_iq.h xmpp_iqrequest.c xmpp_iqrequest.h \
- xmpp_muc.c xmpp_muc.h xmpp_s10n.c xmpp_s10n.h \
- caps.c caps.h fifo.c fifo.h help.c help.h
-
-if OTR
-mcabber_SOURCES += otr.c otr.h nohtml.c nohtml.h
-endif
-
-LDADD = $(GLIB_LIBS) $(LOUDMOUTH_LIBS) $(GPGME_LIBS) $(LIBOTR_LIBS) \
- $(ENCHANT_LIBS)
-
-AM_CPPFLAGS = $(GLIB_CFLAGS) $(LOUDMOUTH_CFLAGS) $(GPGME_CFLAGS) \
- $(LIBOTR_CFLAGS) $(ENCHANT_CFLAGS)
-
-CLEANFILES = hgcset.h
-
-if HGCSET
-BUILT_SOURCES = hgcset.h
-
-hgcset.h:
- ../hgcset.sh
-
-.PHONY: hgcset.h
-endif
-
-#SUBDIRS =
--- a/mcabber/src/caps.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-/*
- * caps.c -- Entity Capabilities Cache for mcabber
- *
- * Copyright (C) 2008 Frank Zschockelt <mcabber@freakysoft.de>
- *
- * 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>
-
-typedef struct {
- char *category;
- char *name;
- char *type;
- GHashTable *features;
-} caps;
-
-static GHashTable *caps_cache = NULL;
-
-void caps_destroy(gpointer data)
-{
- caps *c = data;
- g_free(c->category);
- g_free(c->name);
- g_free(c->type);
- g_hash_table_destroy(c->features);
- g_free(c);
-}
-
-void caps_init(void)
-{
- if (!caps_cache)
- caps_cache = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, caps_destroy);
-}
-
-void caps_free(void)
-{
- if (caps_cache) {
- g_hash_table_destroy(caps_cache);
- caps_cache = NULL;
- }
-}
-
-void caps_add(char *hash)
-{
- if (!hash)
- return;
- caps *c = g_new0(caps, 1);
- c->features = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- g_hash_table_insert(caps_cache, g_strdup(hash), c);
-}
-
-int caps_has_hash(const char *hash)
-{
- return (hash != NULL && (g_hash_table_lookup(caps_cache, hash) != NULL));
-}
-
-void caps_set_identity(char *hash,
- const char *category,
- const char *name,
- const char *type)
-{
- caps *c;
- if (!hash)
- return;
-
- c = g_hash_table_lookup(caps_cache, hash);
- if (c) {
- c->category = g_strdup(category);
- c->name = g_strdup(name);
- c->type = g_strdup(type);
- }
-}
-
-void caps_add_feature(char *hash, const char *feature)
-{
- caps *c;
- if (!hash)
- return;
- c = g_hash_table_lookup(caps_cache, hash);
- if (c) {
- char *f = g_strdup(feature);
- g_hash_table_replace(c->features, f, f);
- }
-}
-
-int caps_has_feature(char *hash, char *feature)
-{
- caps *c;
- if (!hash)
- return 0;
- c = g_hash_table_lookup(caps_cache, hash);
- if (c)
- return (g_hash_table_lookup(c->features, feature) != NULL);
- return 0;
-}
-
-static GFunc _foreach_function;
-
-void _caps_foreach_helper(gpointer key, gpointer value, gpointer user_data)
-{
- // GFunc func = (GFunc)user_data;
- _foreach_function(value, user_data);
-}
-
-void caps_foreach_feature(const char *hash, GFunc func, gpointer user_data)
-{
- caps *c;
- if (!hash)
- return;
- c = g_hash_table_lookup(caps_cache, hash);
- if (!c)
- return;
- _foreach_function = func;
- g_hash_table_foreach(c->features, _caps_foreach_helper, user_data);
-}
-
-gint _strcmp_sort(gconstpointer a, gconstpointer b)
-{
- return g_strcmp0(a, b);
-}
-
-//generates the sha1 hash for the special capability "" and returns it
-const char *caps_generate(void)
-{
- char *identity;
- GList *features;
- GChecksum *sha1;
- guint8 digest[20];
- gsize digest_size = 20;
- gchar *hash, *old_hash = NULL;
- caps *old_caps;
- caps *c = g_hash_table_lookup(caps_cache, "");
-
- g_hash_table_steal(caps_cache, "");
- sha1 = g_checksum_new(G_CHECKSUM_SHA1);
- identity = g_strdup_printf("%s/%s//%s<", c->category, c->type, c->name);
- g_checksum_update(sha1, (guchar*)identity, -1);
- g_free(identity);
-
- features = g_hash_table_get_values(c->features);
- features = g_list_sort(features, _strcmp_sort);
- {
- GList *feature;
- for (feature=features; feature; feature=feature->next) {
- g_checksum_update(sha1, feature->data, -1);
- g_checksum_update(sha1, (guchar *)"<", -1);
- }
- }
- g_list_free(features);
-
- g_checksum_get_digest(sha1, digest, &digest_size);
- hash = g_base64_encode(digest, digest_size);
- g_checksum_free(sha1);
- g_hash_table_lookup_extended(caps_cache, hash,
- (gpointer *)&old_hash, (gpointer *)&old_caps);
- g_hash_table_insert(caps_cache, hash, c);
- if (old_hash)
- return old_hash;
- else
- return hash;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/caps.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-#ifndef __CAPS_H__
-#define __CAPS_H__ 1
-
-#include <glib.h>
-
-void caps_init(void);
-void caps_free(void);
-void caps_add(char *hash);
-int caps_has_hash(const char *hash);
-void caps_set_identity(char *hash,
- const char *category,
- const char *name,
- const char *type);
-void caps_add_feature(char *hash, const char *feature);
-int caps_has_feature(char *hash, char *feature);
-void caps_foreach_feature(const char *hash, GFunc func, gpointer user_data);
-
-char *caps_generate(void);
-
-#endif /* __CAPS_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/commands.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,3828 +0,0 @@
-/*
- * commands.c -- user commands handling
- *
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 <string.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "commands.h"
-#include "help.h"
-#include "roster.h"
-#include "screen.h"
-#include "compl.h"
-#include "hooks.h"
-#include "hbuf.h"
-#include "utils.h"
-#include "settings.h"
-#include "events.h"
-#include "otr.h"
-#include "utf8.h"
-#include "xmpp.h"
-#include "main.h"
-
-#define IMSTATUS_AWAY "away"
-#define IMSTATUS_ONLINE "online"
-#define IMSTATUS_OFFLINE "offline"
-#define IMSTATUS_FREE4CHAT "free"
-#define IMSTATUS_INVISIBLE "invisible"
-#define IMSTATUS_AVAILABLE "avail"
-#define IMSTATUS_NOTAVAILABLE "notavail"
-#define IMSTATUS_DONOTDISTURB "dnd"
-
-// Return value container for the following functions
-static int retval_for_cmds;
-
-// Commands callbacks
-static void do_roster(char *arg);
-static void do_status(char *arg);
-static void do_status_to(char *arg);
-static void do_add(char *arg);
-static void do_del(char *arg);
-static void do_group(char *arg);
-static void do_say(char *arg);
-static void do_msay(char *arg);
-static void do_say_to(char *arg);
-static void do_buffer(char *arg);
-static void do_clear(char *arg);
-static void do_info(char *arg);
-static void do_rename(char *arg);
-static void do_move(char *arg);
-static void do_set(char *arg);
-static void do_alias(char *arg);
-static void do_bind(char *arg);
-static void do_connect(char *arg);
-static void do_disconnect(char *arg);
-static void do_rawxml(char *arg);
-static void do_room(char *arg);
-static void do_authorization(char *arg);
-static void do_version(char *arg);
-static void do_request(char *arg);
-static void do_event(char *arg);
-static void do_help(char *arg);
-static void do_pgp(char *arg);
-static void do_iline(char *arg);
-static void do_screen_refresh(char *arg);
-static void do_chat_disable(char *arg);
-static void do_source(char *arg);
-static void do_color(char *arg);
-static void do_otr(char *arg);
-static void do_otrpolicy(char *arg);
-static void do_echo(char *arg);
-
-static void do_say_internal(char *arg, int parse_flags);
-
-// Global variable for the commands list
-static GSList *Commands;
-
-#ifdef MODULES_ENABLE
-#include <gmodule.h>
-
-static void do_load(char *arg);
-static void do_unload(char *arg);
-
-typedef struct {
- char *name;
- GModule *module;
-} loaded_module_t;
-
-GSList *loaded_modules = NULL;
-
-gpointer cmd_del(const char *name)
-{
- GSList *sl_cmd;
- for (sl_cmd = Commands; sl_cmd; sl_cmd = sl_cmd->next) {
- cmd *command = (cmd *) sl_cmd->data;
- if (!strcmp (command->name, name)) {
- gpointer userdata = command->userdata;
- Commands = g_slist_delete_link(Commands, sl_cmd);
- compl_del_category_word(COMPL_CMD, command->name);
- g_free(command);
- return userdata;
- }
- }
- return NULL;
-}
-
-// cmd_add()
-// Adds a command to the commands list and to the CMD completion list
-void cmd_add(const char *name, const char *help, guint flags_row1,
- guint flags_row2, void (*f)(char*), gpointer userdata)
-#define cmd_add(A, B, C, D, E) cmd_add (A, B, C, D, E, NULL);
-#else
-static void cmd_add(const char *name, const char *help,
- guint flags_row1, guint flags_row2, void (*f)(char*))
-#endif
-{
- cmd *n_cmd = g_new0(cmd, 1);
- strncpy(n_cmd->name, name, 32-1);
- n_cmd->help = help;
- n_cmd->completion_flags[0] = flags_row1;
- n_cmd->completion_flags[1] = flags_row2;
- n_cmd->func = f;
-#ifdef MODULES_ENABLE
- n_cmd->userdata = userdata;
-#endif
- Commands = g_slist_prepend(Commands, n_cmd);
- // Add to completion CMD category
- compl_add_category_word(COMPL_CMD, name);
-}
-
-// cmd_init()
-// Commands table initialization
-// !!!
-// After changing commands names and it arguments names here, you must change
-// ones in init_bindings()!
-//
-void cmd_init(void)
-{
- cmd_add("add", "Add a jabber user", COMPL_JID, 0, &do_add);
- cmd_add("alias", "Add an alias", 0, 0, &do_alias);
- cmd_add("authorization", "Manage subscription authorizations",
- COMPL_AUTH, COMPL_JID, &do_authorization);
- cmd_add("bind", "Add an key binding", 0, 0, &do_bind);
- cmd_add("buffer", "Manipulate current buddy's buffer (chat window)",
- COMPL_BUFFER, 0, &do_buffer);
- cmd_add("chat_disable", "Disable chat mode", 0, 0, &do_chat_disable);
- cmd_add("clear", "Clear the dialog window", 0, 0, &do_clear);
- cmd_add("color", "Set coloring options", COMPL_COLOR, 0, &do_color);
- cmd_add("connect", "Connect to the server", 0, 0, &do_connect);
- cmd_add("del", "Delete the current buddy", 0, 0, &do_del);
- cmd_add("disconnect", "Disconnect from server", 0, 0, &do_disconnect);
- cmd_add("echo", "Display a string in the log window", 0, 0, &do_echo);
- cmd_add("event", "Process an event", COMPL_EVENTSID, COMPL_EVENTS, &do_event);
- cmd_add("group", "Change group display settings",
- COMPL_GROUP, COMPL_GROUPNAME, &do_group);
- cmd_add("help", "Display some help", COMPL_CMD, 0, &do_help);
- cmd_add("iline", "Manipulate input buffer", 0, 0, &do_iline);
- cmd_add("info", "Show basic info on current buddy", 0, 0, &do_info);
- cmd_add("move", "Move the current buddy to another group", COMPL_GROUPNAME,
- 0, &do_move);
- cmd_add("msay", "Send a multi-lines message to the selected buddy",
- COMPL_MULTILINE, 0, &do_msay);
- cmd_add("otr", "Manage OTR settings", COMPL_OTR, COMPL_JID, &do_otr);
- cmd_add("otrpolicy", "Manage OTR policies", COMPL_JID, COMPL_OTRPOLICY,
- &do_otrpolicy);
- cmd_add("pgp", "Manage PGP settings", COMPL_PGP, COMPL_JID, &do_pgp);
- cmd_add("quit", "Exit the software", 0, 0, NULL);
- cmd_add("rawxml", "Send a raw XML string", 0, 0, &do_rawxml);
- cmd_add("rename", "Rename the current buddy", 0, 0, &do_rename);
- cmd_add("request", "Send a Jabber IQ request", COMPL_REQUEST, COMPL_JID,
- &do_request);
- cmd_add("room", "MUC actions command", COMPL_ROOM, 0, &do_room);
- cmd_add("roster", "Manipulate the roster/buddylist", COMPL_ROSTER, 0,
- &do_roster);
- cmd_add("say", "Say something to the selected buddy", 0, 0, &do_say);
- cmd_add("say_to", "Say something to a specific buddy", COMPL_JID, 0,
- &do_say_to);
- cmd_add("screen_refresh", "Redraw mcabber screen", 0, 0, &do_screen_refresh);
- //cmd_add("search");
- cmd_add("set", "Set/query an option value", 0, 0, &do_set);
- cmd_add("source", "Read a configuration file", 0, 0, &do_source);
- cmd_add("status", "Show or set your status", COMPL_STATUS, 0, &do_status);
- cmd_add("status_to", "Show or set your status for one recipient",
- COMPL_JID, COMPL_STATUS, &do_status_to);
- cmd_add("version", "Show mcabber version", 0, 0, &do_version);
-#ifdef MODULES_ENABLE
- cmd_add("load", "Load module", 0, 0, &do_load);
- cmd_add("unload", "Unload module", 0, 0, &do_unload);
-#endif
-
- // Status category
- compl_add_category_word(COMPL_STATUS, "online");
- compl_add_category_word(COMPL_STATUS, "avail");
- compl_add_category_word(COMPL_STATUS, "invisible");
- compl_add_category_word(COMPL_STATUS, "free");
- compl_add_category_word(COMPL_STATUS, "dnd");
- compl_add_category_word(COMPL_STATUS, "notavail");
- compl_add_category_word(COMPL_STATUS, "away");
- compl_add_category_word(COMPL_STATUS, "offline");
- compl_add_category_word(COMPL_STATUS, "message");
-
- // Roster category
- compl_add_category_word(COMPL_ROSTER, "bottom");
- compl_add_category_word(COMPL_ROSTER, "top");
- compl_add_category_word(COMPL_ROSTER, "up");
- compl_add_category_word(COMPL_ROSTER, "down");
- compl_add_category_word(COMPL_ROSTER, "group_prev");
- compl_add_category_word(COMPL_ROSTER, "group_next");
- compl_add_category_word(COMPL_ROSTER, "hide");
- compl_add_category_word(COMPL_ROSTER, "show");
- compl_add_category_word(COMPL_ROSTER, "toggle");
- compl_add_category_word(COMPL_ROSTER, "display");
- compl_add_category_word(COMPL_ROSTER, "hide_offline");
- compl_add_category_word(COMPL_ROSTER, "show_offline");
- compl_add_category_word(COMPL_ROSTER, "toggle_offline");
- compl_add_category_word(COMPL_ROSTER, "item_lock");
- compl_add_category_word(COMPL_ROSTER, "item_unlock");
- compl_add_category_word(COMPL_ROSTER, "item_toggle_lock");
- compl_add_category_word(COMPL_ROSTER, "alternate");
- compl_add_category_word(COMPL_ROSTER, "search");
- compl_add_category_word(COMPL_ROSTER, "unread_first");
- compl_add_category_word(COMPL_ROSTER, "unread_next");
- compl_add_category_word(COMPL_ROSTER, "note");
-
- // Buffer category
- compl_add_category_word(COMPL_BUFFER, "clear");
- compl_add_category_word(COMPL_BUFFER, "bottom");
- compl_add_category_word(COMPL_BUFFER, "top");
- compl_add_category_word(COMPL_BUFFER, "up");
- compl_add_category_word(COMPL_BUFFER, "down");
- compl_add_category_word(COMPL_BUFFER, "search_backward");
- compl_add_category_word(COMPL_BUFFER, "search_forward");
- compl_add_category_word(COMPL_BUFFER, "date");
- compl_add_category_word(COMPL_BUFFER, "%");
- compl_add_category_word(COMPL_BUFFER, "purge");
- compl_add_category_word(COMPL_BUFFER, "close");
- compl_add_category_word(COMPL_BUFFER, "close_all");
- compl_add_category_word(COMPL_BUFFER, "scroll_lock");
- compl_add_category_word(COMPL_BUFFER, "scroll_unlock");
- compl_add_category_word(COMPL_BUFFER, "scroll_toggle");
- compl_add_category_word(COMPL_BUFFER, "list");
- compl_add_category_word(COMPL_BUFFER, "save");
-
- // Group category
- compl_add_category_word(COMPL_GROUP, "fold");
- compl_add_category_word(COMPL_GROUP, "unfold");
- compl_add_category_word(COMPL_GROUP, "toggle");
-
- // Multi-line (msay) category
- compl_add_category_word(COMPL_MULTILINE, "abort");
- compl_add_category_word(COMPL_MULTILINE, "begin");
- compl_add_category_word(COMPL_MULTILINE, "send");
- compl_add_category_word(COMPL_MULTILINE, "send_to");
- compl_add_category_word(COMPL_MULTILINE, "toggle");
- compl_add_category_word(COMPL_MULTILINE, "toggle_verbatim");
- compl_add_category_word(COMPL_MULTILINE, "verbatim");
-
- // Room category
- compl_add_category_word(COMPL_ROOM, "affil");
- compl_add_category_word(COMPL_ROOM, "ban");
- compl_add_category_word(COMPL_ROOM, "bookmark");
- compl_add_category_word(COMPL_ROOM, "destroy");
- compl_add_category_word(COMPL_ROOM, "invite");
- compl_add_category_word(COMPL_ROOM, "join");
- compl_add_category_word(COMPL_ROOM, "kick");
- compl_add_category_word(COMPL_ROOM, "leave");
- compl_add_category_word(COMPL_ROOM, "names");
- compl_add_category_word(COMPL_ROOM, "nick");
- compl_add_category_word(COMPL_ROOM, "privmsg");
- compl_add_category_word(COMPL_ROOM, "remove");
- compl_add_category_word(COMPL_ROOM, "role");
- compl_add_category_word(COMPL_ROOM, "setopt");
- compl_add_category_word(COMPL_ROOM, "topic");
- compl_add_category_word(COMPL_ROOM, "unban");
- compl_add_category_word(COMPL_ROOM, "unlock");
- compl_add_category_word(COMPL_ROOM, "whois");
-
- // Authorization category
- compl_add_category_word(COMPL_AUTH, "allow");
- compl_add_category_word(COMPL_AUTH, "cancel");
- compl_add_category_word(COMPL_AUTH, "request");
- compl_add_category_word(COMPL_AUTH, "request_unsubscribe");
-
- // Request (query) category
- compl_add_category_word(COMPL_REQUEST, "last");
- compl_add_category_word(COMPL_REQUEST, "time");
- compl_add_category_word(COMPL_REQUEST, "vcard");
- compl_add_category_word(COMPL_REQUEST, "version");
-
- // Events category
- compl_add_category_word(COMPL_EVENTS, "accept");
- compl_add_category_word(COMPL_EVENTS, "ignore");
- compl_add_category_word(COMPL_EVENTS, "reject");
-
- // PGP category
- compl_add_category_word(COMPL_PGP, "disable");
- compl_add_category_word(COMPL_PGP, "enable");
- compl_add_category_word(COMPL_PGP, "force");
- compl_add_category_word(COMPL_PGP, "info");
- compl_add_category_word(COMPL_PGP, "setkey");
-
- // OTR category
- compl_add_category_word(COMPL_OTR, "start");
- compl_add_category_word(COMPL_OTR, "stop");
- compl_add_category_word(COMPL_OTR, "fingerprint");
- compl_add_category_word(COMPL_OTR, "smpq");
- compl_add_category_word(COMPL_OTR, "smpr");
- compl_add_category_word(COMPL_OTR, "smpa");
- compl_add_category_word(COMPL_OTR, "info");
- compl_add_category_word(COMPL_OTR, "key");
-
- // OTR Policy category
- compl_add_category_word(COMPL_OTRPOLICY, "plain");
- compl_add_category_word(COMPL_OTRPOLICY, "manual");
- compl_add_category_word(COMPL_OTRPOLICY, "opportunistic");
- compl_add_category_word(COMPL_OTRPOLICY, "always");
-
- // Color category
- compl_add_category_word(COMPL_COLOR, "roster");
- compl_add_category_word(COMPL_COLOR, "muc");
- compl_add_category_word(COMPL_COLOR, "mucnick");
-}
-
-#ifdef MODULES_ENABLE
-void cmd_deinit ()
-{
- GSList *el = loaded_modules;
- while (el) {
- loaded_module_t *module = el->data;
- if (!g_module_close ((GModule *) module->module))
- scr_LogPrint (LPRINT_LOGNORM, "* Module unloading failed: %s",
- g_module_error ());
- g_free (module->name);
- g_free (module);
- el = g_slist_next (el);
- }
- g_slist_free (loaded_modules);
-}
-#endif
-
-// expandalias(line)
-// If there is one, expand the alias in line and returns a new allocated line
-// If no alias is found, returns line
-// Note: if the returned pointer is different from line, the caller should
-// g_free() the pointer after use
-char *expandalias(const char *line)
-{
- const char *p1, *p2;
- char *word;
- const gchar *value;
- char *newline = (char*)line;
-
- // Ignore leading COMMAND_CHAR
- for (p1 = line ; *p1 == COMMAND_CHAR ; p1++)
- ;
- // Locate the end of the word
- for (p2 = p1 ; *p2 && (*p2 != ' ') ; p2++)
- ;
- // Extract the word and look for an alias in the list
- word = g_strndup(p1, p2-p1);
- value = settings_get(SETTINGS_TYPE_ALIAS, (const char*)word);
- g_free(word);
-
- if (value)
- newline = g_strdup_printf("%c%s%s", COMMAND_CHAR, value, p2);
-
- return newline;
-}
-
-// cmd_get
-// Finds command in the command list structure.
-// Returns a pointer to the cmd entry, or NULL if command not found.
-cmd *cmd_get(const char *command)
-{
- const char *p1, *p2;
- char *com;
- GSList *sl_com;
-
- // Ignore leading COMMAND_CHAR
- for (p1 = command ; *p1 == COMMAND_CHAR ; p1++)
- ;
- // Locate the end of the command
- for (p2 = p1 ; *p2 && (*p2 != ' ') ; p2++)
- ;
- // Copy the clean command
- com = g_strndup(p1, p2-p1);
-
- // Look for command in the list
- for (sl_com=Commands; sl_com; sl_com = g_slist_next(sl_com)) {
- if (!strcasecmp(com, ((cmd*)sl_com->data)->name))
- break;
- }
- g_free(com);
-
- if (sl_com) // Command has been found.
- return (cmd*)sl_com->data;
- return NULL;
-}
-
-// process_command(line, iscmd)
-// Process a command line.
-// If iscmd is TRUE, process the command even if verbatim mmode is set;
-// it is intended to be used for key bindings.
-// Return 255 if this is the /quit command, and 0 for the other commands.
-int process_command(const char *line, guint iscmd)
-{
- char *p;
- char *xpline;
- cmd *curcmd;
-
- if (!line)
- return 0;
-
- // We do alias expansion here
- if (iscmd || scr_get_multimode() != 2)
- xpline = expandalias(line);
- else
- xpline = (char*)line; // No expansion in verbatim multi-line mode
-
- // We want to use a copy
- if (xpline == line)
- xpline = g_strdup(line);
-
- // Remove trailing spaces:
- for (p=xpline ; *p ; p++)
- ;
- for (p-- ; p>xpline && (*p == ' ') ; p--)
- *p = 0;
-
- // Command "quit"?
- if ((iscmd || scr_get_multimode() != 2)
- && (!strncasecmp(xpline, mkcmdstr("quit"), strlen(mkcmdstr("quit"))))) {
- if (!xpline[5] || xpline[5] == ' ') {
- g_free(xpline);
- return 255;
- }
- } else if (iscmd && !strncasecmp(xpline, "quit", 4) &&
- (!xpline[4] || xpline[4] == ' ')) {
- // If iscmd is true we can have the command without the command prefix
- // character (usually '/').
- g_free(xpline);
- return 255;
- }
-
- // If verbatim multi-line mode, we check if another /msay command is typed
- if (!iscmd && scr_get_multimode() == 2
- && (strncasecmp(xpline, mkcmdstr("msay "), strlen(mkcmdstr("msay "))))) {
- // It isn't an /msay command
- scr_append_multiline(xpline);
- g_free(xpline);
- return 0;
- }
-
- // Commands handling
- curcmd = cmd_get(xpline);
-
- if (!curcmd) {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized command. "
- "Please see the manual for a list of known commands.");
- g_free(xpline);
- return 0;
- }
- if (!curcmd->func) {
- scr_LogPrint(LPRINT_NORMAL,
- "This functionality is not yet implemented, sorry.");
- g_free(xpline);
- return 0;
- }
- // Lets go to the command parameters
- for (p = xpline+1; *p && (*p != ' ') ; p++)
- ;
- // Skip spaces
- while (*p && (*p == ' '))
- p++;
- // Call command-specific function
- retval_for_cmds = 0;
-#ifdef MODULES_ENABLE
- if (curcmd->userdata)
- (*(void (*)(char *p, gpointer u))curcmd->func)(p, curcmd->userdata);
- else
- (*curcmd->func)(p);
-#else
- (*curcmd->func)(p);
-#endif
- g_free(xpline);
- return retval_for_cmds;
-}
-
-// process_line(line)
-// Process a command/message line.
-// If this isn't a command, this is a message and it is sent to the
-// currently selected buddy.
-// Return 255 if the line is the /quit command, or 0.
-int process_line(const char *line)
-{
- if (!*line) { // User only pressed enter
- if (scr_get_multimode()) {
- scr_append_multiline("");
- return 0;
- }
- if (current_buddy) {
- if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP)
- do_group("toggle");
- else {
- // Enter chat mode
- scr_set_chatmode(TRUE);
- scr_ShowBuddyWindow();
- }
- }
- return 0;
- }
-
- if (*line != COMMAND_CHAR) {
- // This isn't a command
- if (scr_get_multimode())
- scr_append_multiline(line);
- else
- do_say_internal((char*)line, 0);
- return 0;
- }
-
- /* It is _probably_ a command -- except for verbatim multi-line mode */
- return process_command(line, FALSE);
-}
-
-// Helper routine for buffer item_{lock,unlock,toggle_lock}
-// "lock" values: 1=lock 0=unlock -1=invert
-static void roster_buddylock(char *bjid, int lock)
-{
- gpointer bud = NULL;
- bool may_need_refresh = FALSE;
-
- // Allow special jid "" or "." (current buddy)
- if (bjid && (!*bjid || !strcmp(bjid, ".")))
- bjid = NULL;
-
- if (bjid) {
- // The JID has been specified. Quick check...
- if (check_jid_syntax(bjid)) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "<%s> is not a valid Jabber ID.", bjid);
- } else {
- // Find the buddy
- GSList *roster_elt;
- roster_elt = roster_find(bjid, jidsearch,
- ROSTER_TYPE_USER|ROSTER_TYPE_ROOM);
- if (roster_elt)
- bud = roster_elt->data;
- else
- scr_LogPrint(LPRINT_NORMAL, "This jid isn't in the roster.");
- may_need_refresh = TRUE;
- }
- } else {
- // Use the current buddy
- if (current_buddy)
- bud = BUDDATA(current_buddy);
- }
-
- // Update the ROSTER_FLAG_USRLOCK flag
- if (bud) {
- if (lock == -1)
- lock = !(buddy_getflags(bud) & ROSTER_FLAG_USRLOCK);
- buddy_setflags(bud, ROSTER_FLAG_USRLOCK, lock);
- if (may_need_refresh) {
- buddylist_build();
- update_roster = TRUE;
- }
- }
-}
-
-// display_and_free_note(note, winId)
-// Display the note information in the winId buffer, and free note
-// (winId is a bare jid or NULL for the status window, in which case we
-// display the note jid too)
-static void display_and_free_note(struct annotation *note, const char *winId)
-{
- gchar tbuf[128];
- GString *sbuf;
- guint msg_flag = HBB_PREFIX_INFO;
- /* We use the flag prefix_info for the first line, and prefix_cont
- for the other lines, for better readability */
-
- if (!note)
- return;
-
- sbuf = g_string_new("");
-
- if (!winId) {
- // We're writing to the status window, so let's show the jid too.
- g_string_printf(sbuf, "Annotation on <%s>", note->jid);
- scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
- msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT;
- }
-
- // If we have the creation date, display it
- if (note->cdate) {
- strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S",
- localtime(¬e->cdate));
- g_string_printf(sbuf, "Note created %s", tbuf);
- scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
- msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT;
- }
- // If we have the modification date, display it
- // unless it's the same as the creation date
- if (note->mdate && note->mdate != note->cdate) {
- strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S",
- localtime(¬e->mdate));
- g_string_printf(sbuf, "Note modified %s", tbuf);
- scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
- msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT;
- }
- // Note text
- g_string_printf(sbuf, "Note: %s", note->text);
- scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
-
- g_string_free(sbuf, TRUE);
- g_free(note->text);
- g_free(note->jid);
- g_free(note);
-}
-
-static void display_all_annotations(void)
-{
- GSList *notes;
- notes = xmpp_get_all_storage_rosternotes();
-
- if (!notes)
- return;
-
- // Call display_and_free_note() for each note,
- // with winId = NULL (special window)
- g_slist_foreach(notes, (GFunc)&display_and_free_note, NULL);
- scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
- update_roster = TRUE;
- g_slist_free(notes);
-}
-
-static void roster_note(char *arg)
-{
- const char *bjid;
- guint type;
-
- if (!current_buddy)
- return;
-
- bjid = buddy_getjid(BUDDATA(current_buddy));
- type = buddy_gettype(BUDDATA(current_buddy));
-
- if (!bjid && type == ROSTER_TYPE_SPECIAL && !arg) {
- // We're in the status window (the only special buffer currently)
- // Let's display all server notes
- display_all_annotations();
- return;
- }
-
- if (!bjid || (type != ROSTER_TYPE_USER &&
- type != ROSTER_TYPE_ROOM &&
- type != ROSTER_TYPE_AGENT)) {
- scr_LogPrint(LPRINT_NORMAL, "This item can't have a note.");
- return;
- }
-
- if (arg && *arg) { // Set a note
- gchar *msg, *notetxt;
- msg = to_utf8(arg);
- if (!strcmp(msg, "-"))
- notetxt = NULL; // delete note
- else
- notetxt = msg;
- xmpp_set_storage_rosternotes(bjid, notetxt);
- g_free(msg);
- } else { // Display a note
- struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
- if (note) {
- display_and_free_note(note, bjid);
- } else {
- scr_WriteIncomingMessage(bjid, "This item doesn't have a note.", 0,
- HBB_PREFIX_INFO, 0);
- }
- }
-}
-
-// roster_updown(updown, nitems)
-// updown: -1=up, +1=down
-inline static void roster_updown(int updown, char *nitems)
-{
- int nbitems;
-
- if (!nitems || !*nitems)
- nbitems = 1;
- else
- nbitems = strtol(nitems, NULL, 10);
-
- if (nbitems > 0)
- scr_RosterUpDown(updown, nbitems);
-}
-
-/* Commands callback functions */
-/* All these do_*() functions will be called with a "arg" parameter */
-/* (with arg not null) */
-
-static void do_roster(char *arg)
-{
- char **paramlst;
- char *subcmd;
-
- paramlst = split_arg(arg, 2, 1); // subcmd, arg
- subcmd = *paramlst;
- arg = *(paramlst+1);
-
- if (!subcmd || !*subcmd) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- free_arg_lst(paramlst);
- return;
- }
-
- if (!strcasecmp(subcmd, "top")) {
- scr_RosterTop();
- update_roster = TRUE;
- } else if (!strcasecmp(subcmd, "bottom")) {
- scr_RosterBottom();
- update_roster = TRUE;
- } else if (!strcasecmp(subcmd, "hide")) {
- scr_RosterVisibility(0);
- } else if (!strcasecmp(subcmd, "show")) {
- scr_RosterVisibility(1);
- } else if (!strcasecmp(subcmd, "toggle")) {
- scr_RosterVisibility(-1);
- } else if (!strcasecmp(subcmd, "hide_offline")) {
- buddylist_set_hide_offline_buddies(TRUE);
- if (current_buddy)
- buddylist_build();
- update_roster = TRUE;
- } else if (!strcasecmp(subcmd, "show_offline")) {
- buddylist_set_hide_offline_buddies(FALSE);
- buddylist_build();
- update_roster = TRUE;
- } else if (!strcasecmp(subcmd, "toggle_offline")) {
- buddylist_set_hide_offline_buddies(-1);
- buddylist_build();
- update_roster = TRUE;
- } else if (!strcasecmp(subcmd, "display")) {
- scr_RosterDisplay(arg);
- } else if (!strcasecmp(subcmd, "item_lock")) {
- roster_buddylock(arg, 1);
- } else if (!strcasecmp(subcmd, "item_unlock")) {
- roster_buddylock(arg, 0);
- } else if (!strcasecmp(subcmd, "item_toggle_lock")) {
- roster_buddylock(arg, -1);
- } else if (!strcasecmp(subcmd, "unread_first")) {
- scr_RosterUnreadMessage(0);
- } else if (!strcasecmp(subcmd, "unread_next")) {
- scr_RosterUnreadMessage(1);
- } else if (!strcasecmp(subcmd, "alternate")) {
- scr_RosterJumpAlternate();
- } else if (!strncasecmp(subcmd, "search", 6)) {
- strip_arg_special_chars(arg);
- if (!arg || !*arg) {
- scr_LogPrint(LPRINT_NORMAL, "What name or JID are you looking for?");
- free_arg_lst(paramlst);
- return;
- }
- scr_RosterSearch(arg);
- update_roster = TRUE;
- } else if (!strcasecmp(subcmd, "up")) {
- roster_updown(-1, arg);
- } else if (!strcasecmp(subcmd, "down")) {
- roster_updown(1, arg);
- } else if (!strcasecmp(subcmd, "group_prev")) {
- scr_RosterPrevGroup();
- } else if (!strcasecmp(subcmd, "group_next")) {
- scr_RosterNextGroup();
- } else if (!strcasecmp(subcmd, "note")) {
- roster_note(arg);
- } else
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
- free_arg_lst(paramlst);
-}
-
-void do_color(char *arg)
-{
- char **paramlst;
- char *subcmd;
-
- paramlst = split_arg(arg, 2, 1); // subcmd, arg
- subcmd = *paramlst;
- arg = *(paramlst+1);
-
- if (!subcmd || !*subcmd) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- free_arg_lst(paramlst);
- return;
- }
-
- if (!strcasecmp(subcmd, "roster")) {
- char *status, *wildcard, *color;
- char **arglist = split_arg(arg, 3, 0);
-
- status = *arglist;
- wildcard = to_utf8(arglist[1]);
- color = arglist[2];
-
- if (status && !strcmp(status, "clear")) { // Not a color command, clear all
- scr_RosterClearColor();
- update_roster = TRUE;
- } else {
- if (!status || !*status || !wildcard || !*wildcard || !color || !*color) {
- scr_LogPrint(LPRINT_NORMAL, "Missing argument");
- } else {
- update_roster = scr_RosterColor(status, wildcard, color)
- || update_roster;
- }
- }
- free_arg_lst(arglist);
- g_free(wildcard);
- } else if (!strcasecmp(subcmd, "muc")) {
- char **arglist = split_arg(arg, 2, 0);
- char *free_muc = to_utf8(*arglist);
- const char *muc = free_muc, *mode = arglist[1];
- if (!muc || !*muc)
- scr_LogPrint(LPRINT_NORMAL, "What MUC?");
- else {
- if (!strcmp(muc, "."))
- if (!(muc = CURRENT_JID))
- scr_LogPrint(LPRINT_NORMAL, "No JID selected");
- if (muc) {
- if (check_jid_syntax(muc) && strcmp(muc, "*"))
- scr_LogPrint(LPRINT_NORMAL, "Not a JID");
- else {
- if (!mode || !*mode || !strcasecmp(mode, "on"))
- scr_MucColor(muc, MC_ALL);
- else if (!strcasecmp(mode, "preset"))
- scr_MucColor(muc, MC_PRESET);
- else if (!strcasecmp(mode, "off"))
- scr_MucColor(muc, MC_OFF);
- else if (!strcmp(mode, "-"))
- scr_MucColor(muc, MC_REMOVE);
- else
- scr_LogPrint(LPRINT_NORMAL, "Unknown coloring mode");
- }
- }
- }
- free_arg_lst(arglist);
- g_free(free_muc);
- } else if (!strcasecmp(subcmd, "mucnick")) {
- char **arglist = split_arg(arg, 2, 0);
- const char *nick = *arglist, *color = arglist[1];
- if (!nick || !*nick || !color || !*color)
- scr_LogPrint(LPRINT_NORMAL, "Missing argument");
- else
- scr_MucNickColor(nick, color);
- free_arg_lst(arglist);
- } else
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
- free_arg_lst(paramlst);
-}
-
-// cmd_setstatus(recipient, arg)
-// Set your Jabber status.
-// - if recipient is not NULL, the status is sent to this contact only
-// - arg must be "status message" (message is optional)
-void cmd_setstatus(const char *recipient, const char *arg)
-{
- char **paramlst;
- char *status;
- char *msg;
- enum imstatus st;
-
- if (!lm_connection_is_authenticated(lconnection)) {
- scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
- return;
- }
-
- // It makes sense to reset autoaway before changing the status
- // (esp. for FIFO or remote commands) or the behaviour could be
- // unexpected...
- if (!recipient)
- scr_CheckAutoAway(TRUE);
-
- paramlst = split_arg(arg, 2, 1); // status, message
- status = *paramlst;
- msg = *(paramlst+1);
-
- if (!status) {
- free_arg_lst(paramlst);
- return;
- }
-
- if (!strcasecmp(status, IMSTATUS_OFFLINE)) st = offline;
- else if (!strcasecmp(status, IMSTATUS_ONLINE)) st = available;
- else if (!strcasecmp(status, IMSTATUS_AVAILABLE)) st = available;
- else if (!strcasecmp(status, IMSTATUS_AWAY)) st = away;
- else if (!strcasecmp(status, IMSTATUS_INVISIBLE)) st = invisible;
- else if (!strcasecmp(status, IMSTATUS_DONOTDISTURB)) st = dontdisturb;
- else if (!strcasecmp(status, IMSTATUS_NOTAVAILABLE)) st = notavail;
- else if (!strcasecmp(status, IMSTATUS_FREE4CHAT)) st = freeforchat;
- else if (!strcasecmp(status, "message")) {
- if (!msg || !*msg) {
- // We want a message. If there's none, we give up.
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- free_arg_lst(paramlst);
- return;
- }
- st = xmpp_getstatus(); // Preserve current status
- } else {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized status!");
- free_arg_lst(paramlst);
- return;
- }
-
- // Use provided message
- if (msg && !*msg) {
- msg = NULL;
- }
-
- // If a recipient is specified, let's don't use default status messages
- if (recipient && !msg)
- msg = "";
-
- xmpp_setstatus(st, recipient, msg, FALSE);
-
- free_arg_lst(paramlst);
-}
-
-static void do_status(char *arg)
-{
- if (!*arg) {
- const char *sm = xmpp_getstatusmsg();
- scr_LogPrint(LPRINT_NORMAL, "Your status is: [%c] %s",
- imstatus2char[xmpp_getstatus()],
- (sm ? sm : ""));
- return;
- }
- arg = to_utf8(arg);
- cmd_setstatus(NULL, arg);
- g_free(arg);
-}
-
-static void do_status_to(char *arg)
-{
- char **paramlst;
- char *fjid, *st, *msg;
- char *jid_utf8 = NULL;
-
- paramlst = split_arg(arg, 3, 1); // jid, status, [message]
- fjid = *paramlst;
- st = *(paramlst+1);
- msg = *(paramlst+2);
-
- if (!fjid || !st) {
- scr_LogPrint(LPRINT_NORMAL,
- "Please specify both a Jabber ID and a status.");
- free_arg_lst(paramlst);
- return;
- }
-
- // Allow things like /status_to "" away
- if (!*fjid || !strcmp(fjid, "."))
- fjid = NULL;
-
- if (fjid) {
- // The JID has been specified. Quick check...
- if (check_jid_syntax(fjid)) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "<%s> is not a valid Jabber ID.", fjid);
- fjid = NULL;
- } else {
- // Convert jid to lowercase
- char *p = fjid;
- for ( ; *p && *p != JID_RESOURCE_SEPARATOR; p++)
- *p = tolower(*p);
- fjid = jid_utf8 = to_utf8(fjid);
- }
- } else {
- // Add the current buddy
- if (current_buddy)
- fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
- if (!fjid)
- scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
- }
-
- if (fjid) {
- char *cmdline;
- if (!msg)
- msg = "";
- msg = to_utf8(msg);
- cmdline = g_strdup_printf("%s %s", st, msg);
- scr_LogPrint(LPRINT_LOGNORM, "Sending to <%s> /status %s", fjid, cmdline);
- cmd_setstatus(fjid, cmdline);
- g_free(msg);
- g_free(cmdline);
- g_free(jid_utf8);
- }
- free_arg_lst(paramlst);
-}
-
-static void do_add(char *arg)
-{
- char **paramlst;
- char *id, *nick;
- char *jid_utf8 = NULL;
-
- if (!lm_connection_is_authenticated(lconnection)) {
- scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
- return;
- }
-
- paramlst = split_arg(arg, 2, 0); // jid, [nickname]
- id = *paramlst;
- nick = *(paramlst+1);
-
- if (!id)
- nick = NULL; // Allow things like: /add "" nick
- else if (!*id || !strcmp(id, "."))
- id = NULL;
-
- if (id) {
- // The JID has been specified. Quick check...
- if (check_jid_syntax(id)) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "<%s> is not a valid Jabber ID.", id);
- id = NULL;
- } else {
- mc_strtolower(id);
- // Actually an UTF-8 id isn't needed because only the bare jid will
- // be used.
- id = jid_utf8 = to_utf8(id);
- }
- } else {
- // Add the current buddy
- if (current_buddy)
- id = (char*)buddy_getjid(BUDDATA(current_buddy));
- if (!id)
- scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
- }
-
- if (nick)
- nick = to_utf8(nick);
-
- if (id) {
- // 2nd parameter = optional nickname
- xmpp_addbuddy(id, nick, NULL);
- scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
- id);
- }
-
- g_free(jid_utf8);
- g_free(nick);
- free_arg_lst(paramlst);
-}
-
-static void do_del(char *arg)
-{
- const char *bjid;
-
- if (*arg) {
- scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
- "the currently-selected buddy will be deleted.");
- return;
- }
-
- if (!current_buddy)
- return;
- bjid = buddy_getjid(BUDDATA(current_buddy));
- if (!bjid)
- return;
-
- if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_ROOM) {
- // This is a chatroom
- if (buddy_getinsideroom(BUDDATA(current_buddy))) {
- scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!");
- return;
- }
- }
-
- // Close the buffer
- scr_BufferPurge(1, NULL);
-
- scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", bjid);
- xmpp_delbuddy(bjid);
- scr_UpdateBuddyWindow();
-}
-
-static void do_group(char *arg)
-{
- gpointer group = NULL;
- guint leave_buddywindow;
- char **paramlst;
- char *subcmd;
- enum { group_toggle = -1, group_unfold = 0, group_fold = 1 } group_state = 0;
-
- if (!*arg) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- return;
- }
-
- if (!current_buddy)
- return;
-
- paramlst = split_arg(arg, 2, 0); // subcmd, [arg]
- subcmd = *paramlst;
- arg = *(paramlst+1);
-
- if (!subcmd || !*subcmd)
- goto do_group_return; // Should not happen anyway
-
- if (arg && *arg) {
- GSList *roster_elt;
- char *group_utf8 = to_utf8(arg);
- roster_elt = roster_find(group_utf8, namesearch, ROSTER_TYPE_GROUP);
- g_free(group_utf8);
- if (roster_elt)
- group = buddy_getgroup(roster_elt->data);
- } else {
- group = buddy_getgroup(BUDDATA(current_buddy));
- }
- if (!group)
- goto do_group_return;
-
- // We'll have to redraw the chat window if we're not currently on the group
- // entry itself, because it means we'll have to leave the current buddy
- // chat window.
- leave_buddywindow = (group != BUDDATA(current_buddy) &&
- group == buddy_getgroup(BUDDATA(current_buddy)));
-
-
- if (!(buddy_gettype(group) & ROSTER_TYPE_GROUP)) {
- scr_LogPrint(LPRINT_NORMAL, "You need to select a group.");
- goto do_group_return;
- }
-
- if (!strcasecmp(subcmd, "expand") || !strcasecmp(subcmd, "unfold"))
- group_state = group_unfold;
- else if (!strcasecmp(subcmd, "shrink") || !strcasecmp(subcmd, "fold"))
- group_state = group_fold;
- else if (!strcasecmp(subcmd, "toggle"))
- group_state = group_toggle;
- else {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
- goto do_group_return;
- }
-
- if (group_state != group_unfold && leave_buddywindow)
- scr_RosterPrevGroup();
-
- buddy_hide_group(group, group_state);
-
- buddylist_build();
- update_roster = TRUE;
-
-do_group_return:
- free_arg_lst(paramlst);
-}
-
-static int send_message_to(const char *fjid, const char *msg, const char *subj,
- LmMessageSubType type_overwrite, bool quiet)
-{
- char *bare_jid, *rp;
- char *hmsg;
- gint crypted;
- gint retval = 0;
- int isroom;
- gpointer xep184 = NULL;
-
- if (!lm_connection_is_authenticated(lconnection)) {
- scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
- return 1;
- }
- if (!fjid || !*fjid) {
- scr_LogPrint(LPRINT_NORMAL, "You must specify a Jabber ID.");
- return 1;
- }
- if (!msg || !*msg) {
- scr_LogPrint(LPRINT_NORMAL, "You must specify a message.");
- return 1;
- }
- if (check_jid_syntax((char*)fjid)) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "<%s> is not a valid Jabber ID.", fjid);
- return 1;
- }
-
- // We must use the bare jid in hk_message_out()
- rp = strchr(fjid, JID_RESOURCE_SEPARATOR);
- if (rp)
- bare_jid = g_strndup(fjid, rp - fjid);
- else
- bare_jid = (char*)fjid;
-
- if (!quiet) {
- // Jump to window, create one if needed
- scr_RosterJumpJid(bare_jid);
- }
-
- // Check if we're sending a message to a conference room
- // If not, we must make sure rp is NULL, for hk_message_out()
- isroom = !!roster_find(bare_jid, jidsearch, ROSTER_TYPE_ROOM);
- if (rp) {
- if (isroom) rp++;
- else rp = NULL;
- }
- isroom = isroom && (!rp || !*rp);
-
- // local part (UI, logging, etc.)
- if (subj)
- hmsg = g_strdup_printf("[%s]\n%s", subj, msg);
- else
- hmsg = (char*)msg;
-
- // Network part
- xmpp_send_msg(fjid, msg, (isroom ? ROSTER_TYPE_ROOM : ROSTER_TYPE_USER),
- subj, FALSE, &crypted, type_overwrite, &xep184);
-
- if (crypted == -1) {
- scr_LogPrint(LPRINT_LOGNORM, "Encryption error. Message was not sent.");
- retval = 1;
- goto send_message_to_return;
- }
-
- // Hook
- if (!isroom)
- hk_message_out(bare_jid, rp, 0, hmsg, crypted, xep184);
-
-send_message_to_return:
- if (hmsg != msg) g_free(hmsg);
- if (rp) g_free(bare_jid);
- return retval;
-}
-
-// send_message(msg, subj, type_overwrite)
-// Write the message in the buddy's window and send the message on
-// the network.
-static void send_message(const char *msg, const char *subj,
- LmMessageSubType type_overwrite)
-{
- const char *bjid;
-
- if (!current_buddy) {
- scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected.");
- return;
- }
-
- bjid = CURRENT_JID;
- if (!bjid) {
- scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected.");
- return;
- }
-
- send_message_to(bjid, msg, subj, type_overwrite, FALSE);
-}
-
-static LmMessageSubType scan_mtype(char **arg)
-{
- //Try splitting it
- char **parlist = split_arg(*arg, 2, 1);
- LmMessageSubType result = LM_MESSAGE_SUB_TYPE_NOT_SET;
- //Is it any good parameter?
- if (parlist && *parlist) {
- if (!strcmp("-n", *parlist)) {
- result = LM_MESSAGE_SUB_TYPE_NORMAL;
- } else if (!strcmp("-h", *parlist)) {
- result = LM_MESSAGE_SUB_TYPE_HEADLINE;
- }
- if (result != LM_MESSAGE_SUB_TYPE_NOT_SET || (!strcmp("--", *parlist)))
- *arg += strlen(*arg) - (parlist[1] ? strlen(parlist[1]) : 0);
- }
- //Anything found? -> skip it
- free_arg_lst(parlist);
- return result;
-}
-
-static void do_say_internal(char *arg, int parse_flags)
-{
- gpointer bud;
- LmMessageSubType msgtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
-
- scr_set_chatmode(TRUE);
- scr_ShowBuddyWindow();
-
- if (!current_buddy) {
- scr_LogPrint(LPRINT_NORMAL,
- "Whom are you talking to? Please select a buddy.");
- return;
- }
-
- bud = BUDDATA(current_buddy);
- if (!(buddy_gettype(bud) &
- (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) {
- scr_LogPrint(LPRINT_NORMAL, "This is not a user.");
- return;
- }
-
- buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
- if (parse_flags)
- msgtype = scan_mtype(&arg);
- arg = to_utf8(arg);
- send_message(arg, NULL, msgtype);
- g_free(arg);
-}
-
-static void do_say(char *arg) {
- do_say_internal(arg, 1);
-}
-
-static void do_msay(char *arg)
-{
- /* Parameters: begin verbatim abort send send_to */
- char **paramlst;
- char *subcmd;
-
- paramlst = split_arg(arg, 2, 1); // subcmd, arg
- subcmd = *paramlst;
- arg = *(paramlst+1);
-
- if (!subcmd || !*subcmd) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- scr_LogPrint(LPRINT_NORMAL, "Please read the manual before using "
- "the /msay command.");
- scr_LogPrint(LPRINT_NORMAL, "(Use \"%s begin\" to enter "
- "multi-line mode...)", mkcmdstr("msay"));
- goto do_msay_return;
- }
-
- if (!strcasecmp(subcmd, "toggle")) {
- if (scr_get_multimode())
- subcmd = "send";
- else
- subcmd = "begin";
- } else if (!strcasecmp(subcmd, "toggle_verbatim")) {
- if (scr_get_multimode())
- subcmd = "send";
- else
- subcmd = "verbatim";
- }
-
- if (!strcasecmp(subcmd, "abort")) {
- if (scr_get_multimode())
- scr_LogPrint(LPRINT_NORMAL, "Leaving multi-line message mode.");
- scr_set_multimode(FALSE, NULL);
- goto do_msay_return;
- } else if ((!strcasecmp(subcmd, "begin")) ||
- (!strcasecmp(subcmd, "verbatim"))) {
- bool verbat;
- gchar *subj_utf8 = to_utf8(arg);
- if (!strcasecmp(subcmd, "verbatim")) {
- scr_set_multimode(2, subj_utf8);
- verbat = TRUE;
- } else {
- scr_set_multimode(1, subj_utf8);
- verbat = FALSE;
- }
-
- scr_LogPrint(LPRINT_NORMAL, "Entered %smulti-line message mode.",
- verbat ? "VERBATIM " : "");
- scr_LogPrint(LPRINT_NORMAL, "Select a buddy and use \"%s send\" "
- "when your message is ready.", mkcmdstr("msay"));
- if (verbat)
- scr_LogPrint(LPRINT_NORMAL, "Use \"%s abort\" to abort this mode.",
- mkcmdstr("msay"));
- g_free(subj_utf8);
- goto do_msay_return;
- } else if (strcasecmp(subcmd, "send") && strcasecmp(subcmd, "send_to")) {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
- goto do_msay_return;
- }
-
- /* send/send_to command */
-
- if (!scr_get_multimode()) {
- scr_LogPrint(LPRINT_NORMAL, "No message to send. "
- "Use \"%s begin\" first.", mkcmdstr("msay"));
- goto do_msay_return;
- }
-
- scr_set_chatmode(TRUE);
- scr_ShowBuddyWindow();
-
- if (!strcasecmp(subcmd, "send_to")) {
- int err = FALSE;
- gchar *msg_utf8;
- LmMessageSubType msg_type = scan_mtype(&arg);
- // Let's send to the specified JID. We leave now if there
- // has been an error (so we don't leave multi-line mode).
- arg = to_utf8(arg);
- msg_utf8 = to_utf8(scr_get_multiline());
- if (msg_utf8) {
- err = send_message_to(arg, msg_utf8, scr_get_multimode_subj(), msg_type,
- FALSE);
- g_free(msg_utf8);
- }
- g_free(arg);
- if (err)
- goto do_msay_return;
- } else { // Send to currently selected buddy
- gpointer bud;
- gchar *msg_utf8;
-
- if (!current_buddy) {
- scr_LogPrint(LPRINT_NORMAL, "Whom are you talking to?");
- goto do_msay_return;
- }
-
- bud = BUDDATA(current_buddy);
- if (!(buddy_gettype(bud) &
- (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) {
- scr_LogPrint(LPRINT_NORMAL, "This is not a user.");
- goto do_msay_return;
- }
-
- buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
- msg_utf8 = to_utf8(scr_get_multiline());
- if (msg_utf8) {
- send_message(msg_utf8, scr_get_multimode_subj(), scan_mtype(&arg));
- g_free(msg_utf8);
- }
- }
- scr_set_multimode(FALSE, NULL);
- scr_LogPrint(LPRINT_NORMAL, "You have left multi-line message mode.");
-do_msay_return:
- free_arg_lst(paramlst);
-}
-
-// load_message_from_file(filename)
-// Read the whole content of a file.
-// The data are converted to UTF8, they should be freed by the caller after
-// use.
-char *load_message_from_file(const char *filename)
-{
- FILE *fd;
- struct stat buf;
- char *msgbuf, *msgbuf_utf8;
- char *p;
- char *next_utf8_char;
- size_t len;
-
- fd = fopen(filename, "r");
-
- if (!fd || fstat(fileno(fd), &buf)) {
- scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename);
- return NULL;
- }
- if (!buf.st_size || buf.st_size >= HBB_BLOCKSIZE) {
- if (!buf.st_size)
- scr_LogPrint(LPRINT_LOGNORM, "Message file is empty (%s)", filename);
- else
- scr_LogPrint(LPRINT_LOGNORM, "Message file is too big (%s)", filename);
- fclose(fd);
- return NULL;
- }
-
- msgbuf = g_new0(char, HBB_BLOCKSIZE);
- len = fread(msgbuf, 1, HBB_BLOCKSIZE-1, fd);
- fclose(fd);
-
- next_utf8_char = msgbuf;
-
- // Check there is no binary data. It must be a *message* file!
- for (p = msgbuf ; *p ; p++) {
- if (utf8_mode) {
- if (p == next_utf8_char) {
- if (!iswprint(get_char(p)) && *p != '\n' && *p != '\t')
- break;
- next_utf8_char = next_char(p);
- }
- } else {
- unsigned char sc = *p;
- if (!iswprint(sc) && sc != '\n' && sc != '\t')
- break;
- }
- }
-
- if (*p || (size_t)(p-msgbuf) != len) { // We're not at the End Of Line...
- scr_LogPrint(LPRINT_LOGNORM, "Message file contains "
- "invalid characters (%s)", filename);
- g_free(msgbuf);
- return NULL;
- }
-
- // p is now at the EOL
- // Let's strip trailing newlines
- if (p > msgbuf)
- p--;
- while (p > msgbuf && *p == '\n')
- *p-- = 0;
-
- // It could be empty, once the trailing newlines are gone
- if (p == msgbuf && *p == '\n') {
- scr_LogPrint(LPRINT_LOGNORM, "Message file is empty (%s)", filename);
- g_free(msgbuf);
- return NULL;
- }
-
- msgbuf_utf8 = to_utf8(msgbuf);
-
- if (!msgbuf_utf8 && msgbuf)
- scr_LogPrint(LPRINT_LOGNORM, "Message file charset conversion error (%s)",
- filename);
- g_free(msgbuf);
- return msgbuf_utf8;
-}
-
-static void do_say_to(char *arg)
-{
- char **paramlst;
- char *fjid, *msg;
- char *file = NULL;
- LmMessageSubType msg_type = LM_MESSAGE_SUB_TYPE_NOT_SET;
- bool quiet = FALSE;
-
- if (!lm_connection_is_authenticated(lconnection)) {
- scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
- return;
- }
-
- msg_type = scan_mtype(&arg);
- paramlst = split_arg(arg, 2, 1); // jid, message (or option, jid, message)
-
- if (!*paramlst) { // No parameter?
- scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
- free_arg_lst(paramlst);
- return;
- }
-
- // Check for an option parameter
- while (*paramlst) {
- if (!strcmp(*paramlst, "-q")) {
- char **oldparamlst = paramlst;
- paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, message
- free_arg_lst(oldparamlst);
- quiet = TRUE;
- } else if (!strcmp(*paramlst, "-f")) {
- char **oldparamlst = paramlst;
- paramlst = split_arg(*(oldparamlst+1), 2, 1); // filename, jid
- free_arg_lst(oldparamlst);
- if (!*paramlst) {
- scr_LogPrint(LPRINT_NORMAL, "Wrong usage.");
- return;
- }
- file = g_strdup(*paramlst);
- // One more parameter shift...
- oldparamlst = paramlst;
- paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, nothing
- free_arg_lst(oldparamlst);
- } else
- break;
- }
-
- if (!*paramlst) {
- scr_LogPrint(LPRINT_NORMAL, "Wrong usage.");
- return;
- }
-
- fjid = *paramlst;
- msg = *(paramlst+1);
-
- if (!strcmp(fjid, ".")) {
- // Send the message to the current buddy
- if (current_buddy)
- fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
- if (!fjid) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
- free_arg_lst(paramlst);
- return;
- }
- } else if (check_jid_syntax(fjid)) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
- free_arg_lst(paramlst);
- return;
- }
-
- fjid = to_utf8(fjid);
- if (!file) {
- msg = to_utf8(msg);
- } else {
- char *filename_xp;
- if (msg)
- scr_LogPrint(LPRINT_NORMAL, "say_to: extra parameter ignored.");
- filename_xp = expand_filename(file);
- msg = load_message_from_file(filename_xp);
- g_free(filename_xp);
- g_free(file);
- }
-
- send_message_to(fjid, msg, NULL, msg_type, quiet);
-
- g_free(fjid);
- g_free(msg);
- free_arg_lst(paramlst);
-}
-
-// buffer_updown(updown, nblines)
-// updown: -1=up, +1=down
-inline static void buffer_updown(int updown, char *nlines)
-{
- int nblines;
-
- if (!nlines || !*nlines)
- nblines = 0;
- else
- nblines = strtol(nlines, NULL, 10);
-
- if (nblines >= 0)
- scr_BufferScrollUpDown(updown, nblines);
-}
-
-static void buffer_search(int direction, char *arg)
-{
- if (!arg || !*arg) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- return;
- }
-
- scr_BufferSearch(direction, arg);
-}
-
-static void buffer_date(char *date)
-{
- time_t t;
-
- if (!date || !*date) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- return;
- }
-
- strip_arg_special_chars(date);
-
- t = from_iso8601(date, 0);
- if (t)
- scr_BufferDate(t);
- else
- scr_LogPrint(LPRINT_NORMAL, "The date you specified is "
- "not correctly formatted or invalid.");
-}
-
-static void buffer_percent(char *arg1, char *arg2)
-{
- // Basically, user has typed "%arg1 arg2"
- // "%50" -> arg1 = 50, arg2 null pointer
- // "% 50" -> arg1 = \0, arg2 = 50
-
- if (!*arg1 && (!arg2 || !*arg2)) { // No value
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- return;
- }
-
- if (*arg1 && arg2 && *arg2) { // Two values
- scr_LogPrint(LPRINT_NORMAL, "Wrong parameters.");
- return;
- }
-
- scr_BufferPercent(atoi((*arg1 ? arg1 : arg2)));
-}
-
-static void do_buffer(char *arg)
-{
- char **paramlst;
- char *subcmd;
-
- if (!current_buddy)
- return;
-
- paramlst = split_arg(arg, 2, 1); // subcmd, arg
- subcmd = *paramlst;
- arg = *(paramlst+1);
-
- if (!subcmd || !*subcmd) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- free_arg_lst(paramlst);
- return;
- }
-
- if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP &&
- strcasecmp(subcmd, "close_all")) {
- scr_LogPrint(LPRINT_NORMAL, "Groups have no buffer.");
- free_arg_lst(paramlst);
- return;
- }
-
- if (!strcasecmp(subcmd, "top")) {
- scr_BufferTopBottom(-1);
- } else if (!strcasecmp(subcmd, "bottom")) {
- scr_BufferTopBottom(1);
- } else if (!strcasecmp(subcmd, "clear")) {
- scr_BufferClear();
- } else if (!strcasecmp(subcmd, "close")) {
- scr_BufferPurge(1, arg);
- } else if (!strcasecmp(subcmd, "close_all")) {
- scr_BufferPurgeAll(1);
- } else if (!strcasecmp(subcmd, "purge")) {
- scr_BufferPurge(0, arg);
- } else if (!strcasecmp(subcmd, "scroll_lock")) {
- scr_BufferScrollLock(1);
- } else if (!strcasecmp(subcmd, "scroll_unlock")) {
- scr_BufferScrollLock(0);
- } else if (!strcasecmp(subcmd, "scroll_toggle")) {
- scr_BufferScrollLock(-1);
- } else if (!strcasecmp(subcmd, "up")) {
- buffer_updown(-1, arg);
- } else if (!strcasecmp(subcmd, "down")) {
- buffer_updown(1, arg);
- } else if (!strcasecmp(subcmd, "search_backward")) {
- strip_arg_special_chars(arg);
- buffer_search(-1, arg);
- } else if (!strcasecmp(subcmd, "search_forward")) {
- strip_arg_special_chars(arg);
- buffer_search(1, arg);
- } else if (!strcasecmp(subcmd, "date")) {
- buffer_date(arg);
- } else if (*subcmd == '%') {
- buffer_percent(subcmd+1, arg);
- } else if (!strcasecmp(subcmd, "save")) {
- scr_BufferDump(arg);
- } else if (!strcasecmp(subcmd, "list")) {
- scr_BufferList();
- } else {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
- }
-
- free_arg_lst(paramlst);
-}
-
-static void do_clear(char *arg) // Alias for "buffer clear"
-{
- do_buffer("clear");
-}
-
-static void do_info(char *arg)
-{
- gpointer bud;
- const char *bjid, *name;
- guint type, on_srv;
- char *buffer;
- enum subscr esub;
-
- if (!current_buddy)
- return;
- bud = BUDDATA(current_buddy);
-
- bjid = buddy_getjid(bud);
- name = buddy_getname(bud);
- type = buddy_gettype(bud);
- esub = buddy_getsubscription(bud);
- on_srv = buddy_getonserverflag(bud);
-
- buffer = g_new(char, 4096);
-
- if (bjid) {
- GSList *resources, *p_res;
- char *bstr = "unknown";
-
- // Enter chat mode
- scr_set_chatmode(TRUE);
- scr_ShowBuddyWindow();
-
- snprintf(buffer, 4095, "jid: <%s>", bjid);
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
- if (name) {
- snprintf(buffer, 4095, "Name: %s", name);
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
- }
-
- if (type == ROSTER_TYPE_USER) bstr = "user";
- else if (type == ROSTER_TYPE_ROOM) bstr = "chatroom";
- else if (type == ROSTER_TYPE_AGENT) bstr = "agent";
- snprintf(buffer, 127, "Type: %s", bstr);
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
-
- if (!on_srv) {
- scr_WriteIncomingMessage(bjid, "(Local item, not on the server)",
- 0, HBB_PREFIX_INFO, 0);
- }
-
- if (esub == sub_both) bstr = "both";
- else if (esub & sub_from) bstr = "from";
- else if (esub & sub_to) bstr = "to";
- else bstr = "none";
- snprintf(buffer, 64, "Subscription: %s", bstr);
- if (esub & sub_pending)
- strcat(buffer, " (pending)");
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
-
- resources = buddy_getresources(bud);
- if (!resources && type == ROSTER_TYPE_USER) {
- // No resource; display last status message, if any.
- const char *rst_msg = buddy_getstatusmsg(bud, "");
- if (rst_msg) {
- snprintf(buffer, 4095, "Last status message: %s", rst_msg);
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
- }
- }
- for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
- gchar rprio;
- enum imstatus rstatus;
- const char *rst_msg;
- time_t rst_time;
- struct pgp_data *rpgp;
-
- rprio = buddy_getresourceprio(bud, p_res->data);
- rstatus = buddy_getstatus(bud, p_res->data);
- rst_msg = buddy_getstatusmsg(bud, p_res->data);
- rst_time = buddy_getstatustime(bud, p_res->data);
- rpgp = buddy_resource_pgp(bud, p_res->data);
-
- snprintf(buffer, 4095, "Resource: [%c] (%d) %s", imstatus2char[rstatus],
- rprio, (char*)p_res->data);
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
- if (rst_msg) {
- snprintf(buffer, 4095, "Status message: %s", rst_msg);
- scr_WriteIncomingMessage(bjid, buffer,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- }
- if (rst_time) {
- char tbuf[128];
-
- strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", localtime(&rst_time));
- snprintf(buffer, 127, "Status timestamp: %s", tbuf);
- scr_WriteIncomingMessage(bjid, buffer,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- }
-#ifdef HAVE_GPGME
- if (rpgp && rpgp->sign_keyid) {
- snprintf(buffer, 4095, "PGP key id: %s", rpgp->sign_keyid);
- scr_WriteIncomingMessage(bjid, buffer,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- if (rpgp->last_sigsum) {
- gpgme_sigsum_t ss = rpgp->last_sigsum;
- snprintf(buffer, 4095, "Last PGP signature: %s",
- (ss & GPGME_SIGSUM_GREEN ? "good":
- (ss & GPGME_SIGSUM_RED ? "bad" : "unknown")));
- scr_WriteIncomingMessage(bjid, buffer,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- }
- }
-#endif
- g_free(p_res->data);
- }
- g_slist_free(resources);
- } else { /* Item has no jid */
- if (name) scr_LogPrint(LPRINT_NORMAL, "Name: %s", name);
- scr_LogPrint(LPRINT_NORMAL, "Type: %s",
- type == ROSTER_TYPE_GROUP ? "group" :
- (type == ROSTER_TYPE_SPECIAL ? "special" : "unknown"));
- }
- g_free(buffer);
-
- // Tell the user if this item has an annotation.
- if (type == ROSTER_TYPE_USER ||
- type == ROSTER_TYPE_ROOM ||
- type == ROSTER_TYPE_AGENT) {
- struct annotation *note = xmpp_get_storage_rosternotes(bjid, TRUE);
- if (note) {
- // We do not display the note, we just tell the user.
- g_free(note->text);
- g_free(note->jid);
- g_free(note);
- scr_WriteIncomingMessage(bjid, "(This item has an annotation)", 0,
- HBB_PREFIX_INFO, 0);
- }
- }
-}
-
-// room_names() is a variation of do_info(), for chatrooms only
-static void room_names(gpointer bud, char *arg)
-{
- const char *bjid;
- char *buffer;
- GSList *resources, *p_res;
- enum { style_normal = 0, style_detail, style_short,
- style_quiet, style_compact } style = 0;
-
- if (*arg) {
- if (!strcasecmp(arg, "--short"))
- style = style_short;
- else if (!strcasecmp(arg, "--quiet"))
- style = style_quiet;
- else if (!strcasecmp(arg, "--detail"))
- style = style_detail;
- else if (!strcasecmp(arg, "--compact"))
- style = style_compact;
- else {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
- return;
- }
- }
-
- // Enter chat mode
- scr_set_chatmode(TRUE);
- scr_ShowBuddyWindow();
-
- bjid = buddy_getjid(bud);
-
- buffer = g_new(char, 4096);
- strncpy(buffer, "Room members:", 127);
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
-
- resources = buddy_getresources(bud);
- for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
- enum imstatus rstatus;
- const char *rst_msg;
-
- rstatus = buddy_getstatus(bud, p_res->data);
- rst_msg = buddy_getstatusmsg(bud, p_res->data);
-
- if (style == style_short) {
- snprintf(buffer, 4095, "[%c] %s%s%s", imstatus2char[rstatus],
- (char*)p_res->data,
- rst_msg ? " -- " : "", rst_msg ? rst_msg : "");
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
- } else if (style == style_compact) {
- enum imrole role = buddy_getrole(bud, p_res->data);
- enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
- bool showaffil = (affil != affil_none);
-
- snprintf(buffer, 4095, "[%c] %s (%s%s%s)",
- imstatus2char[rstatus], (char*)p_res->data,
- showaffil ? straffil[affil] : "\0",
- showaffil ? "/" : "\0",
- strrole[role]);
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
- } else {
- // (Style "normal", "detail" or "quiet")
- snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
- (char*)p_res->data);
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
- if (rst_msg && style != style_quiet) {
- snprintf(buffer, 4095, "Status message: %s", rst_msg);
- scr_WriteIncomingMessage(bjid, buffer,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- }
- if (style == style_detail) {
- enum imrole role = buddy_getrole(bud, p_res->data);
- enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
-
- snprintf(buffer, 4095, "Role: %s", strrole[role]);
- scr_WriteIncomingMessage(bjid, buffer,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- if (affil != affil_none) {
- snprintf(buffer, 4095, "Affiliat.: %s", straffil[affil]);
- scr_WriteIncomingMessage(bjid, buffer,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- }
- }
- }
- g_free(p_res->data);
- }
- g_slist_free(resources);
- g_free(buffer);
-}
-
-static void move_group_member(gpointer bud, void *groupnamedata)
-{
- const char *bjid, *name, *groupname;
-
- groupname = (char *)groupnamedata;
-
- bjid = buddy_getjid(bud);
- name = buddy_getname(bud);
-
- xmpp_updatebuddy(bjid, name, *groupname ? groupname : NULL);
-}
-
-static void do_rename(char *arg)
-{
- gpointer bud;
- const char *bjid, *group;
- guint type, on_srv;
- char *newname, *p;
- char *name_utf8;
-
- if (!current_buddy)
- return;
- bud = BUDDATA(current_buddy);
-
- bjid = buddy_getjid(bud);
- group = buddy_getgroupname(bud);
- type = buddy_gettype(bud);
- on_srv = buddy_getonserverflag(bud);
-
- if (type & ROSTER_TYPE_SPECIAL) {
- scr_LogPrint(LPRINT_NORMAL, "You can't rename this item.");
- return;
- }
-
- if (!*arg && !(type & ROSTER_TYPE_GROUP)) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a new name.");
- return;
- }
-
- if (!(type & ROSTER_TYPE_GROUP) && !on_srv) {
- scr_LogPrint(LPRINT_NORMAL,
- "Note: this item will be added to your server roster.");
- // If this is a MUC room w/o bookmark, let's give a small hint...
- if ((type & ROSTER_TYPE_ROOM) && !xmpp_is_bookmarked(bjid)) {
- scr_LogPrint(LPRINT_NORMAL,
- "You should add a room bookmark or it will not be "
- "recognized as a MUC room next time you run mcabber.");
- }
- }
-
- newname = g_strdup(arg);
- // Remove trailing space
- for (p = newname; *p; p++) ;
- while (p > newname && *p == ' ') *p = 0;
-
- strip_arg_special_chars(newname);
-
- name_utf8 = to_utf8(newname);
-
- if (type & ROSTER_TYPE_GROUP) {
- // Rename a whole group
- foreach_group_member(bud, &move_group_member, name_utf8);
- // Let's jump to the previous buddy, because this group name should
- // disappear when we receive the server answer.
- scr_RosterUpDown(-1, 1);
- } else {
- // Rename a single buddy
- guint del_name = 0;
- if (!*newname || !strcmp(arg, "-"))
- del_name = TRUE;
- buddy_setname(bud, (del_name ? (char*)bjid : name_utf8));
- xmpp_updatebuddy(bjid, (del_name ? NULL : name_utf8), group);
- }
-
- g_free(name_utf8);
- g_free(newname);
- update_roster = TRUE;
-}
-
-static void do_move(char *arg)
-{
- gpointer bud;
- const char *bjid, *name, *oldgroupname;
- guint type;
- char *newgroupname, *p;
- char *group_utf8;
-
- if (!current_buddy)
- return;
- bud = BUDDATA(current_buddy);
-
- bjid = buddy_getjid(bud);
- name = buddy_getname(bud);
- type = buddy_gettype(bud);
-
- oldgroupname = buddy_getgroupname(bud);
-
- if (type & ROSTER_TYPE_GROUP) {
- scr_LogPrint(LPRINT_NORMAL, "You can't move groups!");
- return;
- }
- if (type & ROSTER_TYPE_SPECIAL) {
- scr_LogPrint(LPRINT_NORMAL, "You can't move this item.");
- return;
- }
-
- newgroupname = g_strdup(arg);
- // Remove trailing space
- for (p = newgroupname; *p; p++) ;
- while (p > newgroupname && *p == ' ') *p-- = 0;
-
- strip_arg_special_chars(newgroupname);
-
- group_utf8 = to_utf8(newgroupname);
- if (strcmp(oldgroupname, group_utf8)) {
- guint msgflag;
-
- xmpp_updatebuddy(bjid, name, *group_utf8 ? group_utf8 : NULL);
- scr_RosterUpDown(-1, 1);
-
- // If the buddy has a pending message flag,
- // we remove it temporarily in order to reset the global group
- // flag. We set it back once the buddy is in the new group,
- // which will update the new group's flag.
- msgflag = buddy_getflags(bud) & ROSTER_FLAG_MSG;
- if (msgflag)
- roster_msg_setflag(bjid, FALSE, FALSE);
- buddy_setgroup(bud, group_utf8);
- if (msgflag)
- roster_msg_setflag(bjid, FALSE, TRUE);
- }
-
- g_free(group_utf8);
- g_free(newgroupname);
- update_roster = TRUE;
-}
-
-static void print_option_cb(char *k, char *v, void *f)
-{
- char *format = (char *)f;
- scr_LogPrint (LPRINT_NORMAL, format, k, v);
-}
-
-static void do_set(char *arg)
-{
- guint assign;
- gchar *option, *value;
- gchar *option_utf8;
-
- if (!*arg) {
- // list all set options
- settings_foreach(SETTINGS_TYPE_OPTION, print_option_cb, "%s = [%s]");
- return;
- }
-
- assign = parse_assigment(arg, &option, &value);
- if (!option) {
- scr_LogPrint(LPRINT_NORMAL, "Set what option?");
- return;
- }
- option_utf8 = to_utf8(option);
- g_free(option);
- if (!assign) { // This is a query
- const char *val = settings_opt_get(option_utf8);
- if (val)
- scr_LogPrint(LPRINT_NORMAL, "%s = [%s]", option_utf8, val);
- else
- scr_LogPrint(LPRINT_NORMAL, "Option %s is not set", option_utf8);
- g_free(option_utf8);
- return;
- }
- // Update the option
- // Maybe some options should be protected when user is connected (server,
- // username, etc.). And we should catch some options here, too
- // (hide_offline_buddies for ex.)
- if (!value) {
- settings_del(SETTINGS_TYPE_OPTION, option_utf8);
- } else {
- gchar *value_utf8 = to_utf8(value);
- settings_set(SETTINGS_TYPE_OPTION, option_utf8, value_utf8);
- g_free(value_utf8);
- g_free(value);
- }
- g_free(option_utf8);
-}
-
-static void dump_alias(char *k, char *v, void *param)
-{
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, "Alias %s = %s", k, v);
-}
-
-static void do_alias(char *arg)
-{
- guint assign;
- gchar *alias, *value;
-
- assign = parse_assigment(arg, &alias, &value);
- if (!alias) {
- settings_foreach(SETTINGS_TYPE_ALIAS, &dump_alias, NULL);
- return;
- }
- if (!assign) { // This is a query
- const char *val = settings_get(SETTINGS_TYPE_ALIAS, alias);
- // NOTE: LPRINT_NOTUTF8 here, see below why it isn't encoded...
- if (val)
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, "%s = %s", alias, val);
- else
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "Alias '%s' does not exist", alias);
- goto do_alias_return;
- }
- // Check the alias does not conflict with a registered command
- if (cmd_get(alias)) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "'%s' is a reserved word!", alias);
- goto do_alias_return;
- }
- // Update the alias
- if (!value) {
- if (settings_get(SETTINGS_TYPE_ALIAS, alias)) {
- settings_del(SETTINGS_TYPE_ALIAS, alias);
- // Remove alias from the completion list
- compl_del_category_word(COMPL_CMD, alias);
- }
- } else {
- /* Add alias to the completion list, if not already in.
- NOTE: We're not UTF8-encoding "alias" and "value" here because UTF-8 is
- not yet supported in the UI... (and we use the values in the completion
- system)
- */
- if (!settings_get(SETTINGS_TYPE_ALIAS, alias))
- compl_add_category_word(COMPL_CMD, alias);
- settings_set(SETTINGS_TYPE_ALIAS, alias, value);
- g_free(value);
- }
-do_alias_return:
- g_free(alias);
-}
-
-static void dump_bind(char *k, char *v, void *param)
-{
- scr_LogPrint(LPRINT_NORMAL, "Key %4s is bound to: %s", k, v);
-}
-
-static void do_bind(char *arg)
-{
- guint assign;
- gchar *k_code, *value;
-
- assign = parse_assigment(arg, &k_code, &value);
- if (!k_code) {
- settings_foreach(SETTINGS_TYPE_BINDING, &dump_bind, NULL);
- return;
- }
- if (!assign) { // This is a query
- const char *val = settings_get(SETTINGS_TYPE_BINDING, k_code);
- if (val)
- scr_LogPrint(LPRINT_NORMAL, "Key %s is bound to: %s", k_code, val);
- else
- scr_LogPrint(LPRINT_NORMAL, "Key %s is not bound.", k_code);
- g_free(k_code);
- return;
- }
- // Update the key binding
- if (!value) {
- settings_del(SETTINGS_TYPE_BINDING, k_code);
- } else {
- gchar *value_utf8 = to_utf8(value);
- settings_set(SETTINGS_TYPE_BINDING, k_code, value_utf8);
- g_free(value_utf8);
- g_free(value);
- }
- g_free(k_code);
-}
-
-static void do_rawxml(char *arg)
-{
- char **paramlst;
- char *subcmd;
-
- if (!lm_connection_is_authenticated(lconnection)) {
- scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
- return;
- }
-
- paramlst = split_arg(arg, 2, 1); // subcmd, arg
- subcmd = *paramlst;
- arg = *(paramlst+1);
-
- if (!subcmd || !*subcmd) {
- scr_LogPrint(LPRINT_NORMAL, "Please read the manual page"
- " before using /rawxml :-)");
- free_arg_lst(paramlst);
- return;
- }
-
- if (!strcasecmp(subcmd, "send")) {
- gchar *buffer;
-
- if (!subcmd || !*subcmd) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- free_arg_lst(paramlst);
- return;
- }
-
- // We don't strip_arg_special_chars() here, because it would be a pain for
- // the user to escape quotes in a XML stream...
-
- buffer = to_utf8(arg);
- if (buffer) {
- scr_LogPrint(LPRINT_NORMAL, "Sending XML string");
- lm_connection_send_raw(lconnection, buffer, NULL);
- g_free(buffer);
- } else {
- scr_LogPrint(LPRINT_NORMAL, "Conversion error in XML string.");
- }
- } else {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
- }
-
- free_arg_lst(paramlst);
-}
-
-// check_room_subcommand(arg, param_needed, buddy_must_be_a_room)
-// - Check if this is a room, if buddy_must_be_a_room is not null
-// - Check there is at least 1 parameter, if param_needed is true
-// - Return null if one of the checks fails, or a pointer to the first
-// non-space character.
-static char *check_room_subcommand(char *arg, bool param_needed,
- gpointer buddy_must_be_a_room)
-{
- if (buddy_must_be_a_room &&
- !(buddy_gettype(buddy_must_be_a_room) & ROSTER_TYPE_ROOM)) {
- scr_LogPrint(LPRINT_NORMAL, "This isn't a conference room.");
- return NULL;
- }
-
- if (param_needed) {
- if (!arg) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- return NULL;
- }
- }
-
- if (arg)
- return arg;
- else
- return "";
-}
-
-static void room_join(gpointer bud, char *arg)
-{
- char **paramlst;
- char *roomname, *nick, *pass;
- char *roomname_tmp = NULL;
- char *pass_utf8;
-
- paramlst = split_arg(arg, 3, 0); // roomid, nickname, password
- roomname = *paramlst;
- nick = *(paramlst+1);
- pass = *(paramlst+2);
-
- if (!roomname)
- nick = NULL;
- if (!nick)
- pass = NULL;
-
- if (!roomname || !strcmp(roomname, ".")) {
- // If the current_buddy is recognized as a room, the room name
- // can be omitted (or "." can be used).
- if (!bud || !(buddy_gettype(bud) & ROSTER_TYPE_ROOM)) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a room name.");
- free_arg_lst(paramlst);
- return;
- }
- roomname = (char*)buddy_getjid(bud);
- } else if (strchr(roomname, '/')) {
- scr_LogPrint(LPRINT_NORMAL, "Invalid room name.");
- free_arg_lst(paramlst);
- return;
- } else {
- // The room id has been specified. Let's convert it and use it.
- mc_strtolower(roomname);
- roomname = roomname_tmp = to_utf8(roomname);
- }
-
- // If no nickname is provided with the /join command,
- // we try to get a default nickname.
- if (!nick || !*nick)
- nick = default_muc_nickname(roomname);
- else
- nick = to_utf8(nick);
- // If we still have no nickname, give up
- if (!nick || !*nick) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a nickname.");
- g_free(nick);
- free_arg_lst(paramlst);
- return;
- }
-
- pass_utf8 = to_utf8(pass);
-
- xmpp_room_join(roomname, nick, pass_utf8);
-
- scr_LogPrint(LPRINT_LOGNORM, "Sent a join request to <%s>...", roomname);
-
- g_free(roomname_tmp);
- g_free(nick);
- g_free(pass_utf8);
- buddylist_build();
- update_roster = TRUE;
- free_arg_lst(paramlst);
-}
-
-static void room_invite(gpointer bud, char *arg)
-{
- char **paramlst;
- const gchar *roomname;
- char* fjid;
- gchar *reason_utf8;
-
- paramlst = split_arg(arg, 2, 1); // jid, [reason]
- fjid = *paramlst;
- arg = *(paramlst+1);
- // An empty reason is no reason...
- if (arg && !*arg)
- arg = NULL;
-
- if (!fjid || !*fjid) {
- scr_LogPrint(LPRINT_NORMAL, "Missing or incorrect Jabber ID.");
- free_arg_lst(paramlst);
- return;
- }
-
- roomname = buddy_getjid(bud);
- reason_utf8 = to_utf8(arg);
- xmpp_room_invite(roomname, fjid, reason_utf8);
- scr_LogPrint(LPRINT_LOGNORM, "Invitation sent to <%s>.", fjid);
- g_free(reason_utf8);
- free_arg_lst(paramlst);
-}
-
-static void room_affil(gpointer bud, char *arg)
-{
- char **paramlst;
- gchar *fjid, *rolename;
- struct role_affil ra;
- const char *roomid = buddy_getjid(bud);
-
- paramlst = split_arg(arg, 3, 1); // jid, new_affil, [reason]
- fjid = *paramlst;
- rolename = *(paramlst+1);
- arg = *(paramlst+2);
-
- if (!fjid || !*fjid || !rolename || !*rolename) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify both a Jabber ID and a role.");
- free_arg_lst(paramlst);
- return;
- }
-
- ra.type = type_affil;
- ra.val.affil = affil_none;
- for (; ra.val.affil < imaffiliation_size; ra.val.affil++)
- if (!strcasecmp(rolename, straffil[ra.val.affil]))
- break;
-
- if (ra.val.affil < imaffiliation_size) {
- gchar *jid_utf8, *reason_utf8;
- jid_utf8 = to_utf8(fjid);
- reason_utf8 = to_utf8(arg);
- xmpp_room_setattrib(roomid, jid_utf8, NULL, ra, reason_utf8);
- g_free(jid_utf8);
- g_free(reason_utf8);
- } else
- scr_LogPrint(LPRINT_NORMAL, "Wrong affiliation parameter.");
-
- free_arg_lst(paramlst);
-}
-
-static void room_role(gpointer bud, char *arg)
-{
- char **paramlst;
- gchar *fjid, *rolename;
- struct role_affil ra;
- const char *roomid = buddy_getjid(bud);
-
- paramlst = split_arg(arg, 3, 1); // jid, new_role, [reason]
- fjid = *paramlst;
- rolename = *(paramlst+1);
- arg = *(paramlst+2);
-
- if (!fjid || !*fjid || !rolename || !*rolename) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify both a Jabber ID and a role.");
- free_arg_lst(paramlst);
- return;
- }
-
- ra.type = type_role;
- ra.val.role = role_none;
- for (; ra.val.role < imrole_size; ra.val.role++)
- if (!strcasecmp(rolename, strrole[ra.val.role]))
- break;
-
- if (ra.val.role < imrole_size) {
- gchar *jid_utf8, *reason_utf8;
- jid_utf8 = to_utf8(fjid);
- reason_utf8 = to_utf8(arg);
- xmpp_room_setattrib(roomid, jid_utf8, NULL, ra, reason_utf8);
- g_free(jid_utf8);
- g_free(reason_utf8);
- } else
- scr_LogPrint(LPRINT_NORMAL, "Wrong role parameter.");
-
- free_arg_lst(paramlst);
-}
-
-
-// The expected argument is a Jabber id
-static void room_ban(gpointer bud, char *arg)
-{
- char **paramlst;
- gchar *fjid, *bjid;
- const gchar *banjid;
- gchar *jid_utf8, *reason_utf8;
- struct role_affil ra;
- const char *roomid = buddy_getjid(bud);
-
- paramlst = split_arg(arg, 2, 1); // jid, [reason]
- fjid = *paramlst;
- arg = *(paramlst+1);
-
- if (!fjid || !*fjid) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
- free_arg_lst(paramlst);
- return;
- }
-
- ra.type = type_affil;
- ra.val.affil = affil_outcast;
-
- bjid = jidtodisp(fjid);
- jid_utf8 = to_utf8(bjid);
-
- // If the argument doesn't look like a jid, we'll try to find a matching
- // nickname.
- if (!strchr(bjid, JID_DOMAIN_SEPARATOR) || check_jid_syntax(bjid)) {
- const gchar *tmp;
- // We want the initial argument, so the fjid variable, because
- // we don't want to strip a resource-like string from the nickname!
- g_free(jid_utf8);
- jid_utf8 = to_utf8(fjid);
- tmp = buddy_getrjid(bud, jid_utf8);
- if (!tmp) {
- scr_LogPrint(LPRINT_NORMAL, "Wrong JID or nickname");
- goto room_ban_return;
- }
- banjid = jidtodisp(tmp);
- } else
- banjid = jid_utf8;
-
- scr_LogPrint(LPRINT_NORMAL, "Requesting a ban for %s", banjid);
-
- reason_utf8 = to_utf8(arg);
- xmpp_room_setattrib(roomid, banjid, NULL, ra, reason_utf8);
- g_free(reason_utf8);
-
-room_ban_return:
- g_free(bjid);
- g_free(jid_utf8);
- free_arg_lst(paramlst);
-}
-
-// The expected argument is a Jabber id
-static void room_unban(gpointer bud, char *arg)
-{
- gchar *fjid = arg;
- gchar *jid_utf8;
- struct role_affil ra;
- const char *roomid = buddy_getjid(bud);
-
- if (!fjid || !*fjid) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
- return;
- }
-
- ra.type = type_affil;
- ra.val.affil = affil_none;
-
- jid_utf8 = to_utf8(fjid);
- xmpp_room_setattrib(roomid, jid_utf8, NULL, ra, NULL);
- g_free(jid_utf8);
-}
-
-// The expected argument is a nickname
-static void room_kick(gpointer bud, char *arg)
-{
- char **paramlst;
- gchar *nick;
- gchar *nick_utf8, *reason_utf8;
- struct role_affil ra;
- const char *roomid = buddy_getjid(bud);
-
- paramlst = split_arg(arg, 2, 1); // nickname, [reason]
- nick = *paramlst;
- arg = *(paramlst+1);
-
- if (!nick || !*nick) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a nickname.");
- free_arg_lst(paramlst);
- return;
- }
-
- ra.type = type_role;
- ra.val.affil = role_none;
-
- nick_utf8 = to_utf8(nick);
- reason_utf8 = to_utf8(arg);
- xmpp_room_setattrib(roomid, NULL, nick_utf8, ra, reason_utf8);
- g_free(nick_utf8);
- g_free(reason_utf8);
-
- free_arg_lst(paramlst);
-}
-
-void cmd_room_leave(gpointer bud, char *arg)
-{
- gchar *roomid, *desc;
- const char *nickname;
-
- nickname = buddy_getnickname(bud);
- if (!nickname) {
- scr_LogPrint(LPRINT_NORMAL, "You are not in this room.");
- return;
- }
-
- roomid = g_strdup_printf("%s/%s", buddy_getjid(bud), nickname);
- desc = to_utf8(arg);
- xmpp_setstatus(offline, roomid, desc, TRUE);
- g_free(desc);
- g_free(roomid);
-}
-
-static void room_nick(gpointer bud, char *arg)
-{
- if (!buddy_getinsideroom(bud)) {
- scr_LogPrint(LPRINT_NORMAL, "You are not in this room.");
- return;
- }
-
- if (!arg || !*arg) {
- const char *nick = buddy_getnickname(bud);
- if (nick)
- scr_LogPrint(LPRINT_NORMAL, "Your nickname is: %s", nick);
- else
- scr_LogPrint(LPRINT_NORMAL, "You have no nickname in this room.");
- } else {
- gchar *nick = to_utf8(arg);
- strip_arg_special_chars(nick);
- xmpp_room_join(buddy_getjid(bud), nick, NULL);
- g_free(nick);
- }
-}
-
-static void room_privmsg(gpointer bud, char *arg)
-{
- char **paramlst;
- gchar *fjid_utf8, *nick, *nick_utf8, *msg;
-
- paramlst = split_arg(arg, 2, 1); // nickname, message
- nick = *paramlst;
- arg = *(paramlst+1);
-
- if (!nick || !*nick || !arg || !*arg) {
- scr_LogPrint(LPRINT_NORMAL,
- "Please specify both a Jabber ID and a message.");
- free_arg_lst(paramlst);
- return;
- }
-
- nick_utf8 = to_utf8(nick);
- fjid_utf8 = g_strdup_printf("%s/%s", buddy_getjid(bud), nick_utf8);
- g_free (nick_utf8);
- msg = to_utf8(arg);
- send_message_to(fjid_utf8, msg, NULL, LM_MESSAGE_SUB_TYPE_NOT_SET, FALSE);
- g_free(fjid_utf8);
- g_free(msg);
- free_arg_lst(paramlst);
-}
-
-static void room_remove(gpointer bud, char *arg)
-{
- if (*arg) {
- scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
- "the currently-selected room will be removed.");
- return;
- }
-
- // Quick check: if there are resources, we haven't left
- if (buddy_getinsideroom(bud)) {
- scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!");
- return;
- }
- // Delete the room
- roster_del_user(buddy_getjid(bud));
- scr_UpdateBuddyWindow();
- buddylist_build();
- update_roster = TRUE;
-}
-
-static void room_topic(gpointer bud, char *arg)
-{
- if (!buddy_getinsideroom(bud)) {
- scr_LogPrint(LPRINT_NORMAL, "You are not in this room.");
- return;
- }
-
- // If no parameter is given, display the current topic
- if (!*arg) {
- const char *topic = buddy_gettopic(bud);
- if (topic)
- scr_LogPrint(LPRINT_NORMAL, "Topic: %s", topic);
- else
- scr_LogPrint(LPRINT_NORMAL, "No topic has been set.");
- return;
- }
-
- // If arg is "-", let's clear the topic
- if (!strcmp(arg, "-"))
- arg = NULL;
-
- arg = to_utf8(arg);
- // Set the topic
- xmpp_send_msg(buddy_getjid(bud), NULL, ROSTER_TYPE_ROOM, arg ? arg : "",
- FALSE, NULL, LM_MESSAGE_SUB_TYPE_NOT_SET, NULL);
- g_free(arg);
-}
-
-static void room_destroy(gpointer bud, char *arg)
-{
- gchar *msg;
-
- if (arg && *arg)
- msg = to_utf8(arg);
- else
- msg = NULL;
-
- xmpp_room_destroy(buddy_getjid(bud), NULL, msg);
- g_free(msg);
-}
-
-static void room_unlock(gpointer bud, char *arg)
-{
- if (*arg) {
- scr_LogPrint(LPRINT_NORMAL, "Unknown parameter.");
- return;
- }
-
- xmpp_room_unlock(buddy_getjid(bud));
-}
-
-static void room_setopt(gpointer bud, char *arg)
-{
- char **paramlst;
- char *param, *value;
- enum { opt_none = 0, opt_printstatus, opt_autowhois } option = 0;
-
- paramlst = split_arg(arg, 2, 1); // param, value
- param = *paramlst;
- value = *(paramlst+1);
- if (!param) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a room option.");
- free_arg_lst(paramlst);
- return;
- }
-
- if (!strcasecmp(param, "print_status"))
- option = opt_printstatus;
- else if (!strcasecmp(param, "auto_whois"))
- option = opt_autowhois;
- else {
- scr_LogPrint(LPRINT_NORMAL, "Wrong option!");
- free_arg_lst(paramlst);
- return;
- }
-
- // If no value is given, display the current value
- if (!value) {
- const char *strval;
- if (option == opt_printstatus)
- strval = strprintstatus[buddy_getprintstatus(bud)];
- else
- strval = strautowhois[buddy_getautowhois(bud)];
- scr_LogPrint(LPRINT_NORMAL, "%s is set to: %s", param, strval);
- free_arg_lst(paramlst);
- return;
- }
-
- if (option == opt_printstatus) {
- enum room_printstatus eval;
- if (!strcasecmp(value, "none"))
- eval = status_none;
- else if (!strcasecmp(value, "in_and_out"))
- eval = status_in_and_out;
- else if (!strcasecmp(value, "all"))
- eval = status_all;
- else {
- eval = status_default;
- if (strcasecmp(value, "default") != 0)
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized value, assuming default...");
- }
- buddy_setprintstatus(bud, eval);
- } else if (option == opt_autowhois) {
- enum room_autowhois eval;
- if (!strcasecmp(value, "on"))
- eval = autowhois_on;
- else if (!strcasecmp(value, "off"))
- eval = autowhois_off;
- else {
- eval = autowhois_default;
- if (strcasecmp(value, "default") != 0)
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized value, assuming default...");
- }
- buddy_setautowhois(bud, eval);
- }
-
- free_arg_lst(paramlst);
-}
-
-// cmd_room_whois(..)
-// If interactive is TRUE, chatmode can be enabled.
-void cmd_room_whois(gpointer bud, char *arg, guint interactive)
-{
- char **paramlst;
- gchar *nick, *buffer;
- const char *bjid, *realjid;
- const char *rst_msg;
- gchar rprio;
- enum imstatus rstatus;
- enum imrole role;
- enum imaffiliation affil;
- time_t rst_time;
- guint msg_flag = HBB_PREFIX_INFO;
-
- paramlst = split_arg(arg, 1, 0); // nickname
- nick = *paramlst;
-
- if (!nick || !*nick) {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a nickname.");
- free_arg_lst(paramlst);
- return;
- }
-
- nick = to_utf8(nick);
-
- if (interactive) {
- // Enter chat mode
- scr_set_chatmode(TRUE);
- scr_ShowBuddyWindow();
- } else
- msg_flag |= HBB_PREFIX_NOFLAG;
-
- bjid = buddy_getjid(bud);
- rstatus = buddy_getstatus(bud, nick);
-
- if (rstatus == offline) {
- scr_LogPrint(LPRINT_NORMAL, "No such member: %s", nick);
- free_arg_lst(paramlst);
- g_free(nick);
- return;
- }
-
- rst_time = buddy_getstatustime(bud, nick);
- rprio = buddy_getresourceprio(bud, nick);
- rst_msg = buddy_getstatusmsg(bud, nick);
- if (!rst_msg) rst_msg = "";
-
- role = buddy_getrole(bud, nick);
- affil = buddy_getaffil(bud, nick);
- realjid = buddy_getrjid(bud, nick);
-
- buffer = g_new(char, 4096);
-
- snprintf(buffer, 4095, "Whois [%s]", nick);
- scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag, 0);
- snprintf(buffer, 4095, "Status : [%c] %s", imstatus2char[rstatus],
- rst_msg);
- scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
-
- if (rst_time) {
- char tbuf[128];
-
- strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", localtime(&rst_time));
- snprintf(buffer, 127, "Timestamp: %s", tbuf);
- scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
- }
-
- if (realjid) {
- snprintf(buffer, 4095, "JID : <%s>", realjid);
- scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
- }
-
- snprintf(buffer, 4095, "Role : %s", strrole[role]);
- scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
- snprintf(buffer, 4095, "Affiliat.: %s", straffil[affil]);
- scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
- snprintf(buffer, 4095, "Priority : %d", rprio);
- scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
-
- scr_WriteIncomingMessage(bjid, "End of WHOIS", 0, msg_flag, 0);
-
- g_free(buffer);
- g_free(nick);
- free_arg_lst(paramlst);
-}
-
-static void room_bookmark(gpointer bud, char *arg)
-{
- const char *roomid;
- const char *name = NULL, *nick = NULL;
- char *tmpnick = NULL;
- enum room_autowhois autowhois = 0;
- enum room_printstatus printstatus = 0;
- enum { bm_add = 0, bm_del = 1 } action = 0;
- int autojoin = 0;
- int nick_set = 0;
-
- if (arg && *arg) {
- // /room bookmark [add|del] [[+|-]autojoin] [-|nick]
- char **paramlst;
- char **pp;
-
- paramlst = split_arg(arg, 3, 0); // At most 3 parameters
- for (pp = paramlst; *pp; pp++) {
- if (!strcasecmp(*pp, "add"))
- action = bm_add;
- else if (!strcasecmp(*pp, "del"))
- action = bm_del;
- else if (!strcasecmp(*pp, "-autojoin"))
- autojoin = 0;
- else if (!strcasecmp(*pp, "+autojoin") || !strcasecmp(*pp, "autojoin"))
- autojoin = 1;
- else if (!strcmp(*pp, "-"))
- nick_set = 1;
- else {
- nick_set = 1;
- nick = tmpnick = to_utf8 (*pp);
- }
- }
- free_arg_lst(paramlst);
- }
-
- roomid = buddy_getjid(bud);
-
- if (action == bm_add) {
- name = buddy_getname(bud);
- if (!nick_set)
- nick = buddy_getnickname(bud);
- printstatus = buddy_getprintstatus(bud);
- autowhois = buddy_getautowhois(bud);
- }
-
- xmpp_set_storage_bookmark(roomid, name, nick, NULL, autojoin,
- printstatus, autowhois);
- g_free (tmpnick);
-}
-
-static void display_all_bookmarks(void)
-{
- GSList *bm, *bmp;
- GString *sbuf;
- struct bookmark *bm_elt;
-
- bm = xmpp_get_all_storage_bookmarks();
-
- if (!bm)
- return;
-
- sbuf = g_string_new("");
-
- scr_WriteIncomingMessage(NULL, "List of MUC bookmarks:",
- 0, HBB_PREFIX_INFO, 0);
-
- for (bmp = bm; bmp; bmp = g_slist_next(bmp)) {
- bm_elt = bmp->data;
- g_string_printf(sbuf, "%c <%s>",
- (bm_elt->autojoin ? '*' : ' '), bm_elt->roomjid);
- if (bm_elt->nick)
- g_string_append_printf(sbuf, " (%s)", bm_elt->nick);
- if (bm_elt->name)
- g_string_append_printf(sbuf, " %s", bm_elt->name);
- g_free(bm_elt->roomjid);
- g_free(bm_elt->name);
- g_free(bm_elt->nick);
- g_free(bm_elt);
- scr_WriteIncomingMessage(NULL, sbuf->str,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- }
-
- scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
- update_roster = TRUE;
- g_string_free(sbuf, TRUE);
- g_slist_free(bm);
-}
-
-#ifdef MODULES_ENABLE
-static gint module_list_comparator(gconstpointer arg1, gconstpointer arg2)
-{
- const loaded_module_t *module = arg1;
- const char *name = arg2;
- return g_strcmp0(module->name, name);
-}
-
-static void do_load(char *arg)
-{
- GModule *mod;
- GSList *lmod;
- char *mdir, *path;
- if (!arg || !*arg) {
- scr_LogPrint(LPRINT_LOGNORM, "Missing modulename.");
- return;
- }
- lmod = g_slist_find_custom(loaded_modules, arg, module_list_comparator);
- if (lmod) {
- scr_LogPrint(LPRINT_LOGNORM, "Module %s is already loaded.", arg);
- return;
- }
- mdir = expand_filename(settings_opt_get("modules_dir"));
- path = g_module_build_path(mdir, arg);
- mod = g_module_open(path, G_MODULE_BIND_LAZY);
- if (!mod)
- scr_LogPrint(LPRINT_LOGNORM, "Module loading failed: %s",
- g_module_error());
- else {
- loaded_module_t *module = g_new(loaded_module_t, 1);
- module->name = g_strdup(arg);
- module->module = mod;
- loaded_modules = g_slist_prepend(loaded_modules, module);
- scr_LogPrint(LPRINT_LOGNORM, "Loaded module %s.", arg);
- }
- g_free(path);
- g_free(mdir);
-}
-
-static void do_unload(char *arg)
-{
- GSList *module;
- if (!arg || !*arg) {
- scr_LogPrint(LPRINT_LOGNORM, "Missing modulename.");
- return;
- }
- module = g_slist_find_custom(loaded_modules, arg, module_list_comparator);
- if (module) {
- loaded_module_t *mod = module->data;
- if (!g_module_close(mod->module))
- scr_LogPrint(LPRINT_LOGNORM, "Module unloading failed: %s",
- g_module_error());
- else {
- g_free(mod->name);
- g_free(mod);
- loaded_modules = g_slist_delete_link(loaded_modules, module);
- scr_LogPrint(LPRINT_LOGNORM, "Unloaded module %s.", arg);
- }
- } else
- scr_LogPrint(LPRINT_LOGNORM, "Module %s not loaded.", arg);
-}
-#endif
-
-static void do_room(char *arg)
-{
- char **paramlst;
- char *subcmd;
- gpointer bud;
-
- if (!lm_connection_is_authenticated(lconnection)) {
- scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
- return;
- }
-
- paramlst = split_arg(arg, 2, 1); // subcmd, arg
- subcmd = *paramlst;
- arg = *(paramlst+1);
-
- if (!subcmd || !*subcmd) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- free_arg_lst(paramlst);
- return;
- }
-
- if (current_buddy) {
- bud = BUDDATA(current_buddy);
- } else {
- if (strcasecmp(subcmd, "join")) {
- free_arg_lst(paramlst);
- return;
- }
- // "room join" is a special case, we don't need to have a valid
- // current_buddy.
- bud = NULL;
- }
-
- if (!strcasecmp(subcmd, "join")) {
- if ((arg = check_room_subcommand(arg, FALSE, NULL)) != NULL)
- room_join(bud, arg);
- } else if (!strcasecmp(subcmd, "invite")) {
- if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
- room_invite(bud, arg);
- } else if (!strcasecmp(subcmd, "affil")) {
- if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
- room_affil(bud, arg);
- } else if (!strcasecmp(subcmd, "role")) {
- if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
- room_role(bud, arg);
- } else if (!strcasecmp(subcmd, "ban")) {
- if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
- room_ban(bud, arg);
- } else if (!strcasecmp(subcmd, "unban")) {
- if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
- room_unban(bud, arg);
- } else if (!strcasecmp(subcmd, "kick")) {
- if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
- room_kick(bud, arg);
- } else if (!strcasecmp(subcmd, "leave")) {
- if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
- cmd_room_leave(bud, arg);
- } else if (!strcasecmp(subcmd, "names")) {
- if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
- room_names(bud, arg);
- } else if (!strcasecmp(subcmd, "nick")) {
- if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
- room_nick(bud, arg);
- } else if (!strcasecmp(subcmd, "privmsg")) {
- if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
- room_privmsg(bud, arg);
- } else if (!strcasecmp(subcmd, "remove")) {
- if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
- room_remove(bud, arg);
- } else if (!strcasecmp(subcmd, "destroy")) {
- if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
- room_destroy(bud, arg);
- } else if (!strcasecmp(subcmd, "unlock")) {
- if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
- room_unlock(bud, arg);
- } else if (!strcasecmp(subcmd, "setopt")) {
- if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
- room_setopt(bud, arg);
- } else if (!strcasecmp(subcmd, "topic")) {
- if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
- room_topic(bud, arg);
- } else if (!strcasecmp(subcmd, "whois")) {
- if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
- cmd_room_whois(bud, arg, TRUE);
- } else if (!strcasecmp(subcmd, "bookmark")) {
- if (!arg && !buddy_getjid(BUDDATA(current_buddy)) &&
- buddy_gettype(BUDDATA(current_buddy)) == ROSTER_TYPE_SPECIAL)
- display_all_bookmarks();
- else if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
- room_bookmark(bud, arg);
- } else {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
- }
-
- free_arg_lst(paramlst);
-}
-
-static void do_authorization(char *arg)
-{
- char **paramlst;
- char *subcmd;
- char *jid_utf8;
-
- if (!lm_connection_is_authenticated(lconnection)) {
- scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
- return;
- }
-
- paramlst = split_arg(arg, 2, 0); // subcmd, [jid]
- subcmd = *paramlst;
- arg = *(paramlst+1);
-
- if (!subcmd || !*subcmd) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
- goto do_authorization_return;
- }
-
- // Use the provided jid, if it looks valid
- if (arg) {
- if (!*arg) {
- // If no jid is provided, we use the current selected buddy
- arg = NULL;
- } else {
- if (check_jid_syntax(arg)) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "<%s> is not a valid Jabber ID.", arg);
- goto do_authorization_return;
- }
- }
- }
-
- if (!arg) { // Use the current selected buddy's jid
- gpointer bud;
- guint type;
-
- if (!current_buddy)
- goto do_authorization_return;
- bud = BUDDATA(current_buddy);
-
- jid_utf8 = arg = (char*)buddy_getjid(bud);
- type = buddy_gettype(bud);
-
- if (!(type & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT))) {
- scr_LogPrint(LPRINT_NORMAL, "Invalid buddy.");
- goto do_authorization_return;
- }
- } else {
- jid_utf8 = to_utf8(arg);
- }
-
- if (!strcasecmp(subcmd, "allow")) {
- xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_SUBSCRIBED);
- scr_LogPrint(LPRINT_LOGNORM,
- "Sent presence subscription approval to <%s>.",
- jid_utf8);
- } else if (!strcasecmp(subcmd, "cancel")) {
- xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED);
- scr_LogPrint(LPRINT_LOGNORM,
- "<%s> will no longer receive your presence updates.",
- jid_utf8);
- } else if (!strcasecmp(subcmd, "request")) {
- xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_SUBSCRIBE);
- scr_LogPrint(LPRINT_LOGNORM,
- "Sent presence notification request to <%s>.", jid_utf8);
- } else if (!strcasecmp(subcmd, "request_unsubscribe")) {
- xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE);
- scr_LogPrint(LPRINT_LOGNORM,
- "Sent presence notification unsubscription request to <%s>.",
- jid_utf8);
- } else {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
- }
-
- // Only free jid_utf8 if it has been allocated, i.e. if != arg.
- if (jid_utf8 && jid_utf8 != arg)
- g_free(jid_utf8);
-do_authorization_return:
- free_arg_lst(paramlst);
-}
-
-static void do_version(char *arg)
-{
- gchar *ver = mcabber_version();
- scr_LogPrint(LPRINT_NORMAL, "This is mcabber version %s.", ver);
- g_free(ver);
-}
-
-static void do_request(char *arg)
-{
- char **paramlst;
- char *fjid, *type;
- enum iqreq_type numtype = iqreq_none;
- char *jid_utf8 = NULL;
-
- paramlst = split_arg(arg, 2, 0); // type, jid
- type = *paramlst;
- fjid = *(paramlst+1);
-
- if (type) {
- // Quick check...
- if (!strcasecmp(type, "version"))
- numtype = iqreq_version;
- else if (!strcasecmp(type, "time"))
- numtype = iqreq_time;
- else if (!strcasecmp(type, "last"))
- numtype = iqreq_last;
- else if (!strcasecmp(type, "vcard"))
- numtype = iqreq_vcard;
- }
-
- if (!type || !numtype) {
- scr_LogPrint(LPRINT_NORMAL,
- "Please specify a query type (version, time...).");
- free_arg_lst(paramlst);
- return;
- }
-
- if (!lm_connection_is_authenticated(lconnection)) {
- scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
- free_arg_lst(paramlst);
- return;
- }
-
- // Allow special jid "" or "." (current buddy)
- if (fjid && (!*fjid || !strcmp(fjid, ".")))
- fjid = NULL;
-
- if (fjid) {
- // The JID has been specified. Quick check...
- if (check_jid_syntax(fjid)) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "<%s> is not a valid Jabber ID.", fjid);
- fjid = NULL;
- } else {
- // Convert jid to lowercase
- char *p;
- for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
- *p = tolower(*p);
- fjid = jid_utf8 = to_utf8(fjid);
- }
- } else {
- // Add the current buddy
- if (current_buddy)
- fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
- if (!fjid)
- scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
- }
-
- if (fjid) {
- switch (numtype) {
- case iqreq_version:
- case iqreq_time:
- case iqreq_last:
- case iqreq_vcard:
- xmpp_request(fjid, numtype);
- break;
- default:
- break;
- }
- }
- g_free(jid_utf8);
- free_arg_lst(paramlst);
-}
-
-static void do_event(char *arg)
-{
- char **paramlst;
- char *evid, *subcmd;
- int action = -1;
- GSList *evidlst;
-
- paramlst = split_arg(arg, 2, 0); // id, subcmd
- evid = *paramlst;
- subcmd = *(paramlst+1);
-
- if (!evid || !subcmd) {
- // Special case: /event list
- if (evid && !strcasecmp(evid, "list"))
- evs_display_list();
- else
- scr_LogPrint(LPRINT_NORMAL,
- "Missing parameter. Usage: /event num action");
- free_arg_lst(paramlst);
- return;
- }
-
- if (!strcasecmp(subcmd, "reject"))
- action = 0;
- else if (!strcasecmp(subcmd, "accept"))
- action = 1;
- else if (!strcasecmp(subcmd, "ignore"))
- action = 2;
-
- if (action == -1) {
- scr_LogPrint(LPRINT_NORMAL, "Wrong action parameter.");
- } else if (action >= 0 && action <= 2) {
- GSList *p;
-
- if (action == 2) {
- action = EVS_CONTEXT_CANCEL;
- } else {
- action += EVS_CONTEXT_USER;
- }
-
- if (!strcmp(evid, "*")) {
- // Use completion list
- evidlst = evs_geteventslist(FALSE);
- } else {
- // Let's create a slist with the provided event id
- evidlst = g_slist_append(NULL, g_strdup(evid));
- }
- for (p = evidlst; p; p = g_slist_next(p)) {
- if (evs_callback(p->data, action) == -1) {
- scr_LogPrint(LPRINT_NORMAL, "Event %s not found.", p->data);
- }
- g_free(p->data);
- }
- g_slist_free(evidlst);
- }
-
- free_arg_lst(paramlst);
-}
-
-static void do_pgp(char *arg)
-{
- char **paramlst;
- char *fjid, *subcmd, *keyid;
- enum {
- pgp_none,
- pgp_enable,
- pgp_disable,
- pgp_setkey,
- pgp_force,
- pgp_info
- } op = 0;
- int force = FALSE;
-
- paramlst = split_arg(arg, 3, 0); // subcmd, jid, [key]
- subcmd = *paramlst;
- fjid = *(paramlst+1);
- keyid = *(paramlst+2);
-
- if (!subcmd)
- fjid = NULL;
- if (!fjid)
- keyid = NULL;
-
- if (subcmd) {
- if (!strcasecmp(subcmd, "enable"))
- op = pgp_enable;
- else if (!strcasecmp(subcmd, "disable"))
- op = pgp_disable;
- else if (!strcasecmp(subcmd, "setkey"))
- op = pgp_setkey;
- else if ((!strcasecmp(subcmd, "force")) ||
- (!strcasecmp(subcmd, "+force"))) {
- op = pgp_force;
- force = TRUE;
- } else if (!strcasecmp(subcmd, "-force"))
- op = pgp_force;
- else if (!strcasecmp(subcmd, "info"))
- op = pgp_info;
- }
-
- if (!op) {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized or missing parameter!");
- free_arg_lst(paramlst);
- return;
- }
-
- // Allow special jid "" or "." (current buddy)
- if (fjid && (!*fjid || !strcmp(fjid, ".")))
- fjid = NULL;
-
- if (fjid) {
- // The JID has been specified. Quick check...
- if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "<%s> is not a valid Jabber ID.", fjid);
- fjid = NULL;
- } else {
- // Convert jid to lowercase and strip resource
- char *p;
- for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
- *p = tolower(*p);
- if (*p == JID_RESOURCE_SEPARATOR)
- *p = '\0';
- }
- } else {
- gpointer bud = NULL;
- if (current_buddy)
- bud = BUDDATA(current_buddy);
- if (bud) {
- guint type = buddy_gettype(bud);
- if (type & ROSTER_TYPE_USER) // Is it a user?
- fjid = (char*)buddy_getjid(bud);
- else
- scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
- }
- }
-
- if (fjid) { // fjid is actually a bare jid...
- guint disabled;
- GString *sbuf;
- switch (op) {
- case pgp_enable:
- case pgp_disable:
- settings_pgp_setdisabled(fjid, (op == pgp_disable ? TRUE : FALSE));
- break;
- case pgp_force:
- settings_pgp_setforce(fjid, force);
- break;
- case pgp_setkey:
- settings_pgp_setkeyid(fjid, keyid);
- break;
- case pgp_info:
- sbuf = g_string_new("");
- if (settings_pgp_getkeyid(fjid)) {
- g_string_printf(sbuf, "PGP Encryption key id: %s",
- settings_pgp_getkeyid(fjid));
- scr_WriteIncomingMessage(fjid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
- }
- disabled = settings_pgp_getdisabled(fjid);
- g_string_printf(sbuf, "PGP encryption is %s",
- (disabled ? "disabled" : "enabled"));
- scr_WriteIncomingMessage(fjid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
- if (!disabled && settings_pgp_getforce(fjid)) {
- scr_WriteIncomingMessage(fjid,
- "Encryption enforced (no negotiation)",
- 0, HBB_PREFIX_INFO, 0);
- }
- g_string_free(sbuf, TRUE);
- break;
- default:
- break;
- }
- } else {
- scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
- }
-
- free_arg_lst(paramlst);
-}
-
-static void do_otr(char *arg)
-{
-#ifdef HAVE_LIBOTR
- char **paramlst;
- char *fjid, *subcmd, *keyid;
- enum {
- otr_none,
- otr_start,
- otr_stop,
- otr_fpr,
- otr_smpq,
- otr_smpr,
- otr_smpa,
- otr_k,
- otr_info
- } op = 0;
-
- if (!otr_enabled()) {
- scr_LogPrint(LPRINT_LOGNORM,
- "Warning: OTR hasn't been enabled -- command ignored.");
- return;
- }
-
- paramlst = split_arg(arg, 3, 0); // subcmd, jid, [key]
- subcmd = *paramlst;
- fjid = *(paramlst+1);
- keyid = *(paramlst+2);
-
- if (!subcmd)
- fjid = NULL;
- if (!fjid)
- keyid = NULL;
-
- if (subcmd) {
- if (!strcasecmp(subcmd, "start"))
- op = otr_start;
- else if (!strcasecmp(subcmd, "stop"))
- op = otr_stop;
- else if (!strcasecmp(subcmd, "fingerprint"))
- op = otr_fpr;
- else if (!strcasecmp(subcmd, "smpq"))
- op = otr_smpq;
- else if (!strcasecmp(subcmd, "smpr"))
- op = otr_smpr;
- else if (!strcasecmp(subcmd, "smpa"))
- op = otr_smpa;
- else if (!strcasecmp(subcmd, "key"))
- op = otr_k;
- else if (!strcasecmp(subcmd, "info"))
- op = otr_info;
- }
-
- if (!op) {
- scr_LogPrint(LPRINT_NORMAL, "Unrecognized or missing parameter!");
- free_arg_lst(paramlst);
- return;
- }
-
- if (op == otr_k)
- otr_key();
- else {
- // Allow special jid "" or "." (current buddy)
- if (fjid && (!*fjid || !strcmp(fjid, ".")))
- fjid = NULL;
-
- if (fjid) {
- // The JID has been specified. Quick check...
- if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "<%s> is not a valid Jabber ID.", fjid);
- fjid = NULL;
- } else {
- // Convert jid to lowercase and strip resource
- char *p;
- for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
- *p = tolower(*p);
- if (*p == JID_RESOURCE_SEPARATOR)
- *p = '\0';
- }
- } else {
- gpointer bud = NULL;
- if (current_buddy)
- bud = BUDDATA(current_buddy);
- if (bud) {
- guint type = buddy_gettype(bud);
- if (type & ROSTER_TYPE_USER) // Is it a user?
- fjid = (char*)buddy_getjid(bud);
- else
- scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
- }
- }
-
- if (fjid) { // fjid is actually a bare jid...
- switch (op) {
- case otr_start:
- otr_establish(fjid); break;
- case otr_stop:
- otr_disconnect(fjid); break;
- case otr_fpr:
- otr_fingerprint(fjid, keyid); break;
- case otr_smpq:
- otr_smp_query(fjid, keyid); break;
- case otr_smpr:
- otr_smp_respond(fjid, keyid); break;
- case otr_smpa:
- otr_smp_abort(fjid); break;
- case otr_info:
- otr_print_info(fjid); break;
- default:
- break;
- }
- } else
- scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
- }
- free_arg_lst(paramlst);
-
-#else
- scr_LogPrint(LPRINT_NORMAL, "Please recompile mcabber with libotr enabled.");
-#endif /* HAVE_LIBOTR */
-}
-
-#ifdef HAVE_LIBOTR
-static char *string_for_otrpolicy(enum otr_policy p)
-{
- switch (p) {
- case plain: return "plain";
- case opportunistic: return "opportunistic";
- case manual: return "manual";
- case always: return "always";
- default: return "unknown";
- }
-}
-
-static void dump_otrpolicy(char *k, char *v, void *nothing)
-{
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, "otrpolicy for %s: %s", k,
- string_for_otrpolicy(*(enum otr_policy*)v));
-}
-#endif
-
-static void do_otrpolicy(char *arg)
-{
-#ifdef HAVE_LIBOTR
- char **paramlst;
- char *fjid, *policy;
- enum otr_policy p;
-
- paramlst = split_arg(arg, 2, 0); // [jid|default] policy
- fjid = *paramlst;
- policy = *(paramlst+1);
-
- if (!fjid && !policy) {
- scr_LogPrint(LPRINT_NORMAL, "default otrpolicy: %s",
- string_for_otrpolicy(settings_otr_getpolicy(NULL)));
- settings_foreach(SETTINGS_TYPE_OTR, &dump_otrpolicy, NULL);
- free_arg_lst(paramlst);
- return;
- }
-
- if (!policy) {
- scr_LogPrint(LPRINT_NORMAL,
- "Please call otrpolicy correctly: /otrpolicy (default|jid) "
- "(plain|manual|opportunistic|always)");
- free_arg_lst(paramlst);
- return;
- }
-
- if (!strcasecmp(policy, "plain"))
- p = plain;
- else if (!strcasecmp(policy, "manual"))
- p = manual;
- else if (!strcasecmp(policy, "opportunistic"))
- p = opportunistic;
- else if (!strcasecmp(policy, "always"))
- p = always;
- else {
- /* Fail, we don't know _this_ policy*/
- scr_LogPrint(LPRINT_NORMAL, "mcabber doesn't support _this_ policy!");
- free_arg_lst(paramlst);
- return;
- }
-
- if (!strcasecmp(fjid, "default") || !strcasecmp(fjid, "*")) {
- /*set default policy*/
- settings_otr_setpolicy(NULL, p);
- free_arg_lst(paramlst);
- return;
- }
- // Allow special jid "" or "." (current buddy)
- if (fjid && (!*fjid || !strcmp(fjid, ".")))
- fjid = NULL;
-
- if (fjid) {
- // The JID has been specified. Quick check...
- if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "<%s> is not a valid Jabber ID.", fjid);
- fjid = NULL;
- } else {
- // Convert jid to lowercase and strip resource
- char *p;
- for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
- *p = tolower(*p);
- if (*p == JID_RESOURCE_SEPARATOR)
- *p = '\0';
- }
- } else {
- gpointer bud = NULL;
- if (current_buddy)
- bud = BUDDATA(current_buddy);
- if (bud) {
- guint type = buddy_gettype(bud);
- if (type & ROSTER_TYPE_USER) // Is it a user?
- fjid = (char*)buddy_getjid(bud);
- else
- scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
- }
- }
-
- if (fjid)
- settings_otr_setpolicy(fjid, p);
- else
- scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
-
- free_arg_lst(paramlst);
-#else
- scr_LogPrint(LPRINT_NORMAL, "Please recompile mcabber with libotr enabled.");
-#endif /* HAVE_LIBOTR */
-}
-
-/* !!!
- After changing the /iline arguments names here, you must change ones
- in init_bindings().
-*/
-static void do_iline(char *arg)
-{
- if (!strcasecmp(arg, "fword")) {
- readline_forward_word();
- } else if (!strcasecmp(arg, "bword")) {
- readline_backward_word();
- } else if (!strcasecmp(arg, "word_fdel")) {
- readline_forward_kill_word();
- } else if (!strcasecmp(arg, "word_bdel")) {
- readline_backward_kill_word();
- } else if (!strcasecmp(arg, "word_upcase")) {
- readline_updowncase_word(1);
- } else if (!strcasecmp(arg, "word_downcase")) {
- readline_updowncase_word(0);
- } else if (!strcasecmp(arg, "word_capit")) {
- readline_capitalize_word();
- } else if (!strcasecmp(arg, "fchar")) {
- readline_forward_char();
- } else if (!strcasecmp(arg, "bchar")) {
- readline_backward_char();
- } else if (!strcasecmp(arg, "char_fdel")) {
- readline_forward_kill_char();
- } else if (!strcasecmp(arg, "char_bdel")) {
- readline_backward_kill_char();
- } else if (!strcasecmp(arg, "char_swap")) {
- readline_transpose_chars();
- } else if (!strcasecmp(arg, "hist_beginning_search_bwd")) {
- readline_hist_beginning_search_bwd();
- } else if (!strcasecmp(arg, "hist_beginning_search_fwd")) {
- readline_hist_beginning_search_fwd();
- } else if (!strcasecmp(arg, "hist_prev")) {
- readline_hist_prev();
- } else if (!strcasecmp(arg, "hist_next")) {
- readline_hist_next();
- } else if (!strcasecmp(arg, "iline_start")) {
- readline_iline_start();
- } else if (!strcasecmp(arg, "iline_end")) {
- readline_iline_end();
- } else if (!strcasecmp(arg, "iline_fdel")) {
- readline_forward_kill_iline();
- } else if (!strcasecmp(arg, "iline_bdel")) {
- readline_backward_kill_iline();
- } else if (!strcasecmp(arg, "send_multiline")) {
- readline_send_multiline();
- } else if (!strcasecmp(arg, "iline_accept")) {
- retval_for_cmds = readline_accept_line(FALSE);
- } else if (!strcasecmp(arg, "iline_accept_down_hist")) {
- retval_for_cmds = readline_accept_line(TRUE);
- } else if (!strcasecmp(arg, "compl_cancel")) {
- readline_cancel_completion();
- } else if (!strcasecmp(arg, "compl_do")) {
- readline_do_completion();
- }
-}
-
-static void do_screen_refresh(char *arg)
-{
- readline_refresh_screen();
-}
-
-static void do_chat_disable(char *arg)
-{
- guint show_roster;
-
- if (arg && !strcasecmp(arg, "--show-roster"))
- show_roster = 1;
- else
- show_roster = 0;
-
- readline_disable_chat_mode(show_roster);
-}
-
-static void do_source(char *arg)
-{
- static int recur_level;
- gchar *filename, *expfname;
- if (!*arg) {
- scr_LogPrint(LPRINT_NORMAL, "Missing filename.");
- return;
- }
- if (recur_level > 20) {
- scr_LogPrint(LPRINT_LOGNORM, "** Too many source commands!");
- return;
- }
- filename = g_strdup(arg);
- strip_arg_special_chars(filename);
- expfname = expand_filename(filename);
- recur_level++;
- cfg_read_file(expfname, FALSE);
- recur_level--;
- g_free(filename);
- g_free(expfname);
-}
-
-static void do_connect(char *arg)
-{
- xmpp_connect();
-}
-
-static void do_disconnect(char *arg)
-{
- xmpp_disconnect();
-}
-
-static void do_help(char *arg)
-{
- help_process(arg);
-}
-
-static void do_echo(char *arg)
-{
- if (arg)
- scr_print_logwindow(arg);
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/commands.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#ifndef __COMMANDS_H__
-#define __COMMANDS_H__ 1
-
-#include <glib.h>
-
-#include "config.h"
-
-// Command structure
-typedef struct {
- char name[32];
- const char *help;
- guint completion_flags[2];
- void (*func)(char *);
-#ifdef MODULES_ENABLE
- gpointer userdata;
-#endif
-} cmd;
-
-void cmd_init(void);
-cmd *cmd_get(const char *command);
-int process_line(const char *line);
-int process_command(const char *line, guint iscmd);
-char *expandalias(const char *line);
-#ifdef MODULES_ENABLE
-void cmd_deinit(void);
-gpointer cmd_del(const char *name);
-void cmd_add(const char *name, const char *help, guint flags1, guint flags2, void (*f)(char*), gpointer userdata);
-#endif
-
-void cmd_room_whois(gpointer bud, char *nick_locale, guint interactive);
-void cmd_room_leave(gpointer bud, char *arg);
-void cmd_setstatus(const char *recipient, const char *arg);
-
-#endif /* __COMMANDS_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/compl.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,286 +0,0 @@
-/*
- * compl.c -- Completion system
- *
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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
- */
-
-/* Usage, basically:
- * - new_completion(); // 1. Initialization
- * - complete(); // 2. 1st completion
- * - cancel_completion(); // 3a. 2nd completion / cancel previous
- * - complete(); // 3b. 2nd completion / complete
- * ...
- * - done_completion(); // n. finished -- free allocated areas
- *
- */
-
-#include <string.h>
-
-#include "compl.h"
-#include "utf8.h"
-#include "roster.h"
-#include "events.h"
-
-// Completion structure
-typedef struct {
- GSList *list; // list of matches
- guint len_prefix; // length of text already typed by the user
- guint len_compl; // length of the last completion
- GSList *next; // pointer to next completion to try
-} compl;
-
-// Category structure
-typedef struct {
- guint flag;
- GSList *words;
-} category;
-
-static GSList *Categories;
-static compl *InputCompl;
-
-#ifdef MODULES_ENABLE
-guint registered_cats = COMPL_CMD|COMPL_JID|COMPL_URLJID|COMPL_NAME| \
- COMPL_STATUS|COMPL_FILENAME|COMPL_ROSTER|COMPL_BUFFER| \
- COMPL_GROUP|COMPL_GROUPNAME|COMPL_MULTILINE|COMPL_ROOM| \
- COMPL_RESOURCE|COMPL_AUTH|COMPL_REQUEST|COMPL_EVENTS| \
- COMPL_EVENTSID|COMPL_PGP|COMPL_COLOR| \
- COMPL_OTR|COMPL_OTRPOLICY| \
- 0;
-
-// compl_new_category()
-// Reserves id for new completion category.
-// Returns 0, if no more categories can be allocated.
-// Note, that user should not make any assumptions about id nature,
-// as it is likely to change in future.
-guint compl_new_category (void)
-{
- guint i = 0;
- while ((registered_cats >> i) & 1)
- i++;
- if (i >= sizeof (guint)*8)
- return 0;
- else {
- guint id = 1 << i;
- registered_cats |= id;
- return id;
- }
-}
-
-// compl_del_category (id)
-// Frees reserved id for category.
-// Note, that for now it not validates its input, so, be careful
-// and specify exactly what you get from compl_new_category.
-void compl_del_category (guint id)
-{
- registered_cats &= ~id;
-}
-#endif
-
-// new_completion(prefix, compl_cat)
-// . prefix = beginning of the word, typed by the user
-// . compl_cat = pointer to a completion category list (list of *char)
-// Set the InputCompl pointer to an allocated compl structure.
-// done_completion() must be called when finished.
-// Returns the number of possible completions.
-guint new_completion(char *prefix, GSList *compl_cat)
-{
- compl *c;
- GSList *sl_cat;
- size_t len = strlen(prefix);
-
- if (InputCompl) { // This should not happen, but hey...
- cancel_completion();
- }
-
- c = g_new0(compl, 1);
- // Build the list of matches
- for (sl_cat = compl_cat; sl_cat; sl_cat = g_slist_next(sl_cat)) {
- char *word = sl_cat->data;
- if (!strncasecmp(prefix, word, len)) {
- if (strlen(word) != len)
- c->list = g_slist_append(c->list, g_strdup(word+len)); // TODO sort
- }
- }
- c->next = c->list;
- InputCompl = c;
- return g_slist_length(c->list);
-}
-
-// done_completion();
-void done_completion(void)
-{
- GSList *clp;
-
- if (!InputCompl) return;
-
- // Free the current completion list
- for (clp = InputCompl->list; clp; clp = g_slist_next(clp))
- g_free(clp->data);
- g_slist_free(InputCompl->list);
- g_free(InputCompl);
- InputCompl = NULL;
-}
-
-// cancel_completion()
-// Returns the number of chars to delete to cancel the completion
-//guint cancel_completion(compl *c)
-guint cancel_completion(void)
-{
- if (!InputCompl) return 0;
- return InputCompl->len_compl;
-}
-
-// Returns pointer to text to insert, NULL if no completion.
-const char *complete()
-{
- compl* c = InputCompl;
- char *r;
-
- if (!InputCompl) return NULL;
-
- if (!c->next) {
- c->next = c->list; // back to the beginning
- c->len_compl = 0;
- return NULL;
- }
- r = (char*)c->next->data;
- c->next = g_slist_next(c->next);
- if (!utf8_mode) {
- c->len_compl = strlen(r);
- } else {
- char *wc;
- c->len_compl = 0;
- for (wc = r; *wc; wc = next_char(wc))
- c->len_compl++;
- }
- return r;
-}
-
-
-/* Categories functions */
-
-// compl_add_category_word(categ, command)
-// Adds a keyword as a possible completion in category categ.
-void compl_add_category_word(guint categ, const char *word)
-{
- GSList *sl_cat;
- category *cat;
- char *nword;
- // Look for category
- for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
- if (categ == ((category*)sl_cat->data)->flag)
- break;
- }
- if (!sl_cat) { // Category not found, let's create it
- cat = g_new0(category, 1);
- cat->flag = categ;
- Categories = g_slist_append(Categories, cat);
- } else
- cat = (category*)sl_cat->data;
-
- // If word is not space-terminated, we add one trailing space
- for (nword = (char*)word; *nword; nword++)
- ;
- if (nword > word) nword--;
- if (*nword != ' ') { // Add a space
- nword = g_strdup_printf("%s ", word);
- } else { // word is fine
- nword = g_strdup(word);
- }
-
- // TODO Check word does not already exist
- cat->words = g_slist_append(cat->words, nword); // TODO sort
-}
-
-// compl_del_category_word(categ, command)
-// Removes a keyword from category categ in completion list.
-void compl_del_category_word(guint categ, const char *word)
-{
- GSList *sl_cat, *sl_elt;
- category *cat;
- char *nword;
- // Look for category
- for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
- if (categ == ((category*)sl_cat->data)->flag)
- break;
- }
- if (!sl_cat) return; // Category not found, finished!
-
- cat = (category*)sl_cat->data;
-
- // If word is not space-terminated, we add one trailing space
- for (nword = (char*)word; *nword; nword++)
- ;
- if (nword > word) nword--;
- if (*nword != ' ') { // Add a space
- nword = g_strdup_printf("%s ", word);
- } else { // word is fine
- nword = g_strdup(word);
- }
-
- sl_elt = cat->words;
- while (sl_elt) {
- if (!strcasecmp((char*)sl_elt->data, nword)) {
- g_free(sl_elt->data);
- cat->words = g_slist_delete_link(cat->words, sl_elt);
- break; // Only remove first occurence
- }
- sl_elt = g_slist_next(sl_elt);
- }
-}
-
-// compl_get_category_list()
-// Returns a slist of all words in the categories specified by the given flags
-// Iff this function sets *dynlist to TRUE, then the caller must free the
-// whole list after use.
-GSList *compl_get_category_list(guint cat_flags, guint *dynlist)
-{
- GSList *sl_cat;
-
- *dynlist = FALSE;
-
- // Look for category
- // XXX Actually that's not that simple... cat_flags can be a combination
- // of several flags!
- for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
- if (cat_flags == ((category*)sl_cat->data)->flag)
- break;
- }
- if (sl_cat) // Category was found, easy...
- return ((category*)sl_cat->data)->words;
-
- // Handle dynamic SLists
- *dynlist = TRUE;
- if (cat_flags == COMPL_GROUPNAME) {
- return compl_list(ROSTER_TYPE_GROUP);
- }
- if (cat_flags == COMPL_JID) {
- return compl_list(ROSTER_TYPE_USER);
- }
- if (cat_flags == COMPL_RESOURCE) {
- return buddy_getresources_locale(NULL);
- }
- if (cat_flags == COMPL_EVENTSID) {
- return evs_geteventslist(TRUE);
- }
-
- *dynlist = FALSE;
- return NULL;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/compl.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-#ifndef __COMPL_H__
-#define __COMPL_H__ 1
-
-#include <glib.h>
-
-#include "config.h"
-
-#define COMPL_CMD (1U<<0)
-#define COMPL_JID (1U<<1)
-#define COMPL_URLJID (1U<<2) // Not implemented yet
-#define COMPL_NAME (1U<<3) // Not implemented yet
-#define COMPL_STATUS (1U<<4)
-#define COMPL_FILENAME (1U<<5) // Not implemented yet
-#define COMPL_ROSTER (1U<<6)
-#define COMPL_BUFFER (1U<<7)
-#define COMPL_GROUP (1U<<8)
-#define COMPL_GROUPNAME (1U<<9)
-#define COMPL_MULTILINE (1U<<10)
-#define COMPL_ROOM (1U<<11)
-#define COMPL_RESOURCE (1U<<12)
-#define COMPL_AUTH (1U<<13)
-#define COMPL_REQUEST (1U<<14)
-#define COMPL_EVENTS (1U<<15)
-#define COMPL_EVENTSID (1U<<16)
-#define COMPL_PGP (1U<<17)
-#define COMPL_COLOR (1U<<18)
-#define COMPL_OTR (1U<<19)
-#define COMPL_OTRPOLICY (1U<<20)
-#ifdef MODULES_ENABLE
-#define COMPL_MAX_BUILTIN (1U<<20)
-
-guint compl_new_category (void);
-void compl_del_category (guint id);
-#endif
-
-void compl_add_category_word(guint, const char *command);
-void compl_del_category_word(guint categ, const char *word);
-GSList *compl_get_category_list(guint cat_flags, guint *dynlist);
-
-guint new_completion(char *prefix, GSList *compl_cat);
-void done_completion(void);
-guint cancel_completion(void);
-const char *complete(void);
-
-#endif /* __COMPL_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/events.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-/*
- * events.c -- Events fonctions
- *
- * Copyright (C) 2006-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 <string.h>
-#include "events.h"
-#include "logprint.h"
-
-static GSList *evs_list; // Events list
-
-static eviqs *evs_find(const char *evid);
-
-// evs_new(type, timeout)
-// Create an events structure.
-eviqs *evs_new(guint8 type, time_t timeout)
-{
- static guint evs_idn;
- eviqs *new_evs;
- time_t now_t;
- char *stridn;
-
- if (!++evs_idn)
- evs_idn = 1;
- /* Check for wrapping, we shouldn't reuse ids */
- stridn = g_strdup_printf("%d", evs_idn);
- if (evs_find(stridn)) {
- g_free(stridn);
- // We could try another id but for now giving up should be fine...
- return NULL;
- }
-
- new_evs = g_new0(eviqs, 1);
- time(&now_t);
- new_evs->ts_create = now_t;
- if (timeout)
- new_evs->ts_expire = now_t + timeout;
- new_evs->type = type;
- new_evs->id = stridn;
-
- if(!g_slist_length(evs_list))
- g_timeout_add_seconds(20, evs_check_timeout, NULL);
- evs_list = g_slist_append(evs_list, new_evs);
- return new_evs;
-}
-
-int evs_del(const char *evid)
-{
- GSList *p;
- eviqs *i;
-
- if (!evid) return 1;
-
- for (p = evs_list; p; p = g_slist_next(p)) {
- i = p->data;
- if (!strcmp(evid, i->id))
- break;
- }
- if (p) {
- g_free(i->id);
- g_free(i->data);
- g_free(i->desc);
- g_free(i);
- evs_list = g_slist_remove(evs_list, p->data);
- return 0; // Ok, deleted
- }
- return -1; // Not found
-}
-
-static eviqs *evs_find(const char *evid)
-{
- GSList *p;
- eviqs *i;
-
- if (!evid) return NULL;
-
- for (p = evs_list; p; p = g_slist_next(p)) {
- i = p->data;
- if (!strcmp(evid, i->id))
- return i;
- }
- return NULL;
-}
-
-// evs_callback(evid, evcontext)
-// Callback processing for the specified event.
-// Return 0 in case of success, -1 if the evid hasn't been found.
-int evs_callback(const char *evid, guint evcontext)
-{
- eviqs *i;
-
- i = evs_find(evid);
- if (!i) return -1;
-
- // IQ processing
- // Note: If xml_result is NULL, this is a timeout
- if (i->callback)
- (void)(*i->callback)(i, evcontext);
-
- evs_del(evid);
- return 0;
-}
-
-gboolean evs_check_timeout()
-{
- time_t now_t;
- GSList *p;
- eviqs *i;
-
- time(&now_t);
- p = evs_list;
- if (!p)
- return FALSE;
- while (p) {
- i = p->data;
- // We must get next IQ eviqs element now because the current one
- // could be freed.
- p = g_slist_next(p);
-
- if ((!i->ts_expire && now_t > i->ts_create + EVS_MAX_TIMEOUT) ||
- (i->ts_expire && now_t > i->ts_expire)) {
- evs_callback(i->id, EVS_CONTEXT_TIMEOUT);
- }
- }
- return TRUE;
-}
-
-void evs_display_list(void)
-{
- GSList *p;
- eviqs *i;
-
- scr_LogPrint(LPRINT_LOGNORM, "Events list:");
- for (p = evs_list; p; p = g_slist_next(p)) {
- i = p->data;
- scr_LogPrint(LPRINT_LOGNORM,
- "Id: %-3s %s", i->id, (i->desc ? i->desc : ""));
- }
- scr_LogPrint(LPRINT_LOGNORM, "End of events list.");
-}
-
-// evs_geteventslist(bool comp)
-// Return a singly-linked-list of events ids, for the completion system.
-// If comp is true, the string "list" is added (it's a completion argument).
-// Note: the caller should free the list (and data) after use.
-GSList *evs_geteventslist(int compl)
-{
- GSList *evidlist = NULL, *p;
- eviqs *i;
-
- for (p = evs_list; p; p = g_slist_next(p)) {
- i = p->data;
- evidlist = g_slist_append(evidlist, g_strdup(i->id));
- }
-
- if (compl) {
- // Last item is the "list" subcommand.
- evidlist = g_slist_append(evidlist, g_strdup("list"));
- }
- return evidlist;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/events.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-#ifndef __EVENTS_H__
-#define __EVENTS_H__ 1
-
-#include "config.h" // MODULES_ENABLE
-
-#define EVS_DEFAULT_TIMEOUT 90
-#define EVS_MAX_TIMEOUT 432000
-
-#define EVS_CONTEXT_TIMEOUT 0U
-#define EVS_CONTEXT_CANCEL 1U
-#define EVS_CONTEXT_USER 2U
-
-typedef enum {
- EVS_TYPE_SUBSCRIPTION = 1,
- EVS_TYPE_INVITATION = 2,
-#ifdef MODULES_ENABLE
- EVS_TYPE_USER = 3,
-#endif
-} evs_type;
-
-/* Common structure for events (evs) and IQ requests (iqs) */
-typedef struct {
- char *id;
- time_t ts_create;
- time_t ts_expire;
- guint8 type;
- gpointer data;
- int (*callback)();
- char *desc;
-} eviqs;
-
-typedef struct {
- char* to;
- char* from;
- char* passwd;
- char* reason;
-} event_muc_invitation;
-
-eviqs *evs_new(guint8 type, time_t timeout);
-int evs_del(const char *evid);
-int evs_callback(const char *evid, guint evcontext);
-gboolean evs_check_timeout();
-void evs_display_list(void);
-GSList *evs_geteventslist(int forcompl);
-
-#endif /* __EVENTS_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/fifo.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-/*
- * fifo.c -- Read commands from a named pipe
- *
- * Copyright (C) 2008,2009 Mikael Berthe <mikael@lilotux.net>
- * Copyrigth (C) 2009 Myhailo Danylenko <isbear@ukrpost.net>
- *
- * 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 <stdlib.h>
-#include <glib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include "commands.h"
-#include "logprint.h"
-#include "utils.h"
-#include "settings.h"
-#include "main.h"
-
-static char *fifo_name = NULL;
-static GIOChannel *fifo_channel = NULL;
-
-static const char *FIFO_ENV_NAME = "MCABBER_FIFO";
-
-static gboolean attach_fifo(const char *name);
-
-static guint fifo_callback(GIOChannel *channel,
- GIOCondition condition,
- gpointer data)
-{
- if (condition & (G_IO_IN|G_IO_PRI)) {
- GIOStatus chstat;
- gchar *buf;
- gsize endpos;
-
- chstat = g_io_channel_read_line(channel, &buf, NULL, &endpos, NULL);
- if (chstat == G_IO_STATUS_ERROR || chstat == G_IO_STATUS_EOF) {
- if (!attach_fifo(fifo_name))
- scr_LogPrint(LPRINT_LOGNORM,
- "Reopening fifo failed! Fifo will not work from now!");
- return FALSE;
- }
- if (buf) {
- guint logflag;
- guint fifo_ignore = settings_opt_get_int("fifo_ignore");
-
- if (endpos)
- buf[endpos] = '\0';
-
- if (settings_opt_get_int("fifo_hide_commands"))
- logflag = LPRINT_LOG;
- else
- logflag = LPRINT_LOGNORM;
- scr_LogPrint(logflag, "%s FIFO command: %s",
- (fifo_ignore ? "Ignoring" : "Executing"), buf);
- if (!fifo_ignore) {
- if (process_command(buf, TRUE) == 255)
- mcabber_set_terminate_ui();
- }
-
- g_free(buf);
- }
- } else if (condition & (G_IO_ERR|G_IO_NVAL|G_IO_HUP)) {
- if (!attach_fifo(fifo_name))
- scr_LogPrint(LPRINT_LOGNORM,
- "Reopening fifo failed! Fifo will not work from now!");
- return FALSE;
- }
- return TRUE;
-}
-
-static void fifo_destroy_callback(gpointer data)
-{
- GIOChannel *channel = (GIOChannel *)data;
- g_io_channel_unref(channel);
-}
-
-static gboolean check_fifo(const char *name)
-{
- struct stat finfo;
- if (stat(name, &finfo) == -1) {
- /* some unknown error */
- if (errno != ENOENT)
- return FALSE;
- /* fifo not yet exists */
- if (mkfifo(name, S_IRUSR|S_IWUSR) != -1)
- return check_fifo(name);
- else
- return FALSE;
- }
-
- /* file exists */
- if (S_ISFIFO(finfo.st_mode))
- return TRUE;
- else
- return FALSE;
-}
-
-static gboolean attach_fifo(const char *name)
-{
- GSource *source;
- int fd = open (name, O_RDONLY|O_NONBLOCK);
- if (fd == -1)
- return FALSE;
-
- fifo_channel = g_io_channel_unix_new(fd);
-
- g_io_channel_set_flags(fifo_channel, G_IO_FLAG_NONBLOCK, NULL);
- g_io_channel_set_encoding(fifo_channel, NULL, NULL);
- g_io_channel_set_close_on_unref(fifo_channel, TRUE);
-
- source = g_io_create_watch(fifo_channel,
- G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL);
- g_source_set_callback(source, (GSourceFunc)fifo_callback,
- (gpointer)fifo_channel,
- (GDestroyNotify)fifo_destroy_callback);
- g_source_attach(source, main_context);
-
- return TRUE;
-}
-
-int fifo_init(const char *fifo_path)
-{
- if (fifo_path) {
- fifo_name = expand_filename(fifo_path);
-
- if (!check_fifo(fifo_name)) {
- scr_LogPrint(LPRINT_LOGNORM, "WARNING: Cannot create the FIFO. "
- "%s already exists and is not a pipe", fifo_name);
- g_free(fifo_name);
- return -1;
- }
- } else if (fifo_name)
- g_source_remove_by_user_data(fifo_channel);
- else
- return -1;
-
- if (!attach_fifo(fifo_name)) {
- scr_LogPrint(LPRINT_LOGNORM, "Error: Cannot open fifo");
- return -1;
- }
-
- setenv(FIFO_ENV_NAME, fifo_name, 1);
-
- scr_LogPrint(LPRINT_LOGNORM, "FIFO initialized (%s)", fifo_path);
- return 1;
-}
-
-void fifo_deinit(void)
-{
- unsetenv(FIFO_ENV_NAME);
-
- /* destroy open fifo */
- unlink(fifo_name);
- g_source_remove_by_user_data(fifo_channel);
- /* channel itself should be destroyed by destruction callback */
- g_free(fifo_name);
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/fifo.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#ifndef __FIFO_H__
-#define __FIFO_H__ 1
-
-int fifo_init(const char *fifo_path);
-void fifo_deinit(void);
-
-#endif /* __FIFO_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/hbuf.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,516 +0,0 @@
-/*
- * hbuf.c -- History buffer implementation
- *
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "hbuf.h"
-#include "utils.h"
-#include "utf8.h"
-#include "screen.h"
-
-
-/* This is a private structure type */
-
-typedef struct {
- char *ptr;
- char *ptr_end; // beginning of the block
- char *ptr_end_alloc; // end of the current persistent block
- guchar flags;
-
- // XXX This should certainly be a pointer, and be allocated only when needed
- // (for ex. when HBB_FLAG_PERSISTENT is set).
- struct { // hbuf_line_info
- time_t timestamp;
- unsigned mucnicklen;
- guint flags;
- gpointer xep184;
- } prefix;
-} hbuf_block;
-
-
-// do_wrap(p_hbuf, first_hbuf_elt, width)
-// Wrap hbuf lines with the specified width.
-// '\n' are handled by this routine (they are removed and persistent lines
-// are created).
-// All hbuf elements are processed, starting from first_hbuf_elt.
-static inline void do_wrap(GList **p_hbuf, GList *first_hbuf_elt,
- unsigned int width)
-{
- GList *curr_elt = first_hbuf_elt;
-
- // Let's add non-persistent blocs if necessary
- // - If there are '\n' in the string
- // - If length > width (and width != 0)
- while (curr_elt) {
- hbuf_block *hbuf_b_curr, *hbuf_b_prev;
- char *c, *end;
- char *br = NULL; // break pointer
- char *cr = NULL; // CR pointer
- unsigned int cur_w = 0;
-
- // We want to break where we can find a space char or a CR
-
- hbuf_b_curr = (hbuf_block*)(curr_elt->data);
- hbuf_b_prev = hbuf_b_curr;
- c = hbuf_b_curr->ptr;
-
- while (*c && (!width || cur_w <= width)) {
- if (*c == '\n') {
- br = cr = c;
- *c = 0;
- break;
- }
- if (iswblank(get_char(c)))
- br = c;
- cur_w += get_char_width(c);
- c = next_char(c);
- }
-
- if (cr || (*c && cur_w > width)) {
- if (!br || br == hbuf_b_curr->ptr)
- br = c;
- else
- br = next_char(br);
- end = hbuf_b_curr->ptr_end;
- hbuf_b_curr->ptr_end = br;
- // Create another block
- hbuf_b_curr = g_new0(hbuf_block, 1);
- // The block must be persistent after a CR
- if (cr) {
- hbuf_b_curr->ptr = hbuf_b_prev->ptr_end + 1; // == cr+1
- hbuf_b_curr->flags = HBB_FLAG_PERSISTENT;
- } else {
- hbuf_b_curr->ptr = hbuf_b_prev->ptr_end; // == br
- hbuf_b_curr->flags = 0;
- }
- hbuf_b_curr->ptr_end = end;
- hbuf_b_curr->ptr_end_alloc = hbuf_b_prev->ptr_end_alloc;
- // This is OK because insert_before(NULL) == append():
- *p_hbuf = g_list_insert_before(*p_hbuf, curr_elt->next, hbuf_b_curr);
- }
- curr_elt = g_list_next(curr_elt);
- }
-}
-
-// hbuf_add_line(p_hbuf, text, prefix_flags, width, maxhbufblocks)
-// Add a line to the given buffer. If width is not null, then lines are
-// wrapped at this length.
-// maxhbufblocks is the maximum number of hbuf blocks we can allocate. If
-// null, there is no limit. If non-null, it should be >= 2.
-//
-// Note 1: Splitting according to width won't work if there are tabs; they
-// should be expanded before.
-// Note 2: width does not include the ending \0.
-void hbuf_add_line(GList **p_hbuf, const char *text, time_t timestamp,
- guint prefix_flags, guint width, guint maxhbufblocks,
- unsigned mucnicklen, gpointer xep184)
-{
- GList *curr_elt;
- char *line;
- guint hbb_blocksize, textlen;
- hbuf_block *hbuf_block_elt;
-
- if (!text) return;
-
- prefix_flags |= (xep184 ? HBB_PREFIX_RECEIPT : 0);
-
- textlen = strlen(text);
- hbb_blocksize = MAX(textlen+1, HBB_BLOCKSIZE);
-
- hbuf_block_elt = g_new0(hbuf_block, 1);
- hbuf_block_elt->prefix.timestamp = timestamp;
- hbuf_block_elt->prefix.flags = prefix_flags;
- hbuf_block_elt->prefix.mucnicklen = mucnicklen;
- hbuf_block_elt->prefix.xep184 = xep184;
- if (!*p_hbuf) {
- hbuf_block_elt->ptr = g_new(char, hbb_blocksize);
- if (!hbuf_block_elt->ptr) {
- g_free(hbuf_block_elt);
- return;
- }
- hbuf_block_elt->flags = HBB_FLAG_ALLOC | HBB_FLAG_PERSISTENT;
- hbuf_block_elt->ptr_end_alloc = hbuf_block_elt->ptr + hbb_blocksize;
- } else {
- hbuf_block *hbuf_b_prev;
- // Set p_hbuf to the end of the list, to speed up history loading
- // (or CPU time will be used by g_list_last() for each line)
- *p_hbuf = g_list_last(*p_hbuf);
- hbuf_b_prev = (*p_hbuf)->data;
- hbuf_block_elt->ptr = hbuf_b_prev->ptr_end;
- hbuf_block_elt->flags = HBB_FLAG_PERSISTENT;
- hbuf_block_elt->ptr_end_alloc = hbuf_b_prev->ptr_end_alloc;
- }
- *p_hbuf = g_list_append(*p_hbuf, hbuf_block_elt);
-
- if (hbuf_block_elt->ptr + textlen >= hbuf_block_elt->ptr_end_alloc) {
- // Too long for the current allocated bloc, we need another one
- if (!maxhbufblocks || textlen >= HBB_BLOCKSIZE) {
- // No limit, let's allocate a new block
- // If the message text is big, we won't bother to reuse an old block
- // as well (it could be too small and cause a segfault).
- hbuf_block_elt->ptr = g_new0(char, hbb_blocksize);
- hbuf_block_elt->ptr_end_alloc = hbuf_block_elt->ptr + hbb_blocksize;
- // XXX We should check the return value.
- } else {
- GList *hbuf_head, *hbuf_elt;
- hbuf_block *hbuf_b_elt;
- guint n = 0;
- hbuf_head = g_list_first(*p_hbuf);
- // We need at least 2 allocated blocks
- if (maxhbufblocks == 1)
- maxhbufblocks = 2;
- // Let's count the number of allocated areas
- for (hbuf_elt = hbuf_head; hbuf_elt; hbuf_elt = g_list_next(hbuf_elt)) {
- hbuf_b_elt = (hbuf_block*)(hbuf_elt->data);
- if (hbuf_b_elt->flags & HBB_FLAG_ALLOC)
- n++;
- }
- // If we can't allocate a new area, reuse the previous block(s)
- if (n < maxhbufblocks) {
- hbuf_block_elt->ptr = g_new0(char, hbb_blocksize);
- hbuf_block_elt->ptr_end_alloc = hbuf_block_elt->ptr + hbb_blocksize;
- } else {
- // Let's use an old block, and free the extra blocks if needed
- char *allocated_block = NULL;
- char *end_of_allocated_block = NULL;
- while (n >= maxhbufblocks) {
- int start_of_block = 1;
- for (hbuf_elt = hbuf_head; hbuf_elt; hbuf_elt = hbuf_head) {
- hbuf_b_elt = (hbuf_block*)(hbuf_elt->data);
- if (hbuf_b_elt->flags & HBB_FLAG_ALLOC) {
- if (start_of_block-- == 0)
- break;
- if (n == maxhbufblocks) {
- allocated_block = hbuf_b_elt->ptr;
- end_of_allocated_block = hbuf_b_elt->ptr_end_alloc;
- } else {
- g_free(hbuf_b_elt->ptr);
- }
- }
- g_free(hbuf_b_elt);
- hbuf_head = *p_hbuf = g_list_delete_link(hbuf_head, hbuf_elt);
- }
- n--;
- }
- memset(allocated_block, 0, end_of_allocated_block-allocated_block);
- hbuf_block_elt->ptr = allocated_block;
- hbuf_block_elt->ptr_end_alloc = end_of_allocated_block;
- }
- }
- hbuf_block_elt->flags = HBB_FLAG_ALLOC | HBB_FLAG_PERSISTENT;
- }
-
- line = hbuf_block_elt->ptr;
- // Ok, now we can copy the text..
- strcpy(line, text);
- hbuf_block_elt->ptr_end = line + textlen + 1;
-
- curr_elt = g_list_last(*p_hbuf);
-
- // Wrap lines and handle CRs ('\n')
- do_wrap(p_hbuf, curr_elt, width);
-}
-
-// hbuf_free()
-// Destroys all hbuf list.
-void hbuf_free(GList **p_hbuf)
-{
- hbuf_block *hbuf_b_elt;
- GList *hbuf_elt;
- GList *first_elt = g_list_first(*p_hbuf);
-
- for (hbuf_elt = first_elt; hbuf_elt; hbuf_elt = g_list_next(hbuf_elt)) {
- hbuf_b_elt = (hbuf_block*)(hbuf_elt->data);
- if (hbuf_b_elt->flags & HBB_FLAG_ALLOC) {
- g_free(hbuf_b_elt->ptr);
- }
- g_free(hbuf_b_elt);
- }
-
- g_list_free(first_elt);
- *p_hbuf = NULL;
-}
-
-// hbuf_rebuild()
-// Rebuild all hbuf list, with the new width.
-// If width == 0, lines are not wrapped.
-void hbuf_rebuild(GList **p_hbuf, unsigned int width)
-{
- GList *first_elt, *curr_elt, *next_elt;
- hbuf_block *hbuf_b_curr, *hbuf_b_next;
-
- // *p_hbuf needs to be the head of the list
- first_elt = *p_hbuf = g_list_first(*p_hbuf);
-
- // #1 Remove non-persistent blocks (ptr_end should be updated!)
- curr_elt = first_elt;
- while (curr_elt) {
- next_elt = g_list_next(curr_elt);
- // Last element?
- if (!next_elt)
- break;
- hbuf_b_curr = (hbuf_block*)(curr_elt->data);
- hbuf_b_next = (hbuf_block*)(next_elt->data);
- // Is next line not-persistent?
- if (!(hbuf_b_next->flags & HBB_FLAG_PERSISTENT)) {
- hbuf_b_curr->ptr_end = hbuf_b_next->ptr_end;
- g_free(hbuf_b_next);
- curr_elt = g_list_delete_link(curr_elt, next_elt);
- } else
- curr_elt = next_elt;
- }
- // #2 Go back to head and create non-persistent blocks when needed
- if (width)
- do_wrap(p_hbuf, first_elt, width);
-}
-
-// hbuf_previous_persistent()
-// Returns the previous persistent block (line). If the given line is
-// persistent, then it is returned.
-// This function is used for example when resizing a buffer. If the top of the
-// screen is on a non-persistent block, then a screen resize could destroy this
-// line...
-GList *hbuf_previous_persistent(GList *l_line)
-{
- hbuf_block *hbuf_b_elt;
-
- while (l_line) {
- hbuf_b_elt = (hbuf_block*)l_line->data;
- if (hbuf_b_elt->flags & HBB_FLAG_PERSISTENT)
- return l_line;
- l_line = g_list_previous(l_line);
- }
-
- return NULL;
-}
-
-// hbuf_get_lines(hbuf, n)
-// Returns an array of n hbb_line pointers
-// (The first line will be the line currently pointed by hbuf)
-// Note: The caller should free the array, the hbb_line pointers and the
-// text pointers after use.
-hbb_line **hbuf_get_lines(GList *hbuf, unsigned int n)
-{
- unsigned int i;
- hbuf_block *blk;
- guint last_persist_prefixflags = 0;
- GList *last_persist; // last persistent flags
- hbb_line **array, **array_elt;
-
- // To be able to correctly highlight multi-line messages,
- // we need to look at the last non-null prefix, which should be the first
- // line of the message.
- last_persist = hbuf_previous_persistent(hbuf);
- while (last_persist) {
- blk = (hbuf_block*)last_persist->data;
- if ((blk->flags & HBB_FLAG_PERSISTENT) && blk->prefix.flags) {
- last_persist_prefixflags = blk->prefix.flags;
- break;
- }
- last_persist = g_list_previous(last_persist);
- }
-
- array = g_new0(hbb_line*, n);
- array_elt = array;
-
- for (i = 0 ; i < n ; i++) {
- if (hbuf) {
- int maxlen;
-
- blk = (hbuf_block*)(hbuf->data);
- maxlen = blk->ptr_end - blk->ptr;
- *array_elt = (hbb_line*)g_new(hbb_line, 1);
- (*array_elt)->timestamp = blk->prefix.timestamp;
- (*array_elt)->flags = blk->prefix.flags;
- (*array_elt)->mucnicklen = blk->prefix.mucnicklen;
- (*array_elt)->text = g_strndup(blk->ptr, maxlen);
-
- if ((blk->flags & HBB_FLAG_PERSISTENT) && blk->prefix.flags) {
- last_persist_prefixflags = blk->prefix.flags;
- } else {
- // Propagate highlighting flags
- (*array_elt)->flags |= last_persist_prefixflags &
- (HBB_PREFIX_HLIGHT_OUT | HBB_PREFIX_HLIGHT |
- HBB_PREFIX_INFO | HBB_PREFIX_IN);
- // Continuation of a message - omit the prefix
- (*array_elt)->flags |= HBB_PREFIX_CONT;
- (*array_elt)->mucnicklen = 0; // The nick is in the first one
- }
-
- hbuf = g_list_next(hbuf);
- } else
- break;
-
- array_elt++;
- }
-
- return array;
-}
-
-// hbuf_search(hbuf, direction, string)
-// Look backward/forward for a line containing string in the history buffer
-// Search starts at hbuf, and goes forward if direction == 1, backward if -1
-GList *hbuf_search(GList *hbuf, int direction, const char *string)
-{
- hbuf_block *blk;
-
- for (;;) {
- if (direction > 0)
- hbuf = g_list_next(hbuf);
- else
- hbuf = g_list_previous(hbuf);
-
- if (!hbuf) break;
-
- blk = (hbuf_block*)(hbuf->data);
- // XXX blk->ptr is (maybe) not really correct, because the match should
- // not be after ptr_end. We should check that...
- if (strcasestr(blk->ptr, string))
- break;
- }
-
- return hbuf;
-}
-
-// hbuf_jump_date(hbuf, t)
-// Return a pointer to the first line after date t in the history buffer
-GList *hbuf_jump_date(GList *hbuf, time_t t)
-{
- hbuf_block *blk;
-
- hbuf = g_list_first(hbuf);
-
- for ( ; hbuf && g_list_next(hbuf); hbuf = g_list_next(hbuf)) {
- blk = (hbuf_block*)(hbuf->data);
- if (blk->prefix.timestamp >= t) break;
- }
-
- return hbuf;
-}
-
-// hbuf_jump_percent(hbuf, pc)
-// Return a pointer to the line at % pc of the history buffer
-GList *hbuf_jump_percent(GList *hbuf, int pc)
-{
- guint hlen;
-
- hbuf = g_list_first(hbuf);
- hlen = g_list_length(hbuf);
-
- return g_list_nth(hbuf, pc*hlen/100);
-}
-
-// hbuf_dump_to_file(hbuf, filename)
-// Save the buffer to a file.
-void hbuf_dump_to_file(GList *hbuf, const char *filename)
-{
- hbuf_block *blk;
- hbb_line line;
- guint last_persist_prefixflags = 0;
- guint prefixwidth;
- char pref[96];
- FILE *fp;
- struct stat statbuf;
-
- if (!stat(filename, &statbuf)) {
- scr_LogPrint(LPRINT_NORMAL, "The file already exists.");
- return;
- }
- fp = fopen(filename, "w");
- if (!fp) {
- scr_LogPrint(LPRINT_NORMAL, "Unable to open the file.");
- return;
- }
-
- prefixwidth = scr_getprefixwidth();
- prefixwidth = MIN(prefixwidth, sizeof pref);
-
- for (hbuf = g_list_first(hbuf); hbuf; hbuf = g_list_next(hbuf)) {
- int maxlen;
-
- blk = (hbuf_block*)(hbuf->data);
- maxlen = blk->ptr_end - blk->ptr;
-
- memset(&line, 0, sizeof(line));
- line.timestamp = blk->prefix.timestamp;
- line.flags = blk->prefix.flags;
- line.mucnicklen = blk->prefix.mucnicklen;
- line.text = g_strndup(blk->ptr, maxlen);
-
- if ((blk->flags & HBB_FLAG_PERSISTENT) && blk->prefix.flags) {
- last_persist_prefixflags = blk->prefix.flags;
- } else {
- // Propagate highlighting flags
- line.flags |= last_persist_prefixflags &
- (HBB_PREFIX_HLIGHT_OUT | HBB_PREFIX_HLIGHT |
- HBB_PREFIX_INFO | HBB_PREFIX_IN);
- // Continuation of a message - omit the prefix
- line.flags |= HBB_PREFIX_CONT;
- line.mucnicklen = 0; // The nick is in the first one
- }
-
- scr_line_prefix(&line, pref, prefixwidth);
- fprintf(fp, "%s%s\n", pref, line.text);
- }
-
- fclose(fp);
- return;
-}
-
-// hbuf_remove_receipt(hbuf, xep184)
-// Remove the Receipt Flag for the message with the given xep184 id
-// Returns TRUE if it was found and removed, otherwise FALSE
-gboolean hbuf_remove_receipt(GList *hbuf, gpointer xep184)
-{
- hbuf_block *blk;
-
- hbuf = g_list_first(hbuf);
-
- for ( ; hbuf; hbuf = g_list_next(hbuf)) {
- blk = (hbuf_block*)(hbuf->data);
- if (blk->prefix.xep184 == xep184) {
- blk->prefix.xep184 = NULL;
- blk->prefix.flags ^= HBB_PREFIX_RECEIPT;
- return TRUE;
- }
- }
- return FALSE;
-}
-
-// hbuf_get_blocks_number()
-// Returns the number of allocated hbuf_block's.
-guint hbuf_get_blocks_number(GList *hbuf)
-{
- hbuf_block *hbuf_b_elt;
- guint count = 0U;
-
- for (hbuf = g_list_first(hbuf); hbuf; hbuf = g_list_next(hbuf)) {
- hbuf_b_elt = (hbuf_block*)(hbuf->data);
- if (hbuf_b_elt->flags & HBB_FLAG_ALLOC)
- count++;
- }
- return count;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/hbuf.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#ifndef __HBUF_H__
-#define __HBUF_H__ 1
-
-#include <time.h>
-#include <glib.h>
-
-// With current implementation a message must fit in a hbuf block,
-// so we shouldn't choose a too small size.
-#define HBB_BLOCKSIZE 8192 // > 20 please
-
-// Flags:
-// - ALLOC: the ptr data has been allocated, it can be freed
-// - PERSISTENT: this is a new history line
-#define HBB_FLAG_ALLOC 1
-#define HBB_FLAG_PERSISTENT 2
-
-#define HBB_PREFIX_IN (1U<<0)
-#define HBB_PREFIX_OUT (1U<<1)
-#define HBB_PREFIX_STATUS (1U<<2)
-#define HBB_PREFIX_AUTH (1U<<3)
-#define HBB_PREFIX_INFO (1U<<4)
-#define HBB_PREFIX_ERR (1U<<5)
-#define HBB_PREFIX_NOFLAG (1U<<6)
-#define HBB_PREFIX_HLIGHT_OUT (1U<<7)
-#define HBB_PREFIX_HLIGHT (1U<<8)
-#define HBB_PREFIX_NONE (1U<<9)
-#define HBB_PREFIX_SPECIAL (1U<<10)
-#define HBB_PREFIX_PGPCRYPT (1U<<11)
-#define HBB_PREFIX_OTRCRYPT (1U<<12)
-#define HBB_PREFIX_CONT (1U<<13)
-#define HBB_PREFIX_RECEIPT (1U<<14)
-
-typedef struct {
- time_t timestamp;
- guint flags;
- unsigned mucnicklen;
- char *text;
-} hbb_line;
-
-void hbuf_add_line(GList **p_hbuf, const char *text, time_t timestamp,
- guint prefix_flags, guint width, guint maxhbufblocks,
- unsigned mucnicklen, gpointer xep184);
-void hbuf_free(GList **p_hbuf);
-void hbuf_rebuild(GList **p_hbuf, unsigned int width);
-GList *hbuf_previous_persistent(GList *l_line);
-
-hbb_line **hbuf_get_lines(GList *hbuf, unsigned int n);
-GList *hbuf_search(GList *hbuf, int direction, const char *string);
-GList *hbuf_jump_date(GList *hbuf, time_t t);
-GList *hbuf_jump_percent(GList *hbuf, int pc);
-gboolean hbuf_remove_receipt(GList *hbuf, gpointer xep184);
-
-void hbuf_dump_to_file(GList *hbuf, const char *filename);
-
-guint hbuf_get_blocks_number(GList *p_hbuf);
-
-#endif /* __HBUF_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/help.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-/*
- * help.c -- Help command
- *
- * Copyright (C) 2006-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <glib.h>
-
-#include "settings.h"
-#include "logprint.h"
-#include "utils.h"
-#include "screen.h"
-
-#define DEFAULT_LANG "en"
-
-// get_lang()
-// Return the language code string (a 2-letters string).
-static const char *get_lang(void) {
- static const char *lang_str = DEFAULT_LANG;
-#ifdef DATA_DIR
- static char lang[3];
- const char *opt_l;
- opt_l = settings_opt_get("lang");
- if (opt_l && strlen(opt_l) == 2 && isalpha(opt_l[0]) && isalpha(opt_l[1])) {
- strncpy(lang, opt_l, sizeof(lang));
- mc_strtolower(lang);
- lang_str = lang;
- }
-#endif /* DATA_DIR */
- return lang_str;
-}
-
-// help_process(string)
-// Display help about the "string" command.
-// If string is null, display general help.
-// Return 0 in case of success.
-int help_process(char *string)
-{
-#ifndef DATA_DIR
- scr_LogPrint(LPRINT_NORMAL, "Help isn't available.");
- return -1;
-#else
- const char *lang;
- FILE *fp;
- char *helpfiles_dir, *filename;
- char *data;
- const int datasize = 4096;
- int linecount = 0;
- char *p;
-
- // Check string is ok
- for (p = string; p && *p; p++) {
- if (!isalnum(*p) && *p != '_' && *p != '-') {
- scr_LogPrint(LPRINT_NORMAL, "Cannot find help (invalid keyword).");
- return 1;
- }
- }
-
- // Look for help file
- lang = get_lang();
- helpfiles_dir = g_strdup_printf("%s/mcabber/help", DATA_DIR);
- p = NULL;
-
- if (string && *string) {
- p = g_strdup(string);
- mc_strtolower(p);
- filename = g_strdup_printf("%s/%s/hlp_%s.txt", helpfiles_dir, lang, p);
- } else
- filename = g_strdup_printf("%s/%s/hlp.txt", helpfiles_dir, lang);
-
- fp = fopen(filename, "r");
-
- if (!(fp) && (g_strcmp0(lang, DEFAULT_LANG)) ) {
- g_free(filename);
- if (p)
- filename = g_strdup_printf("%s/%s/hlp_%s.txt", helpfiles_dir, DEFAULT_LANG, p);
- else
- filename = g_strdup_printf("%s/%s/hlp.txt", helpfiles_dir, DEFAULT_LANG);
-
- fp = fopen(filename, "r");
- }
- g_free(p);
- g_free(filename);
- g_free(helpfiles_dir);
-
- if (!fp) {
- scr_LogPrint(LPRINT_NORMAL, "No help found.");
- return -1;
- }
-
- data = g_new(char, datasize);
- while (!feof(fp)) {
- if (fgets(data, datasize, fp) == NULL) break;
- // Strip trailing newline
- for (p = data; *p; p++) ;
- if (p > data)
- p--;
- if (*p == '\n' || *p == '\r')
- *p = '\0';
- // Displaty the help line
- scr_LogPrint(LPRINT_NORMAL, "%s", data);
- linecount++;
- }
- fclose(fp);
- g_free(data);
-
- if (linecount) {
- scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
- update_roster = TRUE;
- }
-
- return 0;
-#endif /* DATA_DIR */
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/help.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-#ifndef __HELP_H__
-#define __HELP_H__ 1
-
-int help_process(char *string);
-
-#endif /* __HELP_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/histolog.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,536 +0,0 @@
-/*
- * histolog.c -- File history handling
- *
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "histolog.h"
-#include "hbuf.h"
-#include "utils.h"
-#include "screen.h"
-#include "settings.h"
-#include "utils.h"
-#include "roster.h"
-#include "xmpp.h"
-
-static guint UseFileLogging;
-static guint FileLoadLogs;
-static char *RootDir;
-
-
-// user_histo_file(jid)
-// Returns history filename for the given jid
-// Note: the caller *must* free the filename after use (if not null).
-static char *user_histo_file(const char *bjid)
-{
- char *filename;
- char *lowerid;
-
- if (!(UseFileLogging || FileLoadLogs))
- return NULL;
-
- lowerid = g_strdup(bjid);
- if (!lowerid)
- return NULL;
- mc_strtolower(lowerid);
-
- filename = g_strdup_printf("%s%s", RootDir, lowerid);
- g_free(lowerid);
- return filename;
-}
-
-char *hlog_get_log_jid(const char *bjid)
-{
- struct stat bufstat;
- char *path;
- char *log_jid = NULL;
-
- path = user_histo_file(bjid);
- while (path) {
- if (lstat(path, &bufstat) != 0)
- break;
- if (S_ISLNK(bufstat.st_mode)) {
- g_free(log_jid);
- log_jid = g_new0(char, bufstat.st_size+1);
- if (readlink(path, log_jid, bufstat.st_size) < 0) return NULL;
- g_free(path);
- path = user_histo_file(log_jid);
- } else
- break;
- }
-
- g_free(path);
- return log_jid;
-}
-
-// write_histo_line()
-// Adds a history (multi-)line to the jid's history logfile
-static void write_histo_line(const char *bjid,
- time_t timestamp, guchar type, guchar info, const char *data)
-{
- guint len = 0;
- FILE *fp;
- time_t ts;
- const char *p;
- char *filename;
- char str_ts[20];
- int err;
-
- if (!UseFileLogging)
- return;
-
- // Do not log status messages when 'logging_ignore_status' is set
- if (type == 'S' && settings_opt_get_int("logging_ignore_status"))
- return;
-
- filename = user_histo_file(bjid);
-
- // If timestamp is null, get current date
- if (timestamp)
- ts = timestamp;
- else
- time(&ts);
-
- if (!data)
- data = "";
-
- // Count number of extra lines
- for (p=data ; *p ; p++)
- if (*p == '\n') len++;
-
- /* Line format: "TI yyyymmddThh:mm:ssZ LLL [data]"
- * T=Type, I=Info, yyyymmddThh:mm:ssZ=date, LLL=0-padded-len
- *
- * Types:
- * - M message Info: S (send) R (receive) I (info)
- * - S status Info: [_ofdnai]
- * We don't check them, we trust the caller.
- * (Info messages are not sent nor received, they're generated
- * locally by mcabber.)
- */
-
- fp = fopen(filename, "a");
- g_free(filename);
- if (!fp) {
- scr_LogPrint(LPRINT_LOGNORM, "Unable to write history "
- "(cannot open logfile)");
- return;
- }
-
- to_iso8601(str_ts, ts);
- err = fprintf(fp, "%c%c %-18.18s %03d %s\n", type, info, str_ts, len, data);
- fclose(fp);
- if (err < 0) {
- scr_LogPrint(LPRINT_LOGNORM, "Error while writing to log file: %s",
- strerror(errno));
- }
-}
-
-// hlog_read_history()
-// Reads the jid's history logfile
-void hlog_read_history(const char *bjid, GList **p_buddyhbuf, guint width)
-{
- char *filename;
- guchar type, info;
- char *data, *tail;
- guint data_size;
- char *xtext;
- time_t timestamp;
- guint prefix_flags;
- guint len;
- FILE *fp;
- struct stat bufstat;
- guint err = 0;
- guint ln = 0; // line number
- time_t starttime;
- int max_num_of_blocks;
-
- if (!FileLoadLogs)
- return;
-
- if ((roster_gettype(bjid) & ROSTER_TYPE_ROOM) &&
- (settings_opt_get_int("load_muc_logs") != 1))
- return;
-
- data_size = HBB_BLOCKSIZE+32;
- data = g_new(char, data_size);
- if (!data) {
- scr_LogPrint(LPRINT_LOGNORM, "Not enough memory to read history file");
- return;
- }
-
- filename = user_histo_file(bjid);
-
- fp = fopen(filename, "r");
- g_free(filename);
- if (!fp) {
- g_free(data);
- return;
- }
-
- // If file is large (> 3MB here), display a message to inform the user
- // (it can take a while...)
- if (!fstat(fileno(fp), &bufstat)) {
- if (bufstat.st_size > 3145728) {
- scr_LogPrint(LPRINT_NORMAL, "Reading <%s> history file...", bjid);
- scr_DoUpdate();
- }
- }
-
- max_num_of_blocks = get_max_history_blocks();
-
- starttime = 0L;
- if (settings_opt_get_int("max_history_age") > 0) {
- int maxdays = settings_opt_get_int("max_history_age");
- time(&starttime);
- if (maxdays >= starttime/86400L)
- starttime = 0L;
- else
- starttime -= maxdays * 86400L;
- }
-
- /* See write_histo_line() for line format... */
- while (!feof(fp)) {
- guint dataoffset = 25;
- guint noeol;
-
- if (fgets(data, data_size-1, fp) == NULL)
- break;
- ln++;
-
- while (1) {
- for (tail = data; *tail; tail++) ;
- noeol = (*(tail-1) != '\n');
- if (!noeol)
- break;
- /* TODO: duplicated code... could do better... */
- if (tail == data + data_size-2) {
- // The buffer is too small to contain the whole line.
- // Let's allocate some more space.
- if (!max_num_of_blocks ||
- data_size/HBB_BLOCKSIZE < 5U*max_num_of_blocks) {
- guint toffset = tail - data;
- // Allocate one more block.
- data_size = HBB_BLOCKSIZE * (1 + data_size/HBB_BLOCKSIZE);
- data = g_renew(char, data, data_size);
- // Update the tail pointer, as the data may have been moved.
- tail = data + toffset;
- if (fgets(tail, data_size-1 - (tail-data), fp) == NULL)
- break;
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "Line too long in history file!");
- ln--;
- break;
- }
- }
- }
-
- type = data[0];
- info = data[1];
-
- if ((type != 'M' && type != 'S') ||
- ((data[11] != 'T') || (data[20] != 'Z') ||
- (data[21] != ' ') ||
- (data[25] != ' ' && data[26] != ' '))) {
- if (!err) {
- scr_LogPrint(LPRINT_LOGNORM,
- "Error in history file format (%s), l.%u", bjid, ln);
- err = 1;
- }
- continue;
- }
- // The number of lines can be written with 3 or 4 bytes.
- if (data[25] != ' ') dataoffset = 26;
- data[21] = data[dataoffset] = 0;
- timestamp = from_iso8601(&data[3], 1);
- len = (guint) atoi(&data[22]);
-
- // Some checks
- if (((type == 'M') && (info != 'S' && info != 'R' && info != 'I')) ||
- ((type == 'S') && (!strchr("_OFDNAI", info)))) {
- if (!err) {
- scr_LogPrint(LPRINT_LOGNORM, "Error in history file format (%s), l.%u",
- bjid, ln);
- err = 1;
- }
- continue;
- }
-
- while (len--) {
- ln++;
- if (fgets(tail, data_size-1 - (tail-data), fp) == NULL)
- break;
-
- while (*tail) tail++;
- noeol = (*(tail-1) != '\n');
- if (tail == data + data_size-2 && (len || noeol)) {
- // The buffer is too small to contain the whole message.
- // Let's allocate some more space.
- if (!max_num_of_blocks ||
- data_size/HBB_BLOCKSIZE < 5U*max_num_of_blocks) {
- guint toffset = tail - data;
- // If the line hasn't been read completely and we reallocate the
- // buffer, we want to read one more time.
- if (noeol)
- len++;
- // Allocate one more block.
- data_size = HBB_BLOCKSIZE * (1 + data_size/HBB_BLOCKSIZE);
- data = g_renew(char, data, data_size);
- // Update the tail pointer, as the data may have been moved.
- tail = data + toffset;
- } else {
- // There will probably be a parse error on next read, because
- // this message hasn't been read entirely.
- scr_LogPrint(LPRINT_LOGNORM, "Message too big in history file!");
- }
- }
- }
- // Remove last CR (we keep it if the line is empty, too)
- if ((tail > data+dataoffset+1) && (*(tail-1) == '\n'))
- *(tail-1) = 0;
-
- // Check if the data is older than max_history_age
- if (starttime) {
- if (timestamp > starttime)
- starttime = 0L; // From now on, load everything
- else
- continue;
- }
-
- if (type == 'M') {
- char *converted;
- if (info == 'S') {
- prefix_flags = HBB_PREFIX_OUT | HBB_PREFIX_HLIGHT_OUT;
- } else {
- prefix_flags = HBB_PREFIX_IN;
- if (info == 'I')
- prefix_flags = HBB_PREFIX_INFO;
- }
- converted = from_utf8(&data[dataoffset+1]);
- if (converted) {
- xtext = ut_expand_tabs(converted); // Expand tabs
- hbuf_add_line(p_buddyhbuf, xtext, timestamp, prefix_flags, width,
- max_num_of_blocks, 0, NULL);
- if (xtext != converted)
- g_free(xtext);
- g_free(converted);
- }
- err = 0;
- }
- }
- fclose(fp);
- g_free(data);
-}
-
-// hlog_enable()
-// Enable logging to files. If root_dir is NULL, then $HOME/.mcabber is used.
-// If loadfiles is TRUE, we will try to load buddies history logs from file.
-void hlog_enable(guint enable, const char *root_dir, guint loadfiles)
-{
- UseFileLogging = enable;
- FileLoadLogs = loadfiles;
-
- if (enable || loadfiles) {
- if (root_dir) {
- char *xp_root_dir;
- int l = strlen(root_dir);
- if (l < 1) {
- scr_LogPrint(LPRINT_LOGNORM, "Error: logging dir name too short");
- UseFileLogging = FileLoadLogs = FALSE;
- return;
- }
- xp_root_dir = expand_filename(root_dir);
- // RootDir must be slash-terminated
- if (root_dir[l-1] == '/') {
- RootDir = xp_root_dir;
- } else {
- RootDir = g_strdup_printf("%s/", xp_root_dir);
- g_free(xp_root_dir);
- }
- } else {
- char *home = getenv("HOME");
- const char *dir = "/.mcabber/histo/";
- RootDir = g_strdup_printf("%s%s", home, dir);
- }
- // Check directory permissions (should not be readable by group/others)
- if (checkset_perm(RootDir, TRUE) == -1) {
- // The directory does not actually exists
- g_free(RootDir);
- RootDir = NULL;
- scr_LogPrint(LPRINT_LOGNORM, "ERROR: Cannot access "
- "history log directory, logging DISABLED");
- UseFileLogging = FileLoadLogs = FALSE;
- }
- } else { // Disable history logging
- g_free(RootDir);
- RootDir = NULL;
- }
-}
-
-guint hlog_is_enabled(void)
-{
- return UseFileLogging;
-}
-
-inline void hlog_write_message(const char *bjid, time_t timestamp, int sent,
- const char *msg)
-{
- guchar info;
- /* sent=1 message sent by mcabber
- * sent=0 message received by mcabber
- * sent=-1 local info message
- */
- if (sent == 1)
- info = 'S';
- else if (sent == 0)
- info = 'R';
- else
- info = 'I';
- write_histo_line(bjid, timestamp, 'M', info, msg);
-}
-
-inline void hlog_write_status(const char *bjid, time_t timestamp,
- enum imstatus status, const char *status_msg)
-{
- // XXX Check status value?
- write_histo_line(bjid, timestamp, 'S', toupper(imstatus2char[status]),
- status_msg);
-}
-
-
-// hlog_save_state()
-// If enabled, save the current state of the roster
-// (i.e. pending messages) to a temporary file.
-void hlog_save_state(void)
-{
- gpointer unread_ptr, first_unread;
- const char *bjid;
- char *statefile_xp;
- FILE *fp;
- const char *statefile = settings_opt_get("statefile");
-
- if (!statefile || !UseFileLogging)
- return;
-
- statefile_xp = expand_filename(statefile);
- fp = fopen(statefile_xp, "w");
- if (!fp) {
- scr_LogPrint(LPRINT_NORMAL, "Cannot open state file [%s]",
- strerror(errno));
- goto hlog_save_state_return;
- }
-
- if (!lm_connection_is_authenticated(lconnection)) {
- // We're not connected. Let's use the unread_jids hash.
- GList *unread_jid = unread_jid_get_list();
- unread_ptr = unread_jid;
- for ( ; unread_jid ; unread_jid = g_list_next(unread_jid))
- fprintf(fp, "%s\n", (char*)unread_jid->data);
- g_list_free(unread_ptr);
- goto hlog_save_state_return;
- }
-
- if (!current_buddy) // Safety check -- shouldn't happen.
- goto hlog_save_state_return;
-
- // We're connected. Let's use unread_msg().
- unread_ptr = first_unread = unread_msg(NULL);
- if (!first_unread)
- goto hlog_save_state_return;
-
- do {
- guint type = buddy_gettype(unread_ptr);
- if (type & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)) {
- bjid = buddy_getjid(unread_ptr);
- if (bjid)
- fprintf(fp, "%s\n", bjid);
- }
- unread_ptr = unread_msg(unread_ptr);
- } while (unread_ptr && unread_ptr != first_unread);
-
-hlog_save_state_return:
- if (fp) {
- long filelen = ftell(fp);
- fclose(fp);
- if (!filelen)
- unlink(statefile_xp);
- }
- g_free(statefile_xp);
-}
-
-// hlog_load_state()
-// If enabled, load the current state of the roster
-// (i.e. pending messages) from a temporary file.
-// This function adds the JIDs to the unread_jids hash table,
-// so it should only be called at startup.
-void hlog_load_state(void)
-{
- char bjid[1024];
- char *statefile_xp;
- FILE *fp;
- const char *statefile = settings_opt_get("statefile");
-
- if (!statefile || !UseFileLogging)
- return;
-
- statefile_xp = expand_filename(statefile);
- fp = fopen(statefile_xp, "r");
- if (fp) {
- char *eol;
- while (!feof(fp)) {
- if (fgets(bjid, sizeof bjid, fp) == NULL)
- break;
- // Let's remove the trailing newline.
- // Also remove whitespace, if the file as been (badly) manually modified.
- for (eol = bjid; *eol; eol++) ;
- for (eol--; eol >= bjid && (*eol == '\n' || *eol == ' '); *eol-- = 0) ;
- // Safety checks...
- if (!bjid[0])
- continue;
- if (check_jid_syntax(bjid)) {
- scr_LogPrint(LPRINT_LOGNORM,
- "ERROR: Invalid JID in state file. Corrupted file?");
- break;
- }
- // Display a warning if there are pending messages but the user
- // won't see them because load_log isn't set.
- if (!FileLoadLogs) {
- scr_LogPrint(LPRINT_LOGNORM, "WARNING: unread message from <%s>.",
- bjid);
- scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
- }
- // Add the JID to unread_jids. It will be used when the contact is
- // added to the roster.
- unread_jid_add(bjid);
- }
- fclose(fp);
- }
- g_free(statefile_xp);
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/histolog.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-#ifndef __HISTOLOG_H__
-#define __HISTOLOG_H__ 1
-
-#include <glib.h>
-
-#include "xmpp.h"
-
-void hlog_enable(guint enable, const char *root_dir, guint loadfile);
-char *hlog_get_log_jid(const char *bjid);
-void hlog_read_history(const char *bjid, GList **p_buddyhbuf, guint width);
-void hlog_write_message(const char *bjid, time_t timestamp, int sent,
- const char *msg);
-void hlog_write_status(const char *bjid, time_t timestamp,
- enum imstatus status, const char *status_msg);
-void hlog_save_state(void);
-void hlog_load_state(void);
-
-#endif /* __HISTOLOG_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/hooks.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,668 +0,0 @@
-/*
- * hooks.c -- Hooks layer
- *
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 <loudmouth/loudmouth.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "hooks.h"
-#include "screen.h"
-#include "roster.h"
-#include "histolog.h"
-#include "hbuf.h"
-#include "settings.h"
-#include "utils.h"
-#include "utf8.h"
-#include "commands.h"
-#include "main.h"
-
-#ifdef MODULES_ENABLE
-#include <glib.h>
-
-typedef struct {
- hk_handler_t handler;
- guint32 flags;
- gpointer userdata;
-} hook_list_data_t;
-
-static GSList *hk_handler_queue = NULL;
-
-void hk_add_handler (hk_handler_t handler, guint32 flags, gpointer userdata)
-{
- hook_list_data_t *h = g_new (hook_list_data_t, 1);
- h->handler = handler;
- h->flags = flags;
- h->userdata = userdata;
- hk_handler_queue = g_slist_append (hk_handler_queue, h);
-}
-
-static gint hk_queue_search_cb (hook_list_data_t *a, hook_list_data_t *b)
-{
- if (a->handler == b->handler && a->userdata == b->userdata)
- return 0;
- else
- return 1;
-}
-
-void hk_del_handler (hk_handler_t handler, gpointer userdata)
-{
- hook_list_data_t h = { handler, 0, userdata };
- GSList *el = g_slist_find_custom (hk_handler_queue, &h, (GCompareFunc) hk_queue_search_cb);
- if (el) {
- g_free (el->data);
- hk_handler_queue = g_slist_delete_link (hk_handler_queue, el);
- }
-}
-#endif
-
-static char *extcmd;
-
-static const char *COMMAND_ME = "/me ";
-
-void hk_message_in(const char *bjid, const char *resname,
- time_t timestamp, const char *msg, LmMessageSubType type,
- guint encrypted)
-{
- int new_guy = FALSE;
- int is_groupchat = FALSE; // groupchat message
- int is_room = FALSE; // window is a room window
- int log_muc_conf = FALSE;
- int active_window = FALSE;
- int message_flags = 0;
- guint rtype = ROSTER_TYPE_USER;
- char *wmsg = NULL, *bmsg = NULL, *mmsg = NULL;
- GSList *roster_usr;
- unsigned mucnicklen = 0;
- const char *ename = NULL;
-
- if (encrypted == ENCRYPTED_PGP)
- message_flags |= HBB_PREFIX_PGPCRYPT;
- else if (encrypted == ENCRYPTED_OTR)
- message_flags |= HBB_PREFIX_OTRCRYPT;
-
- if (type == LM_MESSAGE_SUB_TYPE_GROUPCHAT) {
- rtype = ROSTER_TYPE_ROOM;
- is_groupchat = TRUE;
- log_muc_conf = settings_opt_get_int("log_muc_conf");
- if (!resname) {
- message_flags = HBB_PREFIX_INFO | HBB_PREFIX_NOFLAG;
- resname = "";
- wmsg = bmsg = g_strdup_printf("~ %s", msg);
- } else {
- wmsg = bmsg = g_strdup_printf("<%s> %s", resname, msg);
- mucnicklen = strlen(resname) + 2;
- if (!strncmp(msg, COMMAND_ME, strlen(COMMAND_ME)))
- wmsg = mmsg = g_strdup_printf("*%s %s", resname, msg+4);
- }
- } else {
- bmsg = g_strdup(msg);
- if (!strncmp(msg, COMMAND_ME, strlen(COMMAND_ME))) {
- gchar *shortid = g_strdup(bjid);
- if (settings_opt_get_int("buddy_me_fulljid") == FALSE) {
- gchar *p = strchr(shortid, '@'); // Truncate the jid
- if (p)
- *p = '\0';
- }
- wmsg = mmsg = g_strdup_printf("*%s %s", shortid, msg+4);
- g_free(shortid);
- } else
- wmsg = (char*) msg;
- }
-
- // If this user isn't in the roster, we add it
- roster_usr = roster_find(bjid, jidsearch, 0);
- if (!roster_usr) {
- new_guy = TRUE;
- roster_usr = roster_add_user(bjid, NULL, NULL, rtype, sub_none, -1);
- if (!roster_usr) { // Shouldn't happen...
- scr_LogPrint(LPRINT_LOGNORM, "ERROR: unable to add buddy!");
- g_free(bmsg);
- g_free(mmsg);
- return;
- }
- } else if (is_groupchat) {
- // Make sure the type is ROOM
- buddy_settype(roster_usr->data, ROSTER_TYPE_ROOM);
- }
-
- is_room = !!(buddy_gettype(roster_usr->data) & ROSTER_TYPE_ROOM);
-
- if (is_room) {
- if (!is_groupchat) {
- // This is a private message from a room participant
- g_free(bmsg);
- if (!resname) {
- resname = "";
- wmsg = bmsg = g_strdup(msg);
- } else {
- wmsg = bmsg = g_strdup_printf("PRIV#<%s> %s", resname, msg);
- if (!strncmp(msg, COMMAND_ME, strlen(COMMAND_ME))) {
- g_free(mmsg);
- wmsg = mmsg = g_strdup_printf("PRIV#*%s %s", resname, msg+4);
- }
- }
- message_flags |= HBB_PREFIX_HLIGHT;
- } else {
- // This is a regular chatroom message.
- const char *nick = buddy_getnickname(roster_usr->data);
-
- if (nick) {
- // Let's see if we are the message sender, in which case we'll
- // highlight it.
- if (resname && !strcmp(resname, nick)) {
- message_flags |= HBB_PREFIX_HLIGHT_OUT;
- } else if (!settings_opt_get_int("muc_disable_nick_hl")) {
- // We're not the sender. Can we see our nick?
- const char *msgptr = msg;
- while ((msgptr = strcasestr(msgptr, nick)) != NULL) {
- const char *leftb, *rightb;
- // The message contains our nick. Let's check it's not
- // in the middle of another word (i.e. preceded/followed
- // immediately by an alphanumeric character or an underscore.
- rightb = msgptr+strlen(nick);
- if (msgptr == msg)
- leftb = NULL;
- else
- leftb = prev_char((char*)msgptr, msg);
- msgptr = next_char((char*)msgptr);
- // Check left boundary
- if (leftb && (iswalnum(get_char(leftb)) || get_char(leftb) == '_'))
- continue;
- // Check right boundary
- if (!iswalnum(get_char(rightb)) && get_char(rightb) != '_')
- message_flags |= HBB_PREFIX_HLIGHT;
- }
- }
- }
- }
- }
-
- if (type == LM_MESSAGE_SUB_TYPE_ERROR) {
- message_flags = HBB_PREFIX_ERR | HBB_PREFIX_IN;
- scr_LogPrint(LPRINT_LOGNORM, "Error message received from <%s>", bjid);
- }
-
- // Note: the hlog_write should not be called first, because in some
- // cases scr_WriteIncomingMessage() will load the history and we'd
- // have the message twice...
- scr_WriteIncomingMessage(bjid, wmsg, timestamp, message_flags, mucnicklen);
-
- // We don't log the modified message, but the original one
- if (wmsg == mmsg)
- wmsg = bmsg;
-
- // - We don't log the message if it is an error message
- // - We don't log the message if it is a private conf. message
- // - We don't log the message if it is groupchat message and the log_muc_conf
- // option is off (and it is not a history line)
- if (!(message_flags & HBB_PREFIX_ERR) &&
- (!is_room || (is_groupchat && log_muc_conf && !timestamp)))
- hlog_write_message(bjid, timestamp, 0, wmsg);
-
- if (settings_opt_get_int("events_ignore_active_window") &&
- current_buddy && scr_get_chatmode()) {
- gpointer bud = BUDDATA(current_buddy);
- if (bud) {
- const char *cjid = buddy_getjid(bud);
- if (cjid && !strcasecmp(cjid, bjid))
- active_window = TRUE;
- }
- }
-
- if (settings_opt_get_int("eventcmd_use_nickname"))
- ename = roster_getname(bjid);
-
-#ifdef MODULES_ENABLE
- {
- GSList *h = hk_handler_queue;
- if (h) {
-#if 0
- hk_arg_t *args = g_new (hk_arg_t, 5);
- args[0].name = "hook";
- args[0].value = "hook-message-in";
- args[1].name = "jid";
- args[1].value = bjid;
- args[2].name = "message";
- args[2].value = wmsg;
- args[3].name = "groupchat";
- args[3].value = is_groupchat ? "true" : "false";
- args[4].name = NULL;
- args[4].value = NULL;
-#else
- // We can use a const array for keys/static values, so modules
- // can do fast known to them args check by just comparing pointers...
- hk_arg_t args[] = {
- { "hook", "hook-message-in" },
- { "jid", bjid },
- { "message", wmsg },
- { "groupchat", is_groupchat ? "true" : "false" },
- { NULL, NULL },
- };
-#endif
- while (h) {
- hook_list_data_t *data = h->data;
- if (data->flags & HOOK_MESSAGE_IN)
- (data->handler) (HOOK_MESSAGE_IN, args, data->userdata);
- h = g_slist_next (h);
- }
- }
- }
-#endif
-
- // External command
- // - We do not call hk_ext_cmd() for history lines in MUC
- // - We do call hk_ext_cmd() for private messages in a room
- // - We do call hk_ext_cmd() for messages to the current window
- if (!active_window && ((is_groupchat && !timestamp) || !is_groupchat))
- hk_ext_cmd(ename ? ename : bjid, (is_groupchat ? 'G' : 'M'), 'R', wmsg);
-
- // Display the sender in the log window
- if ((!is_groupchat) && !(message_flags & HBB_PREFIX_ERR) &&
- settings_opt_get_int("log_display_sender")) {
- const char *name = roster_getname(bjid);
- if (!name) name = "";
- scr_LogPrint(LPRINT_NORMAL, "Message received from %s <%s/%s>",
- name, bjid, (resname ? resname : ""));
- }
-
- // Beep, if enabled:
- // - if it's a private message
- // - if it's a public message and it's highlighted
- if (settings_opt_get_int("beep_on_message")) {
- if ((!is_groupchat && !(message_flags & HBB_PREFIX_ERR)) ||
- (is_groupchat && (message_flags & HBB_PREFIX_HLIGHT)))
- scr_Beep();
- }
-
- // We need to update the roster if the sender is unknown or
- // if the sender is offline/invisible and a filter is set.
- if (new_guy ||
- (buddy_getstatus(roster_usr->data, NULL) == offline &&
- buddylist_isset_filter()))
- {
- update_roster = TRUE;
- }
-
- g_free(bmsg);
- g_free(mmsg);
-}
-
-// hk_message_out()
-// nick should be set for private messages in a chat room, and null for
-// normal messages.
-void hk_message_out(const char *bjid, const char *nick,
- time_t timestamp, const char *msg,
- guint encrypted, gpointer xep184)
-{
- char *wmsg = NULL, *bmsg = NULL, *mmsg = NULL;
- guint cryptflag = 0;
-
- if (nick) {
- wmsg = bmsg = g_strdup_printf("PRIV#<%s> %s", nick, msg);
- if (!strncmp(msg, COMMAND_ME, strlen(COMMAND_ME))) {
- const char *mynick = roster_getnickname(bjid);
- wmsg = mmsg = g_strdup_printf("PRIV#<%s> *%s %s", nick,
- (mynick ? mynick : "me"), msg+4);
- }
- } else {
- wmsg = (char*)msg;
- if (!strncmp(msg, COMMAND_ME, strlen(COMMAND_ME))) {
- char *myid = jid_get_username(settings_opt_get("jid"));
- if (myid) {
- wmsg = mmsg = g_strdup_printf("*%s %s", myid, msg+4);
- g_free(myid);
- }
- }
- }
-
- // Note: the hlog_write should not be called first, because in some
- // cases scr_WriteOutgoingMessage() will load the history and we'd
- // have the message twice...
- if (encrypted == ENCRYPTED_PGP)
- cryptflag = HBB_PREFIX_PGPCRYPT;
- else if (encrypted == ENCRYPTED_OTR)
- cryptflag = HBB_PREFIX_OTRCRYPT;
- scr_WriteOutgoingMessage(bjid, wmsg, cryptflag, xep184);
-
- // We don't log private messages
- if (!nick)
- hlog_write_message(bjid, timestamp, 1, msg);
-
-#ifdef MODULES_ENABLE
- {
- GSList *h = hk_handler_queue;
- if (h) {
- hk_arg_t args[] = {
- { "hook", "hook-message-out" },
- { "jid", bjid },
- { "message", wmsg },
- { NULL, NULL },
- };
- while (h) {
- hook_list_data_t *data = h->data;
- if (data->flags & HOOK_MESSAGE_OUT)
- (data->handler) (HOOK_MESSAGE_OUT, args, data->userdata);
- h = g_slist_next (h);
- }
- }
- }
-#endif
-
- // External command
- hk_ext_cmd(bjid, 'M', 'S', NULL);
-
- g_free(bmsg);
- g_free(mmsg);
-}
-
-void hk_statuschange(const char *bjid, const char *resname, gchar prio,
- time_t timestamp, enum imstatus status,
- const char *status_msg)
-{
- int st_in_buf;
- enum imstatus oldstat;
- char *bn;
- char *logsmsg;
- const char *rn = (resname ? resname : "");
- const char *ename = NULL;
-
- if (settings_opt_get_int("eventcmd_use_nickname"))
- ename = roster_getname(bjid);
-
- oldstat = roster_getstatus(bjid, resname);
-
- st_in_buf = settings_opt_get_int("show_status_in_buffer");
-
- if (settings_opt_get_int("log_display_presence")) {
- int buddy_format = settings_opt_get_int("buddy_format");
- bn = NULL;
- if (buddy_format) {
- const char *name = roster_getname(bjid);
- if (name && strcmp(name, bjid)) {
- if (buddy_format == 1)
- bn = g_strdup_printf("%s <%s/%s>", name, bjid, rn);
- else if (buddy_format == 2)
- bn = g_strdup_printf("%s/%s", name, rn);
- else if (buddy_format == 3)
- bn = g_strdup_printf("%s", name);
- }
- }
-
- if (!bn)
- bn = g_strdup_printf("<%s/%s>", bjid, rn);
-
- logsmsg = g_strdup(status_msg ? status_msg : "");
- replace_nl_with_dots(logsmsg);
-
- scr_LogPrint(LPRINT_LOGNORM, "Buddy status has changed: [%c>%c] %s %s",
- imstatus2char[oldstat], imstatus2char[status], bn, logsmsg);
- g_free(logsmsg);
- g_free(bn);
- }
-
- if (st_in_buf == 2 ||
- (st_in_buf == 1 && (status == offline || oldstat == offline))) {
- // Write the status change in the buddy's buffer, only if it already exists
- if (scr_BuddyBufferExists(bjid)) {
- bn = g_strdup_printf("Buddy status has changed: [%c>%c] %s",
- imstatus2char[oldstat], imstatus2char[status],
- ((status_msg) ? status_msg : ""));
- scr_WriteIncomingMessage(bjid, bn, timestamp,
- HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
- g_free(bn);
- }
- }
-
- roster_setstatus(bjid, rn, prio, status, status_msg, timestamp,
- role_none, affil_none, NULL);
- buddylist_build();
- scr_DrawRoster();
- hlog_write_status(bjid, timestamp, status, status_msg);
-
-#ifdef MODULES_ENABLE
- {
- GSList *h = hk_handler_queue;
- if (h) {
- char os[2] = " \0";
- char ns[2] = " \0";
- hk_arg_t args[] = {
- { "hook", "hook-status-change" },
- { "jid", bjid },
- { "resource", rn },
- { "old_status", os },
- { "new_status", ns },
- { "message", status_msg ? status_msg : "" },
- { NULL, NULL },
- };
- os[0] = imstatus2char[oldstat];
- ns[0] = imstatus2char[status];
- while (h) {
- hook_list_data_t *data = h->data;
- if (data->flags & HOOK_STATUS_CHANGE)
- (data->handler) (HOOK_STATUS_CHANGE, args, data->userdata);
- h = g_slist_next (h);
- }
- }
- }
-#endif
-
- // External command
- hk_ext_cmd(ename ? ename : bjid, 'S', imstatus2char[status], NULL);
-}
-
-void hk_mystatuschange(time_t timestamp, enum imstatus old_status,
- enum imstatus new_status, const char *msg)
-{
- scr_LogPrint(LPRINT_LOGNORM, "Your status has been set: [%c>%c] %s",
- imstatus2char[old_status], imstatus2char[new_status],
- (msg ? msg : ""));
-
-#ifdef MODULES_ENABLE
- {
- GSList *h = hk_handler_queue;
- if (h) {
- char ns[2] = " \0";
- hk_arg_t args[] = {
- { "hook", "hook-my-status-change" },
- { "new_status", ns },
- { "message", msg ? msg : "" },
- { NULL, NULL },
- };
- ns[0] = imstatus2char[new_status];
- while (h) {
- hook_list_data_t *data = h->data;
- if (data->flags & HOOK_MY_STATUS_CHANGE)
- (data->handler) (HOOK_MY_STATUS_CHANGE, args, data->userdata);
- h = g_slist_next (h);
- }
- }
- }
-#endif
-
- //hlog_write_status(NULL, 0, status);
-}
-
-
-/* Internal commands */
-
-void hook_execute_internal(const char *hookname)
-{
- const char *hook_command;
- char *buf;
- char *cmdline;
-
-#ifdef MODULES_ENABLE
- {
- GSList *h = hk_handler_queue;
- if (h) {
- hk_arg_t args[] = {
- { "hook", hookname },
- { NULL, NULL },
- };
- while (h) {
- hook_list_data_t *data = h->data;
- if (data->flags & HOOK_INTERNAL)
- (data->handler) (HOOK_INTERNAL, args, data->userdata);
- h = g_slist_next (h);
- }
- }
- }
-#endif
-
- hook_command = settings_opt_get(hookname);
- if (!hook_command)
- return;
-
- buf = g_strdup_printf("Running %s...", hookname);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
-
- cmdline = from_utf8(hook_command);
- if (process_command(cmdline, TRUE) == 255)
- mcabber_set_terminate_ui();
-
- g_free(cmdline);
- g_free(buf);
-}
-
-
-/* External commands */
-
-// hk_ext_cmd_init()
-// Initialize external command variable.
-// Can be called with parameter NULL to reset and free memory.
-void hk_ext_cmd_init(const char *command)
-{
- if (extcmd) {
- g_free(extcmd);
- extcmd = NULL;
- }
- if (command)
- extcmd = expand_filename(command);
-}
-
-// hk_ext_cmd()
-// Launch an external command (process) for the given event.
-// For now, data should be NULL.
-void hk_ext_cmd(const char *bjid, guchar type, guchar info, const char *data)
-{
- pid_t pid;
- char *arg_type = NULL;
- char *arg_info = NULL;
- char *arg_data = NULL;
- char status_str[2];
- char *datafname = NULL;
- char unread_str[16];
-
- if (!extcmd) return;
-
- // Prepare arg_* (external command parameters)
- switch (type) {
- case 'M': /* Normal message */
- arg_type = "MSG";
- if (info == 'R')
- arg_info = "IN";
- else if (info == 'S')
- arg_info = "OUT";
- break;
- case 'G': /* Groupchat message */
- arg_type = "MSG";
- arg_info = "MUC";
- break;
- case 'S': /* Status change */
- arg_type = "STATUS";
- if (strchr(imstatus2char, tolower(info))) {
- status_str[0] = toupper(info);
- status_str[1] = 0;
- arg_info = status_str;
- }
- break;
- case 'U': /* Unread buffer count */
- arg_type = "UNREAD";
- g_snprintf(unread_str, sizeof unread_str, "%d", info);
- arg_info = unread_str; /* number of remaining unread bjids */
- break;
- default:
- return;
- }
-
- if (!arg_type || !arg_info) return;
-
- if (strchr("MG", type) && data && settings_opt_get_int("event_log_files")) {
- int fd;
- const char *prefix;
- char *prefix_xp = NULL;
- char *data_locale;
-
- data_locale = from_utf8(data);
- prefix = settings_opt_get("event_log_dir");
- if (prefix)
- prefix = prefix_xp = expand_filename(prefix);
- else
- prefix = ut_get_tmpdir();
- datafname = g_strdup_printf("%s/mcabber-%d.XXXXXX", prefix, getpid());
- g_free(prefix_xp);
-
- // XXX Some old systems may require us to set umask first.
- fd = mkstemp(datafname);
- if (fd == -1) {
- g_free(datafname);
- datafname = NULL;
- scr_LogPrint(LPRINT_LOGNORM,
- "Unable to create temp file for external command.");
- } else {
- size_t data_locale_len = strlen(data_locale);
- ssize_t a = write(fd, data_locale, data_locale_len);
- ssize_t b = write(fd, "\n", 1);
- if ((size_t)a != data_locale_len || b != 1) {
- g_free(datafname);
- datafname = NULL;
- scr_LogPrint(LPRINT_LOGNORM,
- "Unable to write to temp file for external command.");
- }
- close(fd);
- arg_data = datafname;
- }
- g_free(data_locale);
- }
-
- if ((pid=fork()) == -1) {
- scr_LogPrint(LPRINT_LOGNORM, "Fork error, cannot launch external command.");
- g_free(datafname);
- return;
- }
-
- if (pid == 0) { // child
- // Close standard file descriptors
- close(STDIN_FILENO);
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
- if (execl(extcmd, extcmd, arg_type, arg_info, bjid, arg_data,
- (char *)NULL) == -1) {
- // scr_LogPrint(LPRINT_LOGNORM, "Cannot execute external command.");
- exit(1);
- }
- }
- g_free(datafname);
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/hooks.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-#ifndef __HOOKS_H__
-#define __HOOKS_H__ 1
-
-#include <time.h>
-#include <loudmouth/loudmouth.h>
-#include "xmpp.h"
-
-// These two defines are used by hk_message_{in,out} arguments
-#define ENCRYPTED_PGP 1
-#define ENCRYPTED_OTR 2
-
-#include "config.h"
-#ifdef MODULES_ENABLE
-#include <glib.h>
-
-#define HOOK_MESSAGE_IN ( 0x00000001 )
-#define HOOK_MESSAGE_OUT ( 0x00000002 )
-#define HOOK_STATUS_CHANGE ( 0x00000004 )
-#define HOOK_MY_STATUS_CHANGE ( 0x00000008 )
-#define HOOK_INTERNAL ( 0x00000010 )
-
-typedef struct {
- const char *name;
- const char *value;
-} hk_arg_t;
-
-typedef void (*hk_handler_t) (guint32 flags, hk_arg_t *args, gpointer userdata);
-
-void hk_add_handler (hk_handler_t handler, guint32 flags, gpointer userdata);
-void hk_del_handler (hk_handler_t handler, gpointer userdata);
-#endif
-
-void hk_message_in(const char *bjid, const char *resname,
- time_t timestamp, const char *msg, LmMessageSubType type,
- guint encrypted);
-void hk_message_out(const char *bjid, const char *nickname,
- time_t timestamp, const char *msg,
- guint encrypted, gpointer xep184);
-void hk_statuschange(const char *bjid, const char *resname, gchar prio,
- time_t timestamp, enum imstatus status,
- char const *status_msg);
-void hk_mystatuschange(time_t timestamp,
- enum imstatus old_status,
- enum imstatus new_status, const char *msg);
-
-void hook_execute_internal(const char *hookname);
-
-void hk_ext_cmd_init(const char *command);
-void hk_ext_cmd(const char *bjid, guchar type, guchar info, const char *data);
-
-#endif /* __HOOKS_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/logprint.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-#ifndef __LOGPRINT_H__
-#define __LOGPRINT_H__ 1
-
-// Flags for scr_LogPrint()
-#define LPRINT_NORMAL 1U // Display in log window
-#define LPRINT_LOG 2U // Log to file (if enabled)
-#define LPRINT_DEBUG 4U // Debug message (log if enabled)
-#define LPRINT_NOTUTF8 8U // Do not convert from UTF-8 to locale
-
-// For convenience...
-#define LPRINT_LOGNORM (LPRINT_NORMAL|LPRINT_LOG)
-
-void scr_print_logwindow(const char *string);
-void scr_LogPrint(unsigned int flag, const char *fmt, ...);
-
-void scr_DoUpdate(void);
-
-#endif /* __LOGPRINT_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/main.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,494 +0,0 @@
-/*
- * main.c
- *
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- * Parts of this file come from Cabber <cabber@ajmacias.com>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <signal.h>
-#include <termios.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <glib.h>
-#include <config.h>
-#include <poll.h>
-
-#include "caps.h"
-#include "screen.h"
-#include "settings.h"
-#include "roster.h"
-#include "commands.h"
-#include "histolog.h"
-#include "hooks.h"
-#include "utils.h"
-#include "pgp.h"
-#include "otr.h"
-#include "fifo.h"
-#include "xmpp.h"
-
-#ifdef ENABLE_HGCSET
-# include "hgcset.h"
-#endif
-
-#ifndef WAIT_ANY
-# define WAIT_ANY -1
-#endif
-
-static unsigned int terminate_ui;
-GMainContext *main_context;
-
-static gboolean update_screen = TRUE;
-
-static struct termios *backup_termios;
-
-char *mcabber_version(void)
-{
- char *ver;
-#ifdef HGCSET
- ver = g_strdup_printf("%s (%s)", PACKAGE_VERSION, HGCSET);
-#else
- ver = g_strdup(PACKAGE_VERSION);
-#endif
- return ver;
-}
-
-static void mcabber_terminate(const char *msg)
-{
- fifo_deinit();
- xmpp_disconnect();
- scr_TerminateCurses();
-
- // Restore term settings, if needed.
- if (backup_termios)
- tcsetattr(fileno(stdin), TCSAFLUSH, backup_termios);
-
- if (msg)
- fprintf(stderr, "%s\n", msg);
- printf("Bye!\n");
- exit(EXIT_SUCCESS);
-}
-
-void sig_handler(int signum)
-{
- if (signum == SIGCHLD) {
- int status;
- pid_t pid;
- do {
- pid = waitpid (WAIT_ANY, &status, WNOHANG);
- // Check the exit status value if 'eventcmd_checkstatus' is set
- if (settings_opt_get_int("eventcmd_checkstatus")) {
- if (pid > 0) {
- // exit status 2 -> beep
- if (WIFEXITED(status) && WEXITSTATUS(status) == 2) {
- scr_Beep();
- }
- }
- }
- } while (pid > 0);
- signal(SIGCHLD, sig_handler);
- } else if (signum == SIGTERM) {
- mcabber_terminate("Killed by SIGTERM");
- } else if (signum == SIGINT) {
- mcabber_terminate("Killed by SIGINT");
-#ifdef USE_SIGWINCH
- } else if (signum == SIGWINCH) {
- ungetch(KEY_RESIZE);
-#endif
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "Caught signal: %d", signum);
- }
-}
-
-// ask_password(what)
-// Return the password, or NULL.
-// The string must be freed after use.
-static char *ask_password(const char *what)
-{
- char *password, *p;
- size_t passsize = 128;
- struct termios orig, new;
-
- password = g_new0(char, passsize);
-
- /* Turn echoing off and fail if we can't. */
- if (tcgetattr(fileno(stdin), &orig) != 0) return NULL;
- backup_termios = &orig;
-
- new = orig;
- new.c_lflag &= ~ECHO;
- if (tcsetattr(fileno(stdin), TCSAFLUSH, &new) != 0) return NULL;
-
- /* Read the password. */
- printf("Please enter %s: ", what);
- if (fgets(password, passsize, stdin) == NULL) return NULL;
-
- /* Restore terminal. */
- tcsetattr(fileno(stdin), TCSAFLUSH, &orig);
- printf("\n");
- backup_termios = NULL;
-
- for (p = (char*)password; *p; p++)
- ;
- for ( ; p > (char*)password ; p--)
- if (*p == '\n' || *p == '\r') *p = 0;
-
- return password;
-}
-
-static void credits(void)
-{
- const char *v_fmt = "MCabber %s -- Email: mcabber [at] lilotux [dot] net\n";
- char *v = mcabber_version();
- printf(v_fmt, v);
- scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8, v_fmt, v);
- g_free(v);
-}
-
-static void compile_options(void)
-{
- puts("Installation data directory: " DATA_DIR "\n");
-#ifdef HAVE_UNICODE
- puts("Compiled with unicode support.");
-#endif
-#ifdef MODULES_ENABLE
- puts ("Compiled with modules support.");
-#endif
-#ifdef HAVE_GPGME
- puts("Compiled with GPG support.");
-#endif
-#ifdef HAVE_LIBOTR
- puts("Compiled with OTR support.");
-#endif
-#ifdef WITH_ENCHANT
- puts("Compiled with Enchant support.");
-#endif
-#ifdef WITH_ASPELL
- puts("Compiled with Aspell support.");
-#endif
-#ifdef ENABLE_DEBUG
- puts("Compiled with debugging support.");
-#endif
-}
-
-static void main_init_pgp(void)
-{
-#ifdef HAVE_GPGME
- const char *pk, *pp;
- char *typed_passwd = NULL;
- char *p;
- bool pgp_invalid = FALSE;
- bool pgp_agent;
- int retries;
-
- p = getenv("GPG_AGENT_INFO");
- pgp_agent = (p && strchr(p, ':'));
-
- pk = settings_opt_get("pgp_private_key");
- pp = settings_opt_get("pgp_passphrase");
-
- if (settings_opt_get("pgp_passphrase_retries"))
- retries = settings_opt_get_int("pgp_passphrase_retries");
- else
- retries = 2;
-
- if (!pk) {
- scr_LogPrint(LPRINT_LOGNORM, "WARNING: unknown PGP private key");
- pgp_invalid = TRUE;
- } else if (!(pp || pgp_agent)) {
- // Request PGP passphrase
- pp = typed_passwd = ask_password("PGP passphrase");
- }
- gpg_init(pk, pp);
- // Erase password from the settings array
- if (pp) {
- memset((char*)pp, 0, strlen(pp));
- if (typed_passwd)
- g_free(typed_passwd);
- else
- settings_set(SETTINGS_TYPE_OPTION, "pgp_passphrase", NULL);
- }
- if (!pgp_agent && pk && pp && gpg_test_passphrase()) {
- // Let's check the pasphrase
- int i;
- for (i = 1; retries < 0 || i <= retries; i++) {
- typed_passwd = ask_password("PGP passphrase"); // Ask again...
- if (typed_passwd) {
- gpg_set_passphrase(typed_passwd);
- memset(typed_passwd, 0, strlen(typed_passwd));
- g_free(typed_passwd);
- }
- if (!gpg_test_passphrase())
- break; // Ok
- }
- if (i > retries)
- pgp_invalid = TRUE;
- }
- if (pgp_invalid)
- scr_LogPrint(LPRINT_LOGNORM, "WARNING: PGP key/pass invalid");
-#else /* not HAVE_GPGME */
- scr_LogPrint(LPRINT_LOGNORM, "WARNING: not compiled with PGP support");
-#endif /* HAVE_GPGME */
-}
-
-void mcabber_set_terminate_ui(void)
-{
- terminate_ui = TRUE;
-}
-
-typedef struct {
- GSource source;
- GPollFD pollfd;
-} mcabber_source_t;
-
-static gboolean mcabber_source_prepare(GSource *source, gint *timeout)
-{
- *timeout = -1;
- return FALSE;
-}
-
-static gboolean mcabber_source_check(GSource *source)
-{
- mcabber_source_t *mc_source = (mcabber_source_t *) source;
- gushort revents = mc_source->pollfd.revents;
- if (revents)
- return TRUE;
- return FALSE;
-}
-
-static gboolean keyboard_activity(void)
-{
- keycode kcode;
-
- if (terminate_ui) {
- return FALSE;
- }
- scr_DoUpdate();
- scr_Getch(&kcode);
-
- while (kcode.value != ERR) {
- process_key(kcode);
- update_screen = TRUE;
- scr_Getch(&kcode);
- }
- scr_CheckAutoAway(FALSE);
-
- return TRUE;
-}
-
-static gboolean mcabber_source_dispatch(GSource *source, GSourceFunc callback,
- gpointer udata) {
- return keyboard_activity();
-}
-
-static GSourceFuncs mcabber_source_funcs = {
- mcabber_source_prepare,
- mcabber_source_check,
- mcabber_source_dispatch,
- NULL,
- NULL,
- NULL
-};
-
-int main(int argc, char **argv)
-{
- char *configFile = NULL;
- const char *optstring;
- int optval, optval2;
- int ret;
-
- credits();
-
- signal(SIGTERM, sig_handler);
- signal(SIGINT, sig_handler);
- signal(SIGCHLD, sig_handler);
-#ifdef USE_SIGWINCH
- signal(SIGWINCH, sig_handler);
-#endif
- signal(SIGPIPE, SIG_IGN);
-
- /* Parse command line options */
- while (1) {
- int c = getopt(argc, argv, "hVf:");
- if (c == -1) {
- break;
- } else
- switch (c) {
- case 'h':
- case '?':
- printf("Usage: %s [-h|-V|-f mcabberrc_file]\n\n", argv[0]);
- return (c == 'h' ? 0 : -1);
- case 'V':
- compile_options();
- return 0;
- case 'f':
- configFile = g_strdup(optarg);
- break;
- }
- }
-
- if (optind < argc) {
- fprintf(stderr, "Usage: %s [-h|-V|-f mcabberrc_file]\n\n", argv[0]);
- return -1;
- }
-
- /* Initialize command system, roster and default key bindings */
- cmd_init();
- roster_init();
- settings_init();
- scr_init_bindings();
- caps_init();
- /* Initialize charset */
- scr_InitLocaleCharSet();
-
- /* Parsing config file... */
- ret = cfg_read_file(configFile, TRUE);
- /* free() configFile if it has been allocated during options parsing */
- g_free(configFile);
- /* Leave if there was an error in the config. file */
- if (ret == -2)
- exit(EXIT_FAILURE);
-
- optstring = settings_opt_get("tracelog_file");
- if (optstring)
- ut_InitDebug(settings_opt_get_int("tracelog_level"), optstring);
-
- /* If no password is stored, we ask for it before entering
- ncurses mode -- unless the username is unknown. */
- if (settings_opt_get("jid") && !settings_opt_get("password")) {
- const char *p;
- char *pwd;
- p = settings_opt_get("server");
- if (p)
- printf("Server: %s\n", p);
- p = settings_opt_get("jid");
- if (p)
- printf("User JID: %s\n", p);
-
- pwd = ask_password("Jabber password");
- settings_set(SETTINGS_TYPE_OPTION, "password", pwd);
- g_free(pwd);
- }
-
- /* Initialize PGP system
- We do it before ncurses initialization because we may need to request
- a passphrase. */
- if (settings_opt_get_int("pgp"))
- main_init_pgp();
-
- /* Initialize N-Curses */
- scr_LogPrint(LPRINT_DEBUG, "Initializing N-Curses...");
- scr_InitCurses();
- scr_DrawMainWindow(TRUE);
-
- optval = (settings_opt_get_int("logging") > 0);
- optval2 = (settings_opt_get_int("load_logs") > 0);
- if (optval || optval2)
- hlog_enable(optval, settings_opt_get("logging_dir"), optval2);
-
-#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
- /* Initialize spelling */
- if (settings_opt_get_int("spell_enable")) {
- spellcheck_init();
- }
-#endif
-
- optstring = settings_opt_get("events_command");
- if (optstring)
- hk_ext_cmd_init(optstring);
-
- optstring = settings_opt_get("roster_display_filter");
- if (optstring)
- scr_RosterDisplay(optstring);
- // Empty filter isn't allowed...
- if (!buddylist_get_filter())
- scr_RosterDisplay("*");
-
- chatstates_disabled = settings_opt_get_int("disable_chatstates");
-
- /* Initialize FIFO named pipe */
- fifo_init(settings_opt_get("fifo_name"));
-
- /* Load previous roster state */
- hlog_load_state();
-
- main_context = g_main_context_default();
-
- if (ret < 0) {
- scr_LogPrint(LPRINT_NORMAL, "No configuration file has been found.");
- scr_ShowBuddyWindow();
- } else {
- /* Connection */
- xmpp_connect();
- }
-
- { // add keypress processing source
- GSource *mc_source = g_source_new(&mcabber_source_funcs,
- sizeof(mcabber_source_t));
- GPollFD *mc_pollfd = &(((mcabber_source_t *)mc_source)->pollfd);
- mc_pollfd->fd = STDIN_FILENO;
- mc_pollfd->events = POLLIN|POLLERR|POLLPRI;
- mc_pollfd->revents = 0;
- g_source_add_poll(mc_source, mc_pollfd);
- g_source_attach(mc_source, main_context);
-
- scr_LogPrint(LPRINT_DEBUG, "Entering into main loop...");
-
- while(!terminate_ui) {
- if (g_main_context_iteration(main_context, TRUE) == FALSE)
- keyboard_activity();
- if (update_roster)
- scr_DrawRoster();
- if(update_screen)
- scr_DoUpdate();
- }
-
- g_source_destroy(mc_source);
- g_source_unref(mc_source);
- }
-
- scr_TerminateCurses();
-#ifdef MODULES_ENABLE
- cmd_deinit();
-#endif
- fifo_deinit();
-#ifdef HAVE_LIBOTR
- otr_terminate();
-#endif
- xmpp_disconnect();
-#ifdef HAVE_GPGME
- gpg_terminate();
-#endif
-#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
- /* Deinitialize spelling */
- if (settings_opt_get_int("spell_enable"))
- spellcheck_deinit();
-#endif
- /* Save pending message state */
- hlog_save_state();
- caps_free();
-
- printf("\n\nThanks for using mcabber!\n");
-
- return 0;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/main.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-#ifndef __MCABBER_MAIN_H__
-#define __MCABBER_MAIN_H__ 1
-
-extern GMainContext *main_context;
-
-void mcabber_set_terminate_ui(void);
-char *mcabber_version(void);
-
-#endif
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/nohtml.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-/*
- * nohtml.c -- (X)HTML helper functions
- *
- * Copyright (C) 2008,2009 Mikael Berthe <mikael@lilotux.net>
- * Some portions come from the jabberd project, see below.
- *
- * 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 parts come from libjabber/str.c:
- * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
- * information for Jabber.com, Inc. is available at http://www.jabber.com/.
- * Portions Copyright (c) 1998-1999 Jeremie Miller.
- */
-
-#include <string.h>
-#include <glib.h>
-#include <config.h>
-
-
-/* html_strip(htmlbuf)
- * Remove html entities from htmlbuf and try to convert it to plain text.
- * The caller must g_free the string after use.
- * Code mostly derived from strunescape(), in libjabber.
- */
-char *html_strip(const char *htmlbuf)
-{
- int i, j=0;
- char *nohtml;
-
- if (!htmlbuf) return(NULL);
-
- nohtml = g_strdup(htmlbuf);
-
- if (!strchr(htmlbuf, '&') && !strchr(htmlbuf, '<'))
- return(nohtml);
-
- for (i = 0; i < (int)strlen(htmlbuf); i++) {
- if (htmlbuf[i] == '&') {
- if (!strncmp(&htmlbuf[i],"&",5)) {
- nohtml[j] = '&';
- i += 4;
- } else if (!strncmp(&htmlbuf[i],""", 6)) {
- nohtml[j] = '\"';
- i += 5;
- } else if (!strncmp(&htmlbuf[i],"'", 6)) {
- nohtml[j] = '\'';
- i += 5;
- } else if (!strncmp(&htmlbuf[i],"<", 4)) {
- nohtml[j] = '<';
- i += 3;
- } else if (!strncmp(&htmlbuf[i],">", 4)) {
- nohtml[j] = '>';
- i += 3;
- }
- } else if (!strncmp(&htmlbuf[i],"<br>", 4) ||
- !strncmp(&htmlbuf[i],"<br/>", 5)) {
- nohtml[j] = '\n';
- i += (htmlbuf[i+3] == '/' ? 4 : 3);
- } else if (htmlbuf[i] == '<') {
- /* Let's strip all unknown tags */
- j--;
- while (htmlbuf[++i] != '>');
- } else
- nohtml[j] = htmlbuf[i];
- j++;
- }
- nohtml[j] = '\0';
- return nohtml;
-}
-
-/* html_escape(text)
- * Add (x)html entities to the text.
- * The caller must g_free the string after use.
- * Code mostly derived from strescape(), in libjabber.
- */
-char *html_escape(const char *text)
-{
- int i, j;
- int oldlen, newlen;
- char *html;
-
- if (!text) return(NULL);
-
- oldlen = newlen = strlen(text);
-
- for (i = 0; i < oldlen; i++) {
- switch(text[i])
- {
- case '&':
- newlen += 5;
- break;
- case '\'':
- newlen += 6;
- break;
- case '\"':
- newlen += 6;
- break;
- case '<':
- newlen += 4;
- break;
- case '>':
- newlen += 4;
- break;
- case '\n':
- newlen += 5;
- }
- }
-
- if (oldlen == newlen)
- return g_strdup(text);
-
- html = g_new0(char, newlen+1);
-
- for (i = j = 0; i < oldlen; i++) {
- switch(text[i])
- {
- case '&':
- memcpy(&html[j], "&", 5);
- j += 5;
- break;
- case '\'':
- memcpy(&html[j], "'", 6);
- j += 6;
- break;
- case '\"':
- memcpy(&html[j], """, 6);
- j += 6;
- break;
- case '<':
- memcpy(&html[j], "<", 4);
- j += 4;
- break;
- case '>':
- memcpy(&html[j], ">", 4);
- j += 4;
- break;
- case '\n':
- memcpy(&html[j], "<br/>", 5);
- j += 5;
- break;
- default:
- html[j++] = text[i];
- }
- }
- return html;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/nohtml.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#ifndef __NOHTML_H__
-#define __NOHTML_H__ 1
-
-char *html_strip(const char *buf);
-char *html_escape(const char *text);
-
-#endif /* __NOHTML_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/otr.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,777 +0,0 @@
-/*
- * otr.c -- Off-The-Record Messaging for mcabber
- *
- * Copyright (C) 2007-2009 Frank Zschockelt <mcabber_otr@freakysoft.de>
- *
- * 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 <config.h>
-#include <glib.h>
-
-#ifdef HAVE_LIBOTR
-
-#include "hbuf.h"
-#include "logprint.h"
-#include "nohtml.h"
-#include "otr.h"
-#include "roster.h"
-#include "screen.h"
-#include "settings.h"
-#include "utils.h"
-#include "xmpp.h"
-
-#define OTR_PROTOCOL_NAME "jabber"
-
-static OtrlUserState userstate = NULL;
-static char *account = NULL;
-static char *keyfile = NULL;
-static char *fprfile = NULL;
-
-static int otr_is_enabled = FALSE;
-
-static OtrlPolicy cb_policy (void *opdata, ConnContext *ctx);
-static void cb_create_privkey (void *opdata,
- const char *accountname,
- const char *protocol);
-static int cb_is_logged_in (void *opdata,
- const char *accountname,
- const char *protocol,
- const char *recipient);
-static void cb_inject_message (void *opdata,
- const char *accountname,
- const char *protocol,
- const char *recipient,
- const char *message);
-static void cb_notify (void *opdata,
- OtrlNotifyLevel level,
- const char *accountname,
- const char *protocol,
- const char *username,
- const char *title,
- const char *primary,
- const char *secondary);
-static int cb_display_otr_message(void *opdata,
- const char *accountname,
- const char *protocol,
- const char *username,
- const char *msg);
-static void cb_update_context_list(void *opdata);
-static const char *cb_protocol_name (void *opdata, const char *protocol);
-static void cb_protocol_name_free (void *opdata,
- const char *protocol_name);
-static void cb_new_fingerprint (void *opdata, OtrlUserState us,
- const char *accountname,
- const char *protocol,
- const char *username,
- unsigned char fingerprint[20]);
-static void cb_write_fingerprints (void *opdata);
-static void cb_gone_secure (void *opdata, ConnContext *context);
-static void cb_gone_insecure (void *opdata, ConnContext *context);
-static void cb_still_secure (void *opdata, ConnContext *context,
- int is_reply);
-static void cb_log_message (void *opdata, const char *message);
-static int cb_max_message_size (void *opdata, ConnContext *context);
-
-static OtrlMessageAppOps ops =
-{
- cb_policy,
- cb_create_privkey,
- cb_is_logged_in,
- cb_inject_message,
- cb_notify,
- cb_display_otr_message,
- cb_update_context_list,
- cb_protocol_name,
- cb_protocol_name_free,
- cb_new_fingerprint,
- cb_write_fingerprints,
- cb_gone_secure,
- cb_gone_insecure,
- cb_still_secure,
- cb_log_message,
- cb_max_message_size,
- NULL, /*account_name*/
- NULL /*account_name_free*/
-};
-
-static void otr_message_disconnect(ConnContext *ctx);
-static ConnContext *otr_get_context(const char *buddy);
-static void otr_startstop(const char *buddy, int start);
-static void otr_handle_smp_tlvs(OtrlTLV *tlvs, ConnContext *ctx);
-
-static char *otr_get_dir(void);
-
-void otr_init(const char *fjid)
-{
- char *root;
-
- if (userstate) //already initialised
- return;
-
- otr_is_enabled = !!settings_opt_get_int("otr");
-
- if (!otr_is_enabled)
- return;
-
- OTRL_INIT;
-
- userstate = otrl_userstate_create();
-
- root = otr_get_dir();
- account = jidtodisp(fjid);
- keyfile = g_strdup_printf("%s%s.key", root, account);
- fprfile = g_strdup_printf("%s%s.fpr", root, account);
- g_free(root);
-
- if (otrl_privkey_read(userstate, keyfile)){
- scr_LogPrint(LPRINT_LOGNORM, "Could not read OTR key from %s", keyfile);
- cb_create_privkey(NULL, account, OTR_PROTOCOL_NAME);
- }
- if (otrl_privkey_read_fingerprints(userstate, fprfile, NULL, NULL)){
- scr_LogPrint(LPRINT_LOGNORM, "Could not read OTR fingerprints from %s",
- fprfile);
- }
-}
-
-void otr_terminate(void)
-{
- ConnContext *ctx;
-
- if (!otr_is_enabled)
- return;
-
- for (ctx = userstate->context_root; ctx; ctx = ctx->next)
- if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
- otr_message_disconnect(ctx);
-
- g_free(account);
- account = NULL;
-
- /* XXX This #ifdef is a quick workaround: when mcabber
- * is linked to both gnutls and libotr, libgcrypt will
- * segfault when we call otrl_userstate_free().
- * This is reported to be a bug in libgcrypt :-/
- * Mikael
- */
-#if defined(HAVE_GNUTLS) && !defined(HAVE_OPENSSL) //TODO: broken now
- if (!settings_opt_get_int("ssl"))
-#endif
- otrl_userstate_free(userstate);
-
- userstate = NULL;
- g_free(keyfile);
- keyfile = NULL;
-}
-
-static char *otr_get_dir(void)
-{
- const char *configured_dir = settings_opt_get("otr_dir");
-
- if (configured_dir && *configured_dir) {
- char *xp_conf_dir;
- int l;
- xp_conf_dir = expand_filename(configured_dir);
- // The path must be slash-terminated
- l = strlen(xp_conf_dir);
- if (xp_conf_dir[l-1] != '/') {
- char *xp_conf_dir_tmp = xp_conf_dir;
- xp_conf_dir = g_strdup_printf("%s/", xp_conf_dir_tmp);
- g_free(xp_conf_dir_tmp);
- }
- return xp_conf_dir;
- } else {
- return expand_filename("~/.mcabber/otr/");
- }
-}
-
-static ConnContext *otr_get_context(const char *buddy)
-{
- int null = 0;
- ConnContext *ctx;
- char *lowcasebuddy = g_strdup(buddy);
-
- mc_strtolower(lowcasebuddy);
- ctx = otrl_context_find(userstate, lowcasebuddy, account, OTR_PROTOCOL_NAME,
- 1, &null, NULL, NULL);
- g_free(lowcasebuddy);
- return ctx;
-}
-
-static void otr_message_disconnect(ConnContext *ctx)
-{
- if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
- cb_gone_insecure(NULL, ctx);
- otrl_message_disconnect(userstate, &ops, NULL, ctx->accountname,
- ctx->protocol, ctx->username);
-}
-
-static void otr_startstop(const char *buddy, int start)
-{
- char *msg = NULL;
- ConnContext *ctx = otr_get_context(buddy);
-
- if (!userstate || !ctx)
- return;
-
- if (start && ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
- otr_message_disconnect(ctx);
-
- if (start) {
- OtrlPolicy policy = cb_policy(NULL, ctx);
- if (policy == plain) {
- scr_LogPrint(LPRINT_LOGNORM, "The OTR policy for this user is set to"
- " plain. You have to change it first.");
- return;
- }
- msg = otrl_proto_default_query_msg(ctx->accountname, policy);
- cb_inject_message(NULL, ctx->accountname, ctx->protocol, ctx->username,
- msg);
- free (msg);
- }
- else
- otr_message_disconnect(ctx);
-}
-
-void otr_establish(const char *buddy)
-{
- otr_startstop(buddy, 1);
-}
-
-void otr_disconnect(const char *buddy)
-{
- otr_startstop(buddy, 0);
-}
-
-void otr_fingerprint(const char *buddy, const char *trust)
-{
- char fpr[45], *tr;
- ConnContext *ctx = otr_get_context(buddy);
- if (!userstate || !ctx)
- return;
-
- if (!ctx->active_fingerprint || !ctx->active_fingerprint->fingerprint) {
- scr_LogPrint(LPRINT_LOGNORM,
- "No active fingerprint - start OTR for this buddy first.");
- return;
- }
-
- otrl_privkey_hash_to_human(fpr, ctx->active_fingerprint->fingerprint);
- if (trust) {
- if (strcmp(fpr, trust) == 0)
- otrl_context_set_trust(ctx->active_fingerprint, "trust");
- else
- otrl_context_set_trust(ctx->active_fingerprint, NULL);
- }
-
- tr = ctx->active_fingerprint->trust;
- scr_LogPrint(LPRINT_LOGNORM, "%s [%44s]: %s", ctx->username, fpr,
- tr && *tr ? "trusted" : "untrusted");
- cb_write_fingerprints(NULL);
-}
-
-static void otr_handle_smp_tlvs(OtrlTLV *tlvs, ConnContext *ctx)
-{
- OtrlTLV *tlv = NULL;
- char *sbuf = NULL;
- NextExpectedSMP nextMsg = ctx->smstate->nextExpected;
-
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
- if (tlv) {
- if (nextMsg != OTRL_SMP_EXPECT1)
- otr_smp_abort(ctx->username);
- else {
- sbuf = g_strdup_printf("OTR: Received SMP Initiation. "
- "Answer with /otr smpr %s $secret",
- ctx->username);
- }
- }
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
- if (tlv) {
- if (nextMsg != OTRL_SMP_EXPECT2)
- otr_smp_abort(ctx->username);
- else {
- sbuf = g_strdup("OTR: Received SMP Response.");
- /* If we received TLV2, we will send TLV3 and expect TLV4 */
- ctx->smstate->nextExpected = OTRL_SMP_EXPECT4;
- }
- }
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
- if (tlv) {
- if (nextMsg != OTRL_SMP_EXPECT3)
- otr_smp_abort(ctx->username);
- else {
- /* If we received TLV3, we will send TLV4
- * We will not expect more messages, so prepare for next SMP */
- ctx->smstate->nextExpected = OTRL_SMP_EXPECT1;
- /* Report result to user */
- if (ctx->active_fingerprint && ctx->active_fingerprint->trust &&
- *ctx->active_fingerprint->trust != '\0')
- sbuf = g_strdup("OTR: SMP succeeded");
- else
- sbuf = g_strdup("OTR: SMP failed");
- }
- }
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
- if (tlv) {
- if (nextMsg != OTRL_SMP_EXPECT4)
- otr_smp_abort(ctx->username);
- else {
- /* We will not expect more messages, so prepare for next SMP */
- ctx->smstate->nextExpected = OTRL_SMP_EXPECT1;
- /* Report result to user */
- if (ctx->active_fingerprint && ctx->active_fingerprint->trust &&
- *ctx->active_fingerprint->trust != '\0')
- sbuf = g_strdup("OTR: SMP succeeded");
- else
- sbuf = g_strdup("OTR: SMP failed");
- }
- }
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
- if (tlv) {
- /* The message we are waiting for will not arrive, so reset
- * and prepare for the next SMP */
- sbuf = g_strdup("OTR: SMP aborted by your buddy");
- ctx->smstate->nextExpected = OTRL_SMP_EXPECT1;
- }
-
- if (sbuf) {
- scr_WriteIncomingMessage(ctx->username, sbuf, 0, HBB_PREFIX_INFO, 0);
- g_free(sbuf);
- }
-}
-
-/*
- * returns whether a otr_message was received
- * sets *otr_data to NULL, when it was an internal otr message
- */
-int otr_receive(char **otr_data, const char *buddy, int *free_msg)
-{
- int ignore_message;
- char *newmessage = NULL;
- OtrlTLV *tlvs = NULL;
- OtrlTLV *tlv = NULL;
- ConnContext *ctx;
-
- ctx = otr_get_context(buddy);
- *free_msg = 0;
- ignore_message = otrl_message_receiving(userstate, &ops, NULL,
- ctx->accountname, ctx->protocol,
- ctx->username, *otr_data,
- &newmessage, &tlvs,NULL, NULL);
-
-
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
- if (tlv) {
- /* Notify the user that the other side disconnected. */
- if (ctx) {
- cb_gone_insecure(NULL, ctx);
- otr_disconnect(ctx->username);
- }
- }
-
- otr_handle_smp_tlvs(tlvs, ctx);
-
- if (tlvs != NULL)
- otrl_tlv_free(tlvs);
-
- if (ignore_message)
- *otr_data = NULL;
-
- if (!ignore_message && newmessage) {
- *free_msg = 1;
- *otr_data = html_strip(newmessage);
- otrl_message_free(newmessage);
- if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
- return 1;
- }
- return 0;
-}
-
-int otr_send(char **msg, const char *buddy)
-{
- gcry_error_t err;
- char *newmessage = NULL;
- char *htmlmsg;
- ConnContext *ctx = otr_get_context(buddy);
-
- if (ctx->msgstate == OTRL_MSGSTATE_PLAINTEXT)
- err = otrl_message_sending(userstate, &ops, NULL, ctx->accountname,
- ctx->protocol, ctx->username, *msg, NULL,
- &newmessage, NULL, NULL);
- else {
- htmlmsg = html_escape(*msg);
- err = otrl_message_sending(userstate, &ops, NULL, ctx->accountname,
- ctx->protocol, ctx->username, htmlmsg, NULL,
- &newmessage, NULL, NULL);
- g_free(htmlmsg);
- }
-
- if (err)
- *msg = NULL; /*something went wrong, don't send the plain-message! */
-
- if (!err && newmessage) {
- *msg = g_strdup(newmessage);
- otrl_message_free(newmessage);
- if (cb_policy(NULL, ctx) & OTRL_POLICY_REQUIRE_ENCRYPTION ||
- ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
- return 1;
- }
- return 0;
-}
-
-/* Prints OTR connection state */
-void otr_print_info(const char *buddy)
-{
- const char *state, *auth, *policy;
- ConnContext *ctx = otr_get_context(buddy);
- OtrlPolicy p = cb_policy(ctx->app_data, ctx);
-
- if (!userstate || !ctx)
- return;
-
- switch (ctx->msgstate) {
- case OTRL_MSGSTATE_PLAINTEXT: state = "plaintext"; break;
- case OTRL_MSGSTATE_ENCRYPTED:
- switch (ctx->protocol_version) {
- case 1: state = "encrypted V1"; break;
- case 2: state = "encrypted V2"; break;
- default:state = "encrypted";
- };
- break;
- case OTRL_MSGSTATE_FINISHED: state = "finished"; break;
- default: state = "unknown state";
- }
- switch (ctx->auth.authstate) {
- case OTRL_AUTHSTATE_NONE:
- switch (ctx->otr_offer) {
- case OFFER_NOT: auth = "no offer sent"; break;
- case OFFER_SENT: auth = "offer sent"; break;
- case OFFER_ACCEPTED: auth = "offer accepted"; break;
- case OFFER_REJECTED: auth = "offer rejected"; break;
- default: auth = "unknown auth";
- }
- break;
- case OTRL_AUTHSTATE_AWAITING_DHKEY:
- auth = "awaiting D-H key"; break;
- case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
- auth = "awaiting reveal signature"; break;
- case OTRL_AUTHSTATE_AWAITING_SIG:
- auth = "awaiting signature"; break;
- case OTRL_AUTHSTATE_V1_SETUP:
- auth = "v1 setup"; break;
- default:
- auth = "unknown auth";
- }
- if (p == OTRL_POLICY_NEVER)
- policy = "plain";
- else if (p == (OTRL_POLICY_OPPORTUNISTIC & ~OTRL_POLICY_ALLOW_V1))
- policy = "opportunistic";
- else if (p == (OTRL_POLICY_MANUAL & ~OTRL_POLICY_ALLOW_V1))
- policy = "manual";
- else if (p == (OTRL_POLICY_ALWAYS & ~OTRL_POLICY_ALLOW_V1))
- policy = "always";
- else
- policy = "unknown";
-
- scr_LogPrint(LPRINT_LOGNORM, "%s: %s (%s) [%s]",
- ctx->username, state, auth, policy);
-}
-
-static ConnContext *otr_context_encrypted(const char *buddy)
-{
- ConnContext *ctx = otr_get_context(buddy);
-
- if (!userstate || !ctx || ctx->msgstate != OTRL_MSGSTATE_ENCRYPTED){
- scr_LogPrint(LPRINT_LOGNORM,
- "You have to start an OTR channel with %s before you can "
- "use SMP.", buddy);
- return NULL;
- }
-
- return ctx;
-}
-
-void otr_smp_query(const char *buddy, const char *secret)
-{
- ConnContext *ctx = otr_context_encrypted(buddy);
-
- if (!secret) {
- scr_LogPrint(LPRINT_LOGNORM,
- "Using SMP without a secret isn't a good idea.");
- return;
- }
-
- if (ctx) {
- otrl_message_initiate_smp(userstate, &ops, NULL, ctx,
- (const unsigned char *)secret,
- strlen(secret));
- scr_WriteIncomingMessage(ctx->username,
- "OTR: Socialist Millionaires' Protocol "
- "initiated.", 0, HBB_PREFIX_INFO, 0);
- }
-}
-
-void otr_smp_respond(const char *buddy, const char *secret)
-{
- ConnContext *ctx = otr_context_encrypted(buddy);
-
- if (!secret) {
- scr_LogPrint(LPRINT_LOGNORM,
- "Using SMP without a secret isn't a good idea.");
- return;
- }
-
- if (ctx) {
- if (!ctx->smstate->secret) {
- scr_LogPrint(LPRINT_LOGNORM,
- "Don't call smpr until you have received an SMP "
- "Initiation!");
- return;
- }
- otrl_message_respond_smp(userstate, &ops, NULL, ctx,
- (const unsigned char *)secret,
- strlen(secret));
- scr_WriteIncomingMessage(ctx->username,
- "OTR: Socialist Millionaires' Protocol: "
- "response sent", 0, HBB_PREFIX_INFO, 0);
- }
-}
-
-void otr_smp_abort(const char *buddy)
-{
- ConnContext *ctx = otr_context_encrypted(buddy);
-
- if (ctx) {
- otrl_message_abort_smp(userstate, &ops, NULL, ctx);
- scr_WriteIncomingMessage(ctx->username,
- "OTR: Socialist Millionaires' Protocol aborted.",
- 0, HBB_PREFIX_INFO, 0);
- }
-}
-
-void otr_key(void)
-{
- OtrlPrivKey *key;
- char readable[45] = "";
-
- if(!userstate)
- return;
- for (key = userstate->privkey_root; key; key = key->next) {
- otrl_privkey_fingerprint(userstate, readable, key->accountname,
- key->protocol);
- scr_LogPrint(LPRINT_LOGNORM, "%s: %s", key->accountname, readable);
- }
-}
-
-/* Return the OTR policy for the given context. */
-static OtrlPolicy cb_policy(void *opdata, ConnContext *ctx)
-{
- enum otr_policy p = settings_otr_getpolicy(NULL);
-
- if(ctx)
- if(settings_otr_getpolicy(ctx->username))
- p = settings_otr_getpolicy(ctx->username);
-
- switch (p) {
- case plain:
- return OTRL_POLICY_NEVER;
- case opportunistic:
- return OTRL_POLICY_OPPORTUNISTIC & ~OTRL_POLICY_ALLOW_V1;
- case manual:
- return OTRL_POLICY_MANUAL & ~OTRL_POLICY_ALLOW_V1;
- case always:
- return OTRL_POLICY_ALWAYS & ~OTRL_POLICY_ALLOW_V1;
- }
-
- return OTRL_POLICY_MANUAL & ~OTRL_POLICY_ALLOW_V1;
-}
-
-/* Create a private key for the given accountname/protocol if
- * desired. */
-static void cb_create_privkey(void *opdata, const char *accountname,
- const char *protocol)
-{
- gcry_error_t e;
- char *root;
-
- scr_LogPrint(LPRINT_LOGNORM,
- "Generating new OTR key for %s. This may take a while...",
- accountname);
- scr_DoUpdate();
-
- e = otrl_privkey_generate(userstate, keyfile, accountname, protocol);
-
- if (e) {
- root = otr_get_dir();
- scr_LogPrint(LPRINT_LOGNORM, "OTR key generation failed! Please mkdir "
- "%s if you want to use otr encryption.", root);
- g_free(root);
- }
- else
- scr_LogPrint(LPRINT_LOGNORM, "OTR key generated.");
-}
-
-/* Report whether you think the given user is online. Return 1 if
- * you think he is, 0 if you think he isn't, -1 if you're not sure.
- * If you return 1, messages such as heartbeats or other
- * notifications may be sent to the user, which could result in "not
- * logged in" errors if you're wrong. */
-static int cb_is_logged_in(void *opdata, const char *accountname,
- const char *protocol, const char *recipient)
-{
- int ret = (roster_getstatus(recipient, NULL) != offline);
- return ret;
-}
-
-/* Send the given IM to the given recipient from the given
- * accountname/protocol. */
-static void cb_inject_message(void *opdata, const char *accountname,
- const char *protocol, const char *recipient,
- const char *message)
-{
- if (roster_gettype(recipient) == ROSTER_TYPE_USER)
- xmpp_send_msg(recipient, message, ROSTER_TYPE_USER, "", TRUE, NULL,
- LM_MESSAGE_SUB_TYPE_NOT_SET, NULL);
-}
-
-/* Display a notification message for a particular
- * accountname / protocol / username conversation. */
-static void cb_notify(void *opdata, OtrlNotifyLevel level,
- const char *accountname, const char *protocol,
- const char *username, const char *title,
- const char *primary, const char *secondary)
-{
- char *type;
- char *sbuf = NULL;
- switch (level) {
- case OTRL_NOTIFY_ERROR: type = "error"; break;
- case OTRL_NOTIFY_WARNING: type = "warning"; break;
- case OTRL_NOTIFY_INFO: type = "info"; break;
- default: type = "unknown";
- }
- sbuf = g_strdup_printf("OTR %s:%s\n%s\n%s",type,title, primary, secondary);
- scr_WriteIncomingMessage(username, sbuf, 0, HBB_PREFIX_INFO, 0);
- g_free(sbuf);
-}
-
-/* Display an OTR control message for a particular
- * accountname / protocol / username conversation. Return 0 if you are able
- * to successfully display it. If you return non-0 (or if this
- * function is NULL), the control message will be displayed inline,
- * as a received message, or else by using the above notify()
- * callback. */
-static int cb_display_otr_message(void *opdata, const char *accountname,
- const char *protocol, const char *username,
- const char *msg)
-{
- char *strippedmsg = html_strip(msg);
- scr_WriteIncomingMessage(username, strippedmsg, 0, HBB_PREFIX_INFO, 0);
- g_free(strippedmsg);
- return 0;
-}
-
-/* When the list of ConnContexts changes (including a change in
- * state), this is called so the UI can be updated. */
-static void cb_update_context_list(void *opdata)
-{
- /*maybe introduce new status characters for mcabber,
- * then use this function (?!)*/
-}
-
-/* Return a newly allocated string containing a human-friendly name
- * for the given protocol id */
-static const char *cb_protocol_name(void *opdata, const char *protocol)
-{
- return protocol;
-}
-
-/* Deallocate a string allocated by protocol_name */
-static void cb_protocol_name_free (void *opdata, const char *protocol_name)
-{
- /* We didn't allocated memory, so we don't have to free anything :p */
-}
-
-/* A new fingerprint for the given user has been received. */
-static void cb_new_fingerprint(void *opdata, OtrlUserState us,
- const char *accountname, const char *protocol,
- const char *username,
- unsigned char fingerprint[20])
-{
- char *sbuf = NULL;
- char readable[45];
-
- otrl_privkey_hash_to_human(readable, fingerprint);
- sbuf = g_strdup_printf("OTR: new fingerprint: %s", readable);
- scr_WriteIncomingMessage(username, sbuf, 0, HBB_PREFIX_INFO, 0);
- g_free(sbuf);
-}
-
-/* The list of known fingerprints has changed. Write them to disk. */
-static void cb_write_fingerprints(void *opdata)
-{
- otrl_privkey_write_fingerprints(userstate, fprfile);
-}
-
-/* A ConnContext has entered a secure state. */
-static void cb_gone_secure(void *opdata, ConnContext *context)
-{
- scr_WriteIncomingMessage(context->username, "OTR: channel established", 0,
- HBB_PREFIX_INFO, 0);
-}
-
-/* A ConnContext has left a secure state. */
-static void cb_gone_insecure(void *opdata, ConnContext *context)
-{
- scr_WriteIncomingMessage(context->username, "OTR: channel closed", 0,
- HBB_PREFIX_INFO, 0);
-}
-
-/* We have completed an authentication, using the D-H keys we
- * already knew. is_reply indicates whether we initiated the AKE. */
-static void cb_still_secure(void *opdata, ConnContext *context, int is_reply)
-{
- scr_WriteIncomingMessage(context->username, "OTR: channel reestablished", 0,
- HBB_PREFIX_INFO, 0);
-}
-
-/* Log a message. The passed message will end in "\n". */
-static void cb_log_message(void *opdata, const char *message)
-{
- scr_LogPrint(LPRINT_DEBUG, "OTR: %s", message);
-}
-
-/* Find the maximum message size supported by this protocol. */
-static int cb_max_message_size(void *opdata, ConnContext *context)
-{
- return 8192;
-}
-
-int otr_enabled(void)
-{
- return otr_is_enabled;
-}
-
-#else /* !HAVE_LIBOTR */
-
-int otr_enabled(void)
-{
- return FALSE;
-}
-
-#endif /* HAVE_LIBOTR */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/otr.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-#ifndef __OTR_H__
-#define __OTR_H__ 1
-
-#ifdef HAVE_LIBOTR
-
-#include <libotr/proto.h>
-#include <libotr/message.h>
-#include <libotr/privkey.h>
-
-enum otr_policy {
- plain,
- opportunistic,
- manual,
- always
-};
-
-void otr_init(const char *jid);
-void otr_terminate(void);
-
-void otr_establish (const char * buddy);
-void otr_disconnect (const char * buddy);
-void otr_fingerprint(const char * buddy, const char * trust);
-void otr_print_info (const char * buddy);
-
-void otr_smp_query (const char * buddy, const char * secret);
-void otr_smp_respond(const char * buddy, const char * secret);
-void otr_smp_abort (const char * buddy);
-
-void otr_key (void);
-
-int otr_receive (char **otr_data, const char * buddy, int * free_msg);
-int otr_send (char **msg, const char *buddy);
-
-#endif /* HAVE_LIBOTR */
-
-int otr_enabled (void);
-
-#endif /* __OTR_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/pgp.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,475 +0,0 @@
-/*
- * pgp.c -- PGP utility functions
- *
- * Copyright (C) 2006-2009 Mikael Berthe <mikael@lilotux.net>
- * Some parts inspired by centericq (impgp.cc)
- *
- * 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 <config.h>
-
-#ifdef HAVE_GPGME
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <locale.h>
-#include <sys/mman.h>
-#include <glib.h>
-
-#include "pgp.h"
-#include "logprint.h"
-
-#define MIN_GPGME_VERSION "1.0.0"
-
-static struct gpg_struct
-{
- int enabled;
- char *private_key;
- char *passphrase;
-} gpg;
-
-
-// gpg_init(priv_key, passphrase)
-// Initialize the GPG sub-systems. This function must be invoked early.
-// Note: priv_key & passphrase are optional, they can be set later.
-// This function returns 0 if gpgme is available and initialized;
-// if not it returns the gpgme error code.
-int gpg_init(const char *priv_key, const char *passphrase)
-{
- gpgme_error_t err;
-
- // Check for version and OpenPGP protocol support.
- if (!gpgme_check_version(MIN_GPGME_VERSION)) {
- scr_LogPrint(LPRINT_LOGNORM,
- "GPGME initialization error: Bad library version");
- return -1;
- }
-
- err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
- if (err) {
- scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
- "GPGME initialization error: %s", gpgme_strerror(err));
- return err;
- }
-
- // Set the locale information.
- gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
- gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
-
- // Store private data.
- gpg_set_private_key(priv_key);
- gpg_set_passphrase(passphrase);
-
- gpg.enabled = 1;
- return 0;
-}
-
-// gpg_terminate()
-// Destroy data and free memory.
-void gpg_terminate(void)
-{
- gpg.enabled = 0;
- gpg_set_passphrase(NULL);
- gpg_set_private_key(NULL);
-}
-
-// gpg_set_passphrase(passphrase)
-// Set the current passphrase (use NULL to erase it).
-void gpg_set_passphrase(const char *passphrase)
-{
- // Remove current passphrase
- if (gpg.passphrase) {
- ssize_t len = strlen(gpg.passphrase);
- memset(gpg.passphrase, 0, len);
- munlock(gpg.passphrase, len);
- g_free(gpg.passphrase);
- }
- if (passphrase) {
- gpg.passphrase = g_strdup(passphrase);
- mlock(gpg.passphrase, strlen(gpg.passphrase));
- } else {
- gpg.passphrase = NULL;
- }
-}
-
-// gpg_set_private_key(keyid)
-// Set the current private key id (use NULL to unset it).
-void gpg_set_private_key(const char *priv_keyid)
-{
- g_free(gpg.private_key);
- if (priv_keyid)
- gpg.private_key = g_strdup(priv_keyid);
- else
- gpg.private_key = NULL;
-}
-
-// strip_header_footer(data)
-// Remove PGP header & footer from data.
-// Return a new string, or NULL.
-// The string must be freed by the caller with g_free() when no longer needed.
-static char *strip_header_footer(const char *data)
-{
- char *p, *q;
-
- if (!data)
- return NULL;
-
- // p: beginning of real data
- // q: end of real data
-
- // Strip header (to the first empty line)
- p = strstr(data, "\n\n");
- if (!p)
- return g_strdup(data);
-
- // Strip footer
- // We want to remove the last lines, until the line beginning with a '-'
- p += 2;
- for (q = p ; *q; q++) ;
- // (q is at the end of data now)
- for (q--; q > p && (*q != '\n' || *(q+1) != '-'); q--) ;
-
- if (q <= p)
- return NULL; // Shouldn't happen...
-
- return g_strndup(p, q-p);
-}
-
-// GCC ignores casts to void, thus we need to hack around that
-static inline void ignore(void*x) {}
-
-// passphrase_cb()
-// GPGME passphrase callback function.
-static gpgme_error_t passphrase_cb(void *hook, const char *uid_hint,
- const char *passphrase_info, int prev_was_bad, int fd)
-{
- ssize_t len;
-
- // Abort if we do not have the password.
- if (!gpg.passphrase) {
- ignore((void*)write(fd, "\n", 1)); // We have an error anyway, thus it does
- // not matter if we fail again.
- return gpg_error(GPG_ERR_CANCELED);
- }
-
- // Write the passphrase to the file descriptor.
- len = strlen(gpg.passphrase);
- if (write(fd, gpg.passphrase, len) != len)
- return gpg_error(GPG_ERR_CANCELED);
- if (write(fd, "\n", 1) != 1)
- return gpg_error(GPG_ERR_CANCELED);
-
- return 0; // Success
-}
-
-// gpg_verify(gpg_data, text, *sigsum)
-// Verify that gpg_data is a correct signature for text.
-// Return the key id (or fingerprint), and set *sigsum to
-// the gpgme signature summary value.
-// The returned string must be freed with g_free() after use.
-char *gpg_verify(const char *gpg_data, const char *text,
- gpgme_sigsum_t *sigsum)
-{
- gpgme_ctx_t ctx;
- gpgme_data_t data_sign, data_text;
- char *data;
- char *verified_key = NULL;
- gpgme_key_t key;
- gpgme_error_t err;
- const char prefix[] = "-----BEGIN PGP SIGNATURE-----\n\n";
- const char suffix[] = "\n-----END PGP SIGNATURE-----\n";
-
- // Reset the summary.
- *sigsum = 0;
-
- if (!gpg.enabled)
- return NULL;
-
- err = gpgme_new(&ctx);
- if (err) {
- scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
- "GPGME error: %s", gpgme_strerror(err));
- return NULL;
- }
-
- gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
-
- // Surround the given data with the prefix & suffix
- data = g_new(char, sizeof(prefix) + sizeof(suffix) + strlen(gpg_data));
- strcpy(data, prefix);
- strcat(data, gpg_data);
- strcat(data, suffix);
-
- err = gpgme_data_new_from_mem(&data_sign, data, strlen(data), 0);
- if (!err) {
- err = gpgme_data_new_from_mem(&data_text, text, strlen(text), 0);
- if (!err) {
- err = gpgme_op_verify(ctx, data_sign, data_text, 0);
- if (!err) {
- gpgme_verify_result_t vr = gpgme_op_verify_result(ctx);
- if (vr && vr->signatures) {
- char *r = vr->signatures->fpr;
- // Found the fingerprint. Let's try to get the key id.
- if (!gpgme_get_key(ctx, r, &key, 0) && key) {
- r = key->subkeys->keyid;
- gpgme_key_release(key);
- }
- // r is a static variable, let's copy it.
- verified_key = g_strdup(r);
- *sigsum = vr->signatures->summary;
- // For some reason summary could be 0 when status is 0 too,
- // which means the signature is valid...
- if (!*sigsum && !vr->signatures->status)
- *sigsum = GPGME_SIGSUM_GREEN;
- }
- }
- gpgme_data_release(data_text);
- }
- gpgme_data_release(data_sign);
- }
- if (err)
- scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
- "GPGME verification error: %s", gpgme_strerror(err));
- gpgme_release(ctx);
- g_free(data);
- return verified_key;
-}
-
-// gpg_sign(gpg_data)
-// Return a signature of gpg_data (or NULL).
-// The returned string must be freed with g_free() after use.
-char *gpg_sign(const char *gpg_data)
-{
- gpgme_ctx_t ctx;
- gpgme_data_t in, out;
- char *p;
- char *signed_data = NULL;
- size_t nread;
- gpgme_key_t key;
- gpgme_error_t err;
-
- if (!gpg.enabled || !gpg.private_key)
- return NULL;
-
- err = gpgme_new(&ctx);
- if (err) {
- scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
- "GPGME error: %s", gpgme_strerror(err));
- return NULL;
- }
-
- gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
- gpgme_set_textmode(ctx, 0);
- gpgme_set_armor(ctx, 1);
-
- p = getenv("GPG_AGENT_INFO");
- if (!(p && strchr(p, ':')))
- gpgme_set_passphrase_cb(ctx, passphrase_cb, 0);
-
- err = gpgme_get_key(ctx, gpg.private_key, &key, 1);
- if (err || !key) {
- scr_LogPrint(LPRINT_LOGNORM, "GPGME error: private key not found");
- gpgme_release(ctx);
- return NULL;
- }
-
- gpgme_signers_clear(ctx);
- gpgme_signers_add(ctx, key);
- gpgme_key_release(key);
- err = gpgme_data_new_from_mem(&in, gpg_data, strlen(gpg_data), 0);
- if (!err) {
- err = gpgme_data_new(&out);
- if (!err) {
- err = gpgme_op_sign(ctx, in, out, GPGME_SIG_MODE_DETACH);
- if (!err) {
- signed_data = gpgme_data_release_and_get_mem(out, &nread);
- if (signed_data) {
- // We need to add a trailing NULL
- char *dd = g_strndup(signed_data, nread);
- free(signed_data);
- signed_data = strip_header_footer(dd);
- g_free(dd);
- }
- } else {
- gpgme_data_release(out);
- }
- }
- gpgme_data_release(in);
- }
- if (err && err != GPG_ERR_CANCELED)
- scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
- "GPGME signature error: %s", gpgme_strerror(err));
- gpgme_release(ctx);
- return signed_data;
-}
-
-// gpg_decrypt(gpg_data)
-// Return decrypted gpg_data (or NULL).
-// The returned string must be freed with g_free() after use.
-char *gpg_decrypt(const char *gpg_data)
-{
- gpgme_ctx_t ctx;
- gpgme_data_t in, out;
- char *p, *data;
- char *decrypted_data = NULL;
- size_t nread;
- gpgme_error_t err;
- const char prefix[] = "-----BEGIN PGP MESSAGE-----\n\n";
- const char suffix[] = "\n-----END PGP MESSAGE-----\n";
-
- if (!gpg.enabled)
- return NULL;
-
- err = gpgme_new(&ctx);
- if (err) {
- scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
- "GPGME error: %s", gpgme_strerror(err));
- return NULL;
- }
-
- gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
-
- p = getenv("GPG_AGENT_INFO");
- if (!(p && strchr(p, ':')))
- gpgme_set_passphrase_cb(ctx, passphrase_cb, 0);
-
- // Surround the given data with the prefix & suffix
- data = g_new(char, sizeof(prefix) + sizeof(suffix) + strlen(gpg_data));
- strcpy(data, prefix);
- strcat(data, gpg_data);
- strcat(data, suffix);
-
- err = gpgme_data_new_from_mem(&in, data, strlen(data), 0);
- if (!err) {
- err = gpgme_data_new(&out);
- if (!err) {
- err = gpgme_op_decrypt(ctx, in, out);
- if (!err) {
- decrypted_data = gpgme_data_release_and_get_mem(out, &nread);
- if (decrypted_data) {
- // We need to add a trailing NULL
- char *dd = g_strndup(decrypted_data, nread);
- free(decrypted_data);
- decrypted_data = dd;
- }
- } else {
- gpgme_data_release(out);
- }
- }
- gpgme_data_release(in);
- }
- if (err && err != GPG_ERR_CANCELED)
- scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
- "GPGME decryption error: %s", gpgme_strerror(err));
- gpgme_release(ctx);
- g_free(data);
- return decrypted_data;
-}
-
-// gpg_encrypt(gpg_data, keyid)
-// Return encrypted gpg_data with the key keyid (or NULL).
-// The returned string must be freed with g_free() after use.
-char *gpg_encrypt(const char *gpg_data, const char *keyid)
-{
- gpgme_ctx_t ctx;
- gpgme_data_t in, out;
- char *encrypted_data = NULL, *edata;
- size_t nread;
- gpgme_key_t key;
- gpgme_error_t err;
-
- if (!gpg.enabled)
- return NULL;
-
- err = gpgme_new(&ctx);
- if (err) {
- scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
- "GPGME error: %s", gpgme_strerror(err));
- return NULL;
- }
-
- gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
- gpgme_set_textmode(ctx, 0);
- gpgme_set_armor(ctx, 1);
-
- err = gpgme_get_key(ctx, keyid, &key, 0);
- if (!err && key) {
- gpgme_key_t keys[] = { key, 0 };
- err = gpgme_data_new_from_mem(&in, gpg_data, strlen(gpg_data), 0);
- if (!err) {
- err = gpgme_data_new(&out);
- if (!err) {
- err = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
- if (!err)
- encrypted_data = gpgme_data_release_and_get_mem(out, &nread);
- else
- gpgme_data_release(out);
- }
- gpgme_data_release(in);
- }
- gpgme_key_release(key);
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "GPGME encryption error: key not found");
- err = 0;
- }
- if (err && err != GPG_ERR_CANCELED)
- scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
- "GPGME encryption error: %s", gpgme_strerror(err));
- gpgme_release(ctx);
- edata = strip_header_footer(encrypted_data);
- if (encrypted_data)
- free(encrypted_data);
- return edata;
-}
-
-// gpg_test_passphrase()
-// Test the current gpg.passphrase with gpg.private_key.
-// If the test doesn't succeed, the passphrase is cleared and a non-null
-// value is returned.
-int gpg_test_passphrase(void)
-{
- char *s;
-
- if (!gpg.private_key)
- return -1; // No private key...
-
- s = gpg_sign("test");
- if (s) {
- free(s);
- return 0; // Ok, test successful
- }
- // The passphrase is wrong (if provided)
- gpg_set_passphrase(NULL);
- return -1;
-}
-
-int gpg_enabled(void)
-{
- return gpg.enabled;
-}
-
-#else /* not HAVE_GPGME */
-
-int gpg_enabled(void)
-{
- return 0;
-}
-
-#endif /* HAVE_GPGME */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/pgp.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-#ifndef __PGP_H__
-#define __PGP_H__ 1
-
-#include <config.h>
-
-#ifdef HAVE_GPGME
-
-#define GPGME_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_USER_1
-#include <gpgme.h>
-
-int gpg_init(const char *priv_key, const char *passphrase);
-void gpg_terminate(void);
-void gpg_set_passphrase(const char *passphrase);
-void gpg_set_private_key(const char *priv_keyid);
-char *gpg_verify(const char *gpg_data, const char *text,
- gpgme_sigsum_t *sigsum);
-char *gpg_sign(const char *gpg_data);
-char *gpg_decrypt(const char *gpg_data);
-char *gpg_encrypt(const char *gpg_data, const char *keyid);
-
-int gpg_test_passphrase(void);
-
-#endif /* HAVE_GPGME */
-
-int gpg_enabled(void);
-
-#endif /* __PGP_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/roster.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1624 +0,0 @@
-/*
- * roster.c -- Local roster implementation
- *
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 <string.h>
-
-#include "roster.h"
-#include "utils.h"
-#include "hooks.h"
-
-extern void hlog_save_state(void);
-
-char *strrole[] = { /* Should match enum in roster.h */
- "none",
- "moderator",
- "participant",
- "visitor"
-};
-
-char *straffil[] = { /* Should match enum in roster.h */
- "none",
- "owner",
- "admin",
- "member",
- "outcast"
-};
-
-char *strprintstatus[] = { /* Should match enum in roster.h */
- "default",
- "none",
- "in_and_out",
- "all"
-};
-
-char *strautowhois[] = { /* Should match enum in roster.h */
- "default",
- "off",
- "on",
-};
-
-/* Resource structure */
-
-typedef struct {
- gchar *name;
- gchar prio;
- enum imstatus status;
- gchar *status_msg;
- time_t status_timestamp;
- enum imrole role;
- enum imaffiliation affil;
- gchar *realjid; /* for chatrooms, if buddy's real jid is known */
- guint events;
- char *caps;
-#ifdef JEP0022
- struct jep0022 jep22;
-#endif
-#ifdef JEP0085
- struct jep0085 jep85;
-#endif
-#ifdef HAVE_GPGME
- struct pgp_data pgpdata;
-#endif
-} res;
-
-/* This is a private structure type for the roster */
-
-typedef struct {
- gchar *name;
- gchar *jid;
- guint type;
- enum subscr subscription;
- GSList *resource;
-
- /* For groupchats */
- gchar *nickname;
- gchar *topic;
- guint inside_room;
- guint print_status;
- guint auto_whois;
-
- /* on_server is TRUE if the item is present on the server roster */
- guint on_server;
-
- /* To keep track of last status message */
- gchar *offline_status_message;
-
- /* Flag used for the UI */
- guint flags;
-
- // list: user -> points to his group; group -> points to its users list
- GSList *list;
-} roster;
-
-
-/* ### Variables ### */
-
-static guchar display_filter;
-static GSList *groups;
-static GSList *unread_list;
-static GHashTable *unread_jids;
-GList *buddylist;
-GList *current_buddy;
-GList *alternate_buddy;
-
-static roster roster_special;
-
-static int unread_jid_del(const char *jid);
-
-#define DFILTER_ALL 63
-#define DFILTER_ONLINE 62
-
-
-/* ### Initialization ### */
-
-void roster_init(void)
-{
- roster_special.name = SPECIAL_BUFFER_STATUS_ID;
- roster_special.type = ROSTER_TYPE_SPECIAL;
-}
-
-/* ### Resources functions ### */
-
-static inline void free_resource_data(res *p_res)
-{
- if (!p_res)
- return;
- g_free((gchar*)p_res->status_msg);
- g_free((gchar*)p_res->name);
- g_free((gchar*)p_res->realjid);
-#ifdef JEP0022
- g_free(p_res->jep22.last_msgid_sent);
- g_free(p_res->jep22.last_msgid_rcvd);
-#endif
-#ifdef HAVE_GPGME
- g_free(p_res->pgpdata.sign_keyid);
-#endif
- g_free(p_res->caps);
- g_free(p_res);
-}
-
-static void free_all_resources(GSList **reslist)
-{
- GSList *lip;
-
- for (lip = *reslist; lip ; lip = g_slist_next(lip))
- free_resource_data((res*)lip->data);
- // Free all nodes but the first (which is static)
- g_slist_free(*reslist);
- *reslist = NULL;
-}
-
-// Resources are sorted in ascending order
-static gint resource_compare_prio(res *a, res *b) {
- //return (a->prio - b->prio);
- if (a->prio < b->prio) return -1;
- else return 1;
-}
-
-// get_resource(rost, resname)
-// Return a pointer to the resource with name resname, in rost's resources list
-// - if rost has no resources, return NULL
-// - if resname is defined, return the match or NULL
-// - if resname is NULL, the last resource is returned, currently
-// This could change in the future, because we should return the best one
-// (priority? last used? and fall back to the first resource)
-//
-static res *get_resource(roster *rost, const char *resname)
-{
- GSList *p;
- res *r = NULL;
-
- for (p = rost->resource; p; p = g_slist_next(p)) {
- r = p->data;
- if (resname && !strcmp(r->name, resname))
- return r;
- }
-
- // The last resource is one of the resources with the highest priority,
- // however, we don't know if it is the more-recently-used.
- if (!resname) return r;
- return NULL;
-}
-
-// get_or_add_resource(rost, resname, priority)
-// - if there is a "resname" resource in rost's resources, return a pointer
-// on this resource
-// - if not, add the resource, set the name, and return a pointer on this
-// new resource
-static res *get_or_add_resource(roster *rost, const char *resname, gchar prio)
-{
- GSList *p;
- res *nres;
-
- if (!resname) return NULL;
-
- for (p = rost->resource; p; p = g_slist_next(p)) {
- res *r = p->data;
- if (!strcmp(r->name, resname)) {
- if (prio != r->prio) {
- r->prio = prio;
- rost->resource = g_slist_sort(rost->resource,
- (GCompareFunc)&resource_compare_prio);
- }
- return r;
- }
- }
-
- // Resource not found
- nres = g_new0(res, 1);
- nres->name = g_strdup(resname);
- nres->prio = prio;
- rost->resource = g_slist_insert_sorted(rost->resource, nres,
- (GCompareFunc)&resource_compare_prio);
- return nres;
-}
-
-static void del_resource(roster *rost, const char *resname)
-{
- GSList *p;
- GSList *p_res_elt = NULL;
- res *p_res;
-
- if (!resname) return;
-
- for (p = rost->resource; p; p = g_slist_next(p)) {
- res *r = p->data;
- if (!strcmp(r->name, resname))
- p_res_elt = p;
- }
-
- if (!p_res_elt) return; // Resource not found
-
- p_res = p_res_elt->data;
-
- // Keep a copy of the status message when a buddy goes offline
- if (g_slist_length(rost->resource) == 1) {
- g_free(rost->offline_status_message);
- rost->offline_status_message = p_res->status_msg;
- p_res->status_msg = NULL;
- }
-
- // Free allocations and delete resource node
- free_resource_data(p_res);
- rost->resource = g_slist_delete_link(rost->resource, p_res_elt);
- return;
-}
-
-
-/* ### Roster functions ### */
-
-static inline void free_roster_user_data(roster *roster_usr)
-{
- if (!roster_usr)
- return;
- g_free((gchar*)roster_usr->jid);
- g_free((gchar*)roster_usr->name);
- g_free((gchar*)roster_usr->nickname);
- g_free((gchar*)roster_usr->topic);
- g_free((gchar*)roster_usr->offline_status_message);
- free_all_resources(&roster_usr->resource);
- g_free(roster_usr);
-}
-
-// Comparison function used to search in the roster (compares jids and types)
-static gint roster_compare_jid_type(roster *a, roster *b) {
- if (! (a->type & b->type))
- return -1; // arbitrary (but should be != 0, of course)
- return strcasecmp(a->jid, b->jid);
-}
-
-// Comparison function used to search in the roster (compares names and types)
-static gint roster_compare_name_type(roster *a, roster *b) {
- if (! (a->type & b->type))
- return -1; // arbitrary (but should be != 0, of course)
- return strcmp(a->name, b->name);
-}
-
-// Comparison function used to sort the roster (by name)
-static gint roster_compare_name(roster *a, roster *b) {
- return strcmp(a->name, b->name);
-}
-
-// Finds a roster element (user, group, agent...), by jid or name
-// If roster_type is 0, returns match of any type.
-// Returns the roster GSList element, or NULL if jid/name not found
-GSList *roster_find(const char *jidname, enum findwhat type, guint roster_type)
-{
- GSList *sl_roster_elt = groups;
- GSList *resource;
- roster sample;
- GCompareFunc comp;
-
- if (!jidname) return NULL;
-
- if (!roster_type)
- roster_type = ROSTER_TYPE_USER | ROSTER_TYPE_ROOM |
- ROSTER_TYPE_AGENT | ROSTER_TYPE_GROUP;
-
- sample.type = roster_type;
- if (type == jidsearch) {
- sample.jid = (gchar*)jidname;
- comp = (GCompareFunc)&roster_compare_jid_type;
- } else if (type == namesearch) {
- sample.name = (gchar*)jidname;
- comp = (GCompareFunc)&roster_compare_name_type;
- } else
- return NULL; // Should not happen...
-
- while (sl_roster_elt) {
- roster *roster_elt = (roster*)sl_roster_elt->data;
- if (roster_type & ROSTER_TYPE_GROUP) {
- if ((type == namesearch) && !strcmp(jidname, roster_elt->name))
- return sl_roster_elt;
- }
- resource = g_slist_find_custom(roster_elt->list, &sample, comp);
- if (resource) return resource;
- sl_roster_elt = g_slist_next(sl_roster_elt);
- }
- return NULL;
-}
-
-// Returns pointer to new group, or existing group with that name
-GSList *roster_add_group(const char *name)
-{
- roster *roster_grp;
- GSList *p_group;
-
- // #1 Check name doesn't already exist
- p_group = roster_find(name, namesearch, ROSTER_TYPE_GROUP);
- if (!p_group) {
- // #2 Create the group node
- roster_grp = g_new0(roster, 1);
- roster_grp->name = g_strdup(name);
- roster_grp->type = ROSTER_TYPE_GROUP;
- // #3 Insert (sorted)
- groups = g_slist_insert_sorted(groups, roster_grp,
- (GCompareFunc)&roster_compare_name);
- p_group = roster_find(name, namesearch, ROSTER_TYPE_GROUP);
- }
- return p_group;
-}
-
-// Returns a pointer to the new user, or existing user with that name
-// Note: if onserver is -1, the flag won't be changed.
-GSList *roster_add_user(const char *jid, const char *name, const char *group,
- guint type, enum subscr esub, gint onserver)
-{
- roster *roster_usr;
- roster *my_group;
- GSList *slist;
-
- if ((type != ROSTER_TYPE_USER) &&
- (type != ROSTER_TYPE_ROOM) &&
- (type != ROSTER_TYPE_AGENT)) {
- // XXX Error message?
- return NULL;
- }
-
- // Let's be arbitrary: default group has an empty name ("").
- if (!group) group = "";
-
- // #1 Check this user doesn't already exist
- slist = roster_find(jid, jidsearch, 0);
- if (slist) {
- char *oldgroupname;
- // That's an update
- roster_usr = slist->data;
- roster_usr->subscription = esub;
- if (onserver >= 0)
- buddy_setonserverflag(slist->data, onserver);
- if (name)
- buddy_setname(slist->data, (char*)name);
- // Let's check if the group name has changed
- oldgroupname = ((roster*)((GSList*)roster_usr->list)->data)->name;
- if (group && strcmp(oldgroupname, group)) {
- buddy_setgroup(slist->data, (char*)group);
- // Note: buddy_setgroup() updates the user lists so we cannot
- // use slist anymore.
- return roster_find(jid, jidsearch, 0);
- }
- return slist;
- }
- // #2 add group if necessary
- slist = roster_add_group(group);
- if (!slist) return NULL;
- my_group = (roster*)slist->data;
- // #3 Create user node
- roster_usr = g_new0(roster, 1);
- roster_usr->jid = g_strdup(jid);
- if (name) {
- roster_usr->name = g_strdup(name);
- } else {
- gchar *p, *str = g_strdup(jid);
- p = strchr(str, JID_RESOURCE_SEPARATOR);
- if (p) *p = '\0';
- roster_usr->name = g_strdup(str);
- g_free(str);
- }
- if (unread_jid_del(jid)) {
- roster_usr->flags |= ROSTER_FLAG_MSG;
- // Append the roster_usr to unread_list
- unread_list = g_slist_append(unread_list, roster_usr);
- }
- roster_usr->type = type;
- roster_usr->subscription = esub;
- roster_usr->list = slist; // (my_group SList element)
- if (onserver == 1)
- roster_usr->on_server = TRUE;
- // #4 Insert node (sorted)
- my_group->list = g_slist_insert_sorted(my_group->list, roster_usr,
- (GCompareFunc)&roster_compare_name);
- return roster_find(jid, jidsearch, type);
-}
-
-// Removes user (jid) from roster, frees allocated memory
-void roster_del_user(const char *jid)
-{
- GSList *sl_user, *sl_group;
- GSList **sl_group_listptr;
- roster *roster_usr;
- GSList *node;
-
- sl_user = roster_find(jid, jidsearch,
- ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM);
- if (sl_user == NULL)
- return;
- roster_usr = (roster*)sl_user->data;
-
- // Remove (if present) from unread messages list
- node = g_slist_find(unread_list, roster_usr);
- if (node) unread_list = g_slist_delete_link(unread_list, node);
- // If there is a pending unread message, keep track of it
- if (roster_usr->flags & ROSTER_FLAG_MSG)
- unread_jid_add(roster_usr->jid);
-
- sl_group = roster_usr->list;
-
- // Let's free roster_usr memory (jid, name, status message...)
- free_roster_user_data(roster_usr);
-
- // That's a little complex, we need to dereference twice
- sl_group_listptr = &((roster*)(sl_group->data))->list;
- *sl_group_listptr = g_slist_delete_link(*sl_group_listptr, sl_user);
-
- // We need to rebuild the list
- if (current_buddy)
- buddylist_build();
- // TODO What we could do, too, is to check if the deleted node is
- // current_buddy, in which case we could move current_buddy to the
- // previous (or next) node.
-}
-
-// Free all roster data and call buddylist_build() to free the buddylist.
-void roster_free(void)
-{
- GSList *sl_grp = groups;
-
- // Free unread_list
- if (unread_list) {
- g_slist_free(unread_list);
- unread_list = NULL;
- }
-
- // Walk through groups
- while (sl_grp) {
- roster *roster_grp = (roster*)sl_grp->data;
- GSList *sl_usr = roster_grp->list;
- // Walk through this group users
- while (sl_usr) {
- roster *roster_usr = (roster*)sl_usr->data;
- // If there is a pending unread message, keep track of it
- if (roster_usr->flags & ROSTER_FLAG_MSG)
- unread_jid_add(roster_usr->jid);
- // Free roster_usr data (jid, name, status message...)
- free_roster_user_data(roster_usr);
- sl_usr = g_slist_next(sl_usr);
- }
- // Free group's users list
- if (roster_grp->list)
- g_slist_free(roster_grp->list);
- // Free group's name and jid
- g_free((gchar*)roster_grp->jid);
- g_free((gchar*)roster_grp->name);
- g_free(roster_grp);
- sl_grp = g_slist_next(sl_grp);
- }
- // Free groups list
- if (groups) {
- g_slist_free(groups);
- groups = NULL;
- // Update (i.e. free) buddylist
- if (buddylist)
- buddylist_build();
- }
-}
-
-// roster_setstatus()
-// Note: resname, role, affil and realjid are for room members only
-void roster_setstatus(const char *jid, const char *resname, gchar prio,
- enum imstatus bstat, const char *status_msg,
- time_t status_time,
- enum imrole role, enum imaffiliation affil,
- const char *realjid)
-{
- GSList *sl_user;
- roster *roster_usr;
- res *p_res;
-
- sl_user = roster_find(jid, jidsearch,
- ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
- // If we can't find it, we add it
- if (sl_user == NULL)
- sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER,
- sub_none, -1);
-
- // If there is no resource name, we can leave now
- if (!resname) return;
-
- roster_usr = (roster*)sl_user->data;
-
- // New or updated resource
- p_res = get_or_add_resource(roster_usr, resname, prio);
- p_res->status = bstat;
- if (p_res->status_msg) {
- g_free((gchar*)p_res->status_msg);
- p_res->status_msg = NULL;
- }
- if (status_msg)
- p_res->status_msg = g_strdup(status_msg);
- if (!status_time)
- time(&status_time);
- p_res->status_timestamp = status_time;
-
- p_res->role = role;
- p_res->affil = affil;
-
- if (p_res->realjid) {
- g_free((gchar*)p_res->realjid);
- p_res->realjid = NULL;
- }
- if (realjid)
- p_res->realjid = g_strdup(realjid);
-
- // If bstat is offline, we MUST delete the resource, actually
- if (bstat == offline) {
- del_resource(roster_usr, resname);
- return;
- }
-}
-
-// roster_setflags()
-// Set one or several flags to value (TRUE/FALSE)
-void roster_setflags(const char *jid, guint flags, guint value)
-{
- GSList *sl_user;
- roster *roster_usr;
-
- sl_user = roster_find(jid, jidsearch,
- ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
- if (sl_user == NULL)
- return;
-
- roster_usr = (roster*)sl_user->data;
- if (value)
- roster_usr->flags |= flags;
- else
- roster_usr->flags &= ~flags;
-}
-
-// roster_msg_setflag()
-// Set the ROSTER_FLAG_MSG to the given value for the given jid.
-// It will update the buddy's group message flag.
-// Update the unread messages list too.
-void roster_msg_setflag(const char *jid, guint special, guint value)
-{
- GSList *sl_user;
- roster *roster_usr, *roster_grp;
- int new_roster_item = FALSE;
- guint unread_list_modified = FALSE;
-
- if (special) {
- //sl_user = roster_find(jid, namesearch, ROSTER_TYPE_SPECIAL);
- //if (!sl_user) return;
- //roster_usr = (roster*)sl_user->data;
- roster_usr = &roster_special;
- if (value) {
- if (!(roster_usr->flags & ROSTER_FLAG_MSG))
- unread_list_modified = TRUE;
- roster_usr->flags |= ROSTER_FLAG_MSG;
- // Append the roster_usr to unread_list, but avoid duplicates
- if (!g_slist_find(unread_list, roster_usr))
- unread_list = g_slist_append(unread_list, roster_usr);
- } else {
- if (roster_usr->flags & ROSTER_FLAG_MSG)
- unread_list_modified = TRUE;
- roster_usr->flags &= ~ROSTER_FLAG_MSG;
- if (unread_list) {
- GSList *node = g_slist_find(unread_list, roster_usr);
- if (node)
- unread_list = g_slist_delete_link(unread_list, node);
- }
- }
- goto roster_msg_setflag_return;
- }
-
- sl_user = roster_find(jid, jidsearch,
- ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
- // If we can't find it, we add it
- if (sl_user == NULL) {
- sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER, sub_none, -1);
- new_roster_item = TRUE;
- }
-
- roster_usr = (roster*)sl_user->data;
- roster_grp = (roster*)roster_usr->list->data;
- if (value) {
- if (!(roster_usr->flags & ROSTER_FLAG_MSG))
- unread_list_modified = TRUE;
- // Message flag is TRUE. This is easy, we just have to set both flags
- // to TRUE...
- roster_usr->flags |= ROSTER_FLAG_MSG;
- roster_grp->flags |= ROSTER_FLAG_MSG; // group
- // Append the roster_usr to unread_list, but avoid duplicates
- if (!g_slist_find(unread_list, roster_usr))
- unread_list = g_slist_append(unread_list, roster_usr);
- } else {
- // Message flag is FALSE.
- guint msg = FALSE;
- if (roster_usr->flags & ROSTER_FLAG_MSG)
- unread_list_modified = TRUE;
- roster_usr->flags &= ~ROSTER_FLAG_MSG;
- if (unread_list) {
- GSList *node = g_slist_find(unread_list, roster_usr);
- if (node)
- unread_list = g_slist_delete_link(unread_list, node);
- }
- // For the group value we need to watch all buddies in this group;
- // if one is flagged, then the group will be flagged.
- // I will re-use sl_user and roster_usr here, as they aren't used
- // anymore.
- sl_user = roster_grp->list;
- while (sl_user) {
- roster_usr = (roster*)sl_user->data;
- if (roster_usr->flags & ROSTER_FLAG_MSG) {
- msg = TRUE;
- break;
- }
- sl_user = g_slist_next(sl_user);
- }
- if (!msg)
- roster_grp->flags &= ~ROSTER_FLAG_MSG;
- else
- roster_grp->flags |= ROSTER_FLAG_MSG;
- // Actually the "else" part is useless, because the group
- // ROSTER_FLAG_MSG should already be set...
- }
-
- if (buddylist && (new_roster_item || !g_list_find(buddylist, roster_usr)))
- buddylist_build();
-
-roster_msg_setflag_return:
- if (unread_list_modified) {
- guint unread_count = g_slist_length(unread_list);
- hlog_save_state();
- /* Call external command */
- hk_ext_cmd("", 'U', (guchar)MIN(255, unread_count), NULL);
- }
-}
-
-const char *roster_getname(const char *jid)
-{
- GSList *sl_user;
- roster *roster_usr;
-
- sl_user = roster_find(jid, jidsearch,
- ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
- if (sl_user == NULL)
- return NULL; // Not in the roster...
-
- roster_usr = (roster*)sl_user->data;
- return roster_usr->name;
-}
-
-const char *roster_getnickname(const char *jid)
-{
- GSList *sl_user;
- roster *roster_usr;
-
- sl_user = roster_find(jid, jidsearch,
- ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
- if (sl_user == NULL)
- return NULL; // Not in the roster...
-
- roster_usr = (roster*)sl_user->data;
- return roster_usr->nickname;
-}
-
-void roster_settype(const char *jid, guint type)
-{
- GSList *sl_user;
- roster *roster_usr;
-
- if ((sl_user = roster_find(jid, jidsearch, 0)) == NULL)
- return;
-
- roster_usr = (roster*)sl_user->data;
- roster_usr->type = type;
-}
-
-enum imstatus roster_getstatus(const char *jid, const char *resname)
-{
- GSList *sl_user;
- roster *roster_usr;
- res *p_res;
-
- sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
- if (sl_user == NULL)
- return offline; // Not in the roster, anyway...
-
- roster_usr = (roster*)sl_user->data;
- p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->status;
- return offline;
-}
-
-const char *roster_getstatusmsg(const char *jid, const char *resname)
-{
- GSList *sl_user;
- roster *roster_usr;
- res *p_res;
-
- sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
- if (sl_user == NULL)
- return NULL; // Not in the roster, anyway...
-
- roster_usr = (roster*)sl_user->data;
- p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->status_msg;
- return roster_usr->offline_status_message;
-}
-
-guint roster_gettype(const char *jid)
-{
- GSList *sl_user;
- roster *roster_usr;
-
- if ((sl_user = roster_find(jid, jidsearch, 0)) == NULL)
- return 0;
-
- roster_usr = (roster*)sl_user->data;
- return roster_usr->type;
-}
-
-guint roster_getsubscription(const char *jid)
-{
- GSList *sl_user;
- roster *roster_usr;
-
- if ((sl_user = roster_find(jid, jidsearch, 0)) == NULL)
- return 0;
-
- roster_usr = (roster*)sl_user->data;
- return roster_usr->subscription;
-}
-
-// roster_unsubscribed()
-// We have lost buddy's presence updates; this function clears the status
-// message, sets the buddy offline and frees the resources
-void roster_unsubscribed(const char *jid)
-{
- GSList *sl_user;
- roster *roster_usr;
-
- sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
- if (sl_user == NULL)
- return;
-
- roster_usr = (roster*)sl_user->data;
- free_all_resources(&roster_usr->resource);
-}
-
-
-/* ### BuddyList functions ### */
-
-// buddylist_set_hide_offline_buddies(hide)
-// "hide" values: 1=hide 0=show_all -1=invert
-void buddylist_set_hide_offline_buddies(int hide)
-{
- if (hide < 0) { // NEG (invert)
- if (display_filter == DFILTER_ALL)
- display_filter = DFILTER_ONLINE;
- else
- display_filter = DFILTER_ALL;
- } else if (hide == 0) { // FALSE (don't hide -- andfo_)
- display_filter = DFILTER_ALL;
- } else { // TRUE (hide -- andfo)
- display_filter = DFILTER_ONLINE;
- }
-}
-
-int buddylist_isset_filter(void)
-{
- return (display_filter != DFILTER_ALL);
-}
-
-int buddylist_is_status_filtered(enum imstatus status)
-{
- return display_filter & (1 << status);
-}
-
-void buddylist_set_filter(guchar filter)
-{
- display_filter = filter;
-}
-
-guchar buddylist_get_filter(void)
-{
- return display_filter;
-}
-
-// buddylist_build()
-// Creates the buddylist from the roster entries.
-void buddylist_build(void)
-{
- GSList *sl_roster_elt = groups;
- roster *roster_elt;
- roster *roster_current_buddy = NULL;
- roster *roster_alternate_buddy = NULL;
- int shrunk_group;
-
- // We need to remember which buddy is selected.
- if (current_buddy)
- roster_current_buddy = BUDDATA(current_buddy);
- current_buddy = NULL;
- if (alternate_buddy)
- roster_alternate_buddy = BUDDATA(alternate_buddy);
- alternate_buddy = NULL;
-
- // Destroy old buddylist
- if (buddylist) {
- g_list_free(buddylist);
- buddylist = NULL;
- }
-
- buddylist = g_list_append(buddylist, &roster_special);
-
- // Create the new list
- while (sl_roster_elt) {
- GSList *sl_roster_usrelt;
- roster *roster_usrelt;
- guint pending_group = TRUE;
- roster_elt = (roster*) sl_roster_elt->data;
-
- shrunk_group = roster_elt->flags & ROSTER_FLAG_HIDE;
-
- sl_roster_usrelt = roster_elt->list;
- while (sl_roster_usrelt) {
- roster_usrelt = (roster*) sl_roster_usrelt->data;
-
- // Buddy will be added if either:
- // - buddy's status matches the display_filter
- // - buddy has a lock (for example the buddy window is currently open)
- // - buddy has a pending (non-read) message
- // - group isn't hidden (shrunk)
- // - this is the current_buddy
- if (roster_usrelt == roster_current_buddy ||
- buddylist_is_status_filtered(buddy_getstatus((gpointer)roster_usrelt,
- NULL)) ||
- (buddy_getflags((gpointer)roster_usrelt) &
- (ROSTER_FLAG_LOCK | ROSTER_FLAG_USRLOCK | ROSTER_FLAG_MSG))) {
- // This user should be added. Maybe the group hasn't been added yet?
- if (pending_group) {
- // It hasn't been done yet
- buddylist = g_list_append(buddylist, roster_elt);
- pending_group = FALSE;
- }
- // Add user
- // XXX Should we add the user if there is a message and
- // the group is shrunk? If so, we'd need to check LOCK flag too,
- // perhaps...
- if (!shrunk_group)
- buddylist = g_list_append(buddylist, roster_usrelt);
- }
-
- sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
- }
- sl_roster_elt = g_slist_next(sl_roster_elt);
- }
-
- // Check if we can find our saved current_buddy...
- if (roster_current_buddy)
- current_buddy = g_list_find(buddylist, roster_current_buddy);
- if (roster_alternate_buddy)
- alternate_buddy = g_list_find(buddylist, roster_alternate_buddy);
- // current_buddy initialization
- if (!current_buddy || (g_list_position(buddylist, current_buddy) == -1))
- current_buddy = g_list_first(buddylist);
-}
-
-// buddy_hide_group(roster, hide)
-// "hide" values: 1=hide 0=show_all -1=invert
-void buddy_hide_group(gpointer rosterdata, int hide)
-{
- roster *roster_usr = rosterdata;
- if (hide > 0) // TRUE (hide)
- roster_usr->flags |= ROSTER_FLAG_HIDE;
- else if (hide < 0) // NEG (invert)
- roster_usr->flags ^= ROSTER_FLAG_HIDE;
- else // FALSE (don't hide)
- roster_usr->flags &= ~ROSTER_FLAG_HIDE;
-}
-
-const char *buddy_getjid(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- if (!rosterdata)
- return NULL;
- return roster_usr->jid;
-}
-
-// buddy_setgroup()
-// Change the group of current buddy
-//
-// Note: buddy_setgroup() updates the user lists.
-//
-void buddy_setgroup(gpointer rosterdata, char *newgroupname)
-{
- roster *roster_usr = rosterdata;
- GSList **sl_group;
- GSList *sl_newgroup;
- roster *my_newgroup;
-
- // A group has no group :)
- if (roster_usr->type & ROSTER_TYPE_GROUP) return;
-
- // Add newgroup if necessary
- if (!newgroupname) newgroupname = "";
- sl_newgroup = roster_add_group(newgroupname);
- if (!sl_newgroup) return;
- my_newgroup = (roster*)sl_newgroup->data;
-
- // Remove the buddy from current group
- sl_group = &((roster*)((GSList*)roster_usr->list)->data)->list;
- *sl_group = g_slist_remove(*sl_group, rosterdata);
-
- // Remove old group if it is empty
- if (!*sl_group) {
- roster *roster_grp = (roster*)((GSList*)roster_usr->list)->data;
- g_free((gchar*)roster_grp->jid);
- g_free((gchar*)roster_grp->name);
- g_free(roster_grp);
- groups = g_slist_remove(groups, roster_grp);
- }
-
- // Add the buddy to its new group
- roster_usr->list = sl_newgroup; // (my_newgroup SList element)
- my_newgroup->list = g_slist_insert_sorted(my_newgroup->list, roster_usr,
- (GCompareFunc)&roster_compare_name);
-
- buddylist_build();
-}
-
-void buddy_setname(gpointer rosterdata, char *newname)
-{
- roster *roster_usr = rosterdata;
- GSList **sl_group;
-
- // TODO For groups, we need to check for unicity
- // However, renaming a group boils down to moving all its buddies to
- // another group, so calling this function is not really necessary...
- if (roster_usr->type & ROSTER_TYPE_GROUP) return;
-
- if (roster_usr->name) {
- g_free((gchar*)roster_usr->name);
- roster_usr->name = NULL;
- }
- if (newname)
- roster_usr->name = g_strdup(newname);
-
- // We need to resort the group list
- sl_group = &((roster*)((GSList*)roster_usr->list)->data)->list;
- *sl_group = g_slist_sort(*sl_group, (GCompareFunc)&roster_compare_name);
-
- buddylist_build();
-}
-
-const char *buddy_getname(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- return roster_usr->name;
-}
-
-// buddy_setnickname(buddy, newnickname)
-// Only for chatrooms
-void buddy_setnickname(gpointer rosterdata, const char *newname)
-{
- roster *roster_usr = rosterdata;
-
- if (!(roster_usr->type & ROSTER_TYPE_ROOM)) return; // XXX Error message?
-
- if (roster_usr->nickname) {
- g_free((gchar*)roster_usr->nickname);
- roster_usr->nickname = NULL;
- }
- if (newname)
- roster_usr->nickname = g_strdup(newname);
-}
-
-const char *buddy_getnickname(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- return roster_usr->nickname;
-}
-
-// buddy_setinsideroom(buddy, inside)
-// Only for chatrooms
-void buddy_setinsideroom(gpointer rosterdata, guint inside)
-{
- roster *roster_usr = rosterdata;
-
- if (!(roster_usr->type & ROSTER_TYPE_ROOM)) return;
-
- roster_usr->inside_room = inside;
-}
-
-guint buddy_getinsideroom(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- return roster_usr->inside_room;
-}
-
-// buddy_settopic(buddy, newtopic)
-// Only for chatrooms
-void buddy_settopic(gpointer rosterdata, const char *newtopic)
-{
- roster *roster_usr = rosterdata;
-
- if (!(roster_usr->type & ROSTER_TYPE_ROOM)) return;
-
- if (roster_usr->topic) {
- g_free((gchar*)roster_usr->topic);
- roster_usr->topic = NULL;
- }
- if (newtopic)
- roster_usr->topic = g_strdup(newtopic);
-}
-
-const char *buddy_gettopic(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- return roster_usr->topic;
-}
-
-void buddy_setprintstatus(gpointer rosterdata, enum room_printstatus pstatus)
-{
- roster *roster_usr = rosterdata;
- roster_usr->print_status = pstatus;
-}
-
-enum room_printstatus buddy_getprintstatus(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- return roster_usr->print_status;
-}
-
-void buddy_setautowhois(gpointer rosterdata, enum room_autowhois awhois)
-{
- roster *roster_usr = rosterdata;
- roster_usr->auto_whois = awhois;
-}
-
-enum room_autowhois buddy_getautowhois(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- return roster_usr->auto_whois;
-}
-
-// buddy_getgroupname()
-// Returns a pointer on buddy's group name.
-const char *buddy_getgroupname(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
-
- if (roster_usr->type & ROSTER_TYPE_GROUP)
- return roster_usr->name;
-
- if (roster_usr->type & ROSTER_TYPE_SPECIAL)
- return NULL;
-
- // This is a user
- return ((roster*)((GSList*)roster_usr->list)->data)->name;
-}
-
-// buddy_getgroup()
-// Returns a pointer on buddy's group.
-gpointer buddy_getgroup(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
-
- if (roster_usr->type & ROSTER_TYPE_GROUP)
- return rosterdata;
-
- if (roster_usr->type & ROSTER_TYPE_SPECIAL)
- return NULL;
-
- // This is a user
- return (gpointer)((GSList*)roster_usr->list)->data;
-}
-
-void buddy_settype(gpointer rosterdata, guint type)
-{
- roster *roster_usr = rosterdata;
- roster_usr->type = type;
-}
-
-guint buddy_gettype(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- return roster_usr->type;
-}
-
-guint buddy_getsubscription(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- return roster_usr->subscription;
-}
-
-enum imstatus buddy_getstatus(gpointer rosterdata, const char *resname)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->status;
- return offline;
-}
-
-const char *buddy_getstatusmsg(gpointer rosterdata, const char *resname)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->status_msg;
- return roster_usr->offline_status_message;
-}
-
-time_t buddy_getstatustime(gpointer rosterdata, const char *resname)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->status_timestamp;
- return 0;
-}
-
-gchar buddy_getresourceprio(gpointer rosterdata, const char *resname)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->prio;
- return 0;
-}
-
-guint buddy_resource_getevents(gpointer rosterdata, const char *resname)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->events;
- return ROSTER_EVENT_NONE;
-}
-
-void buddy_resource_setevents(gpointer rosterdata, const char *resname,
- guint events)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- p_res->events = events;
-}
-
-char *buddy_resource_getcaps(gpointer rosterdata, const char *resname)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->caps;
- return NULL;
-}
-
-void buddy_resource_setcaps(gpointer rosterdata, const char *resname,
- const char *caps)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res) {
- g_free(p_res->caps);
- p_res->caps = g_strdup(caps);
- }
-}
-
-struct jep0022 *buddy_resource_jep22(gpointer rosterdata, const char *resname)
-{
-#ifdef JEP0022
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return &p_res->jep22;
-#endif
- return NULL;
-}
-
-struct jep0085 *buddy_resource_jep85(gpointer rosterdata, const char *resname)
-{
-#ifdef JEP0085
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return &p_res->jep85;
-#endif
- return NULL;
-}
-
-struct pgp_data *buddy_resource_pgp(gpointer rosterdata, const char *resname)
-{
-#ifdef HAVE_GPGME
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return &p_res->pgpdata;
-#endif
- return NULL;
-}
-
-enum imrole buddy_getrole(gpointer rosterdata, const char *resname)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->role;
- return role_none;
-}
-
-enum imaffiliation buddy_getaffil(gpointer rosterdata, const char *resname)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->affil;
- return affil_none;
-}
-
-const char *buddy_getrjid(gpointer rosterdata, const char *resname)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res)
- return p_res->realjid;
- return NULL;
-}
-
-// buddy_getresources(roster_data)
-// Return a singly-linked-list of resource names
-// Note: the caller should free the list (and data) after use
-// If roster_data is null, the current buddy is selected
-GSList *buddy_getresources(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- GSList *reslist = NULL, *lp;
-
- if (!roster_usr) {
- if (!current_buddy) return NULL;
- roster_usr = BUDDATA(current_buddy);
- }
- for (lp = roster_usr->resource; lp; lp = g_slist_next(lp))
- reslist = g_slist_append(reslist, g_strdup(((res*)lp->data)->name));
-
- return reslist;
-}
-
-// buddy_getresources_locale(roster_data)
-// Same as buddy_getresources() but names are converted to user's locale
-// Note: the caller should free the list (and data) after use
-GSList *buddy_getresources_locale(gpointer rosterdata)
-{
- GSList *reslist, *lp;
-
- reslist = buddy_getresources(rosterdata);
- // Convert each item to UI's locale
- for (lp = reslist; lp; lp = g_slist_next(lp)) {
- gchar *oldname = lp->data;
- lp->data = from_utf8(oldname);
- if (lp->data)
- g_free(oldname);
- else
- lp->data = oldname;
- }
- return reslist;
-}
-
-/*
-// buddy_isresource(roster_data)
-// Return true if there is at least one resource
-// (which means, for a room, that it isn't empty)
-int buddy_isresource(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- if (!roster_usr)
- return FALSE;
- if (roster_usr->resource)
- return TRUE;
- return FALSE;
-}
-*/
-
-// buddy_resource_setname(roster_data, oldname, newname)
-// Useful for nickname change in a MUC room
-void buddy_resource_setname(gpointer rosterdata, const char *resname,
- const char *newname)
-{
- roster *roster_usr = rosterdata;
- res *p_res = get_resource(roster_usr, resname);
- if (p_res) {
- if (p_res->name) {
- g_free((gchar*)p_res->name);
- p_res->name = NULL;
- }
- if (newname)
- p_res->name = g_strdup(newname);
- }
-}
-
-// buddy_del_all_resources()
-// Remove all resources from the specified buddy
-void buddy_del_all_resources(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
-
- while (roster_usr->resource) {
- res *r = roster_usr->resource->data;
- del_resource(roster_usr, r->name);
- }
-}
-
-// buddy_setflags()
-// Set one or several flags to value (TRUE/FALSE)
-void buddy_setflags(gpointer rosterdata, guint flags, guint value)
-{
- roster *roster_usr = rosterdata;
- if (value)
- roster_usr->flags |= flags;
- else
- roster_usr->flags &= ~flags;
-}
-
-guint buddy_getflags(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- return roster_usr->flags;
-}
-
-// buddy_setonserverflag()
-// Set the on_server flag
-void buddy_setonserverflag(gpointer rosterdata, guint onserver)
-{
- roster *roster_usr = rosterdata;
- roster_usr->on_server = onserver;
-}
-
-guint buddy_getonserverflag(gpointer rosterdata)
-{
- roster *roster_usr = rosterdata;
- return roster_usr->on_server;
-}
-
-// buddy_search_jid(jid)
-// Look for a buddy with specified jid.
-// Search begins at buddylist; if no match is found in the the buddylist,
-// return NULL;
-GList *buddy_search_jid(const char *jid)
-{
- GList *buddy;
- roster *roster_usr;
-
- if (!buddylist) return NULL;
-
- for (buddy = buddylist; buddy; buddy = g_list_next(buddy)) {
- roster_usr = (roster*)buddy->data;
- if (roster_usr->jid && !strcasecmp(roster_usr->jid, jid))
- return buddy;
- }
- return NULL;
-}
-
-// buddy_search(string)
-// Look for a buddy whose name or jid contains string.
-// Search begins at current_buddy; if no match is found in the the buddylist,
-// return NULL;
-GList *buddy_search(char *string)
-{
- GList *buddy = current_buddy;
- roster *roster_usr;
- if (!buddylist || !current_buddy) return NULL;
- for (;;) {
- gchar *jid_locale, *name_locale;
- char *found = NULL;
-
- buddy = g_list_next(buddy);
- if (!buddy)
- buddy = buddylist;
-
- roster_usr = (roster*)buddy->data;
-
- jid_locale = from_utf8(roster_usr->jid);
- if (jid_locale) {
- found = strcasestr(jid_locale, string);
- g_free(jid_locale);
- if (found)
- return buddy;
- }
- name_locale = from_utf8(roster_usr->name);
- if (name_locale) {
- found = strcasestr(name_locale, string);
- g_free(name_locale);
- if (found)
- return buddy;
- }
-
- if (buddy == current_buddy)
- return NULL; // Back to the beginning, and no match found
- }
-}
-
-// foreach_buddy(roster_type, pfunction, param)
-// Call pfunction(buddy, param) for each buddy from the roster with
-// type matching roster_type.
-void foreach_buddy(guint roster_type,
- void (*pfunc)(gpointer rosterdata, void *param),
- void *param)
-{
- GSList *sl_roster_elt = groups;
- roster *roster_elt;
- GSList *sl_roster_usrelt;
- roster *roster_usrelt;
-
- while (sl_roster_elt) { // group list loop
- roster_elt = (roster*) sl_roster_elt->data;
- if (roster_elt->type & ROSTER_TYPE_SPECIAL)
- continue; // Skip special items
- sl_roster_usrelt = roster_elt->list;
- while (sl_roster_usrelt) { // user list loop
- roster_usrelt = (roster*) sl_roster_usrelt->data;
-
- if (roster_usrelt->type & roster_type)
- pfunc(roster_usrelt, param);
-
- sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
- }
- sl_roster_elt = g_slist_next(sl_roster_elt);
- }
-}
-
-// foreach_group_member(group, pfunction, param)
-// Call pfunction(buddy, param) for each buddy in the specified group.
-void foreach_group_member(gpointer groupdata,
- void (*pfunc)(gpointer rosterdata, void *param),
- void *param)
-{
- roster *roster_elt;
- GSList *sl_roster_usrelt;
- roster *roster_usrelt;
-
- roster_elt = groupdata;
-
- if (!(roster_elt->type & ROSTER_TYPE_GROUP))
- return;
-
- sl_roster_usrelt = roster_elt->list;
- while (sl_roster_usrelt) { // user list loop
- roster_usrelt = (roster*) sl_roster_usrelt->data;
-
- pfunc(roster_usrelt, param);
- sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
- }
-}
-
-// compl_list(type)
-// Returns a list of jid's or groups. (For commands completion)
-// type: ROSTER_TYPE_USER (jid's) or ROSTER_TYPE_GROUP (group names)
-// The list should be freed by the caller after use.
-GSList *compl_list(guint type)
-{
- GSList *list = NULL;
- GSList *sl_roster_elt = groups;
- roster *roster_elt;
- GSList *sl_roster_usrelt;
- roster *roster_usrelt;
-
- while (sl_roster_elt) { // group list loop
- roster_elt = (roster*) sl_roster_elt->data;
-
- if (roster_elt->type & ROSTER_TYPE_SPECIAL)
- continue; // Skip special items
-
- if (type == ROSTER_TYPE_GROUP) { // (group names)
- if (roster_elt->name && *(roster_elt->name))
- list = g_slist_append(list, from_utf8(roster_elt->name));
- } else { // ROSTER_TYPE_USER (jid) (or agent, or chatroom...)
- sl_roster_usrelt = roster_elt->list;
- while (sl_roster_usrelt) { // user list loop
- roster_usrelt = (roster*) sl_roster_usrelt->data;
-
- if (roster_usrelt->jid)
- list = g_slist_append(list, from_utf8(roster_usrelt->jid));
-
- sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
- }
- }
- sl_roster_elt = g_slist_next(sl_roster_elt);
- }
-
- return list;
-}
-
-// unread_msg(rosterdata)
-// Return the next buddy with an unread message. If the parameter is NULL,
-// return the first buddy with an unread message.
-gpointer unread_msg(gpointer rosterdata)
-{
- GSList *unread, *next_unread;
-
- if (!unread_list)
- return NULL;
-
- // First unread message
- if (!rosterdata)
- return unread_list->data;
-
- unread = g_slist_find(unread_list, rosterdata);
- if (!unread)
- return unread_list->data;
-
- next_unread = g_slist_next(unread);
- if (next_unread)
- return next_unread->data;
- return unread_list->data;
-}
-
-
-/* ### "unread_jids" functions ###
- *
- * The unread_jids hash table is used to keep track of the buddies with
- * unread messages when a disconnection occurs.
- * When removing a buddy with an unread message from the roster, the
- * jid should be added to the unread_jids table. When adding a buddy to
- * the roster, we check if (s)he had a pending unread message.
- */
-
-// unread_jid_add(jid)
-// Add jid to the unread_jids hash table
-void unread_jid_add(const char *jid)
-{
- if (!unread_jids) {
- // Initialize unread_jids hash table
- unread_jids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- }
- // The 2nd unread_jids is an arbitrary non-null pointer:
- g_hash_table_insert(unread_jids, g_strdup(jid), unread_jids);
-}
-
-// unread_jid_del(jid)
-// Return TRUE if jid is found in the table (and remove it), FALSE if not
-static int unread_jid_del(const char *jid)
-{
- if (!unread_jids)
- return FALSE;
- return g_hash_table_remove(unread_jids, jid);
-}
-
-// Helper function for unread_jid_get_list()
-static void add_to_unreadjids(gpointer key, gpointer value, gpointer udata)
-{
- GList **listp = udata;
- *listp = g_list_append(*listp, key);
-}
-
-// unread_jid_get_list()
-// Return the JID list.
-// The content of the list should not be modified or freed.
-// The caller should call g_list_free() after use.
-GList *unread_jid_get_list(void)
-{
- GList *list = NULL;
-
- if (!unread_jids)
- return NULL;
-
- // g_hash_table_get_keys() is only in glib >= 2.14
- //return g_hash_table_get_keys(unread_jids);
-
- g_hash_table_foreach(unread_jids, add_to_unreadjids, &list);
- return list;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/roster.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,246 +0,0 @@
-#ifndef __ROSTER_H__
-#define __ROSTER_H__ 1
-
-#include <glib.h>
-#include <time.h>
-
-#include "pgp.h"
-
-#define SPECIAL_BUFFER_STATUS_ID "[status]"
-
-enum imstatus {
- offline,
- available,
- freeforchat,
- dontdisturb,
- notavail,
- away,
- invisible,
- imstatus_size
-};
-
-extern char imstatus2char[]; // Should match enum above
-
-enum imrole {
- role_none,
- role_moderator,
- role_participant,
- role_visitor,
- imrole_size
-};
-
-extern char *strrole[]; // Should match enum above
-
-enum imaffiliation {
- affil_none,
- affil_owner,
- affil_admin,
- affil_member,
- affil_outcast,
- imaffiliation_size
-};
-
-extern char *straffil[]; // Should match enum above
-
-enum subscr {
- sub_none = 0,
- sub_pending = 1,
- sub_to = 1 << 1,
- sub_from = 1 << 2,
- sub_both = sub_to|sub_from,
- sub_remove = 1 << 3
-};
-
-enum findwhat {
- jidsearch,
- namesearch
-};
-
-extern char *strprintstatus[];
-
-// Note: do not change the ordering as these values are visible
-// to the user (option 'muc_print_status')!
-enum room_printstatus {
- status_default,
- status_none,
- status_in_and_out,
- status_all
-};
-
-extern char *strautowhois[];
-
-enum room_autowhois {
- autowhois_default,
- autowhois_off,
- autowhois_on
-};
-
-struct role_affil {
- enum { type_role, type_affil } type;
- union {
- enum imrole role;
- enum imaffiliation affil;
- } val;
-};
-
-// Roster_type is a set of flags, so values should be 2^n
-#define ROSTER_TYPE_USER 1U
-#define ROSTER_TYPE_GROUP (1U<<1)
-#define ROSTER_TYPE_AGENT (1U<<2)
-#define ROSTER_TYPE_ROOM (1U<<3)
-#define ROSTER_TYPE_SPECIAL (1U<<4)
-
-// Flags:
-#define ROSTER_FLAG_MSG 1U // Message not read
-#define ROSTER_FLAG_HIDE (1U<<1) // Group hidden (or buddy window closed)
-#define ROSTER_FLAG_LOCK (1U<<2) // Node should not be removed from buddylist
-#define ROSTER_FLAG_USRLOCK (1U<<3) // Node should not be removed from buddylist
-// ROSTER_FLAG_LOCAL (1U<<4) // Buddy not on server's roster (??)
-
-#define JEP0022
-#define JEP0085
-
-struct jep0022 {
- guint support;
- guint last_state_sent;
- gchar *last_msgid_sent;
- guint last_state_rcvd;
- gchar *last_msgid_rcvd;
-};
-struct jep0085 {
- guint support;
- guint last_state_sent;
- guint last_state_rcvd;
-};
-
-enum chatstate_support {
- CHATSTATES_SUPPORT_UNKNOWN = 0,
- CHATSTATES_SUPPORT_PROBED,
- CHATSTATES_SUPPORT_NONE,
- CHATSTATES_SUPPORT_OK
-};
-
-struct pgp_data {
- gchar *sign_keyid; // KeyId used by the contact to sign their presence/msg
-#ifdef HAVE_GPGME
- gpgme_sigsum_t last_sigsum; // Last signature summary
-#endif
-};
-
-/* Message event and chat state flags */
-#define ROSTER_EVENT_NONE 0U
-/* JEP-22 Message Events */
-#define ROSTER_EVENT_OFFLINE (1U<<0)
-#define ROSTER_EVENT_DELIVERED (1U<<1)
-#define ROSTER_EVENT_DISPLAYED (1U<<2)
-/* JEP-22 & JEP-85 */
-#define ROSTER_EVENT_COMPOSING (1U<<3)
-/* JEP-85 Chat State Notifications */
-#define ROSTER_EVENT_ACTIVE (1U<<4)
-#define ROSTER_EVENT_PAUSED (1U<<5)
-#define ROSTER_EVENT_INACTIVE (1U<<6)
-#define ROSTER_EVENT_GONE (1U<<7)
-
-extern GList *buddylist;
-extern GList *current_buddy;
-extern GList *alternate_buddy;
-
-// Macros...
-
-#define BUDDATA(glist_node) ((glist_node)->data)
-#define CURRENT_JID buddy_getjid(BUDDATA(current_buddy))
-
-// Prototypes...
-void roster_init(void);
-GSList *roster_add_group(const char *name);
-GSList *roster_add_user(const char *jid, const char *name, const char *group,
- guint type, enum subscr esub, gint on_server);
-GSList *roster_find(const char *jidname, enum findwhat type, guint roster_type);
-void roster_del_user(const char *jid);
-void roster_free(void);
-void roster_setstatus(const char *jid, const char *resname, gchar prio,
- enum imstatus bstat, const char *status_msg,
- time_t timestamp,
- enum imrole role, enum imaffiliation affil,
- const char *realjid);
-void roster_setflags(const char *jid, guint flags, guint value);
-void roster_msg_setflag(const char *jid, guint special, guint value);
-const char *roster_getname(const char *jid);
-const char *roster_getnickname(const char *jid);
-void roster_settype(const char *jid, guint type);
-enum imstatus roster_getstatus(const char *jid, const char *resname);
-const char *roster_getstatusmsg(const char *jid, const char *resname);
-guint roster_gettype(const char *jid);
-guint roster_getsubscription(const char *jid);
-void roster_unsubscribed(const char *jid);
-
-void buddylist_build(void);
-void buddy_hide_group(gpointer rosterdata, int hide);
-void buddylist_set_hide_offline_buddies(int hide);
-int buddylist_isset_filter(void);
-int buddylist_is_status_filtered(enum imstatus status);
-void buddylist_set_filter(guchar);
-guchar buddylist_get_filter(void);
-const char *buddy_getjid(gpointer rosterdata);
-void buddy_setname(gpointer rosterdata, char *newname);
-const char *buddy_getname(gpointer rosterdata);
-void buddy_setnickname(gpointer rosterdata, const char *newname);
-const char *buddy_getnickname(gpointer rosterdata);
-void buddy_setinsideroom(gpointer rosterdata, guint inside);
-guint buddy_getinsideroom(gpointer rosterdata);
-void buddy_settopic(gpointer rosterdata, const char *newtopic);
-const char *buddy_gettopic(gpointer rosterdata);
-void buddy_setprintstatus(gpointer rosterdata, enum room_printstatus);
-enum room_printstatus buddy_getprintstatus(gpointer rosterdata);
-void buddy_setautowhois(gpointer rosterdata, enum room_autowhois);
-enum room_autowhois buddy_getautowhois(gpointer rosterdata);
-void buddy_settype(gpointer rosterdata, guint type);
-guint buddy_gettype(gpointer rosterdata);
-guint buddy_getsubscription(gpointer rosterdata);
-void buddy_setgroup(gpointer rosterdata, char *newgroupname);
-const char *buddy_getgroupname(gpointer rosterdata);
-gpointer buddy_getgroup(gpointer rosterdata);
-enum imstatus buddy_getstatus(gpointer rosterdata, const char *resname);
-const char *buddy_getstatusmsg(gpointer rosterdata, const char *resname);
-time_t buddy_getstatustime(gpointer rosterdata, const char *resname);
-gchar buddy_getresourceprio(gpointer rosterdata, const char *resname);
-//int buddy_isresource(gpointer rosterdata);
-GSList *buddy_getresources(gpointer rosterdata);
-GSList *buddy_getresources_locale(gpointer rosterdata);
-void buddy_resource_setname(gpointer rosterdata, const char *resname,
- const char *newname);
-void buddy_resource_setevents(gpointer rosterdata, const char *resname,
- guint event);
-guint buddy_resource_getevents(gpointer rosterdata, const char *resname);
-void buddy_resource_setcaps(gpointer rosterdata, const char *resname,
- const char *caps);
-char *buddy_resource_getcaps(gpointer rosterdata, const char *resname);
-struct jep0022 *buddy_resource_jep22(gpointer rosterdata, const char *resname);
-struct jep0085 *buddy_resource_jep85(gpointer rosterdata, const char *resname);
-struct pgp_data *buddy_resource_pgp(gpointer rosterdata, const char *resname);
-enum imrole buddy_getrole(gpointer rosterdata, const char *resname);
-enum imaffiliation buddy_getaffil(gpointer rosterdata, const char *resname);
-const char *buddy_getrjid(gpointer rosterdata, const char *resname);
-void buddy_del_all_resources(gpointer rosterdata);
-void buddy_setflags(gpointer rosterdata, guint flags, guint value);
-guint buddy_getflags(gpointer rosterdata);
-void buddy_setonserverflag(gpointer rosterdata, guint onserver);
-guint buddy_getonserverflag(gpointer rosterdata);
-GList *buddy_search_jid(const char *jid);
-GList *buddy_search(char *string);
-void foreach_buddy(guint roster_type,
- void (*pfunc)(gpointer rosterdata, void *param),
- void *param);
-void foreach_group_member(gpointer groupdata,
- void (*pfunc)(gpointer rosterdata, void *param),
- void *param);
-gpointer unread_msg(gpointer rosterdata);
-
-void unread_jid_add(const char *jid);
-GList *unread_jid_get_list(void);
-
-GSList *compl_list(guint type);
-
-#endif /* __ROSTER_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/screen.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,4124 +0,0 @@
-/*
- * screen.c -- UI stuff
- *
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- * Parts of this file come from the Cabber project <cabber@ajmacias.com>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <ctype.h>
-
-#include <config.h>
-#include <locale.h>
-#include <assert.h>
-#ifdef USE_SIGWINCH
-# include <sys/ioctl.h>
-# include <termios.h>
-# include <unistd.h>
-#endif
-
-#ifdef HAVE_LOCALCHARSET_H
-# include <localcharset.h>
-#else
-# include <langinfo.h>
-#endif
-
-#ifdef WITH_ENCHANT
-# include <enchant.h>
-#endif
-
-#ifdef WITH_ASPELL
-# include <aspell.h>
-#endif
-
-#include "screen.h"
-#include "utf8.h"
-#include "hbuf.h"
-#include "commands.h"
-#include "compl.h"
-#include "roster.h"
-#include "histolog.h"
-#include "settings.h"
-#include "utils.h"
-#include "xmpp.h"
-#include "main.h"
-
-#define get_color(col) (COLOR_PAIR(col)|COLOR_ATTRIB[col])
-#define compose_color(col) (COLOR_PAIR(col->color_pair)|col->color_attrib)
-
-#define DEFAULT_LOG_WIN_HEIGHT (5+2)
-#define DEFAULT_ROSTER_WIDTH 24
-#define CHAT_WIN_HEIGHT (maxY-1-Log_Win_Height)
-
-const char *LocaleCharSet = "C";
-
-static unsigned short int Log_Win_Height;
-static unsigned short int Roster_Width;
-
-static inline void check_offset(int);
-static void scr_cancel_current_completion(void);
-static void scr_end_current_completion(void);
-static void scr_insert_text(const char*);
-static void scr_handle_tab(void);
-
-#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
-static void spellcheck(char *, char *);
-#endif
-
-static GHashTable *winbufhash;
-
-typedef struct {
- GList *hbuf;
- GList *top; // If top is NULL, we'll display the last lines
- char cleared; // For ex, user has issued a /clear command...
- char lock;
-} buffdata;
-
-typedef struct {
- WINDOW *win;
- PANEL *panel;
- buffdata *bd;
-} winbuf;
-
-struct dimensions {
- int l;
- int c;
-};
-
-static WINDOW *rosterWnd, *chatWnd, *activechatWnd, *inputWnd, *logWnd;
-static WINDOW *mainstatusWnd, *chatstatusWnd;
-static PANEL *rosterPanel, *chatPanel, *activechatPanel, *inputPanel;
-static PANEL *mainstatusPanel, *chatstatusPanel;
-static PANEL *logPanel;
-static int maxY, maxX;
-static int prev_chatwidth;
-static winbuf *statusWindow;
-static winbuf *currentWindow;
-static GList *statushbuf;
-
-static int roster_hidden;
-static int chatmode;
-static int multimode;
-static char *multiline, *multimode_subj;
-int update_roster;
-int utf8_mode = 0;
-static bool Curses;
-static bool log_win_on_top;
-static bool roster_win_on_right;
-static time_t LastActivity;
-
-static char inputLine[INPUTLINE_LENGTH+1];
-#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
-static char maskLine[INPUTLINE_LENGTH+1];
-#endif
-static char *ptr_inputline;
-static short int inputline_offset;
-static int completion_started;
-static GList *cmdhisto;
-static GList *cmdhisto_cur;
-static guint cmdhisto_nblines;
-static char cmdhisto_backup[INPUTLINE_LENGTH+1];
-
-static int chatstate; /* (0=active, 1=composing, 2=paused) */
-static bool lock_chatstate;
-static time_t chatstate_timestamp;
-static guint chatstate_timeout_id = 0;
-int chatstates_disabled;
-
-#define MAX_KEYSEQ_LENGTH 8
-
-typedef struct {
- char *seqstr;
- guint mkeycode;
- gint value;
-} keyseq;
-
-#ifdef HAVE_GLIB_REGEX
-static GRegex *url_regex;
-#endif
-
-GSList *keyseqlist;
-static void add_keyseq(char *seqstr, guint mkeycode, gint value);
-
-void scr_WriteInWindow(const char *winId, const char *text, time_t timestamp,
- unsigned int prefix_flags, int force_show,
- unsigned mucnicklen, gpointer xep184);
-
-void scr_WriteMessage(const char *bjid, const char *text,
- time_t timestamp, guint prefix_flags,
- unsigned mucnicklen, gpointer xep184);
-
-inline void scr_UpdateBuddyWindow(void);
-inline void scr_set_chatmode(int enable);
-
-#define SPELLBADCHAR 5
-
-#ifdef WITH_ENCHANT
-EnchantBroker *spell_broker;
-EnchantDict *spell_checker;
-#endif
-
-#ifdef WITH_ASPELL
-AspellConfig *spell_config;
-AspellSpeller *spell_checker;
-#endif
-
-typedef struct {
- int color_pair;
- int color_attrib;
-} ccolor;
-
-typedef struct {
- char *status, *wildcard;
- ccolor *color;
- GPatternSpec *compiled;
-} rostercolor;
-
-static GSList *rostercolrules = NULL;
-
-static GHashTable *muccolors = NULL, *nickcolors = NULL;
-
-typedef struct {
- bool manual; // Manually set?
- ccolor *color;
-} nickcolor;
-
-static int nickcolcount = 0;
-static ccolor ** nickcols = NULL;
-static muccoltype glob_muccol = MC_OFF;
-
-/* Functions */
-
-static int FindColor(const char *name)
-{
- int result;
-
- if (!strcmp(name, "default"))
- return -1;
- if (!strcmp(name, "black"))
- return COLOR_BLACK;
- if (!strcmp(name, "red"))
- return COLOR_RED;
- if (!strcmp(name, "green"))
- return COLOR_GREEN;
- if (!strcmp(name, "yellow"))
- return COLOR_YELLOW;
- if (!strcmp(name, "blue"))
- return COLOR_BLUE;
- if (!strcmp(name, "magenta"))
- return COLOR_MAGENTA;
- if (!strcmp(name, "cyan"))
- return COLOR_CYAN;
- if (!strcmp(name, "white"))
- return COLOR_WHITE;
-
- // Directly support 256-color values
- result = atoi(name);
- if (result > 0 && result < COLORS)
- return result;
-
- scr_LogPrint(LPRINT_LOGNORM, "ERROR: Wrong color: %s", name);
- return -1;
-}
-
-static ccolor *get_user_color(const char *color)
-{
- bool isbright = FALSE;
- int cl;
- ccolor *ccol;
- if (!strncmp(color, "bright", 6)) {
- isbright = TRUE;
- color += 6;
- }
- cl = FindColor(color);
- if (cl < 0)
- return NULL;
- ccol = g_new0(ccolor, 1);
- ccol->color_attrib = isbright ? A_BOLD : A_NORMAL;
- ccol->color_pair = cl + COLOR_max; //user colors come after the internal ones
- return ccol;
-}
-
-static void ensure_string_htable(GHashTable **table,
- GDestroyNotify value_destroy_func)
-{
- if (*table)//Have it already
- return;
- *table = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, value_destroy_func);
-}
-
-// Sets the coloring mode for given MUC
-// The MUC room does not need to be in the roster at that time
-// muc - the JID of room
-// type - the new type
-void scr_MucColor(const char *muc, muccoltype type)
-{
- gchar *muclow = g_utf8_strdown(muc, -1);
- if (type == MC_REMOVE) {//Remove it
- if (strcmp(muc, "*")) {
- if (muccolors && g_hash_table_lookup(muccolors, muclow))
- g_hash_table_remove(muccolors, muclow);
- } else {
- scr_LogPrint(LPRINT_NORMAL, "Can not remove global coloring mode");
- }
- g_free(muclow);
- } else {//Add or overwrite
- if (strcmp(muc, "*")) {
- muccoltype *value = g_new(muccoltype, 1);
- *value = type;
- ensure_string_htable(&muccolors, g_free);
- g_hash_table_replace(muccolors, muclow, value);
- } else {
- glob_muccol = type;
- g_free(muclow);
- }
- }
- //Need to redraw?
- if (chatmode &&
- ((buddy_search_jid(muc) == current_buddy) || !strcmp(muc, "*")))
- scr_UpdateBuddyWindow();
-}
-
-// Sets the color for nick in MUC
-// If color is "-", the color is marked as automaticly assigned and is
-// not used if the room is in the "preset" mode
-void scr_MucNickColor(const char *nick, const char *color)
-{
- char *snick, *mnick;
- bool need_update = FALSE;
- snick = g_strdup_printf("<%s>", nick);
- mnick = g_strdup_printf("*%s ", nick);
- if (!strcmp(color, "-")) {//Remove the color
- if (nickcolors) {
- nickcolor *nc = g_hash_table_lookup(nickcolors, snick);
- if (nc) {//Have this nick already
- nc->manual = FALSE;
- nc = g_hash_table_lookup(nickcolors, mnick);
- assert(nc);//Must have both at the same time
- nc->manual = FALSE;
- }// Else -> no color saved, nothing to delete
- }
- g_free(snick);//They are not saved in the hash
- g_free(mnick);
- need_update = TRUE;
- } else {
- ccolor *cl = get_user_color(color);
- if (!cl) {
- scr_LogPrint(LPRINT_NORMAL, "No such color name");
- g_free(snick);
- g_free(mnick);
- } else {
- nickcolor *nc = g_new(nickcolor, 1);
- ensure_string_htable(&nickcolors, NULL);
- nc->manual = TRUE;
- nc->color = cl;
- //Free the struct, if any there already
- g_free(g_hash_table_lookup(nickcolors, mnick));
- //Save the new ones
- g_hash_table_replace(nickcolors, mnick, nc);
- g_hash_table_replace(nickcolors, snick, nc);
- need_update = TRUE;
- }
- }
- if (need_update && chatmode &&
- (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_ROOM))
- scr_UpdateBuddyWindow();
-}
-
-static void free_rostercolrule(rostercolor *col)
-{
- g_free(col->status);
- g_free(col->wildcard);
- g_free(col->color);
- g_pattern_spec_free(col->compiled);
- g_free(col);
-}
-
-// Removes all roster coloring rules
-void scr_RosterClearColor(void)
-{
- GSList *head;
- for (head = rostercolrules; head; head = g_slist_next(head)) {
- free_rostercolrule(head->data);
- }
- g_slist_free(rostercolrules);
- rostercolrules = NULL;
-}
-
-// Adds, modifies or removes roster coloring rule
-// color set to "-" removes the rule,
-// otherwise it is modified (if exists) or added
-//
-// Returns weather it was successfull (therefore the roster should be
-// redrawed) or not. If it failed, for example because of invalid color
-// name, it also prints the error.
-bool scr_RosterColor(const char *status, const char *wildcard,
- const char *color)
-{
- GSList *head;
- GSList *found = NULL;
- for (head = rostercolrules; head; head = g_slist_next(head)) {
- rostercolor *rc = head->data;
- if ((!strcmp(status, rc->status)) && (!strcmp(wildcard, rc->wildcard))) {
- found = head;
- break;
- }
- }
- if (!strcmp(color,"-")) {//Delete the rule
- if (found) {
- free_rostercolrule(found->data);
- rostercolrules = g_slist_delete_link(rostercolrules, found);
- return TRUE;
- } else {
- scr_LogPrint(LPRINT_NORMAL, "No such color rule, nothing removed");
- return FALSE;
- }
- } else {
- ccolor *cl = get_user_color(color);
- if (!cl) {
- scr_LogPrint(LPRINT_NORMAL, "No such color name");
- return FALSE;
- }
- if (found) {
- rostercolor *rc = found->data;
- g_free(rc->color);
- rc->color = cl;
- } else {
- rostercolor *rc = g_new(rostercolor, 1);
- rc->status = g_strdup(status);
- rc->wildcard = g_strdup(wildcard);
- rc->compiled = g_pattern_spec_new(wildcard);
- rc->color = cl;
- rostercolrules = g_slist_prepend(rostercolrules, rc);
- }
- return TRUE;
- }
-}
-
-static void ParseColors(void)
-{
- const char *colors[] = {
- "", "",
- "general",
- "msgout",
- "msghl",
- "status",
- "roster",
- "rostersel",
- "rosterselmsg",
- "rosternewmsg",
- "info",
- "msgin",
- NULL
- };
-
- const char *color;
- const char *background = settings_opt_get("color_background");
- const char *backselected = settings_opt_get("color_bgrostersel");
- const char *backstatus = settings_opt_get("color_bgstatus");
- char *tmp;
- int i;
-
- // Initialize color attributes
- memset(COLOR_ATTRIB, 0, sizeof(COLOR_ATTRIB));
-
- // Default values
- if (!background) background = "black";
- if (!backselected) backselected = "cyan";
- if (!backstatus) backstatus = "blue";
-
- for (i=0; colors[i]; i++) {
- tmp = g_strdup_printf("color_%s", colors[i]);
- color = settings_opt_get(tmp);
- g_free(tmp);
-
- if (color) {
- if (!strncmp(color, "bright", 6)) {
- COLOR_ATTRIB[i+1] = A_BOLD;
- color += 6;
- }
- }
-
- switch (i + 1) {
- case 1:
- init_pair(1, COLOR_BLACK, COLOR_WHITE);
- break;
- case 2:
- init_pair(2, COLOR_WHITE, COLOR_BLACK);
- break;
- case COLOR_GENERAL:
- init_pair(i+1, ((color) ? FindColor(color) : COLOR_WHITE),
- FindColor(background));
- break;
- case COLOR_MSGOUT:
- init_pair(i+1, ((color) ? FindColor(color) : COLOR_CYAN),
- FindColor(background));
- break;
- case COLOR_MSGHL:
- init_pair(i+1, ((color) ? FindColor(color) : COLOR_YELLOW),
- FindColor(background));
- break;
- case COLOR_STATUS:
- init_pair(i+1, ((color) ? FindColor(color) : COLOR_WHITE),
- FindColor(backstatus));
- break;
- case COLOR_ROSTER:
- init_pair(i+1, ((color) ? FindColor(color) : COLOR_GREEN),
- FindColor(background));
- break;
- case COLOR_ROSTERSEL:
- init_pair(i+1, ((color) ? FindColor(color) : COLOR_BLUE),
- FindColor(backselected));
- break;
- case COLOR_ROSTERSELNMSG:
- init_pair(i+1, ((color) ? FindColor(color) : COLOR_RED),
- FindColor(backselected));
- break;
- case COLOR_ROSTERNMSG:
- init_pair(i+1, ((color) ? FindColor(color) : COLOR_RED),
- FindColor(background));
- break;
- case COLOR_INFO:
- init_pair(i+1, ((color) ? FindColor(color) : COLOR_WHITE),
- FindColor(background));
- break;
- case COLOR_MSGIN:
- init_pair(i+1, ((color) ? FindColor(color) : COLOR_WHITE),
- FindColor(background));
- break;
- }
- }
- for (i = COLOR_max; i < (COLOR_max + COLORS); i++)
- init_pair(i, i-COLOR_max, FindColor(background));
-
- if (!nickcols) {
- char *ncolors = g_strdup(settings_opt_get("nick_colors"));
- if (ncolors) {
- char *ncolor_start, *ncolor_end;
- ncolor_start = ncolor_end = ncolors;
-
- while (*ncolor_end)
- ncolor_end++;
-
- while (ncolors < ncolor_end && *ncolors) {
- if ((*ncolors == ' ') || (*ncolors == '\t')) {
- ncolors++;
- } else {
- char *end = ncolors;
- ccolor *cl;
- while (*end && (*end != ' ') && (*end != '\t'))
- end++;
- *end = '\0';
- cl = get_user_color(ncolors);
- if (!cl) {
- scr_LogPrint(LPRINT_NORMAL, "Unknown color %s", ncolors);
- } else {
- nickcols = g_realloc(nickcols, (++nickcolcount) * sizeof *nickcols);
- nickcols[nickcolcount-1] = cl;
- }
- ncolors = end+1;
- }
- }
- g_free(ncolor_start);
- }
- if (!nickcols) {//Fallback to have something
- nickcolcount = 1;
- nickcols = g_new(ccolor*, 1);
- *nickcols = g_new(ccolor, 1);
- (*nickcols)->color_pair = COLOR_GENERAL;
- (*nickcols)->color_attrib = A_NORMAL;
- }
- }
-}
-
-static void init_keycodes(void)
-{
- add_keyseq("O5A", MKEY_EQUIV, 521); // Ctrl-Up
- add_keyseq("O5B", MKEY_EQUIV, 514); // Ctrl-Down
- add_keyseq("O5C", MKEY_EQUIV, 518); // Ctrl-Right
- add_keyseq("O5D", MKEY_EQUIV, 516); // Ctrl-Left
- add_keyseq("O6A", MKEY_EQUIV, 520); // Shift-Up
- add_keyseq("O6B", MKEY_EQUIV, 513); // Shift-Down
- add_keyseq("O6C", MKEY_EQUIV, 402); // Shift-Right
- add_keyseq("O6D", MKEY_EQUIV, 393); // Shift-Left
- add_keyseq("O2A", MKEY_EQUIV, 520); // Shift-Up
- add_keyseq("O2B", MKEY_EQUIV, 513); // Shift-Down
- add_keyseq("O2C", MKEY_EQUIV, 402); // Shift-Right
- add_keyseq("O2D", MKEY_EQUIV, 393); // Shift-Left
- add_keyseq("[5^", MKEY_CTRL_PGUP, 0); // Ctrl-PageUp
- add_keyseq("[6^", MKEY_CTRL_PGDOWN, 0); // Ctrl-PageDown
- add_keyseq("[5@", MKEY_CTRL_SHIFT_PGUP, 0); // Ctrl-Shift-PageUp
- add_keyseq("[6@", MKEY_CTRL_SHIFT_PGDOWN, 0); // Ctrl-Shift-PageDown
- add_keyseq("[7@", MKEY_CTRL_SHIFT_HOME, 0); // Ctrl-Shift-Home
- add_keyseq("[8@", MKEY_CTRL_SHIFT_END, 0); // Ctrl-Shift-End
- add_keyseq("[8^", MKEY_CTRL_END, 0); // Ctrl-End
- add_keyseq("[7^", MKEY_CTRL_HOME, 0); // Ctrl-Home
- add_keyseq("[2^", MKEY_CTRL_INS, 0); // Ctrl-Insert
- add_keyseq("[3^", MKEY_CTRL_DEL, 0); // Ctrl-Delete
-
- // Xterm
- add_keyseq("[1;5A", MKEY_EQUIV, 521); // Ctrl-Up
- add_keyseq("[1;5B", MKEY_EQUIV, 514); // Ctrl-Down
- add_keyseq("[1;5C", MKEY_EQUIV, 518); // Ctrl-Right
- add_keyseq("[1;5D", MKEY_EQUIV, 516); // Ctrl-Left
- add_keyseq("[1;6A", MKEY_EQUIV, 520); // Ctrl-Shift-Up
- add_keyseq("[1;6B", MKEY_EQUIV, 513); // Ctrl-Shift-Down
- add_keyseq("[1;6C", MKEY_EQUIV, 402); // Ctrl-Shift-Right
- add_keyseq("[1;6D", MKEY_EQUIV, 393); // Ctrl-Shift-Left
- add_keyseq("[1;6H", MKEY_CTRL_SHIFT_HOME, 0); // Ctrl-Shift-Home
- add_keyseq("[1;6F", MKEY_CTRL_SHIFT_END, 0); // Ctrl-Shift-End
- add_keyseq("[1;2A", MKEY_EQUIV, 521); // Shift-Up
- add_keyseq("[1;2B", MKEY_EQUIV, 514); // Shift-Down
- add_keyseq("[5;5~", MKEY_CTRL_PGUP, 0); // Ctrl-PageUp
- add_keyseq("[6;5~", MKEY_CTRL_PGDOWN, 0); // Ctrl-PageDown
- add_keyseq("[1;5F", MKEY_CTRL_END, 0); // Ctrl-End
- add_keyseq("[1;5H", MKEY_CTRL_HOME, 0); // Ctrl-Home
- add_keyseq("[2;5~", MKEY_CTRL_INS, 0); // Ctrl-Insert
- add_keyseq("[3;5~", MKEY_CTRL_DEL, 0); // Ctrl-Delete
-
- // PuTTY
- add_keyseq("[A", MKEY_EQUIV, 521); // Ctrl-Up
- add_keyseq("[B", MKEY_EQUIV, 514); // Ctrl-Down
- add_keyseq("[C", MKEY_EQUIV, 518); // Ctrl-Right
- add_keyseq("[D", MKEY_EQUIV, 516); // Ctrl-Left
-
- // screen
- add_keyseq("Oa", MKEY_EQUIV, 521); // Ctrl-Up
- add_keyseq("Ob", MKEY_EQUIV, 514); // Ctrl-Down
- add_keyseq("Oc", MKEY_EQUIV, 518); // Ctrl-Right
- add_keyseq("Od", MKEY_EQUIV, 516); // Ctrl-Left
- add_keyseq("[a", MKEY_EQUIV, 520); // Shift-Up
- add_keyseq("[b", MKEY_EQUIV, 513); // Shift-Down
- add_keyseq("[c", MKEY_EQUIV, 402); // Shift-Right
- add_keyseq("[d", MKEY_EQUIV, 393); // Shift-Left
- add_keyseq("[5$", MKEY_SHIFT_PGUP, 0); // Shift-PageUp
- add_keyseq("[6$", MKEY_SHIFT_PGDOWN, 0); // Shift-PageDown
-
- // VT100
- add_keyseq("[H", MKEY_EQUIV, KEY_HOME); // Home
- add_keyseq("[F", MKEY_EQUIV, KEY_END); // End
-
- // Konsole Linux
- add_keyseq("[1~", MKEY_EQUIV, KEY_HOME); // Home
- add_keyseq("[4~", MKEY_EQUIV, KEY_END); // End
-}
-
-// scr_init_bindings()
-// Create default key bindings
-// Return 0 if error and 1 if none
-void scr_init_bindings(void)
-{
- GString *sbuf = g_string_new("");
-
- // Common backspace key codes: 8, 127
- settings_set(SETTINGS_TYPE_BINDING, "8", "iline char_bdel"); // Ctrl-h
- settings_set(SETTINGS_TYPE_BINDING, "127", "iline char_bdel");
- g_string_printf(sbuf, "%d", KEY_BACKSPACE);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline char_bdel");
- g_string_printf(sbuf, "%d", KEY_DC);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline char_fdel");
- g_string_printf(sbuf, "%d", KEY_LEFT);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline bchar");
- g_string_printf(sbuf, "%d", KEY_RIGHT);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline fchar");
- settings_set(SETTINGS_TYPE_BINDING, "7", "iline compl_cancel"); // Ctrl-g
- g_string_printf(sbuf, "%d", KEY_UP);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str,
- "iline hist_beginning_search_bwd");
- g_string_printf(sbuf, "%d", KEY_DOWN);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str,
- "iline hist_beginning_search_fwd");
- g_string_printf(sbuf, "%d", KEY_PPAGE);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "roster up");
- g_string_printf(sbuf, "%d", KEY_NPAGE);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "roster down");
- g_string_printf(sbuf, "%d", KEY_HOME);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline iline_start");
- settings_set(SETTINGS_TYPE_BINDING, "1", "iline iline_start"); // Ctrl-a
- g_string_printf(sbuf, "%d", KEY_END);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline iline_end");
- settings_set(SETTINGS_TYPE_BINDING, "5", "iline iline_end"); // Ctrl-e
- // Ctrl-o (accept-line-and-down-history):
- settings_set(SETTINGS_TYPE_BINDING, "15", "iline iline_accept_down_hist");
- settings_set(SETTINGS_TYPE_BINDING, "21", "iline iline_bdel"); // Ctrl-u
- g_string_printf(sbuf, "%d", KEY_EOL);
- settings_set(SETTINGS_TYPE_BINDING, sbuf->str, "iline iline_fdel");
- settings_set(SETTINGS_TYPE_BINDING, "11", "iline iline_fdel"); // Ctrl-k
- settings_set(SETTINGS_TYPE_BINDING, "16", "buffer up"); // Ctrl-p
- settings_set(SETTINGS_TYPE_BINDING, "14", "buffer down"); // Ctrl-n
- settings_set(SETTINGS_TYPE_BINDING, "20", "iline char_swap"); // Ctrl-t
- settings_set(SETTINGS_TYPE_BINDING, "23", "iline word_bdel"); // Ctrl-w
- settings_set(SETTINGS_TYPE_BINDING, "M98", "iline bword"); // Meta-b
- settings_set(SETTINGS_TYPE_BINDING, "M102", "iline fword"); // Meta-f
- settings_set(SETTINGS_TYPE_BINDING, "M100", "iline word_fdel"); // Meta-d
- // Ctrl-Left (2 codes):
- settings_set(SETTINGS_TYPE_BINDING, "515", "iline bword");
- settings_set(SETTINGS_TYPE_BINDING, "516", "iline bword");
- // Ctrl-Right (2 codes):
- settings_set(SETTINGS_TYPE_BINDING, "517", "iline fword");
- settings_set(SETTINGS_TYPE_BINDING, "518", "iline fword");
- settings_set(SETTINGS_TYPE_BINDING, "12", "screen_refresh"); // Ctrl-l
- settings_set(SETTINGS_TYPE_BINDING, "27", "chat_disable --show-roster");// Esc
- settings_set(SETTINGS_TYPE_BINDING, "M27", "chat_disable"); // Esc-Esc
- settings_set(SETTINGS_TYPE_BINDING, "4", "iline send_multiline"); // Ctrl-d
- settings_set(SETTINGS_TYPE_BINDING, "M117", "iline word_upcase"); // Meta-u
- settings_set(SETTINGS_TYPE_BINDING, "M108", "iline word_downcase"); // Meta-l
- settings_set(SETTINGS_TYPE_BINDING, "M99", "iline word_capit"); // Meta-c
-
- settings_set(SETTINGS_TYPE_BINDING, "265", "help"); // Bind F1 to help...
-
- g_string_free(sbuf, TRUE);
-}
-
-// is_speckey(key)
-// Return TRUE if key is a special code, i.e. no char should be displayed on
-// the screen. It's not very nice, it's a workaround for the systems where
-// isprint(KEY_PPAGE) returns TRUE...
-static int is_speckey(int key)
-{
- switch (key) {
- case 127:
- case 393:
- case 402:
- case KEY_BACKSPACE:
- case KEY_DC:
- case KEY_LEFT:
- case KEY_RIGHT:
- case KEY_UP:
- case KEY_DOWN:
- case KEY_PPAGE:
- case KEY_NPAGE:
- case KEY_HOME:
- case KEY_END:
- case KEY_EOL:
- return TRUE;
- }
-
- // Fn keys
- if (key >= 265 && key < 265+12)
- return TRUE;
-
- // Special key combinations
- if (key >= 513 && key <= 521)
- return TRUE;
-
- return FALSE;
-}
-
-void scr_InitLocaleCharSet(void)
-{
- setlocale(LC_ALL, "");
-#ifdef HAVE_LOCALCHARSET_H
- LocaleCharSet = locale_charset();
-#else
- LocaleCharSet = nl_langinfo(CODESET);
-#endif
- utf8_mode = (strcmp(LocaleCharSet, "UTF-8") == 0);
-}
-
-void scr_InitCurses(void)
-{
- /* Key sequences initialization */
- init_keycodes();
-
- initscr();
- raw();
- noecho();
- nonl();
- intrflush(stdscr, FALSE);
- start_color();
- use_default_colors();
-#ifdef NCURSES_MOUSE_VERSION
- if (settings_opt_get_int("use_mouse"))
- mousemask(ALL_MOUSE_EVENTS, NULL);
-#endif
-
- if (settings_opt_get("escdelay")) {
-#ifdef HAVE_ESCDELAY
- ESCDELAY = (unsigned) settings_opt_get_int("escdelay");
-#else
- scr_LogPrint(LPRINT_LOGNORM, "ERROR: no ESCDELAY support.");
-#endif
- }
-
- ParseColors();
-
- getmaxyx(stdscr, maxY, maxX);
- Log_Win_Height = DEFAULT_LOG_WIN_HEIGHT;
- // Note scr_DrawMainWindow() should be called early after scr_InitCurses()
- // to update Log_Win_Height and set max{X,Y}
-
- inputLine[0] = 0;
- ptr_inputline = inputLine;
-
- if (settings_opt_get("url_regex")) {
-#ifdef HAVE_GLIB_REGEX
- url_regex = g_regex_new(settings_opt_get("url_regex"),
- G_REGEX_OPTIMIZE, 0, NULL);
-#else
- scr_LogPrint(LPRINT_LOGNORM, "ERROR: Your glib version is too old, "
- "cannot use url_regex.");
-#endif // HAVE_GLIB_REGEX
- }
-
- Curses = TRUE;
- return;
-}
-
-void scr_TerminateCurses(void)
-{
- if (!Curses) return;
- clear();
- refresh();
- endwin();
-#ifdef HAVE_GLIB_REGEX
- if (url_regex)
- g_regex_unref(url_regex);
-#endif
- Curses = FALSE;
- return;
-}
-
-void scr_Beep(void)
-{
- beep();
-}
-
-// This and following belongs to dynamic setting of time prefix
-static const char *timeprefixes[] = {
- "%m-%d %H:%M ",
- "%H:%M ",
- " "
-};
-
-static const char *spectimeprefixes[] = {
- "%m-%d %H:%M:%S ",
- "%H:%M:%S ",
- " "
-};
-
-static int timepreflengths[] = {
- // (length of the corresponding timeprefix + 5)
- 17,
- 11,
- 6
-};
-
-static const char *gettprefix(void)
-{
- guint n = settings_opt_get_int("time_prefix");
- return timeprefixes[(n < 3 ? n : 0)];
-}
-
-static const char *getspectprefix(void)
-{
- guint n = settings_opt_get_int("time_prefix");
- return spectimeprefixes[(n < 3 ? n : 0)];
-}
-
-guint scr_getprefixwidth(void)
-{
- guint n = settings_opt_get_int("time_prefix");
- return timepreflengths[(n < 3 ? n : 0)];
-}
-
-// scr_print_logwindow(string)
-// Display the string in the log window.
-// Note: The string must be in the user's locale!
-void scr_print_logwindow(const char *string)
-{
- time_t timestamp;
- char strtimestamp[64];
-
- timestamp = time(NULL);
- strftime(strtimestamp, 48, "[%H:%M:%S]", localtime(×tamp));
- if (Curses) {
- wprintw(logWnd, "\n%s %s", strtimestamp, string);
- update_panels();
- } else {
- printf("%s %s\n", strtimestamp, string);
- }
-}
-
-// scr_LogPrint(...)
-// Display a message in the log window and in the status buffer.
-// Add the message to the tracelog file if the log flag is set.
-// This function will convert from UTF-8 unless the LPRINT_NOTUTF8 flag is set.
-void scr_LogPrint(unsigned int flag, const char *fmt, ...)
-{
- time_t timestamp;
- char strtimestamp[64];
- char *buffer, *btext;
- char *convbuf1 = NULL, *convbuf2 = NULL;
- va_list ap;
-
- if (!(flag & ~LPRINT_NOTUTF8)) return; // Shouldn't happen
-
- timestamp = time(NULL);
- strftime(strtimestamp, 48, "[%H:%M:%S]", localtime(×tamp));
- va_start(ap, fmt);
- btext = g_strdup_vprintf(fmt, ap);
- va_end(ap);
-
- if (flag & LPRINT_NORMAL) {
- char *buffer_locale;
- char *buf_specialwindow;
-
- buffer = g_strdup_printf("%s %s", strtimestamp, btext);
-
- // Convert buffer to current locale for wprintw()
- if (!(flag & LPRINT_NOTUTF8))
- buffer_locale = convbuf1 = from_utf8(buffer);
- else
- buffer_locale = buffer;
-
- if (!buffer_locale) {
- wprintw(logWnd,
- "\n%s*Error: cannot convert string to locale.", strtimestamp);
- update_panels();
- g_free(buffer);
- g_free(btext);
- return;
- }
-
- // For the special status buffer, we need utf-8, but without the timestamp
- if (flag & LPRINT_NOTUTF8)
- buf_specialwindow = convbuf2 = to_utf8(btext);
- else
- buf_specialwindow = btext;
-
- if (Curses) {
- wprintw(logWnd, "\n%s", buffer_locale);
- update_panels();
- scr_WriteInWindow(NULL, buf_specialwindow, timestamp,
- HBB_PREFIX_SPECIAL, FALSE, 0, NULL);
- } else {
- printf("%s\n", buffer_locale);
- // ncurses are not initialized yet, so we call directly hbuf routine
- hbuf_add_line(&statushbuf, buf_specialwindow, timestamp,
- HBB_PREFIX_SPECIAL, 0, 0, 0, NULL);
- }
-
- g_free(convbuf1);
- g_free(convbuf2);
- g_free(buffer);
- }
-
- if (flag & (LPRINT_LOG|LPRINT_DEBUG)) {
- strftime(strtimestamp, 23, "[%Y-%m-%d %H:%M:%S]", localtime(×tamp));
- buffer = g_strdup_printf("%s %s\n", strtimestamp, btext);
- ut_WriteLog(flag, buffer);
- g_free(buffer);
- }
- g_free(btext);
-}
-
-static winbuf *scr_SearchWindow(const char *winId, int special)
-{
- char *id;
- winbuf *wbp;
-
- if (special)
- return statusWindow; // Only one special window atm.
-
- if (!winId)
- return NULL;
-
- id = g_strdup(winId);
- mc_strtolower(id);
- wbp = g_hash_table_lookup(winbufhash, id);
- g_free(id);
- return wbp;
-}
-
-int scr_BuddyBufferExists(const char *bjid)
-{
- return (scr_SearchWindow(bjid, FALSE) != NULL);
-}
-
-// scr_new_buddy(title, dontshow)
-// Note: title (aka winId/jid) can be NULL for special buffers
-static winbuf *scr_new_buddy(const char *title, int dont_show)
-{
- winbuf *tmp;
-
- tmp = g_new0(winbuf, 1);
-
- tmp->win = activechatWnd;
- tmp->panel = activechatPanel;
-
- if (!dont_show) {
- currentWindow = tmp;
- } else {
- if (currentWindow)
- top_panel(currentWindow->panel);
- else
- top_panel(chatPanel);
- }
- update_panels();
-
- // If title is NULL, this is a special buffer
- if (title) {
- char *id;
- id = hlog_get_log_jid(title);
- if (id) {
- winbuf *wb = scr_SearchWindow(id, FALSE);
- if (!wb)
- wb = scr_new_buddy(id, TRUE);
- tmp->bd=wb->bd;
- g_free(id);
- } else { // Load buddy history from file (if enabled)
- tmp->bd = g_new0(buffdata, 1);
- hlog_read_history(title, &tmp->bd->hbuf,
- maxX - Roster_Width - scr_getprefixwidth());
- }
-
- id = g_strdup(title);
- mc_strtolower(id);
- g_hash_table_insert(winbufhash, id, tmp);
- } else {
- tmp->bd = g_new0(buffdata, 1);
- }
- return tmp;
-}
-
-// scr_line_prefix(line, pref, preflen)
-// Use data from the hbb_line structure and write the prefix
-// to pref (not exceeding preflen, trailing null byte included).
-void scr_line_prefix(hbb_line *line, char *pref, guint preflen)
-{
- char date[64];
-
- if (line->timestamp &&
- !(line->flags & (HBB_PREFIX_SPECIAL|HBB_PREFIX_CONT))) {
- strftime(date, 30, gettprefix(), localtime(&line->timestamp));
- } else
- strcpy(date, " ");
-
- if (!(line->flags & HBB_PREFIX_CONT)) {
- if (line->flags & HBB_PREFIX_INFO) {
- char dir = '*';
- if (line->flags & HBB_PREFIX_IN)
- dir = '<';
- else if (line->flags & HBB_PREFIX_OUT)
- dir = '>';
- g_snprintf(pref, preflen, "%s*%c* ", date, dir);
- } else if (line->flags & HBB_PREFIX_ERR) {
- char dir = '#';
- if (line->flags & HBB_PREFIX_IN)
- dir = '<';
- else if (line->flags & HBB_PREFIX_OUT)
- dir = '>';
- g_snprintf(pref, preflen, "%s#%c# ", date, dir);
- } else if (line->flags & HBB_PREFIX_IN) {
- char cryptflag;
- if (line->flags & HBB_PREFIX_PGPCRYPT)
- cryptflag = '~';
- else if (line->flags & HBB_PREFIX_OTRCRYPT)
- cryptflag = 'O';
- else
- cryptflag = '=';
- g_snprintf(pref, preflen, "%s<%c= ", date, cryptflag);
- } else if (line->flags & HBB_PREFIX_OUT) {
- char cryptflag, receiptflag;
- if (line->flags & HBB_PREFIX_PGPCRYPT)
- cryptflag = '~';
- else if (line->flags & HBB_PREFIX_OTRCRYPT)
- cryptflag = 'O';
- else
- cryptflag = '-';
- if (line->flags & HBB_PREFIX_RECEIPT)
- receiptflag = 'r';
- else
- receiptflag = '-';
- g_snprintf(pref, preflen, "%s%c%c> ", date, receiptflag, cryptflag);
- } else if (line->flags & HBB_PREFIX_SPECIAL) {
- strftime(date, 30, getspectprefix(), localtime(&line->timestamp));
- g_snprintf(pref, preflen, "%s ", date);
- } else {
- g_snprintf(pref, preflen, "%s ", date);
- }
- } else {
- g_snprintf(pref, preflen, " ");
- }
-}
-
-// scr_UpdateWindow()
-// (Re-)Display the given chat window.
-static void scr_UpdateWindow(winbuf *win_entry)
-{
- int n;
- guint prefixwidth;
- char pref[96];
- hbb_line **lines, *line;
- GList *hbuf_head;
- int color;
-
- prefixwidth = scr_getprefixwidth();
- prefixwidth = MIN(prefixwidth, sizeof pref);
-
- // Should the window be empty?
- if (win_entry->bd->cleared) {
- werase(win_entry->win);
- return;
- }
-
- // win_entry->bd->top is the top message of the screen. If it set to NULL,
- // we are displaying the last messages.
-
- // We will show the last CHAT_WIN_HEIGHT lines.
- // Let's find out where it begins.
- if (!win_entry->bd->top || (g_list_position(g_list_first(win_entry->bd->hbuf),
- win_entry->bd->top) == -1)) {
- // Move up CHAT_WIN_HEIGHT lines
- win_entry->bd->hbuf = g_list_last(win_entry->bd->hbuf);
- hbuf_head = win_entry->bd->hbuf;
- win_entry->bd->top = NULL; // (Just to make sure)
- n = 0;
- while (hbuf_head && (n < CHAT_WIN_HEIGHT-1) && g_list_previous(hbuf_head)) {
- hbuf_head = g_list_previous(hbuf_head);
- n++;
- }
- // If the buffer is locked, remember current "top" line for the next time.
- if (win_entry->bd->lock)
- win_entry->bd->top = hbuf_head;
- } else
- hbuf_head = win_entry->bd->top;
-
- // Get the last CHAT_WIN_HEIGHT lines.
- lines = hbuf_get_lines(hbuf_head, CHAT_WIN_HEIGHT);
-
- // Display these lines
- for (n = 0; n < CHAT_WIN_HEIGHT; n++) {
- wmove(win_entry->win, n, 0);
- line = *(lines+n);
- if (line) {
- if (line->flags & HBB_PREFIX_HLIGHT_OUT)
- color = COLOR_MSGOUT;
- else if (line->flags & HBB_PREFIX_HLIGHT)
- color = COLOR_MSGHL;
- else if (line->flags & HBB_PREFIX_INFO)
- color = COLOR_INFO;
- else if (line->flags & HBB_PREFIX_IN)
- color = COLOR_MSGIN;
- else
- color = COLOR_GENERAL;
-
- if (color != COLOR_GENERAL)
- wattrset(win_entry->win, get_color(color));
-
- // Generate the prefix area and display it
- scr_line_prefix(line, pref, prefixwidth);
- wprintw(win_entry->win, pref);
-
- // Make sure we are at the right position
- wmove(win_entry->win, n, prefixwidth-1);
-
- // The MUC nick - overwrite with proper color
- if (line->mucnicklen) {
- char *mucjid;
- char tmp;
- nickcolor *actual = NULL;
- muccoltype type, *typetmp;
-
- // Store the char after the nick
- tmp = line->text[line->mucnicklen];
- type = glob_muccol;
- // Terminate the string after the nick
- line->text[line->mucnicklen] = '\0';
- mucjid = g_utf8_strdown(CURRENT_JID, -1);
- if (muccolors) {
- typetmp = g_hash_table_lookup(muccolors, mucjid);
- if (typetmp)
- type = *typetmp;
- }
- g_free(mucjid);
- // Need to generate a color for the specified nick?
- if ((type == MC_ALL) && (!nickcolors ||
- !g_hash_table_lookup(nickcolors, line->text))) {
- char *snick, *mnick;
- nickcolor *nc;
- const char *p = line->text;
- unsigned int nicksum = 0;
- snick = g_strdup(line->text);
- mnick = g_strdup(line->text);
- nc = g_new(nickcolor, 1);
- ensure_string_htable(&nickcolors, NULL);
- while (*p)
- nicksum += *p++;
- nc->color = nickcols[nicksum % nickcolcount];
- nc->manual = FALSE;
- *snick = '<';
- snick[strlen(snick)-1] = '>';
- *mnick = '*';
- mnick[strlen(mnick)-1] = ' ';
- // Insert them
- g_hash_table_insert(nickcolors, snick, nc);
- g_hash_table_insert(nickcolors, mnick, nc);
- }
- if (nickcolors)
- actual = g_hash_table_lookup(nickcolors, line->text);
- if (actual && ((type == MC_ALL) || (actual->manual))
- && (line->flags & HBB_PREFIX_IN) &&
- (!(line->flags & HBB_PREFIX_HLIGHT_OUT)))
- wattrset(win_entry->win, compose_color(actual->color));
- wprintw(win_entry->win, "%s", line->text);
- // Return the char
- line->text[line->mucnicklen] = tmp;
- // Return the color back
- wattrset(win_entry->win, get_color(color));
- }
-
- // Display text line
- wprintw(win_entry->win, "%s", line->text+line->mucnicklen);
- wclrtoeol(win_entry->win);
-
- // Return the color back
- if (color != COLOR_GENERAL)
- wattrset(win_entry->win, get_color(COLOR_GENERAL));
-
- g_free(line->text);
- g_free(line);
- } else {
- wclrtobot(win_entry->win);
- break;
- }
- }
- g_free(lines);
-}
-
-static winbuf *scr_CreateWindow(const char *winId, int special, int dont_show)
-{
- if (special) {
- if (!statusWindow) {
- statusWindow = scr_new_buddy(NULL, dont_show);
- statusWindow->bd->hbuf = statushbuf;
- }
- return statusWindow;
- } else {
- return scr_new_buddy(winId, dont_show);
- }
-}
-
-// scr_ShowWindow()
-// Display the chat window with the given identifier.
-// "special" must be true if this is a special buffer window.
-static void scr_ShowWindow(const char *winId, int special)
-{
- winbuf *win_entry;
-
- win_entry = scr_SearchWindow(winId, special);
-
- if (!win_entry) {
- win_entry = scr_CreateWindow(winId, special, FALSE);
- }
-
- top_panel(win_entry->panel);
- currentWindow = win_entry;
- chatmode = TRUE;
- if (!win_entry->bd->lock)
- roster_msg_setflag(winId, special, FALSE);
- if (!special)
- roster_setflags(winId, ROSTER_FLAG_LOCK, TRUE);
- update_roster = TRUE;
-
- // Refresh the window
- scr_UpdateWindow(win_entry);
-
- // Finished :)
- update_panels();
-
- top_panel(inputPanel);
-}
-
-// scr_ShowBuddyWindow()
-// Display the chat window buffer for the current buddy.
-void scr_ShowBuddyWindow(void)
-{
- const gchar *bjid;
-
- if (!current_buddy) {
- bjid = NULL;
- } else {
- bjid = CURRENT_JID;
- if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL) {
- scr_ShowWindow(buddy_getname(BUDDATA(current_buddy)), TRUE);
- return;
- }
- }
-
- if (!bjid) {
- top_panel(chatPanel);
- top_panel(inputPanel);
- currentWindow = NULL;
- return;
- }
-
- scr_ShowWindow(bjid, FALSE);
-}
-
-// scr_UpdateBuddyWindow()
-// (Re)Display the current window.
-// If chatmode is enabled, call scr_ShowBuddyWindow(),
-// else display the chat window.
-inline void scr_UpdateBuddyWindow(void)
-{
- if (chatmode) {
- scr_ShowBuddyWindow();
- return;
- }
-
- top_panel(chatPanel);
- top_panel(inputPanel);
-}
-
-// scr_WriteInWindow()
-// Write some text in the winId window (this usually is a jid).
-// Use winId == NULL for the special status buffer.
-// Lines are splitted when they are too long to fit in the chat window.
-// If this window doesn't exist, it is created.
-void scr_WriteInWindow(const char *winId, const char *text, time_t timestamp,
- unsigned int prefix_flags, int force_show,
- unsigned mucnicklen, gpointer xep184)
-{
- winbuf *win_entry;
- char *text_locale;
- int dont_show = FALSE;
- int special;
- guint num_history_blocks;
- bool setmsgflg = FALSE;
- char *nicktmp, *nicklocaltmp;
-
- // Look for the window entry.
- special = (winId == NULL);
- win_entry = scr_SearchWindow(winId, special);
-
- // Do we have to really show the window?
- if (!chatmode)
- dont_show = TRUE;
- else if ((!force_show) && ((!currentWindow || (currentWindow != win_entry))))
- dont_show = TRUE;
-
- // If the window entry doesn't exist yet, let's create it.
- if (!win_entry) {
- win_entry = scr_CreateWindow(winId, special, dont_show);
- }
-
- // The message must be displayed -> update top pointer
- if (win_entry->bd->cleared)
- win_entry->bd->top = g_list_last(win_entry->bd->hbuf);
-
- // Make sure we do not free the buffer while it's locked or when
- // top is set.
- if (win_entry->bd->lock || win_entry->bd->top)
- num_history_blocks = 0U;
- else
- num_history_blocks = get_max_history_blocks();
-
- text_locale = from_utf8(text);
- //Convert the nick alone and compute its length
- if (mucnicklen) {
- nicktmp = g_strndup(text, mucnicklen);
- nicklocaltmp = from_utf8(nicktmp);
- mucnicklen = strlen(nicklocaltmp);
- g_free(nicklocaltmp);
- g_free(nicktmp);
- }
- hbuf_add_line(&win_entry->bd->hbuf, text_locale, timestamp, prefix_flags,
- maxX - Roster_Width - scr_getprefixwidth(), num_history_blocks,
- mucnicklen, xep184);
- g_free(text_locale);
-
- if (win_entry->bd->cleared) {
- win_entry->bd->cleared = FALSE;
- if (g_list_next(win_entry->bd->top))
- win_entry->bd->top = g_list_next(win_entry->bd->top);
- }
-
- // Make sure the last line appears in the window; update top if necessary
- if (!win_entry->bd->lock && win_entry->bd->top) {
- int dist;
- GList *first = g_list_first(win_entry->bd->hbuf);
- dist = g_list_position(first, g_list_last(win_entry->bd->hbuf)) -
- g_list_position(first, win_entry->bd->top);
- if (dist >= CHAT_WIN_HEIGHT)
- win_entry->bd->top = NULL;
- }
-
- if (!dont_show) {
- if (win_entry->bd->lock)
- setmsgflg = TRUE;
- // Show and refresh the window
- top_panel(win_entry->panel);
- scr_UpdateWindow(win_entry);
- top_panel(inputPanel);
- update_panels();
- } else if (!(prefix_flags & HBB_PREFIX_NOFLAG)) {
- setmsgflg = TRUE;
- }
- if (setmsgflg && !special) {
- if (special && !winId)
- winId = SPECIAL_BUFFER_STATUS_ID;
- roster_msg_setflag(winId, special, TRUE);
- update_roster = TRUE;
- }
-}
-
-// scr_UpdateMainStatus()
-// Redraw the main (bottom) status line.
-void scr_UpdateMainStatus(int forceupdate)
-{
- char *sm = from_utf8(xmpp_getstatusmsg());
- const char *info = settings_opt_get("info");
-
- werase(mainstatusWnd);
- if (info) {
- char *info_locale = from_utf8(info);
- mvwprintw(mainstatusWnd, 0, 0, "%c[%c] %s: %s",
- (unread_msg(NULL) ? '#' : ' '),
- imstatus2char[xmpp_getstatus()],
- info_locale, (sm ? sm : ""));
- g_free(info_locale);
- } else
- mvwprintw(mainstatusWnd, 0, 0, "%c[%c] %s",
- (unread_msg(NULL) ? '#' : ' '),
- imstatus2char[xmpp_getstatus()], (sm ? sm : ""));
- if (forceupdate) {
- top_panel(inputPanel);
- update_panels();
- }
- g_free(sm);
-}
-
-// scr_DrawMainWindow()
-// Set fullinit to TRUE to also create panels. Set it to FALSE for a resize.
-//
-// I think it could be improved a _lot_ but I'm really not an ncurses
-// expert... :-\ Mikael.
-//
-void scr_DrawMainWindow(unsigned int fullinit)
-{
- int requested_size;
- gchar *ver, *message;
- int chat_y_pos, chatstatus_y_pos, log_y_pos;
- int roster_x_pos, chat_x_pos;
-
- Log_Win_Height = DEFAULT_LOG_WIN_HEIGHT;
- requested_size = settings_opt_get_int("log_win_height");
- if (requested_size > 0) {
- if (maxY > requested_size + 3)
- Log_Win_Height = requested_size + 2;
- else
- Log_Win_Height = ((maxY > 5) ? (maxY - 2) : 3);
- } else if (requested_size < 0) {
- Log_Win_Height = 3;
- }
-
- if (maxY < Log_Win_Height+2) {
- if (maxY < 5) {
- Log_Win_Height = 3;
- maxY = Log_Win_Height+2;
- } else {
- Log_Win_Height = maxY - 2;
- }
- }
-
- if (roster_hidden) {
- Roster_Width = 0;
- } else {
- requested_size = settings_opt_get_int("roster_width");
- if (requested_size > 1)
- Roster_Width = requested_size;
- else if (requested_size == 1)
- Roster_Width = 2;
- else
- Roster_Width = DEFAULT_ROSTER_WIDTH;
- }
-
- log_win_on_top = (settings_opt_get_int("log_win_on_top") == 1);
- roster_win_on_right = (settings_opt_get_int("roster_win_on_right") == 1);
-
- if (log_win_on_top) {
- chat_y_pos = Log_Win_Height-1;
- log_y_pos = 0;
- chatstatus_y_pos = Log_Win_Height-2;
- } else {
- chat_y_pos = 0;
- log_y_pos = CHAT_WIN_HEIGHT+1;
- chatstatus_y_pos = CHAT_WIN_HEIGHT;
- }
-
- if (roster_win_on_right) {
- roster_x_pos = maxX - Roster_Width;
- chat_x_pos = 0;
- } else {
- roster_x_pos = 0;
- chat_x_pos = Roster_Width;
- }
-
- if (fullinit) {
- if (!winbufhash)
- winbufhash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- /* Create windows */
- rosterWnd = newwin(CHAT_WIN_HEIGHT, Roster_Width, chat_y_pos, roster_x_pos);
- chatWnd = newwin(CHAT_WIN_HEIGHT, maxX - Roster_Width, chat_y_pos,
- chat_x_pos);
- activechatWnd = newwin(CHAT_WIN_HEIGHT, maxX - Roster_Width, chat_y_pos,
- chat_x_pos);
- logWnd = newwin(Log_Win_Height-2, maxX, log_y_pos, 0);
- chatstatusWnd = newwin(1, maxX, chatstatus_y_pos, 0);
- mainstatusWnd = newwin(1, maxX, maxY-2, 0);
- inputWnd = newwin(1, maxX, maxY-1, 0);
- if (!rosterWnd || !chatWnd || !logWnd || !inputWnd) {
- scr_TerminateCurses();
- fprintf(stderr, "Cannot create windows!\n");
- exit(EXIT_FAILURE);
- }
- wbkgd(rosterWnd, get_color(COLOR_GENERAL));
- wbkgd(chatWnd, get_color(COLOR_GENERAL));
- wbkgd(activechatWnd, get_color(COLOR_GENERAL));
- wbkgd(logWnd, get_color(COLOR_GENERAL));
- wbkgd(chatstatusWnd, get_color(COLOR_STATUS));
- wbkgd(mainstatusWnd, get_color(COLOR_STATUS));
- } else {
- /* Resize/move windows */
- wresize(rosterWnd, CHAT_WIN_HEIGHT, Roster_Width);
- wresize(chatWnd, CHAT_WIN_HEIGHT, maxX - Roster_Width);
- wresize(logWnd, Log_Win_Height-2, maxX);
-
- mvwin(chatWnd, chat_y_pos, chat_x_pos);
- mvwin(rosterWnd, chat_y_pos, roster_x_pos);
- mvwin(logWnd, log_y_pos, 0);
-
- // Resize & move chat status window
- wresize(chatstatusWnd, 1, maxX);
- mvwin(chatstatusWnd, chatstatus_y_pos, 0);
- // Resize & move main status window
- wresize(mainstatusWnd, 1, maxX);
- mvwin(mainstatusWnd, maxY-2, 0);
- // Resize & move input line window
- wresize(inputWnd, 1, maxX);
- mvwin(inputWnd, maxY-1, 0);
-
- werase(chatWnd);
- }
-
- /* Draw/init windows */
-
- ver = mcabber_version();
- message = g_strdup_printf("MCabber version %s.\n", ver);
- mvwprintw(chatWnd, 0, 0, message);
- mvwprintw(chatWnd, 1, 0, "http://mcabber.com/");
- g_free(ver);
- g_free(message);
-
- // Auto-scrolling in log window
- scrollok(logWnd, TRUE);
-
-
- if (fullinit) {
- // Enable keypad (+ special keys)
- keypad(inputWnd, TRUE);
-#ifdef __MirBSD__
- wtimeout(inputWnd, 50 /* ms */);
-#else
- nodelay(inputWnd, TRUE);
-#endif
-
- // Create panels
- rosterPanel = new_panel(rosterWnd);
- chatPanel = new_panel(chatWnd);
- activechatPanel = new_panel(activechatWnd);
- logPanel = new_panel(logWnd);
- chatstatusPanel = new_panel(chatstatusWnd);
- mainstatusPanel = new_panel(mainstatusWnd);
- inputPanel = new_panel(inputWnd);
-
- // Build the buddylist at least once, to make sure the special buffer
- // is added
- buddylist_build();
-
- // Init prev_chatwidth; this variable will be used to prevent us
- // from rewrapping buffers when the width doesn't change.
- prev_chatwidth = maxX - Roster_Width - scr_getprefixwidth();
- // Wrap existing status buffer lines
- hbuf_rebuild(&statushbuf, prev_chatwidth);
-
-#ifndef UNICODE
- if (utf8_mode)
- scr_LogPrint(LPRINT_NORMAL,
- "WARNING: Compiled without full UTF-8 support!");
-#endif
- } else {
- // Update panels
- replace_panel(rosterPanel, rosterWnd);
- replace_panel(chatPanel, chatWnd);
- replace_panel(logPanel, logWnd);
- replace_panel(chatstatusPanel, chatstatusWnd);
- replace_panel(mainstatusPanel, mainstatusWnd);
- replace_panel(inputPanel, inputWnd);
- }
-
- // We'll need to redraw the roster
- update_roster = TRUE;
- return;
-}
-
-static void resize_win_buffer(gpointer key, gpointer value, gpointer data)
-{
- winbuf *wbp = value;
- struct dimensions *dim = data;
- int chat_x_pos, chat_y_pos;
- int new_chatwidth;
-
- if (!(wbp && wbp->win))
- return;
-
- if (log_win_on_top)
- chat_y_pos = Log_Win_Height-1;
- else
- chat_y_pos = 0;
-
- if (roster_win_on_right)
- chat_x_pos = 0;
- else
- chat_x_pos = Roster_Width;
-
- // Resize/move buddy window
- wresize(wbp->win, dim->l, dim->c);
- mvwin(wbp->win, chat_y_pos, chat_x_pos);
- werase(wbp->win);
- // If a panel exists, replace the old window with the new
- if (wbp->panel)
- replace_panel(wbp->panel, wbp->win);
- // Redo line wrapping
- wbp->bd->top = hbuf_previous_persistent(wbp->bd->top);
-
- new_chatwidth = maxX - Roster_Width - scr_getprefixwidth();
- if (new_chatwidth != prev_chatwidth)
- hbuf_rebuild(&wbp->bd->hbuf, new_chatwidth);
-}
-
-// scr_Resize()
-// Function called when the window is resized.
-// - Resize windows
-// - Rewrap lines in each buddy buffer
-void scr_Resize(void)
-{
- struct dimensions dim;
-
- // First, update the global variables
- getmaxyx(stdscr, maxY, maxX);
- // scr_DrawMainWindow() will take care of maxY and Log_Win_Height
-
- // Make sure the cursor stays inside the window
- check_offset(0);
-
- // Resize windows and update panels
- scr_DrawMainWindow(FALSE);
-
- // Resize all buddy windows
- dim.l = CHAT_WIN_HEIGHT;
- dim.c = maxX - Roster_Width;
- if (dim.c < 1)
- dim.c = 1;
-
- // Resize all buffers
- g_hash_table_foreach(winbufhash, resize_win_buffer, &dim);
-
- // Resize/move special status buffer
- if (statusWindow)
- resize_win_buffer(NULL, statusWindow, &dim);
-
- // Update prev_chatwidth, now that all buffers have been resized
- prev_chatwidth = maxX - Roster_Width - scr_getprefixwidth();
-
- // Refresh current buddy window
- if (chatmode)
- scr_ShowBuddyWindow();
-}
-
-// scr_UpdateChatStatus(forceupdate)
-// Redraw the buddy status bar.
-// Set forceupdate to TRUE if update_panels() must be called.
-void scr_UpdateChatStatus(int forceupdate)
-{
- unsigned short btype, isgrp, ismuc, isspe;
- const char *btypetext = "Unknown";
- const char *fullname;
- const char *msg = NULL;
- char status;
- char *buf, *buf_locale;
-
- // Usually we need to update the bottom status line too,
- // at least to refresh the pending message flag.
- scr_UpdateMainStatus(FALSE);
-
- // Clear the line
- werase(chatstatusWnd);
-
- if (!current_buddy) {
- if (forceupdate) {
- update_panels();
- }
- return;
- }
-
- fullname = buddy_getname(BUDDATA(current_buddy));
- btype = buddy_gettype(BUDDATA(current_buddy));
-
- isgrp = ismuc = isspe = 0;
- if (btype & ROSTER_TYPE_USER) {
- btypetext = "Buddy";
- } else if (btype & ROSTER_TYPE_GROUP) {
- btypetext = "Group";
- isgrp = 1;
- } else if (btype & ROSTER_TYPE_AGENT) {
- btypetext = "Agent";
- } else if (btype & ROSTER_TYPE_ROOM) {
- btypetext = "Room";
- ismuc = 1;
- } else if (btype & ROSTER_TYPE_SPECIAL) {
- btypetext = "Special buffer";
- isspe = 1;
- }
-
- if (chatmode) {
- wprintw(chatstatusWnd, "~");
- } else {
- unsigned short bflags = buddy_getflags(BUDDATA(current_buddy));
- if (bflags & ROSTER_FLAG_MSG) {
- // There is an unread message from the current buddy
- wprintw(chatstatusWnd, "#");
- }
- }
-
- if (chatmode && !isgrp) {
- winbuf *win_entry;
- win_entry = scr_SearchWindow(buddy_getjid(BUDDATA(current_buddy)), isspe);
- if (win_entry && win_entry->bd->lock)
- mvwprintw(chatstatusWnd, 0, 0, "*");
- }
-
- if (isgrp || isspe) {
- buf_locale = from_utf8(fullname);
- mvwprintw(chatstatusWnd, 0, 5, "%s: %s", btypetext, buf_locale);
- g_free(buf_locale);
- if (forceupdate) {
- update_panels();
- }
- return;
- }
-
- status = '?';
-
- if (ismuc) {
- if (buddy_getinsideroom(BUDDATA(current_buddy)))
- status = 'C';
- else
- status = 'x';
- } else if (xmpp_getstatus() != offline) {
- enum imstatus budstate;
- budstate = buddy_getstatus(BUDDATA(current_buddy), NULL);
- if (budstate < imstatus_size)
- status = imstatus2char[budstate];
- }
-
- // No status message for MUC rooms
- if (!ismuc) {
- GSList *resources, *p_res, *p_next_res;
- resources = buddy_getresources(BUDDATA(current_buddy));
-
- for (p_res = resources ; p_res ; p_res = p_next_res) {
- p_next_res = g_slist_next(p_res);
- // Store the status message of the latest resource (highest priority)
- if (!p_next_res)
- msg = buddy_getstatusmsg(BUDDATA(current_buddy), p_res->data);
- g_free(p_res->data);
- }
- g_slist_free(resources);
- } else {
- msg = buddy_gettopic(BUDDATA(current_buddy));
- }
-
- if (msg)
- buf = g_strdup_printf("[%c] %s: %s -- %s", status, btypetext, fullname, msg);
- else
- buf = g_strdup_printf("[%c] %s: %s", status, btypetext, fullname);
- replace_nl_with_dots(buf);
- buf_locale = from_utf8(buf);
- mvwprintw(chatstatusWnd, 0, 1, "%s", buf_locale);
- g_free(buf_locale);
- g_free(buf);
-
- // Display chatstates of the contact, if available.
- if (btype & ROSTER_TYPE_USER) {
- char eventchar = 0;
- guint event;
-
- // We do not specify the resource here, so one of the resources with the
- // highest priority will be used.
- event = buddy_resource_getevents(BUDDATA(current_buddy), NULL);
-
- if (event == ROSTER_EVENT_ACTIVE)
- eventchar = 'A';
- else if (event == ROSTER_EVENT_COMPOSING)
- eventchar = 'C';
- else if (event == ROSTER_EVENT_PAUSED)
- eventchar = 'P';
- else if (event == ROSTER_EVENT_INACTIVE)
- eventchar = 'I';
- else if (event == ROSTER_EVENT_GONE)
- eventchar = 'G';
-
- if (eventchar)
- mvwprintw(chatstatusWnd, 0, maxX-3, "[%c]", eventchar);
- }
-
-
- if (forceupdate) {
- update_panels();
- }
-}
-
-void increment_if_buddy_not_filtered(gpointer rosterdata, void *param)
-{
- int *p = param;
- if (buddylist_is_status_filtered(buddy_getstatus(rosterdata, NULL)))
- *p=*p+1;
-}
-
-// scr_DrawRoster()
-// Display the buddylist (not really the roster) on the screen
-void scr_DrawRoster(void)
-{
- static int offset = 0;
- char *name, *rline;
- int maxx, maxy;
- GList *buddy;
- int i, n;
- int rOffset;
- int cursor_backup;
- char status, pending;
- enum imstatus currentstatus = xmpp_getstatus();
- int x_pos;
-
- // We can reset update_roster
- update_roster = FALSE;
-
- getmaxyx(rosterWnd, maxy, maxx);
- maxx--; // Last char is for vertical border
-
- cursor_backup = curs_set(0);
-
- if (!buddylist)
- offset = 0;
- else
- scr_UpdateChatStatus(FALSE);
-
- // Cleanup of roster window
- werase(rosterWnd);
-
- if (Roster_Width) {
- int line_x_pos = roster_win_on_right ? 0 : Roster_Width-1;
- // Redraw the vertical line (not very good...)
- wattrset(rosterWnd, get_color(COLOR_GENERAL));
- for (i=0 ; i < CHAT_WIN_HEIGHT ; i++)
- mvwaddch(rosterWnd, i, line_x_pos, ACS_VLINE);
- }
-
- // Leave now if buddylist is empty or the roster is hidden
- if (!buddylist || !Roster_Width) {
- update_panels();
- curs_set(cursor_backup);
- return;
- }
-
- // Update offset if necessary
- // a) Try to show as many buddylist items as possible
- i = g_list_length(buddylist) - maxy;
- if (i < 0)
- i = 0;
- if (i < offset)
- offset = i;
- // b) Make sure the current_buddy is visible
- i = g_list_position(buddylist, current_buddy);
- if (i == -1) { // This is bad
- scr_LogPrint(LPRINT_NORMAL, "Doh! Can't find current selected buddy!!");
- curs_set(cursor_backup);
- return;
- } else if (i < offset) {
- offset = i;
- } else if (i+1 > offset + maxy) {
- offset = i + 1 - maxy;
- }
-
- if (roster_win_on_right)
- x_pos = 1; // 1 char offset (vertical line)
- else
- x_pos = 0;
-
- name = g_new0(char, 4*Roster_Width);
- rline = g_new0(char, 4*Roster_Width+1);
-
- buddy = buddylist;
- rOffset = offset;
-
- for (i=0; i<maxy && buddy; buddy = g_list_next(buddy)) {
- unsigned short bflags, btype, ismsg, isgrp, ismuc, ishid, isspe;
- gchar *rline_locale;
- GSList *resources, *p_res;
-
- bflags = buddy_getflags(BUDDATA(buddy));
- btype = buddy_gettype(BUDDATA(buddy));
-
- ismsg = bflags & ROSTER_FLAG_MSG;
- ishid = bflags & ROSTER_FLAG_HIDE;
- isgrp = btype & ROSTER_TYPE_GROUP;
- ismuc = btype & ROSTER_TYPE_ROOM;
- isspe = btype & ROSTER_TYPE_SPECIAL;
-
- if (rOffset > 0) {
- rOffset--;
- continue;
- }
-
- status = '?';
- pending = ' ';
-
- resources = buddy_getresources(BUDDATA(buddy));
- for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
- guint events = buddy_resource_getevents(BUDDATA(buddy),
- p_res ? p_res->data : "");
- if ((events & ROSTER_EVENT_PAUSED) && pending != '+')
- pending = '.';
- if (events & ROSTER_EVENT_COMPOSING)
- pending = '+';
- g_free(p_res->data);
- }
- g_slist_free(resources);
-
- // Display message notice if there is a message flag, but not
- // for unfolded groups.
- if (ismsg && (!isgrp || ishid)) {
- pending = '#';
- }
-
- if (ismuc) {
- if (buddy_getinsideroom(BUDDATA(buddy)))
- status = 'C';
- else
- status = 'x';
- } else if (currentstatus != offline) {
- enum imstatus budstate;
- budstate = buddy_getstatus(BUDDATA(buddy), NULL);
- if (budstate < imstatus_size)
- status = imstatus2char[budstate];
- }
- if (buddy == current_buddy) {
- if (pending == '#')
- wattrset(rosterWnd, get_color(COLOR_ROSTERSELNMSG));
- else
- wattrset(rosterWnd, get_color(COLOR_ROSTERSEL));
- // The 3 following lines aim at coloring the whole line
- wmove(rosterWnd, i, x_pos);
- for (n = 0; n < maxx; n++)
- waddch(rosterWnd, ' ');
- } else {
- if (pending == '#')
- wattrset(rosterWnd, get_color(COLOR_ROSTERNMSG));
- else {
- int color = get_color(COLOR_ROSTER);
- if ((!isspe) && (!isgrp)) {//Look for color rules
- GSList *head;
- const char *jid = buddy_getjid(BUDDATA(buddy));
- for (head = rostercolrules; head; head = g_slist_next(head)) {
- rostercolor *rc = head->data;
- if (g_pattern_match_string(rc->compiled, jid) &&
- (!strcmp("*", rc->status) || strchr(rc->status, status))) {
- color = compose_color(rc->color);
- break;
- }
- }
- }
- wattrset(rosterWnd, color);
- }
- }
-
- if (Roster_Width > 7)
- g_utf8_strncpy(name, buddy_getname(BUDDATA(buddy)), Roster_Width-7);
- else
- name[0] = 0;
-
- if (isgrp) {
- if (ishid) {
- int group_count = 0;
- foreach_group_member(BUDDATA(buddy), increment_if_buddy_not_filtered,
- &group_count);
- snprintf(rline, 4*Roster_Width, " %c+++ %s (%i)", pending, name,
- group_count);
- /* Do not display the item count if there isn't enough space */
- if (g_utf8_strlen(rline, 4*Roster_Width) >= Roster_Width)
- snprintf(rline, 4*Roster_Width, " %c--- %s", pending, name);
- }
- else
- snprintf(rline, 4*Roster_Width, " %c--- %s", pending, name);
- } else if (isspe) {
- snprintf(rline, 4*Roster_Width, " %c%s", pending, name);
- } else {
- char sepleft = '[';
- char sepright = ']';
- if (btype & ROSTER_TYPE_USER) {
- guint subtype = buddy_getsubscription(BUDDATA(buddy));
- if (status == '_' && !(subtype & sub_to))
- status = '?';
- if (!(subtype & sub_from)) {
- sepleft = '{';
- sepright = '}';
- }
- }
-
- snprintf(rline, 4*Roster_Width,
- " %c%c%c%c %s", pending, sepleft, status, sepright, name);
- }
-
- rline_locale = from_utf8(rline);
- mvwprintw(rosterWnd, i, x_pos, "%s", rline_locale);
- g_free(rline_locale);
- i++;
- }
-
- g_free(rline);
- g_free(name);
- top_panel(inputPanel);
- update_panels();
- curs_set(cursor_backup);
-}
-
-// scr_RosterVisibility(status)
-// Set the roster visibility:
-// status=1 Show roster
-// status=0 Hide roster
-// status=-1 Toggle roster status
-void scr_RosterVisibility(int status)
-{
- int old_roster_status = roster_hidden;
-
- if (status > 0)
- roster_hidden = FALSE;
- else if (status == 0)
- roster_hidden = TRUE;
- else
- roster_hidden = !roster_hidden;
-
- if (roster_hidden != old_roster_status) {
- // Recalculate windows size and redraw
- scr_Resize();
- redrawwin(stdscr);
- }
-}
-
-#ifdef HAVE_GLIB_REGEX
-static inline void scr_LogUrls(const gchar *string)
-{
- GMatchInfo *match_info;
-
- g_regex_match_full(url_regex, string, -1, 0, 0, &match_info, NULL);
- while (g_match_info_matches(match_info)) {
- gchar *url = g_match_info_fetch(match_info, 0);
- scr_print_logwindow(url);
- g_free(url);
- g_match_info_next(match_info, NULL);
- }
- g_match_info_free(match_info);
-}
-#endif
-
-void scr_WriteMessage(const char *bjid, const char *text,
- time_t timestamp, guint prefix_flags,
- unsigned mucnicklen, gpointer xep184)
-{
- char *xtext;
-
- if (!timestamp) timestamp = time(NULL);
-
- xtext = ut_expand_tabs(text); // Expand tabs and filter out some chars
-
- scr_WriteInWindow(bjid, xtext, timestamp, prefix_flags, FALSE, mucnicklen,
- xep184);
-
- if (xtext != (char*)text)
- g_free(xtext);
-}
-
-// If prefix is NULL, HBB_PREFIX_IN is supposed.
-void scr_WriteIncomingMessage(const char *jidfrom, const char *text,
- time_t timestamp, guint prefix, unsigned mucnicklen)
-{
- if (!(prefix &
- ~HBB_PREFIX_NOFLAG & ~HBB_PREFIX_HLIGHT & ~HBB_PREFIX_HLIGHT_OUT &
- ~HBB_PREFIX_PGPCRYPT & ~HBB_PREFIX_OTRCRYPT))
- prefix |= HBB_PREFIX_IN;
-
-#ifdef HAVE_GLIB_REGEX
- if (url_regex)
- scr_LogUrls(text);
-#endif
- scr_WriteMessage(jidfrom, text, timestamp, prefix, mucnicklen, NULL);
-}
-
-void scr_WriteOutgoingMessage(const char *jidto, const char *text, guint prefix,
- gpointer xep184)
-{
- GSList *roster_elt;
- roster_elt = roster_find(jidto, jidsearch,
- ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM);
-
- scr_WriteMessage(jidto, text,
- 0, prefix|HBB_PREFIX_OUT|HBB_PREFIX_HLIGHT_OUT, 0, xep184);
-
- // Show jidto's buffer unless the buddy is not in the buddylist
- if (roster_elt && g_list_position(buddylist, roster_elt->data) != -1)
- scr_ShowWindow(jidto, FALSE);
-}
-
-void scr_RemoveReceiptFlag(const char *bjid, gpointer xep184)
-{
- winbuf *win_entry = scr_SearchWindow(bjid, FALSE);
- if (win_entry) {
- hbuf_remove_receipt(win_entry->bd->hbuf, xep184);
- if (chatmode && (buddy_search_jid(bjid) == current_buddy))
- scr_UpdateBuddyWindow();
- }
-}
-
-static inline void set_autoaway(bool setaway)
-{
- static enum imstatus oldstatus;
- static char *oldmsg;
- Autoaway = setaway;
-
- if (setaway) {
- const char *msg, *prevmsg;
- oldstatus = xmpp_getstatus();
- if (oldmsg) {
- g_free(oldmsg);
- oldmsg = NULL;
- }
- prevmsg = xmpp_getstatusmsg();
- msg = settings_opt_get("message_autoaway");
- if (!msg)
- msg = prevmsg;
- if (prevmsg)
- oldmsg = g_strdup(prevmsg);
- xmpp_setstatus(away, NULL, msg, FALSE);
- } else {
- // Back
- xmpp_setstatus(oldstatus, NULL, (oldmsg ? oldmsg : ""), FALSE);
- if (oldmsg) {
- g_free(oldmsg);
- oldmsg = NULL;
- }
- }
-}
-
-// set_chatstate(state)
-// Set the current chat state (0=active, 1=composing, 2=paused)
-// If the chat state has changed, call xmpp_send_chatstate()
-static inline void set_chatstate(int state)
-{
-#if defined JEP0022 || defined JEP0085
- if (chatstates_disabled)
- return;
- if (!chatmode)
- state = 0;
- if (state != chatstate) {
- chatstate = state;
- if (current_buddy &&
- buddy_gettype(BUDDATA(current_buddy)) == ROSTER_TYPE_USER) {
- guint jep_state;
- if (chatstate == 1) {
- if (chatstate_timeout_id == 0)
- chatstate_timeout_id = g_timeout_add_seconds(1,
- scr_ChatStatesTimeout,
- NULL);
- jep_state = ROSTER_EVENT_COMPOSING;
- }
- else if (chatstate == 2)
- jep_state = ROSTER_EVENT_PAUSED;
- else
- jep_state = ROSTER_EVENT_ACTIVE;
- xmpp_send_chatstate(BUDDATA(current_buddy), jep_state);
- }
- if (!chatstate)
- chatstate_timestamp = 0;
- }
-#endif
-}
-
-#if defined JEP0022 || defined JEP0085
-gboolean scr_ChatStatesTimeout(void)
-{
- time_t now;
- time(&now);
- // Check if we're currently composing...
- if (chatstate != 1 || !chatstate_timestamp) {
- chatstate_timeout_id = 0;
- return FALSE;
- }
-
- // If the timeout is reached, let's change the state right now.
- if (now >= chatstate_timestamp + COMPOSING_TIMEOUT) {
- chatstate_timestamp = now;
- set_chatstate(2);
- chatstate_timeout_id = 0;
- return FALSE;
- }
- return TRUE;
-}
-#endif
-
-// Check if we should enter/leave automatic away status
-void scr_CheckAutoAway(int activity)
-{
- enum imstatus cur_st;
- unsigned int autoaway_timeout = settings_opt_get_int("autoaway");
-
- if (Autoaway && activity) set_autoaway(FALSE);
- if (!autoaway_timeout) return;
- if (!LastActivity || activity) time(&LastActivity);
-
- cur_st = xmpp_getstatus();
- // Auto-away is disabled for the following states
- if ((cur_st != available) && (cur_st != freeforchat))
- return;
-
- if (!activity) {
- time_t now;
- time(&now);
- if (!Autoaway && (now > LastActivity + (time_t)autoaway_timeout))
- set_autoaway(TRUE);
- }
-}
-
-// set_current_buddy(newbuddy)
-// Set the current_buddy to newbuddy (if not NULL)
-// Lock the newbuddy, and unlock the previous current_buddy
-static void set_current_buddy(GList *newbuddy)
-{
- enum imstatus prev_st = imstatus_size;
- /* prev_st initialized to imstatus_size, which is used as "undef" value.
- * We are sure prev_st will get a different status value after the
- * buddy_getstatus() call.
- */
-
- if (!current_buddy || !newbuddy) return;
- if (newbuddy == current_buddy) return;
-
- // We're moving to another buddy. We're thus inactive wrt current_buddy.
- set_chatstate(0);
- // We don't want the chatstate to be changed again right now.
- lock_chatstate = TRUE;
-
- prev_st = buddy_getstatus(BUDDATA(current_buddy), NULL);
- buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
- if (chatmode)
- alternate_buddy = current_buddy;
- current_buddy = newbuddy;
- // Lock the buddy in the buddylist if we're in chat mode
- if (chatmode)
- buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE);
- // We should rebuild the buddylist but not everytime
- // Here we check if we were locking a buddy who is actually offline,
- // and hide_offline_buddies is TRUE. In which case we need to rebuild.
- if (!(buddylist_get_filter() & 1<<prev_st))
- buddylist_build();
- update_roster = TRUE;
-}
-
-// scr_RosterTop()
-// Go to the first buddy in the buddylist
-void scr_RosterTop(void)
-{
- set_current_buddy(buddylist);
- if (chatmode)
- scr_ShowBuddyWindow();
-}
-
-// scr_RosterBottom()
-// Go to the last buddy in the buddylist
-void scr_RosterBottom(void)
-{
- set_current_buddy(g_list_last(buddylist));
- if (chatmode)
- scr_ShowBuddyWindow();
-}
-
-// scr_RosterUpDown(updown, n)
-// Go to the nth next buddy in the buddylist
-// (up if updown == -1, down if updown == 1)
-void scr_RosterUpDown(int updown, unsigned int n)
-{
- unsigned int i;
-
- if (updown < 0) {
- for (i = 0; i < n; i++)
- set_current_buddy(g_list_previous(current_buddy));
- } else {
- for (i = 0; i < n; i++)
- set_current_buddy(g_list_next(current_buddy));
- }
- if (chatmode)
- scr_ShowBuddyWindow();
-}
-
-// scr_RosterPrevGroup()
-// Go to the previous group in the buddylist
-void scr_RosterPrevGroup(void)
-{
- GList *bud;
-
- for (bud = current_buddy ; bud ; ) {
- bud = g_list_previous(bud);
- if (!bud)
- break;
- if (buddy_gettype(BUDDATA(bud)) & ROSTER_TYPE_GROUP) {
- set_current_buddy(bud);
- if (chatmode)
- scr_ShowBuddyWindow();
- break;
- }
- }
-}
-
-// scr_RosterNextGroup()
-// Go to the next group in the buddylist
-void scr_RosterNextGroup(void)
-{
- GList *bud;
-
- for (bud = current_buddy ; bud ; ) {
- bud = g_list_next(bud);
- if (!bud)
- break;
- if (buddy_gettype(BUDDATA(bud)) & ROSTER_TYPE_GROUP) {
- set_current_buddy(bud);
- if (chatmode)
- scr_ShowBuddyWindow();
- break;
- }
- }
-}
-
-// scr_RosterSearch(str)
-// Look forward for a buddy with jid/name containing str.
-void scr_RosterSearch(char *str)
-{
- set_current_buddy(buddy_search(str));
- if (chatmode)
- scr_ShowBuddyWindow();
-}
-
-// scr_RosterJumpJid(bjid)
-// Jump to buddy bjid.
-// NOTE: With this function, the buddy is added to the roster if doesn't exist.
-void scr_RosterJumpJid(char *barejid)
-{
- GSList *roster_elt;
- // Look for an existing buddy
- roster_elt = roster_find(barejid, jidsearch,
- ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM);
- // Create it if necessary
- if (!roster_elt)
- roster_elt = roster_add_user(barejid, NULL, NULL, ROSTER_TYPE_USER,
- sub_none, -1);
- // Set a lock to see it in the buddylist
- buddy_setflags(BUDDATA(roster_elt), ROSTER_FLAG_LOCK, TRUE);
- buddylist_build();
- // Jump to the buddy
- set_current_buddy(buddy_search_jid(barejid));
- if (chatmode)
- scr_ShowBuddyWindow();
-}
-
-// scr_RosterUnreadMessage(next)
-// Go to a new message. If next is not null, try to go to the next new
-// message. If it is not possible or if next is NULL, go to the first new
-// message from unread_list.
-void scr_RosterUnreadMessage(int next)
-{
- gpointer unread_ptr;
- gpointer refbuddata;
- GList *nbuddy;
-
- if (!current_buddy) return;
-
- if (next) refbuddata = BUDDATA(current_buddy);
- else refbuddata = NULL;
-
- unread_ptr = unread_msg(refbuddata);
- if (!unread_ptr) return;
-
- if (!(buddy_gettype(unread_ptr) & ROSTER_TYPE_SPECIAL)) {
- gpointer ngroup;
- // If buddy is in a folded group, we need to expand it
- ngroup = buddy_getgroup(unread_ptr);
- if (buddy_getflags(ngroup) & ROSTER_FLAG_HIDE) {
- buddy_setflags(ngroup, ROSTER_FLAG_HIDE, FALSE);
- buddylist_build();
- }
- }
-
- nbuddy = g_list_find(buddylist, unread_ptr);
- if (nbuddy) {
- set_current_buddy(nbuddy);
- if (chatmode) scr_ShowBuddyWindow();
- } else
- scr_LogPrint(LPRINT_LOGNORM, "Error: nbuddy == NULL"); // should not happen
-}
-
-// scr_RosterJumpAlternate()
-// Try to jump to alternate (== previous) buddy
-void scr_RosterJumpAlternate(void)
-{
- if (!alternate_buddy || g_list_position(buddylist, alternate_buddy) == -1)
- return;
- set_current_buddy(alternate_buddy);
- if (chatmode)
- scr_ShowBuddyWindow();
-}
-
-// scr_RosterDisplay(filter)
-// Set the roster filter mask. If filter is null/empty, the current
-// mask is displayed.
-void scr_RosterDisplay(const char *filter)
-{
- guchar status;
- enum imstatus budstate;
- char strfilter[imstatus_size+1];
- char *psfilter;
-
- if (filter && *filter) {
- int show_all = (*filter == '*');
- status = 0;
- for (budstate = 0; budstate < imstatus_size-1; budstate++)
- if (strchr(filter, imstatus2char[budstate]) || show_all)
- status |= 1<<budstate;
- buddylist_set_filter(status);
- buddylist_build();
- update_roster = TRUE;
- return;
- }
-
- // Display current filter
- psfilter = strfilter;
- status = buddylist_get_filter();
- for (budstate = 0; budstate < imstatus_size-1; budstate++)
- if (status & 1<<budstate)
- *psfilter++ = imstatus2char[budstate];
- *psfilter = '\0';
- scr_LogPrint(LPRINT_NORMAL, "Roster status filter: %s", strfilter);
-}
-
-// scr_BufferScrollUpDown()
-// Scroll up/down the current buddy window,
-// - half a screen if nblines is 0,
-// - up if updown == -1, down if updown == 1
-void scr_BufferScrollUpDown(int updown, unsigned int nblines)
-{
- winbuf *win_entry;
- int n, nbl;
- GList *hbuf_top;
- guint isspe;
-
- // Get win_entry
- if (!current_buddy) return;
-
- isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
- win_entry = scr_SearchWindow(CURRENT_JID, isspe);
- if (!win_entry) return;
-
- if (!nblines) {
- // Scroll half a screen (or less)
- nbl = CHAT_WIN_HEIGHT/2;
- } else {
- nbl = nblines;
- }
- hbuf_top = win_entry->bd->top;
-
- if (updown == -1) { // UP
- if (!hbuf_top) {
- hbuf_top = g_list_last(win_entry->bd->hbuf);
- if (!win_entry->bd->cleared) {
- if (!nblines) nbl = nbl*3 - 1;
- else nbl += CHAT_WIN_HEIGHT - 1;
- } else {
- win_entry->bd->cleared = FALSE;
- }
- }
- for (n=0 ; hbuf_top && n < nbl && g_list_previous(hbuf_top) ; n++)
- hbuf_top = g_list_previous(hbuf_top);
- win_entry->bd->top = hbuf_top;
- } else { // DOWN
- for (n=0 ; hbuf_top && n < nbl ; n++)
- hbuf_top = g_list_next(hbuf_top);
- win_entry->bd->top = hbuf_top;
- // Check if we are at the bottom
- for (n=0 ; hbuf_top && n < CHAT_WIN_HEIGHT-1 ; n++)
- hbuf_top = g_list_next(hbuf_top);
- if (!hbuf_top)
- win_entry->bd->top = NULL; // End reached
- }
-
- // Refresh the window
- scr_UpdateWindow(win_entry);
-
- // Finished :)
- update_panels();
-}
-
-// scr_BufferClear()
-// Clear the current buddy window (used for the /clear command)
-void scr_BufferClear(void)
-{
- winbuf *win_entry;
- guint isspe;
-
- // Get win_entry
- if (!current_buddy) return;
- isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
- win_entry = scr_SearchWindow(CURRENT_JID, isspe);
- if (!win_entry) return;
-
- win_entry->bd->cleared = TRUE;
- win_entry->bd->top = NULL;
-
- // Refresh the window
- scr_UpdateWindow(win_entry);
-
- // Finished :)
- update_panels();
-}
-
-// buffer_purge()
-// key: winId/jid
-// value: winbuf structure
-// data: int, set to 1 if the buffer should be closed.
-// NOTE: does not work for special buffers.
-static void buffer_purge(gpointer key, gpointer value, gpointer data)
-{
- int *p_closebuf = data;
- winbuf *win_entry = value;
-
- // Delete the current hbuf
- hbuf_free(&win_entry->bd->hbuf);
-
- if (*p_closebuf) {
- g_hash_table_remove(winbufhash, key);
- } else {
- win_entry->bd->cleared = FALSE;
- win_entry->bd->top = NULL;
- }
-}
-
-// scr_BufferPurge(closebuf, jid)
-// Purge/Drop the current buddy buffer or jid's buffer if jid != NULL.
-// If closebuf is 1, close the buffer.
-void scr_BufferPurge(int closebuf, const char *jid)
-{
- winbuf *win_entry;
- guint isspe;
- guint *p_closebuf;
- const char *cjid;
- guint hold_chatmode = FALSE;
-
- if (jid) {
- cjid = jid;
- isspe = FALSE;
- // If closebuf is TRUE, it's probably better not to leave chat mode
- // if the change isn't related to the current buffer.
- if (closebuf && current_buddy) {
- if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL ||
- strcasecmp(jid, CURRENT_JID))
- hold_chatmode = TRUE;
- }
- } else {
- // Get win_entry
- if (!current_buddy) return;
- cjid = CURRENT_JID;
- isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
- }
- win_entry = scr_SearchWindow(cjid, isspe);
- if (!win_entry) return;
-
- if (!isspe) {
- p_closebuf = g_new(guint, 1);
- *p_closebuf = closebuf;
- buffer_purge((gpointer)cjid, win_entry, p_closebuf);
- g_free(p_closebuf);
- if (closebuf && !hold_chatmode) {
- scr_set_chatmode(FALSE);
- currentWindow = NULL;
- }
- } else {
- // (Special buffer)
- // Reset the current hbuf
- hbuf_free(&win_entry->bd->hbuf);
- // Currently it can only be the status buffer
- statushbuf = NULL;
-
- win_entry->bd->cleared = FALSE;
- win_entry->bd->top = NULL;
- }
-
- // Refresh the window
- scr_UpdateBuddyWindow();
-
- // Finished :)
- update_panels();
-}
-
-void scr_BufferPurgeAll(int closebuf)
-{
- guint *p_closebuf;
- p_closebuf = g_new(guint, 1);
-
- *p_closebuf = closebuf;
- g_hash_table_foreach(winbufhash, buffer_purge, p_closebuf);
- g_free(p_closebuf);
-
- if (closebuf) {
- scr_set_chatmode(FALSE);
- currentWindow = NULL;
- }
-
- // Refresh the window
- scr_UpdateBuddyWindow();
-
- // Finished :)
- update_panels();
-}
-
-// scr_BufferScrollLock(lock)
-// Lock/unlock the current buddy buffer
-// lock = 1 : lock
-// lock = 0 : unlock
-// lock = -1: toggle lock status
-void scr_BufferScrollLock(int lock)
-{
- winbuf *win_entry;
- guint isspe;
-
- // Get win_entry
- if (!current_buddy) return;
- isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
- win_entry = scr_SearchWindow(CURRENT_JID, isspe);
- if (!win_entry) return;
-
- if (lock == -1)
- lock = !win_entry->bd->lock;
-
- if (lock) {
- win_entry->bd->lock = TRUE;
- } else {
- win_entry->bd->lock = FALSE;
- //win_entry->bd->cleared = FALSE;
- if (isspe || (buddy_getflags(BUDDATA(current_buddy)) & ROSTER_FLAG_MSG))
- win_entry->bd->top = NULL;
- }
-
- // If chatmode is disabled and we're at the bottom of the buffer,
- // we need to set the "top" line, so we need to call scr_ShowBuddyWindow()
- // at least once. (Maybe it will cause a double refresh...)
- if (!chatmode && !win_entry->bd->top) {
- chatmode = TRUE;
- scr_ShowBuddyWindow();
- chatmode = FALSE;
- }
-
- // Refresh the window
- scr_UpdateBuddyWindow();
-
- // Finished :)
- update_panels();
-}
-
-// scr_BufferTopBottom()
-// Jump to the head/tail of the current buddy window
-// (top if topbottom == -1, bottom topbottom == 1)
-void scr_BufferTopBottom(int topbottom)
-{
- winbuf *win_entry;
- guint isspe;
-
- // Get win_entry
- if (!current_buddy) return;
- isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
- win_entry = scr_SearchWindow(CURRENT_JID, isspe);
- if (!win_entry) return;
-
- win_entry->bd->cleared = FALSE;
- if (topbottom == 1)
- win_entry->bd->top = NULL;
- else
- win_entry->bd->top = g_list_first(win_entry->bd->hbuf);
-
- // Refresh the window
- scr_UpdateWindow(win_entry);
-
- // Finished :)
- update_panels();
-}
-
-// scr_BufferSearch(direction, text)
-// Jump to the next line containing text
-// (backward search if direction == -1, forward if topbottom == 1)
-void scr_BufferSearch(int direction, const char *text)
-{
- winbuf *win_entry;
- GList *current_line, *search_res;
- guint isspe;
-
- // Get win_entry
- if (!current_buddy) return;
- isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
- win_entry = scr_SearchWindow(CURRENT_JID, isspe);
- if (!win_entry) return;
-
- if (win_entry->bd->top)
- current_line = win_entry->bd->top;
- else
- current_line = g_list_last(win_entry->bd->hbuf);
-
- search_res = hbuf_search(current_line, direction, text);
-
- if (search_res) {
- win_entry->bd->cleared = FALSE;
- win_entry->bd->top = search_res;
-
- // Refresh the window
- scr_UpdateWindow(win_entry);
-
- // Finished :)
- update_panels();
- } else
- scr_LogPrint(LPRINT_NORMAL, "Search string not found");
-}
-
-// scr_BufferPercent(n)
-// Jump to the specified position in the buffer, in %
-void scr_BufferPercent(int pc)
-{
- winbuf *win_entry;
- GList *search_res;
- guint isspe;
-
- // Get win_entry
- if (!current_buddy) return;
- isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
- win_entry = scr_SearchWindow(CURRENT_JID, isspe);
- if (!win_entry) return;
-
- if (pc < 0 || pc > 100) {
- scr_LogPrint(LPRINT_NORMAL, "Bad % value");
- return;
- }
-
- search_res = hbuf_jump_percent(win_entry->bd->hbuf, pc);
-
- win_entry->bd->cleared = FALSE;
- win_entry->bd->top = search_res;
-
- // Refresh the window
- scr_UpdateWindow(win_entry);
-
- // Finished :)
- update_panels();
-}
-
-// scr_BufferDate(t)
-// Jump to the first line after date t in the buffer
-// t is a date in seconds since `00:00:00 1970-01-01 UTC'
-void scr_BufferDate(time_t t)
-{
- winbuf *win_entry;
- GList *search_res;
- guint isspe;
-
- // Get win_entry
- if (!current_buddy) return;
- isspe = buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_SPECIAL;
- win_entry = scr_SearchWindow(CURRENT_JID, isspe);
- if (!win_entry) return;
-
- search_res = hbuf_jump_date(win_entry->bd->hbuf, t);
-
- win_entry->bd->cleared = FALSE;
- win_entry->bd->top = search_res;
-
- // Refresh the window
- scr_UpdateWindow(win_entry);
-
- // Finished :)
- update_panels();
-}
-
-void scr_BufferDump(const char *file)
-{
- char *extfname;
-
- if (!currentWindow) {
- scr_LogPrint(LPRINT_NORMAL, "No current buffer!");
- return;
- }
-
- if (!file || !*file) {
- scr_LogPrint(LPRINT_NORMAL, "Missing parameter (file name)!");
- return;
- }
-
- extfname = expand_filename(file);
- hbuf_dump_to_file(currentWindow->bd->hbuf, extfname);
- g_free(extfname);
-}
-
-// buffer_list()
-// key: winId/jid
-// value: winbuf structure
-// data: none.
-static void buffer_list(gpointer key, gpointer value, gpointer data)
-{
- GList *head;
- winbuf *win_entry = value;
-
- head = g_list_first(win_entry->bd->hbuf);
-
- scr_LogPrint(LPRINT_NORMAL, " %s (%u/%u)", key,
- g_list_length(head), hbuf_get_blocks_number(head));
-}
-
-void scr_BufferList(void)
-{
- scr_LogPrint(LPRINT_NORMAL, "Buffer list:");
- buffer_list("[status]", statusWindow, NULL);
- g_hash_table_foreach(winbufhash, buffer_list, NULL);
- scr_LogPrint(LPRINT_NORMAL, "End of buffer list.");
- scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
- update_roster = TRUE;
-}
-
-// scr_set_chatmode()
-// Public function to (un)set chatmode...
-inline void scr_set_chatmode(int enable)
-{
- chatmode = enable;
- scr_UpdateChatStatus(TRUE);
-}
-
-// scr_get_chatmode()
-// Public function to get chatmode state.
-inline int scr_get_chatmode(void)
-{
- return chatmode;
-}
-
-// scr_get_multimode()
-// Public function to get multimode status...
-inline int scr_get_multimode(void)
-{
- return multimode;
-}
-
-// scr_setmsgflag_if_needed(jid)
-// Set the message flag unless we're already in the jid buffer window
-void scr_setmsgflag_if_needed(const char *bjid, int special)
-{
- const char *current_id;
- bool iscurrentlocked = FALSE;
-
- if (!bjid)
- return;
-
- if (current_buddy) {
- if (special)
- current_id = buddy_getname(BUDDATA(current_buddy));
- else
- current_id = buddy_getjid(BUDDATA(current_buddy));
- if (current_id) {
- winbuf *win_entry = scr_SearchWindow(current_id, special);
- if (!win_entry) return;
- iscurrentlocked = win_entry->bd->lock;
- }
- } else {
- current_id = NULL;
- }
- if (!chatmode || !current_id || strcmp(bjid, current_id) || iscurrentlocked)
- roster_msg_setflag(bjid, special, TRUE);
-}
-
-// scr_set_multimode()
-// Public function to (un)set multimode...
-// Convention:
-// 0 = disabled / 1 = multimode / 2 = multimode verbatim (commands disabled)
-void scr_set_multimode(int enable, char *subject)
-{
- g_free(multiline);
- multiline = NULL;
-
- g_free(multimode_subj);
- if (enable && subject)
- multimode_subj = g_strdup(subject);
- else
- multimode_subj = NULL;
-
- multimode = enable;
-}
-
-// scr_get_multiline()
-// Public function to get the current multi-line.
-const char *scr_get_multiline(void)
-{
- if (multimode && multiline)
- return multiline;
- return NULL;
-}
-
-// scr_get_multimode_subj()
-// Public function to get the multi-line subject, if any.
-const char *scr_get_multimode_subj(void)
-{
- if (multimode)
- return multimode_subj;
- return NULL;
-}
-
-// scr_append_multiline(line)
-// Public function to append a line to the current multi-line message.
-// Skip empty leading lines.
-void scr_append_multiline(const char *line)
-{
- static int num;
-
- if (!multimode) {
- scr_LogPrint(LPRINT_NORMAL, "Error: Not in multi-line message mode!");
- return;
- }
- if (multiline) {
- int len = strlen(multiline)+strlen(line)+2;
- if (len >= HBB_BLOCKSIZE - 1) {
- // We don't handle single messages with size > HBB_BLOCKSIZE
- // (see hbuf)
- scr_LogPrint(LPRINT_NORMAL, "Your multi-line message is too big, "
- "this line has not been added.");
- scr_LogPrint(LPRINT_NORMAL, "Please send this part now...");
- return;
- }
- if (num >= MULTILINE_MAX_LINE_NUMBER) {
- // We don't allow too many lines; however the maximum is arbitrary
- // (It should be < 1000 yet)
- scr_LogPrint(LPRINT_NORMAL, "Your message has too many lines, "
- "this one has not been added.");
- scr_LogPrint(LPRINT_NORMAL, "Please send this part now...");
- return;
- }
- multiline = g_renew(char, multiline, len);
- strcat(multiline, "\n");
- strcat(multiline, line);
- num++;
- } else {
- // First message line (we skip leading empty lines)
- num = 0;
- if (line[0]) {
- multiline = g_strdup(line);
- num++;
- } else
- return;
- }
- scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
- "Multi-line mode: line #%d added [%.25s...", num, line);
-}
-
-// scr_cmdhisto_addline()
-// Add a line to the inputLine history
-static inline void scr_cmdhisto_addline(char *line)
-{
- int max_histo_lines;
-
- if (!line || !*line)
- return;
-
- max_histo_lines = settings_opt_get_int("cmdhistory_lines");
-
- if (max_histo_lines < 0)
- max_histo_lines = 1;
-
- if (max_histo_lines)
- while (cmdhisto_nblines >= (guint)max_histo_lines) {
- if (cmdhisto_cur && cmdhisto_cur == cmdhisto)
- break;
- g_free(cmdhisto->data);
- cmdhisto = g_list_delete_link(cmdhisto, cmdhisto);
- cmdhisto_nblines--;
- }
-
- cmdhisto = g_list_append(cmdhisto, g_strdup(line));
- cmdhisto_nblines++;
-}
-
-// scr_cmdhisto_prev()
-// Look for previous line beginning w/ the given mask in the inputLine history
-// Returns NULL if none found
-static const char *scr_cmdhisto_prev(char *mask, guint len)
-{
- GList *hl;
- if (!cmdhisto_cur) {
- hl = g_list_last(cmdhisto);
- if (hl) { // backup current line
- strncpy(cmdhisto_backup, mask, INPUTLINE_LENGTH);
- }
- } else {
- hl = g_list_previous(cmdhisto_cur);
- }
- while (hl) {
- if (!strncmp((char*)hl->data, mask, len)) {
- // Found a match
- cmdhisto_cur = hl;
- return (const char*)hl->data;
- }
- hl = g_list_previous(hl);
- }
- return NULL;
-}
-
-// scr_cmdhisto_next()
-// Look for next line beginning w/ the given mask in the inputLine history
-// Returns NULL if none found
-static const char *scr_cmdhisto_next(char *mask, guint len)
-{
- GList *hl;
- if (!cmdhisto_cur) return NULL;
- hl = cmdhisto_cur;
- while ((hl = g_list_next(hl)) != NULL)
- if (!strncmp((char*)hl->data, mask, len)) {
- // Found a match
- cmdhisto_cur = hl;
- return (const char*)hl->data;
- }
- // If the "backuped" line matches, we'll use it
- if (strncmp(cmdhisto_backup, mask, len)) return NULL; // No match
- cmdhisto_cur = NULL;
- return cmdhisto_backup;
-}
-
-// readline_transpose_chars()
-// Drag the character before point forward over the character at
-// point, moving point forward as well. If point is at the end of
-// the line, then this transposes the two characters before point.
-void readline_transpose_chars(void)
-{
- char *c1, *c2;
- unsigned a, b;
-
- if (ptr_inputline == inputLine) return;
-
- if (!*ptr_inputline) { // We're at EOL
- // If line is only 1 char long, nothing to do...
- if (ptr_inputline == prev_char(ptr_inputline, inputLine)) return;
- // Transpose the two previous characters
- c2 = prev_char(ptr_inputline, inputLine);
- c1 = prev_char(c2, inputLine);
- a = get_char(c1);
- b = get_char(c2);
- put_char(put_char(c1, b), a);
- } else {
- // Swap the two characters before the cursor and move right.
- c2 = ptr_inputline;
- c1 = prev_char(c2, inputLine);
- a = get_char(c1);
- b = get_char(c2);
- put_char(put_char(c1, b), a);
- check_offset(1);
- }
-}
-
-void readline_forward_kill_word(void)
-{
- char *c, *old = ptr_inputline;
- int spaceallowed = 1;
-
- if (! *ptr_inputline) return;
-
- for (c = ptr_inputline ; *c ; c = next_char(c)) {
- if (!iswalnum(get_char(c))) {
- if (iswblank(get_char(c))) {
- if (!spaceallowed) break;
- } else spaceallowed = 0;
- } else spaceallowed = 0;
- }
-
- // Modify the line
- for (;;) {
- *old = *c++;
- if (!*old++) break;
- }
-}
-
-// readline_backward_kill_word()
-// Kill the word before the cursor, in input line
-void readline_backward_kill_word(void)
-{
- char *c, *old = ptr_inputline;
- int spaceallowed = 1;
-
- if (ptr_inputline == inputLine) return;
-
- c = prev_char(ptr_inputline, inputLine);
- for ( ; c > inputLine ; c = prev_char(c, inputLine)) {
- if (!iswalnum(get_char(c))) {
- if (iswblank(get_char(c))) {
- if (!spaceallowed) break;
- } else spaceallowed = 0;
- } else spaceallowed = 0;
- }
-
- if (c == inputLine && *c == COMMAND_CHAR && old != c+1) {
- c = next_char(c);
- } else if (c != inputLine || iswblank(get_char(c))) {
- if ((c < prev_char(ptr_inputline, inputLine)) && (!iswalnum(get_char(c))))
- c = next_char(c);
- }
-
- // Modify the line
- ptr_inputline = c;
- for (;;) {
- *c = *old++;
- if (!*c++) break;
- }
- check_offset(-1);
-}
-
-// readline_backward_word()
-// Move back to the start of the current or previous word
-void readline_backward_word(void)
-{
- int i = 0;
-
- if (ptr_inputline == inputLine) return;
-
- if (iswalnum(get_char(ptr_inputline)) &&
- !iswalnum(get_char(prev_char(ptr_inputline, inputLine))))
- i--;
-
- for ( ;
- ptr_inputline > inputLine;
- ptr_inputline = prev_char(ptr_inputline, inputLine)) {
- if (!iswalnum(get_char(ptr_inputline))) {
- if (i) {
- ptr_inputline = next_char(ptr_inputline);
- break;
- }
- } else i++;
- }
-
- check_offset(-1);
-}
-
-// readline_forward_word()
-// Move forward to the end of the next word
-void readline_forward_word(void)
-{
- int stopsymbol_allowed = 1;
-
- while (*ptr_inputline) {
- if (!iswalnum(get_char(ptr_inputline))) {
- if (!stopsymbol_allowed) break;
- } else stopsymbol_allowed = 0;
- ptr_inputline = next_char(ptr_inputline);
- }
-
- check_offset(1);
-}
-
-void readline_updowncase_word(int upcase)
-{
- int stopsymbol_allowed = 1;
-
- while (*ptr_inputline) {
- if (!iswalnum(get_char(ptr_inputline))) {
- if (!stopsymbol_allowed) break;
- } else {
- stopsymbol_allowed = 0;
- if (upcase)
- *ptr_inputline = towupper(get_char(ptr_inputline));
- else
- *ptr_inputline = towlower(get_char(ptr_inputline));
- }
- ptr_inputline = next_char(ptr_inputline);
- }
-
- check_offset(1);
-}
-
-void readline_capitalize_word(void)
-{
- int stopsymbol_allowed = 1;
- int upcased = 0;
-
- while (*ptr_inputline) {
- if (!iswalnum(get_char(ptr_inputline))) {
- if (!stopsymbol_allowed) break;
- } else {
- stopsymbol_allowed = 0;
- if (!upcased) {
- *ptr_inputline = towupper(get_char(ptr_inputline));
- upcased = 1;
- } else *ptr_inputline = towlower(get_char(ptr_inputline));
- }
- ptr_inputline = next_char(ptr_inputline);
- }
-
- check_offset(1);
-}
-
-void readline_backward_char(void)
-{
- if (ptr_inputline == (char*)&inputLine) return;
-
- ptr_inputline = prev_char(ptr_inputline, inputLine);
- check_offset(-1);
-}
-
-void readline_forward_char(void)
-{
- if (!*ptr_inputline) return;
-
- ptr_inputline = next_char(ptr_inputline);
- check_offset(1);
-}
-
-// readline_accept_line(down_history)
-// Validate current command line.
-// If down_history is true, load the next history line.
-int readline_accept_line(int down_history)
-{
- scr_CheckAutoAway(TRUE);
- if (process_line(inputLine))
- return 255;
- // Add line to history
- scr_cmdhisto_addline(inputLine);
- // Reset the line
- ptr_inputline = inputLine;
- *ptr_inputline = 0;
- inputline_offset = 0;
-
- if (down_history) {
- // Use next history line instead of a blank line
- const char *l = scr_cmdhisto_next("", 0);
- if (l) strcpy(inputLine, l);
- // Reset backup history line
- cmdhisto_backup[0] = 0;
- } else {
- // Reset history line pointer
- cmdhisto_cur = NULL;
- }
- return 0;
-}
-
-void readline_cancel_completion(void)
-{
- scr_cancel_current_completion();
- scr_end_current_completion();
- check_offset(-1);
-}
-
-void readline_do_completion(void)
-{
- int i, n;
-
- if (multimode != 2) {
- // Not in verbatim multi-line mode
- scr_handle_tab();
- } else {
- // Verbatim multi-line mode: expand tab
- char tabstr[9];
- n = 8 - (ptr_inputline - inputLine) % 8;
- for (i = 0; i < n; i++)
- tabstr[i] = ' ';
- tabstr[i] = '\0';
- scr_insert_text(tabstr);
- }
- check_offset(0);
-}
-
-void readline_refresh_screen(void)
-{
- scr_CheckAutoAway(TRUE);
- ParseColors();
- scr_Resize();
- redrawwin(stdscr);
-}
-
-void readline_disable_chat_mode(guint show_roster)
-{
- scr_CheckAutoAway(TRUE);
- currentWindow = NULL;
- chatmode = FALSE;
- if (current_buddy)
- buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
- if (show_roster)
- scr_RosterVisibility(1);
- scr_UpdateChatStatus(FALSE);
- top_panel(chatPanel);
- top_panel(inputPanel);
- update_panels();
-}
-
-void readline_hist_beginning_search_bwd(void)
-{
- const char *l = scr_cmdhisto_prev(inputLine, ptr_inputline-inputLine);
- if (l) strcpy(inputLine, l);
-}
-
-void readline_hist_beginning_search_fwd(void)
-{
- const char *l = scr_cmdhisto_next(inputLine, ptr_inputline-inputLine);
- if (l) strcpy(inputLine, l);
-}
-
-void readline_hist_prev(void)
-{
- const char *l = scr_cmdhisto_prev(inputLine, 0);
- if (l) {
- strcpy(inputLine, l);
- // Set the pointer at the EOL.
- // We have to move it to BOL first, because we could be too far already.
- readline_iline_start();
- readline_iline_end();
- }
-}
-
-void readline_hist_next(void)
-{
- const char *l = scr_cmdhisto_next(inputLine, 0);
- if (l) {
- strcpy(inputLine, l);
- // Set the pointer at the EOL.
- // We have to move it to BOL first, because we could be too far already.
- readline_iline_start();
- readline_iline_end();
- }
-}
-
-void readline_backward_kill_char(void)
-{
- char *src, *c;
-
- if (ptr_inputline == (char*)&inputLine)
- return;
-
- src = ptr_inputline;
- c = prev_char(ptr_inputline, inputLine);
- ptr_inputline = c;
- for ( ; *src ; )
- *c++ = *src++;
- *c = 0;
- check_offset(-1);
-}
-
-void readline_forward_kill_char(void)
-{
- if (!*ptr_inputline)
- return;
-
- strcpy(ptr_inputline, next_char(ptr_inputline));
-}
-
-void readline_iline_start(void)
-{
- ptr_inputline = inputLine;
- inputline_offset = 0;
-}
-
-void readline_iline_end(void)
-{
- for (; *ptr_inputline; ptr_inputline++) ;
- check_offset(1);
-}
-
-void readline_backward_kill_iline(void)
-{
- strcpy(inputLine, ptr_inputline);
- ptr_inputline = inputLine;
- inputline_offset = 0;
-}
-
-void readline_forward_kill_iline(void)
-{
- *ptr_inputline = 0;
-}
-
-void readline_send_multiline(void)
-{
- // Validate current multi-line
- if (multimode)
- process_command(mkcmdstr("msay send"), TRUE);
-}
-
-// which_row()
-// Tells which row our cursor is in, in the command line.
-// -2 -> normal text
-// -1 -> room: nickname completion
-// 0 -> command
-// 1 -> parameter 1 (etc.)
-// If > 0, then *p_row is set to the beginning of the row
-static int which_row(const char **p_row)
-{
- int row = -1;
- char *p;
- int quote = FALSE;
-
- // Not a command?
- if ((ptr_inputline == inputLine) || (inputLine[0] != COMMAND_CHAR)) {
- if (!current_buddy) return -2;
- if (buddy_gettype(BUDDATA(current_buddy)) == ROSTER_TYPE_ROOM) {
- *p_row = inputLine;
- return -1;
- }
- return -2;
- }
-
- // This is a command
- row = 0;
- for (p = inputLine ; p < ptr_inputline ; p = next_char(p)) {
- if (quote) {
- if (*p == '"' && *(p-1) != '\\')
- quote = FALSE;
- continue;
- }
- if (*p == '"' && *(p-1) != '\\') {
- quote = TRUE;
- } else if (*p == ' ') {
- if (*(p-1) != ' ')
- row++;
- *p_row = p+1;
- }
- }
- return row;
-}
-
-// scr_insert_text()
-// Insert the given text at the current cursor position.
-// The cursor is moved. We don't check if the cursor still is in the screen
-// after, the caller should do that.
-static void scr_insert_text(const char *text)
-{
- char tmpLine[INPUTLINE_LENGTH+1];
- int len = strlen(text);
- // Check the line isn't too long
- if (strlen(inputLine) + len >= INPUTLINE_LENGTH) {
- scr_LogPrint(LPRINT_LOGNORM, "Cannot insert text, line too long.");
- return;
- }
-
- strcpy(tmpLine, ptr_inputline);
- strcpy(ptr_inputline, text);
- ptr_inputline += len;
- strcpy(ptr_inputline, tmpLine);
-}
-
-static void scr_cancel_current_completion(void);
-
-// scr_handle_tab()
-// Function called when tab is pressed.
-// Initiate or continue a completion...
-static void scr_handle_tab(void)
-{
- int nrow;
- const char *row;
- const char *cchar;
- guint compl_categ;
-
- row = inputLine; // (Kills a GCC warning)
- nrow = which_row(&row);
-
- // a) No completion if no leading slash ('cause not a command),
- // unless this is a room (then, it is a nickname completion)
- // b) We can't have more than 2 parameters (we use 2 flags)
- if ((nrow == -2) || (nrow == 3 && !completion_started) || nrow > 3)
- return;
-
- if (nrow == 0) { // Command completion
- row = next_char(inputLine);
- compl_categ = COMPL_CMD;
- } else if (nrow == -1) { // Nickname completion
- compl_categ = COMPL_RESOURCE;
- } else { // Other completion, depending on the command
- int alias = FALSE;
- cmd *com;
- char *xpline = expandalias(inputLine);
- com = cmd_get(xpline);
- if (xpline != inputLine) {
- // This is an alias, so we can't complete rows > 0
- alias = TRUE;
- g_free(xpline);
- }
- if ((!com && (!alias || !completion_started)) || !row) {
- scr_LogPrint(LPRINT_NORMAL, "I cannot complete that...");
- return;
- }
- if (!alias)
- compl_categ = com->completion_flags[nrow-1];
- else
- compl_categ = 0;
- }
-
- if (!completion_started) {
- guint dynlist;
- GSList *list = compl_get_category_list(compl_categ, &dynlist);
- if (list) {
- guint n;
- char *prefix = g_strndup(row, ptr_inputline-row);
- // Init completion
- n = new_completion(prefix, list);
- g_free(prefix);
- if (n == 0 && nrow == -1) {
- // This is a MUC room and we can't complete from the beginning of the
- // line. Let's try a bit harder and complete the current word.
- row = prev_char(ptr_inputline, inputLine);
- while (row >= inputLine) {
- if (iswspace(get_char(row)) || get_char(row) == '(') {
- row = next_char((char*)row);
- break;
- }
- if (row == inputLine)
- break;
- row = prev_char((char*)row, inputLine);
- }
- // There's no need to try again if row == inputLine
- if (row > inputLine) {
- prefix = g_strndup(row, ptr_inputline-row);
- new_completion(prefix, list);
- g_free(prefix);
- }
- }
- // Free the list if it's a dynamic one
- if (dynlist) {
- GSList *slp;
- for (slp = list; slp; slp = g_slist_next(slp))
- g_free(slp->data);
- g_slist_free(list);
- }
- // Now complete
- cchar = complete();
- if (cchar)
- scr_insert_text(cchar);
- completion_started = TRUE;
- }
- } else { // Completion already initialized
- scr_cancel_current_completion();
- // Now complete again
- cchar = complete();
- if (cchar)
- scr_insert_text(cchar);
- }
-}
-
-static void scr_cancel_current_completion(void)
-{
- char *c;
- char *src = ptr_inputline;
- guint back = cancel_completion();
- guint i;
- // Remove $back chars
- for (i = 0; i < back; i++)
- ptr_inputline = prev_char(ptr_inputline, inputLine);
- c = ptr_inputline;
- for ( ; *src ; )
- *c++ = *src++;
- *c = 0;
-}
-
-static void scr_end_current_completion(void)
-{
- done_completion();
- completion_started = FALSE;
-}
-
-// check_offset(int direction)
-// Check inputline_offset value, and make sure the cursor is inside the
-// screen.
-static inline void check_offset(int direction)
-{
- int i;
- char *c = &inputLine[inputline_offset];
- // Left side
- if (inputline_offset && direction <= 0) {
- while (ptr_inputline <= c) {
- for (i = 0; i < 5; i++)
- c = prev_char(c, inputLine);
- if (c == inputLine)
- break;
- }
- }
- // Right side
- if (direction >= 0) {
- int delta = get_char_width(c);
- while (ptr_inputline > c) {
- c = next_char(c);
- delta += get_char_width(c);
- }
- c = &inputLine[inputline_offset];
- while (delta >= maxX) {
- for (i = 0; i < 5; i++) {
- delta -= get_char_width(c);
- c = next_char(c);
- }
- }
- }
- inputline_offset = c - inputLine;
-}
-
-#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
-// prints inputLine with underlined words when misspelled
-static inline void print_checked_line(void)
-{
- char *wprint_char_fmt = "%c";
- int point;
- int nrchar = maxX;
- char *ptrCur = inputLine + inputline_offset;
-
-#ifdef UNICODE
- // We need this to display a single UTF-8 char... Any better solution?
- if (utf8_mode)
- wprint_char_fmt = "%lc";
-#endif
-
- wmove(inputWnd, 0, 0); // problem with backspace
-
- while (*ptrCur && nrchar-- > 0) {
- point = ptrCur - inputLine;
- if (maskLine[point])
- wattrset(inputWnd, A_UNDERLINE);
- wprintw(inputWnd, wprint_char_fmt, get_char(ptrCur));
- wattrset(inputWnd, A_NORMAL);
- ptrCur = next_char(ptrCur);
- }
-}
-#endif
-
-static inline void refresh_inputline(void)
-{
-#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
- if (settings_opt_get_int("spell_enable")) {
- memset(maskLine, 0, INPUTLINE_LENGTH+1);
- spellcheck(inputLine, maskLine);
- }
- print_checked_line();
- wclrtoeol(inputWnd);
- if (*ptr_inputline) {
- // hack to set cursor pos. Characters can have different width,
- // so I know of no better way.
- char c = *ptr_inputline;
- *ptr_inputline = 0;
- print_checked_line();
- *ptr_inputline = c;
- }
-#else
- mvwprintw(inputWnd, 0, 0, "%s", inputLine + inputline_offset);
- wclrtoeol(inputWnd);
- if (*ptr_inputline) {
- // hack to set cursor pos. Characters can have different width,
- // so I know of no better way.
- char c = *ptr_inputline;
- *ptr_inputline = 0;
- mvwprintw(inputWnd, 0, 0, "%s", inputLine + inputline_offset);
- *ptr_inputline = c;
- }
-#endif
-}
-
-void scr_handle_CtrlC(void)
-{
- if (!Curses) return;
- // Leave multi-line mode
- process_command(mkcmdstr("msay abort"), TRUE);
- // Same as Ctrl-g, now
- scr_cancel_current_completion();
- scr_end_current_completion();
- check_offset(-1);
- refresh_inputline();
-}
-
-static void add_keyseq(char *seqstr, guint mkeycode, gint value)
-{
- keyseq *ks;
-
- // Let's make sure the length is correct
- if (strlen(seqstr) > MAX_KEYSEQ_LENGTH) {
- scr_LogPrint(LPRINT_LOGNORM, "add_keyseq(): key sequence is too long!");
- return;
- }
-
- ks = g_new0(keyseq, 1);
- ks->seqstr = g_strdup(seqstr);
- ks->mkeycode = mkeycode;
- ks->value = value;
- keyseqlist = g_slist_append(keyseqlist, ks);
-}
-
-// match_keyseq(iseq, &ret)
-// Check if "iseq" is a known key escape sequence.
-// Return value:
-// -1 if "seq" matches no known sequence
-// 0 if "seq" could match 1 or more known sequences
-// >0 if "seq" matches a key sequence; the mkey code is returned
-// and *ret is set to the matching keyseq structure.
-static inline gint match_keyseq(int *iseq, keyseq **ret)
-{
- GSList *ksl;
- keyseq *ksp;
- char *p, c;
- int *i;
- int needmore = FALSE;
-
- for (ksl = keyseqlist; ksl; ksl = g_slist_next(ksl)) {
- ksp = ksl->data;
- p = ksp->seqstr;
- i = iseq;
- while (1) {
- c = (unsigned char)*i;
- if (!*p && !c) { // Match
- (*ret) = ksp;
- return ksp->mkeycode;
- }
- if (!c) {
- // iseq is too short
- needmore = TRUE;
- break;
- } else if (!*p || c != *p) {
- // This isn't a match
- break;
- }
- p++; i++;
- }
- }
-
- if (needmore)
- return 0;
- return -1;
-}
-
-static inline int match_utf8_keyseq(int *iseq)
-{
- int *strp = iseq;
- unsigned c = *strp++;
- unsigned mask = 0x80;
- int len = -1;
- while (c & mask) {
- mask >>= 1;
- len++;
- }
- if (len <= 0 || len > 4)
- return -1;
- c &= mask - 1;
- while ((*strp & 0xc0) == 0x80) {
- if (len-- <= 0) // can't happen
- return -1;
- c = (c << 6) | (*strp++ & 0x3f);
- }
- if (len)
- return 0;
- return c;
-}
-
-void scr_Getch(keycode *kcode)
-{
- keyseq *mks = NULL;
- int ks[MAX_KEYSEQ_LENGTH+1];
- int i;
-
- memset(kcode, 0, sizeof(keycode));
- memset(ks, 0, sizeof(ks));
-
- kcode->value = wgetch(inputWnd);
- if (utf8_mode) {
- bool ismeta = (kcode->value == 27);
-#ifdef NCURSES_MOUSE_VERSION
- bool ismouse = (kcode->value == KEY_MOUSE);
-
- if (ismouse) {
- MEVENT mouse;
- getmouse(&mouse);
- kcode->value = mouse.bstate;
- kcode->mcode = MKEY_MOUSE;
- return;
- } else if (ismeta)
-#else
- if (ismeta)
-#endif
- ks[0] = wgetch(inputWnd);
- else
- ks[0] = kcode->value;
-
- for (i = 0; i < MAX_KEYSEQ_LENGTH - 1; i++) {
- int match = match_utf8_keyseq(ks);
- if (match == -1)
- break;
- if (match > 0) {
- kcode->value = match;
- kcode->utf8 = 1;
- if (ismeta)
- kcode->mcode = MKEY_META;
- return;
- }
- ks[i + 1] = wgetch(inputWnd);
- if (ks[i + 1] == ERR)
- break;
- }
- while (i > 0)
- ungetch(ks[i--]);
- if (ismeta)
- ungetch(ks[0]);
- memset(ks, 0, sizeof(ks));
- }
- if (kcode->value != 27)
- return;
-
- // Check for escape key sequence
- for (i=0; i < MAX_KEYSEQ_LENGTH; i++) {
- int match;
- ks[i] = wgetch(inputWnd);
- if (ks[i] == ERR) break;
- match = match_keyseq(ks, &mks);
- if (match == -1) {
- // No such key sequence. Let's increment i as it is a valid key.
- i++;
- break;
- }
- if (match > 0) {
- // We have a matching sequence
- kcode->mcode = mks->mkeycode;
- kcode->value = mks->value;
- return;
- }
- }
-
- // No match. Let's return a meta-key.
- if (i > 0) {
- kcode->mcode = MKEY_META;
- kcode->value = ks[0];
- }
- if (i > 1) {
- // We need to push some keys back to the keyboard buffer
- while (i-- > 1)
- ungetch(ks[i]);
- }
- return;
-}
-
-void scr_DoUpdate(void)
-{
- doupdate();
-}
-
-static int bindcommand(keycode kcode)
-{
- gchar asciikey[16], asciicode[16];
- const gchar *boundcmd;
-
- if (kcode.utf8)
- g_snprintf(asciicode, 15, "U%d", kcode.value);
- else
- g_snprintf(asciicode, 15, "%d", kcode.value);
-
- if (!kcode.mcode || kcode.mcode == MKEY_EQUIV)
- g_snprintf(asciikey, 15, "%s", asciicode);
- else if (kcode.mcode == MKEY_META)
- g_snprintf(asciikey, 15, "M%s", asciicode);
- else if (kcode.mcode == MKEY_MOUSE)
- g_snprintf(asciikey, 15, "p%s", asciicode);
- else
- g_snprintf(asciikey, 15, "MK%d", kcode.mcode);
-
- boundcmd = settings_get(SETTINGS_TYPE_BINDING, asciikey);
-
- if (boundcmd) {
- gchar *cmdline = from_utf8(boundcmd);
- scr_CheckAutoAway(TRUE);
- if (process_command(cmdline, TRUE))
- return 255; // Quit
- g_free(cmdline);
- return 0;
- }
-
- scr_LogPrint(LPRINT_NORMAL, "Unknown key=%s", asciikey);
-#ifndef UNICODE
- if (utf8_mode)
- scr_LogPrint(LPRINT_NORMAL,
- "WARNING: Compiled without full UTF-8 support!");
-#endif
- return -1;
-}
-
-// process_key(key)
-// Handle the pressed key, in the command line (bottom).
-void process_key(keycode kcode)
-{
- int key = kcode.value;
- int display_char = FALSE;
-
- lock_chatstate = FALSE;
-
- switch (kcode.mcode) {
- case 0:
- break;
- case MKEY_EQUIV:
- key = kcode.value;
- break;
- case MKEY_META:
- default:
- if (bindcommand(kcode) == 255) {
- mcabber_set_terminate_ui();
- return;
- }
- key = ERR; // Do not process any further
- }
-
- if (kcode.utf8) {
- if (key != ERR && !kcode.mcode)
- display_char = TRUE;
- goto display;
- }
-
- switch (key) {
- case 0:
- case ERR:
- break;
- case 9: // Tab
- readline_do_completion();
- break;
- case 13: // Enter
- if (readline_accept_line(FALSE) == 255) {
- mcabber_set_terminate_ui();
- return;
- }
- break;
- case 3: // Ctrl-C
- scr_handle_CtrlC();
- break;
- case KEY_RESIZE:
-#ifdef USE_SIGWINCH
- {
- struct winsize size;
- if (ioctl(STDIN_FILENO, TIOCGWINSZ, &size) != -1)
- resizeterm(size.ws_row, size.ws_col);
- }
-#endif
- scr_Resize();
- break;
- default:
- display_char = TRUE;
- } // switch
-
-display:
- if (display_char) {
- guint printable;
-
- if (kcode.utf8) {
- printable = iswprint(key);
- } else {
-#ifdef __CYGWIN__
- printable = (isprint(key) || (key >= 161 && key <= 255))
- && !is_speckey(key);
-#else
- printable = isprint(key) && !is_speckey(key);
-#endif
- }
- if (printable) {
- char tmpLine[INPUTLINE_LENGTH+1];
-
- // Check the line isn't too long
- if (strlen(inputLine) + 4 > INPUTLINE_LENGTH)
- return;
-
- // Insert char
- strcpy(tmpLine, ptr_inputline);
- ptr_inputline = put_char(ptr_inputline, key);
- strcpy(ptr_inputline, tmpLine);
- check_offset(1);
- } else {
- // Look for a key binding.
- if (!kcode.utf8 && (bindcommand(kcode) == 255)) {
- mcabber_set_terminate_ui();
- return;
- }
- }
- }
-
- if (completion_started && key != 9 && key != KEY_RESIZE)
- scr_end_current_completion();
- refresh_inputline();
-
- if (!lock_chatstate) {
- // Set chat state to composing (1) if the user is currently composing,
- // i.e. not an empty line and not a command line.
- if (inputLine[0] == 0 || inputLine[0] == COMMAND_CHAR)
- set_chatstate(0);
- else
- set_chatstate(1);
- if (chatstate)
- time(&chatstate_timestamp);
- }
- return;
-}
-
-#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
-// initialization
-void spellcheck_init(void)
-{
- int spell_enable = settings_opt_get_int("spell_enable");
- const char *spell_lang = settings_opt_get("spell_lang");
-#ifdef WITH_ASPELL
- const char *spell_encoding = settings_opt_get("spell_encoding");
- AspellCanHaveError *possible_err;
-#endif
-
- if (!spell_enable)
- return;
-
-#ifdef WITH_ENCHANT
- if (spell_checker) {
- enchant_broker_free_dict(spell_broker, spell_checker);
- enchant_broker_free(spell_broker);
- spell_checker = NULL;
- spell_broker = NULL;
- }
-
- spell_broker = enchant_broker_init();
- spell_checker = enchant_broker_request_dict(spell_broker, spell_lang);
-#endif
-#ifdef WITH_ASPELL
- if (spell_checker) {
- delete_aspell_speller(spell_checker);
- delete_aspell_config(spell_config);
- spell_checker = NULL;
- spell_config = NULL;
- }
-
- spell_config = new_aspell_config();
- aspell_config_replace(spell_config, "encoding", spell_encoding);
- aspell_config_replace(spell_config, "lang", spell_lang);
- possible_err = new_aspell_speller(spell_config);
-
- if (aspell_error_number(possible_err) != 0) {
- spell_checker = NULL;
- delete_aspell_config(spell_config);
- spell_config = NULL;
- } else {
- spell_checker = to_aspell_speller(possible_err);
- }
-#endif
-}
-
-// Deinitialization of spellchecker
-void spellcheck_deinit(void)
-{
- if (spell_checker) {
-#ifdef WITH_ENCHANT
- enchant_broker_free_dict(spell_broker, spell_checker);
-#endif
-#ifdef WITH_ASPELL
- delete_aspell_speller(spell_checker);
-#endif
- spell_checker = NULL;
- }
-
-#ifdef WITH_ENCHANT
- if (spell_broker) {
- enchant_broker_free(spell_broker);
- spell_broker = NULL;
- }
-#endif
-#ifdef WITH_ASPELL
- if (spell_config) {
- delete_aspell_config(spell_config);
- spell_config = NULL;
- }
-#endif
-}
-
-#define spell_isalpha(c) (utf8_mode ? iswalpha(get_char(c)) : isalpha(*c))
-
-// Spell checking function
-static void spellcheck(char *line, char *checked)
-{
- const char *start, *line_start;
-
- if (inputLine[0] == 0 || inputLine[0] == COMMAND_CHAR)
- return;
-
- line_start = line;
-
- while (*line) {
-
- if (!spell_isalpha(line)) {
- line = next_char(line);
- continue;
- }
-
- if (!strncmp(line, "http://", 7)) {
- line += 7; // : and / characters are 1 byte long in utf8, right?
-
- while (!strchr(" \t\r\n", *line))
- line = next_char(line); // i think line++ would be fine here?
-
- continue;
- }
-
- if (!strncmp(line, "ftp://", 6)) {
- line += 6;
-
- while (!strchr(" \t\r\n", *line))
- line = next_char(line);
-
- continue;
- }
-
- start = line;
-
- while (spell_isalpha(line))
- line = next_char(line);
-
- if (spell_checker &&
-#ifdef WITH_ENCHANT
- enchant_dict_check(spell_checker, start, line - start) != 0
-#endif
-#ifdef WITH_ASPELL
- aspell_speller_check(spell_checker, start, line - start) == 0
-#endif
- )
- memset(&checked[start - line_start], SPELLBADCHAR, line - start);
- }
-}
-#endif
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/screen.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-#ifndef __SCREEN_H__
-#define __SCREEN_H__ 1
-
-#include <glib.h>
-
-#if HAVE_NCURSESW_NCURSES_H
-# include <ncursesw/ncurses.h>
-# include <ncursesw/panel.h>
-#elif HAVE_NCURSES_NCURSES_H
-# include <ncurses/ncurses.h>
-# include <ncurses/panel.h>
-#else
-# include <ncurses.h>
-# include <panel.h>
-#endif
-
-#if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
-void spellcheck_init(void);
-void spellcheck_deinit(void);
-//static void spellcheck(char*, char*);
-#endif
-
-#include "hbuf.h"
-#include "logprint.h"
-#include "roster.h"
-
-#define INPUTLINE_LENGTH 1024
-
-// Only used in screen.c; this is the maximum line number
-// in a multi-line message. Should be < 1000
-// Note: message length is limited by the HBB_BLOCKSIZE size too
-#define MULTILINE_MAX_LINE_NUMBER 299
-
-// When chatstates are enabled, timeout (in seconds) before "composing"
-// becomes "paused" because of user inactivity.
-// Warning: setting this very low will cause more network traffic.
-#define COMPOSING_TIMEOUT 6L
-
-enum colors {
- COLOR_GENERAL = 3,
- COLOR_MSGOUT,
- COLOR_MSGHL,
- COLOR_STATUS,
- COLOR_ROSTER,
- COLOR_ROSTERSEL,
- COLOR_ROSTERSELNMSG,
- COLOR_ROSTERNMSG,
- COLOR_INFO,
- COLOR_MSGIN,
- COLOR_max
-};
-
-int COLOR_ATTRIB[COLOR_max];
-
-extern int update_roster;
-
-typedef struct {
- int value;
- int utf8;
- enum {
- MKEY_META = 1,
- MKEY_EQUIV,
- MKEY_CTRL_PGUP,
- MKEY_CTRL_PGDOWN,
- MKEY_SHIFT_PGUP,
- MKEY_SHIFT_PGDOWN,
- MKEY_CTRL_SHIFT_PGUP,
- MKEY_CTRL_SHIFT_PGDOWN,
- MKEY_CTRL_HOME,
- MKEY_CTRL_END,
- MKEY_CTRL_INS,
- MKEY_CTRL_DEL,
- MKEY_CTRL_SHIFT_HOME,
- MKEY_CTRL_SHIFT_END,
- MKEY_MOUSE
- } mcode;
-} keycode;
-
-typedef enum {
- MC_ALL,
- MC_PRESET,
- MC_OFF,
- MC_REMOVE
-} muccoltype;
-
-void scr_init_bindings(void);
-
-void scr_Getch(keycode *kcode);
-void process_key(keycode kcode);
-
-void scr_InitLocaleCharSet(void);
-void scr_InitCurses(void);
-void scr_TerminateCurses(void);
-void scr_DrawMainWindow(unsigned int fullinit);
-void scr_DrawRoster(void);
-void scr_UpdateMainStatus(int forceupdate);
-void scr_UpdateChatStatus(int forceupdate);
-void scr_RosterVisibility(int status);
-void scr_WriteIncomingMessage(const char *jidfrom, const char *text,
- time_t timestamp, guint prefix,
- unsigned mucnicklen);
-void scr_WriteOutgoingMessage(const char *jidto, const char *text,
- guint prefix, gpointer xep184);
-void scr_RemoveReceiptFlag(const char *jidto, gpointer xep184);
-void scr_ShowBuddyWindow(void);
-int scr_BuddyBufferExists(const char *jid);
-void scr_UpdateBuddyWindow(void);
-void scr_set_chatmode(int enable);
-int scr_get_chatmode(void);
-void scr_set_multimode(int enable, char *subject);
-int scr_get_multimode(void);
-void scr_setmsgflag_if_needed(const char *jid, int special);
-void scr_append_multiline(const char *line);
-const char *scr_get_multiline(void);
-const char *scr_get_multimode_subj(void);
-
-guint scr_getprefixwidth(void);
-void scr_line_prefix(hbb_line *line, char *prefix, guint preflen);
-
-void scr_Beep(void);
-
-bool Autoaway;
-
-void scr_CheckAutoAway(int activity);
-
-#if defined JEP0022 || defined JEP0085
-gboolean scr_ChatStatesTimeout();
-#endif
-int chatstates_disabled;
-
-// For commands...
-void scr_RosterTop(void);
-void scr_RosterBottom(void);
-void scr_RosterUpDown(int updown, unsigned int n);
-void scr_RosterPrevGroup(void);
-void scr_RosterNextGroup(void);
-void scr_RosterSearch(char *);
-void scr_RosterJumpJid(char *);
-void scr_RosterDisplay(const char *);
-void scr_BufferTopBottom(int topbottom);
-void scr_BufferClear(void);
-void scr_BufferScrollLock(int lock);
-void scr_BufferPurge(int, const char*);
-void scr_BufferPurgeAll(int);
-void scr_BufferSearch(int direction, const char *text);
-void scr_BufferPercent(int pc);
-void scr_BufferDate(time_t t);
-void scr_BufferDump(const char *file);
-void scr_RosterUnreadMessage(int);
-void scr_RosterJumpAlternate(void);
-void scr_BufferScrollUpDown(int updown, unsigned int nblines);
-bool scr_RosterColor(const char *status, const char *wildcard,
- const char *color);
-void scr_RosterClearColor(void);
-void scr_MucColor(const char *muc, muccoltype type);
-void scr_MucNickColor(const char *nick, const char *color);
-void scr_BufferList(void);
-
-void readline_transpose_chars(void);
-void readline_forward_kill_word(void);
-void readline_backward_kill_word(void);
-void readline_backward_word(void);
-void readline_forward_word(void);
-void readline_updowncase_word(int);
-void readline_capitalize_word(void);
-void readline_backward_char(void);
-void readline_forward_char(void);
-int readline_accept_line(int down_history);
-void readline_cancel_completion(void);
-void readline_do_completion(void);
-void readline_refresh_screen(void);
-void readline_disable_chat_mode(guint show_roster);
-void readline_hist_beginning_search_bwd(void);
-void readline_hist_beginning_search_fwd(void);
-void readline_hist_prev(void);
-void readline_hist_next(void);
-void readline_backward_kill_char(void);
-void readline_forward_kill_char(void);
-void readline_iline_start(void);
-void readline_iline_end(void);
-void readline_backward_kill_iline(void);
-void readline_forward_kill_iline(void);
-void readline_send_multiline(void);
-
-#endif
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/settings.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,609 +0,0 @@
-/*
- * settings.c -- Configuration stuff
- *
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "config.h"
-#include "settings.h"
-#include "commands.h"
-#include "logprint.h"
-#include "otr.h"
-#include "utils.h"
-#include "xmpp.h"
-#include "main.h"
-
-// Maximum line length
-// (probably best to use the same value as INPUTLINE_LENGTH)
-#define CONFLINE_LENGTH 1024
-
-static GHashTable *option;
-static GHashTable *alias;
-static GHashTable *binding;
-
-#ifdef HAVE_GPGME /* PGP settings */
-static GHashTable *pgpopt;
-
-typedef struct {
- gchar *pgp_keyid; /* KeyId the contact is supposed to use */
- guint pgp_disabled; /* If TRUE, PGP is disabled for outgoing messages */
- guint pgp_force; /* If TRUE, PGP is used w/o negotiation */
-} T_pgpopt;
-#endif
-
-#ifdef HAVE_LIBOTR
-static GHashTable *otrpolicy;
-static enum otr_policy default_policy;
-#endif
-
-static inline GHashTable *get_hash(guint type)
-{
- if (type == SETTINGS_TYPE_OPTION) return option;
- else if (type == SETTINGS_TYPE_ALIAS) return alias;
- else if (type == SETTINGS_TYPE_BINDING) return binding;
-#ifdef HAVE_LIBOTR
- else if (type == SETTINGS_TYPE_OTR) return otrpolicy;
-#endif
- return NULL;
-}
-
-/* -- */
-
-void settings_init(void)
-{
- option = g_hash_table_new_full(&g_str_hash, &g_str_equal, &g_free, &g_free);
- alias = g_hash_table_new_full(&g_str_hash, &g_str_equal, &g_free, &g_free);
- binding = g_hash_table_new_full(&g_str_hash, &g_str_equal, &g_free, &g_free);
-#ifdef HAVE_GPGME
- pgpopt = g_hash_table_new(&g_str_hash, &g_str_equal);
-#endif
-#ifdef HAVE_LIBOTR
- otrpolicy = g_hash_table_new(&g_str_hash, &g_str_equal);
-#endif
-}
-
-// cfg_read_file(filename, mainfile)
-// Read and parse config file "filename". If filename is NULL,
-// try to open the configuration file at the default locations.
-// mainfile must be set to TRUE for the startup config file.
-// If mainfile is TRUE, the permissions of the configuration file will
-// be fixed if they're insecure.
-//
-int cfg_read_file(char *filename, guint mainfile)
-{
- static unsigned int runtime;
- FILE *fp;
- char *buf;
- char *line, *eol;
- unsigned int ln = 0;
- int err = 0;
-
- if (!filename) {
- // Use default config file locations
- char *home;
- GString *sfilename;
-
- if (!mainfile) {
- scr_LogPrint(LPRINT_LOGNORM, "No file name provided");
- return -1;
- }
-
- home = getenv("HOME");
- if (!home) {
- scr_LogPrint(LPRINT_LOG, "Can't find home dir!");
- fprintf(stderr, "Can't find home dir!\n");
- err = -1;
- goto cfg_read_file_return;
- }
- sfilename = g_string_new("");
- g_string_printf(sfilename, "%s/.mcabber/mcabberrc", home);
- if ((fp = fopen(sfilename->str, "r")) == NULL) {
- // 2nd try...
- g_string_printf(sfilename, "%s/.mcabberrc", home);
- if ((fp = fopen(sfilename->str, "r")) == NULL) {
- fprintf(stderr, "Cannot open config file!\n");
- g_string_free(sfilename, TRUE);
- err = -1;
- goto cfg_read_file_return;
- }
- }
- // Check configuration file permissions
- // As it could contain sensitive data, we make it user-readable only.
- checkset_perm(sfilename->str, TRUE);
- scr_LogPrint(LPRINT_LOGNORM, "Reading %s", sfilename->str);
- // Check mcabber dir. Here we just warn, we don't change the modes.
- g_string_printf(sfilename, "%s/.mcabber/", home);
- checkset_perm(sfilename->str, FALSE);
- g_string_free(sfilename, TRUE);
- } else {
- // filename was specified
- if ((fp = fopen(filename, "r")) == NULL) {
- const char *msg = "Cannot open configuration file";
- if (mainfile)
- perror(msg);
- else
- scr_LogPrint(LPRINT_LOGNORM, "%s (%s).", msg, filename);
- err = -2;
- goto cfg_read_file_return;
- }
- // Check configuration file permissions (see above)
- // We don't change the permissions if that's not the main file.
- if (mainfile)
- checkset_perm(filename, TRUE);
- scr_LogPrint(LPRINT_LOGNORM, "Reading %s", filename);
- }
-
- buf = g_new(char, CONFLINE_LENGTH+1);
-
- while (fgets(buf+1, CONFLINE_LENGTH, fp) != NULL) {
- // The first char is reserved to add a '/', to make a command line
- line = buf+1;
- ln++;
-
- // Strip leading spaces
- while (isspace(*line))
- line++;
-
- // Make eol point to the last char of the line
- for (eol = line ; *eol ; eol++)
- ;
- if (eol > line)
- eol--;
-
- // Strip trailing spaces
- while (eol > line && isspace(*eol))
- *eol-- = 0;
-
- // Ignore empty lines and comments
- if ((*line == '\n') || (*line == '\0') || (*line == '#'))
- continue;
-
- // We only allow assignments line, except for commands "pgp", "source",
- // "color", "load" and "otrpolicy", unless we're in runtime (i.e. not startup).
- if (runtime ||
- (strchr(line, '=') != NULL) ||
- startswith(line, "pgp ", FALSE) ||
- startswith(line, "source ", FALSE) ||
- startswith(line, "color ", FALSE) ||
-#ifdef MODULES_ENABLE
- startswith(line, "load ", FALSE) ||
-#endif
- startswith(line, "otrpolicy", FALSE)) {
- // Only accept a few "safe" commands
- if (!runtime &&
- !startswith(line, "set ", FALSE) &&
- !startswith(line, "bind ", FALSE) &&
- !startswith(line, "alias ", FALSE) &&
- !startswith(line, "pgp ", FALSE) &&
- !startswith(line, "source ", FALSE) &&
- !startswith(line, "color ", FALSE) &&
-#ifdef MODULES_ENABLE
- !startswith(line, "load ", FALSE) &&
-#endif
- !startswith(line, "otrpolicy ", FALSE)) {
- scr_LogPrint(LPRINT_LOGNORM, "Error in configuration file (l. %d): "
- "this command can't be used here", ln);
- err++;
- continue;
- }
- // Set the leading COMMAND_CHAR to build a command line
- // and process the command
- *(--line) = COMMAND_CHAR;
- if (process_command(line, TRUE) == 255)
- mcabber_set_terminate_ui();
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "Error in configuration file (l. %d): "
- "this is not an assignment", ln);
- err++;
- }
- }
- g_free(buf);
- fclose(fp);
-
- if (filename)
- scr_LogPrint(LPRINT_LOGNORM, "Loaded %s.", filename);
-
-cfg_read_file_return:
- // If we're done with the main file parsing, we can assume that
- // the next time this function is called will be at run time.
- if (mainfile)
- runtime = TRUE;
- return err;
-}
-
-// parse_assigment(assignment, pkey, pval)
-// Read assignment and split it to key, value
-//
-// If this is an assignment, the function will return TRUE and
-// set *pkey and *pval (*pval is set to NULL if value field is empty).
-//
-// If this isn't a assignment (no = char), the function will set *pval
-// to NULL and return FALSE.
-//
-// The caller should g_free() *pkey and *pval (if not NULL) after use.
-guint parse_assigment(gchar *assignment, gchar **pkey, gchar **pval)
-{
- char *key, *val, *t, *p;
-
- *pkey = *pval = NULL;
-
- key = assignment;
- // Remove leading spaces in option name
- while ((!isalnum(*key)) && (*key != '=') && *key) {
- //if (!isblank(*key))
- // scr_LogPrint("Error in assignment parsing!");
- key++;
- }
- if (!*key) return FALSE; // Empty assignment
-
- if (*key == '=') {
- //scr_LogPrint("Cannot parse assignment!");
- return FALSE;
- }
- // Ok, key points to the option name
-
- for (val = key+1 ; *val && (*val != '=') ; val++)
- if (!isalnum(*val) && !isblank(*val) && (*val != '_') && (*val != '-')) {
- // Key should only have alnum chars...
- //scr_LogPrint("Error in assignment parsing!");
- return FALSE;
- }
- // Remove trailing spaces in option name:
- for (t = val-1 ; t > key && isblank(*t) ; t--)
- ;
- // Check for embedded whitespace characters
- for (p = key; p < t; p++) {
- if (isblank(*p)) {
- //scr_LogPrint("Error in assignment parsing!"
- // " (Name should not contain space chars)");
- return FALSE;
- }
- }
-
- *pkey = g_strndup(key, t+1-key);
-
- if (!*val) return FALSE; // Not an assignment
-
- // Remove leading and trailing spaces in option value:
- for (val++; *val && isblank(*val) ; val++) ;
- for (t = val ; *t ; t++) ;
- for (t-- ; t >= val && isblank(*t) ; t--) ;
-
- if (t < val) return TRUE; // no value (variable reset for example)
-
- // If the value begins and ends with quotes ("), these quotes are
- // removed and whitespace is not stripped
- if ((t>val) && (*val == '"' && *t == '"')) {
- val++;
- t--;
- }
- *pval = g_strndup(val, t+1-val);
- return TRUE;
-}
-
-void settings_set(guint type, const gchar *key, const gchar *value)
-{
- GHashTable *hash;
-
- hash = get_hash(type);
- if (!hash)
- return;
-
- if (!value) {
- g_hash_table_remove(hash, key);
- } else {
- g_hash_table_insert(hash, g_strdup(key), g_strdup(value));
- }
-}
-
-void settings_del(guint type, const gchar *key)
-{
- settings_set(type, key, NULL);
-}
-
-const gchar *settings_get(guint type, const gchar *key)
-{
- GHashTable *hash;
-
- hash = get_hash(type);
- if (!hash)
- return NULL;
-
- return g_hash_table_lookup(hash, key);
-}
-
-int settings_get_int(guint type, const gchar *key)
-{
- const gchar *setval = settings_get(type, key);
-
- if (setval) return atoi(setval);
- return 0;
-}
-
-// settings_get_status_msg(status)
-// Return a string with the current status message:
-// - if there is a user-defined message ("message" option),
-// return this message
-// - if there is a user-defined message for the given status (and no
-// generic user message), it is returned
-// - if no message is found, return NULL
-const gchar *settings_get_status_msg(enum imstatus status)
-{
- const gchar *rstatus = settings_opt_get("message");
-
- if (rstatus) return rstatus;
-
- switch(status) {
- case available:
- rstatus = settings_opt_get("message_avail");
- break;
-
- case freeforchat:
- rstatus = settings_opt_get("message_free");
- break;
-
- case dontdisturb:
- rstatus = settings_opt_get("message_dnd");
- break;
-
- case notavail:
- rstatus = settings_opt_get("message_notavail");
- break;
-
- case away:
- rstatus = settings_opt_get("message_away");
- break;
-
- default: // offline, invisible
- break;
- }
- return rstatus;
-}
-
-// settings_foreach(type, pfunction, param)
-// Call pfunction(key, value, param) for each setting with requested type.
-void settings_foreach(guint type, void (*pfunc)(char *k, char *v, void *param),
- void *param)
-{
- GHashTable *hash;
-
- hash = get_hash(type);
- if (!hash)
- return;
-
- g_hash_table_foreach(hash, (GHFunc)pfunc, param);
-}
-
-
-// default_muc_nickname()
-// Return the user's default nickname
-// The caller should free the string after use
-char *default_muc_nickname(const char *roomid)
-{
- char *nick;
-
- nick = (char*)xmpp_get_bookmark_nick(roomid);
- if (nick)
- return g_strdup(nick);
-
- // We try the "nickname" option, then the username part of the jid.
- nick = (char*)settings_opt_get("nickname");
- if (nick)
- return g_strdup(nick);
-
- nick = jid_get_username(settings_opt_get("jid"));
- return nick;
-}
-
-
-/* PGP settings */
-
-// settings_pgp_setdisabled(jid, value)
-// Enable/disable PGP encryption for jid.
-// (Set value to TRUE to disable encryption)
-void settings_pgp_setdisabled(const char *bjid, guint value)
-{
-#ifdef HAVE_GPGME
- T_pgpopt *pgpdata;
- pgpdata = g_hash_table_lookup(pgpopt, bjid);
- if (!pgpdata) {
- // If value is 0, we do not need to create a structure (that's
- // the default value).
- if (value) {
- pgpdata = g_new0(T_pgpopt, 1);
- pgpdata->pgp_disabled = value;
- g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
- }
- } else {
- pgpdata->pgp_disabled = value;
- // We could remove the key/value if pgp_disabled is 0 and
- // pgp_keyid is NULL, actually.
- }
-#endif
-}
-
-// settings_pgp_getdisabled(jid)
-// Return TRUE if PGP encryption should be disabled for jid.
-guint settings_pgp_getdisabled(const char *bjid)
-{
-#ifdef HAVE_GPGME
- T_pgpopt *pgpdata;
- pgpdata = g_hash_table_lookup(pgpopt, bjid);
- if (pgpdata)
- return pgpdata->pgp_disabled;
- else
- return FALSE; // Default: not disabled
-#else
- return TRUE; // No PGP support, let's say it's disabled.
-#endif
-}
-
-// settings_pgp_setforce(jid, value)
-// Force (or not) PGP encryption for jid.
-// When value is TRUE, PGP support will be assumed for the remote client.
-void settings_pgp_setforce(const char *bjid, guint value)
-{
-#ifdef HAVE_GPGME
- T_pgpopt *pgpdata;
- pgpdata = g_hash_table_lookup(pgpopt, bjid);
- if (!pgpdata) {
- // If value is 0, we do not need to create a structure (that's
- // the default value).
- if (value) {
- pgpdata = g_new0(T_pgpopt, 1);
- pgpdata->pgp_force = value;
- g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
- }
- } else {
- pgpdata->pgp_force = value;
- }
- if (value && pgpdata && !pgpdata->pgp_keyid)
- scr_LogPrint(LPRINT_NORMAL, "Warning: the Key Id is not set!");
-#endif
-}
-
-// settings_pgp_getforce(jid)
-// Return TRUE if PGP enforcement is set for jid.
-guint settings_pgp_getforce(const char *bjid)
-{
-#ifdef HAVE_GPGME
- T_pgpopt *pgpdata;
- pgpdata = g_hash_table_lookup(pgpopt, bjid);
- if (pgpdata)
- return pgpdata->pgp_force;
- else
- return FALSE; // Default
-#else
- return FALSE; // No PGP support
-#endif
-}
-
-// settings_pgp_setkeyid(jid, keyid)
-// Set the PGP KeyId for user jid.
-// Use keyid = NULL to erase the previous KeyId.
-void settings_pgp_setkeyid(const char *bjid, const char *keyid)
-{
-#ifdef HAVE_GPGME
- T_pgpopt *pgpdata;
- pgpdata = g_hash_table_lookup(pgpopt, bjid);
- if (!pgpdata) {
- // If keyid is NULL, we do not need to create a structure (that's
- // the default value).
- if (keyid) {
- pgpdata = g_new0(T_pgpopt, 1);
- pgpdata->pgp_keyid = g_strdup(keyid);
- g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
- }
- } else {
- g_free(pgpdata->pgp_keyid);
- if (keyid)
- pgpdata->pgp_keyid = g_strdup(keyid);
- else
- pgpdata->pgp_keyid = NULL;
- // We could remove the key/value if pgp_disabled is 0 and
- // pgp_keyid is NULL, actually.
- }
-#endif
-}
-
-// settings_pgp_getkeyid(jid)
-// Get the PGP KeyId for user jid.
-const char *settings_pgp_getkeyid(const char *bjid)
-{
-#ifdef HAVE_GPGME
- T_pgpopt *pgpdata;
- pgpdata = g_hash_table_lookup(pgpopt, bjid);
- if (pgpdata)
- return pgpdata->pgp_keyid;
-#endif
- return NULL;
-}
-
-/* otr settings */
-
-#ifdef HAVE_LIBOTR
-static void remove_default_policies(char *k, char *policy, void *defaultp)
-{
- if (*(enum otr_policy *)policy == *(enum otr_policy *)defaultp) {
- g_free((enum otr_policy *) policy);
- g_hash_table_remove(otrpolicy, k);
- }
-}
-#endif
-
-void settings_otr_setpolicy(const char *bjid, guint value)
-{
-#ifdef HAVE_LIBOTR
- enum otr_policy *otrdata;
-
- if (!bjid) {
- default_policy = value;
- /* refresh hash */
- settings_foreach(SETTINGS_TYPE_OTR, &remove_default_policies, &value);
- return;
- }
-
- otrdata = g_hash_table_lookup(otrpolicy, bjid);
-
- if (value == default_policy) {
- if (otrdata) {
- g_free(otrdata);
- g_hash_table_remove(otrpolicy, bjid);
- }
- } else if (otrdata) {
- *otrdata = value;
- } else {
- otrdata = g_new(enum otr_policy, 1);
- *otrdata = value;
- g_hash_table_insert(otrpolicy, g_strdup(bjid), otrdata);
- }
-#endif
-}
-
-guint settings_otr_getpolicy(const char *bjid)
-{
-#ifdef HAVE_LIBOTR
- enum otr_policy *otrdata;
- if (!bjid)
- return default_policy;
-
- otrdata = g_hash_table_lookup(otrpolicy, bjid);
- if (otrdata)
- return *otrdata;
- else
- return default_policy;
-#else
- return 0;
-#endif
-}
-
-guint get_max_history_blocks(void)
-{
- int max_num_of_blocks = settings_opt_get_int("max_history_blocks");
- if (max_num_of_blocks < 0)
- max_num_of_blocks = 0;
- else if (max_num_of_blocks == 1)
- max_num_of_blocks = 2;
- return (guint)max_num_of_blocks;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/settings.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-#ifndef __SETTINGS_H__
-#define __SETTINGS_H__ 1
-
-#include <ctype.h>
-#include <glib.h>
-
-#include "roster.h"
-
-#ifndef isblank
-# define isblank(c) ((c) == 0x20 || (c) == 0x09)
-#endif
-
-
-#define SETTINGS_TYPE_OPTION 1
-#define SETTINGS_TYPE_ALIAS 2
-#define SETTINGS_TYPE_BINDING 3
-#ifdef HAVE_LIBOTR
-#define SETTINGS_TYPE_OTR 4
-#endif
-
-#define COMMAND_CHAR '/'
-#define COMMAND_CHARSTR "/"
-
-#define settings_opt_get(k) settings_get(SETTINGS_TYPE_OPTION, k)
-#define settings_opt_get_int(k) settings_get_int(SETTINGS_TYPE_OPTION, k)
-
-#define mkcmdstr(cmd) COMMAND_CHARSTR cmd
-
-void settings_init(void);
-int cfg_read_file(char *filename, guint mainfile);
-guint parse_assigment(gchar *assignment, gchar **pkey, gchar **pval);
-void settings_set(guint type, const gchar *key, const gchar *value);
-void settings_del(guint type, const gchar *key);
-const gchar *settings_get(guint type, const gchar *key);
-int settings_get_int(guint type, const gchar *key);
-const gchar *settings_get_status_msg(enum imstatus status);
-void settings_foreach(guint type,
- void (*pfunc)(char *k, char *v, void *param),
- void *param);
-
-void settings_pgp_setdisabled(const char *bjid, guint value);
-guint settings_pgp_getdisabled(const char *bjid);
-void settings_pgp_setforce(const char *bjid, guint value);
-guint settings_pgp_getforce(const char *bjid);
-void settings_pgp_setkeyid(const char *bjid, const char *keyid);
-const char *settings_pgp_getkeyid(const char *bjid);
-
-#ifdef HAVE_LIBOTR
-guint settings_otr_getpolicy(const char *bjid);
-void settings_otr_setpolicy(const char *bjid, guint value);
-#endif
-
-guint get_max_history_blocks(void);
-
-char *default_muc_nickname(const char *roomid);
-
-const gchar *isbound(int key);
-
-#endif /* __SETTINGS_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/utf8.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-/*
- * utf8.c -- UTF-8 routines
- *
- * Copyright (C) 2006 Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>
- *
- * 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 "utf8.h"
-
-char *prev_char(char *str, const char *limit)
-{
- if (str <= limit)
- return str;
- str--;
- if (utf8_mode)
- while ((str > limit) && ((*str & 0xc0) == 0x80))
- str--;
- return str;
-}
-
-char *next_char(char *str)
-{
- if (!*str)
- return str;
- str++;
- if (utf8_mode)
- while ((*str & 0xc0) == 0x80)
- str++;
- return str;
-}
-
-unsigned get_char(const char *str)
-{
- unsigned char *strp = (unsigned char *)str;
- unsigned c = *strp++;
- unsigned mask = 0x80;
- int len = -1;
- if (!utf8_mode)
- return c;
- while (c & mask) {
- mask >>= 1;
- len++;
- }
- if (len <= 0 || len > 4)
- goto no_utf8;
- c &= mask - 1;
- while ((*strp & 0xc0) == 0x80) {
- if (len-- <= 0)
- goto no_utf8;
- c = (c << 6) | (*strp++ & 0x3f);
- }
- if (len)
- goto no_utf8;
- return c;
-
-no_utf8:
- return *str;
-}
-
-char *put_char(char *str, unsigned c)
-{
- int mask = 0xffffffc0;
- int i = 4;
- char code[5];
- if (!utf8_mode || c < 128) {
- *str++ = c;
- return str;
- }
- while (c & mask) {
- code[i--] = 0x80 | (c & 0x3f);
- c >>= 6;
- mask >>= 1;
- if (i < 0) {
- *str++ = '?';
- return str;
- }
- }
- code[i] = (mask << 1) | c;
- for (; i < 5; i++)
- *str++ = code[i];
- return str;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/utf8.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-#ifndef __UTF8_H__
-#define __UTF8_H__ 1
-
-#include <config.h>
-
-#if defined HAVE_UNICODE && defined HAVE_WCHAR_H && defined HAVE_WCTYPE_H
-# define UNICODE
-#endif
-
-#ifdef HAVE_WCHAR_H
-# include <wchar.h>
-# define get_char_width(c) (utf8_mode ? wcwidth(get_char(c)) : 1)
-#else
-# define wcwidth(c) 1
-# define get_char_width(c) 1
-#endif
-
-#ifdef HAVE_WCTYPE_H
-# include <wctype.h>
-
-/* The following bit is a hack for Solaris 8&9 systems that don't have
- * iswblank().
- * For now i made sure it comes after wctype.h so it doesn't create
- * problems (wctype.h has calls to iswblank() before wctype() is declared).
- * (Sebastian Kayser)
- */
-# ifndef HAVE_ISWBLANK
-# define iswblank(wc) iswctype(wc, wctype("blank"))
-# endif
-
-#else
-# define iswblank(c) (c == ' ')
-# define iswalnum(c) isalnum(c)
-# define iswprint(c) isprint(c)
-# define towupper(c) toupper(c)
-# define towlower(c) tolower(c)
-# define iswalpha(c) isalpha(c)
-#endif
-
-extern int utf8_mode;
-
-char *prev_char(char *str, const char *limit);
-char *next_char(char *str);
-unsigned get_char(const char *str);
-char *put_char(char *str, unsigned c);
-
-#endif /* __UTF8_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/utils.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,789 +0,0 @@
-/*
- * utils.c -- Various utility functions
- *
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- * Some of the ut_* functions are derived from Cabber debug/log code.
- * from_iso8601() comes from the Pidgin (libpurple) 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 <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-
-#ifdef HAVE_LIBIDN
-#include <idna.h>
-#include <stringprep.h>
-static char idnprep[1024];
-#endif
-
-#include <glib.h>
-#include <glib/gprintf.h>
-
-/* For Cygwin (thanks go to Yitzchak Scott-Thoennes) */
-#ifdef __CYGWIN__
-# define timezonevar
- extern long timezone;
-#endif
-#include <time.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <ctype.h>
-
-#include "utils.h"
-#include "logprint.h"
-
-static int DebugEnabled;
-static char *FName;
-
-// jidtodisp(jid)
-// Strips the resource part from the jid
-// The caller should g_free the result after use.
-char *jidtodisp(const char *fjid)
-{
- char *ptr;
- char *alias;
-
- alias = g_strdup(fjid);
-
- if ((ptr = strchr(alias, JID_RESOURCE_SEPARATOR)) != NULL) {
- *ptr = 0;
- }
- return alias;
-}
-
-char *jid_get_username(const char *fjid)
-{
- char *ptr;
- char *username;
-
- username = g_strdup(fjid);
- if ((ptr = strchr(username, JID_DOMAIN_SEPARATOR)) != NULL) {
- *ptr = 0;
- }
- return username;
-}
-
-char *compose_jid(const char *username, const char *servername,
- const char *resource)
-{
- char *fjid;
-
- if (!strchr(username, JID_DOMAIN_SEPARATOR)) {
- fjid = g_strdup_printf("%s%c%s%c%s", username,
- JID_DOMAIN_SEPARATOR, servername,
- JID_RESOURCE_SEPARATOR, resource);
- } else {
- fjid = g_strdup_printf("%s%c%s", username,
- JID_RESOURCE_SEPARATOR, resource);
- }
- return fjid;
-}
-
-gboolean jid_equal(const char *jid1, const char *jid2)
-{
- char *a,*b;
- int ret;
- if (!jid1 && !jid2)
- return TRUE;
- if (!jid1 || !jid2)
- return FALSE;
-
- a = jidtodisp(jid1);
- b = jidtodisp(jid2);
- ret = strcasecmp(a, b);
- g_free(a);
- g_free(b);
- return (ret == 0) ? TRUE : FALSE;
-}
-
-// expand_filename(filename)
-// Expand "~/" with the $HOME env. variable in a file name.
-// The caller must free the string after use.
-char *expand_filename(const char *fname)
-{
- if (!fname)
- return NULL;
- if (!strncmp(fname, "~/", 2)) {
- char *homedir = getenv("HOME");
- if (homedir)
- return g_strdup_printf("%s%s", homedir, fname+1);
- }
- return g_strdup(fname);
-}
-
-void fingerprint_to_hex(const unsigned char *fpr, char hex[49])
-{
- int i;
- char *p;
-
- for (p = hex, i = 0; i < 15; i++, p+=3)
- g_sprintf(p, "%02X:", fpr[i]);
- g_sprintf(p, "%02X", fpr[i]);
- hex[48] = '\0';
-}
-
-gboolean hex_to_fingerprint(const char *hex, char fpr[16])
-{
- int i;
- char *p;
-
- if (strlen(hex) != 47)
- return FALSE;
- for (i = 0, p = (char*)hex; *p && *(p+1); i++, p += 3)
- fpr[i] = (char) g_ascii_strtoull (p, NULL, 16);
- return TRUE;
-}
-
-void ut_InitDebug(int level, const char *filename)
-{
- FILE *fp;
- struct stat buf;
- int err;
-
- if (level < 1) {
- DebugEnabled = 0;
- FName = NULL;
- return;
- }
-
- if (filename)
- FName = expand_filename(filename);
- else {
- FName = getenv("HOME");
- if (!FName)
- FName = g_strdup("/tmp/mcabberlog");
- else {
- FName = g_strdup_printf("%s/mcabberlog", FName);
- }
- }
-
- DebugEnabled = level;
-
- fp = fopen(FName, "a");
- if (!fp) {
- fprintf(stderr, "ERROR: Cannot open tracelog file\n");
- return;
- }
-
- err = fstat(fileno(fp), &buf);
- if (err || buf.st_uid != geteuid()) {
- fclose(fp);
- DebugEnabled = 0;
- FName = NULL;
- if (err) {
- fprintf(stderr, "ERROR: cannot stat the tracelog file!\n");
- } else {
- fprintf(stderr, "ERROR: tracelog file does not belong to you!\n");
- }
- return;
- }
- fchmod(fileno(fp), S_IRUSR|S_IWUSR);
-
- fprintf(fp, "New trace log started.\n----------------------\n");
- fclose(fp);
-}
-
-void ut_WriteLog(unsigned int flag, const char *data)
-{
- if (!DebugEnabled || !FName) return;
-
- if (((DebugEnabled >= 2) && (flag & (LPRINT_LOG|LPRINT_DEBUG))) ||
- ((DebugEnabled == 1) && (flag & LPRINT_LOG))) {
- FILE *fp = fopen(FName, "a+");
- if (!fp) {
- scr_LogPrint(LPRINT_NORMAL, "ERROR: Cannot open tracelog file");
- return;
- }
- if (fputs(data, fp) == EOF)
- scr_LogPrint(LPRINT_NORMAL, "ERROR: Cannot write to tracelog file");
- fclose(fp);
- }
-}
-
-// checkset_perm(name, setmode)
-// Check the permissions of the "name" file/dir
-// If setmode is true, correct the permissions if they are wrong
-// Return values: -1 == bad file/dir, 0 == success, 1 == cannot correct
-int checkset_perm(const char *name, unsigned int setmode)
-{
- int fd;
- struct stat buf;
-
-#ifdef __CYGWIN__
- // Permission checking isn't efficient on Cygwin
- return 0;
-#endif
-
- fd = stat(name, &buf);
- if (fd == -1) return -1;
-
- if (buf.st_uid != geteuid()) {
- scr_LogPrint(LPRINT_LOGNORM, "Wrong file owner [%s]", name);
- return 1;
- }
-
- if (buf.st_mode & (S_IRGRP | S_IWGRP | S_IXGRP) ||
- buf.st_mode & (S_IROTH | S_IWOTH | S_IXOTH)) {
- if (setmode) {
- mode_t newmode = 0;
- scr_LogPrint(LPRINT_LOGNORM, "Bad permissions [%s]", name);
- if (S_ISDIR(buf.st_mode))
- newmode |= S_IXUSR;
- newmode |= S_IRUSR | S_IWUSR;
- if (chmod(name, newmode)) {
- scr_LogPrint(LPRINT_LOGNORM, "WARNING: Failed to correct permissions!");
- return 1;
- }
- scr_LogPrint(LPRINT_LOGNORM, "Permissions have been corrected");
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "WARNING: Bad permissions [%s]", name);
- return 1;
- }
- }
-
- return 0;
-}
-
-const char *ut_get_tmpdir(void)
-{
- static const char *tmpdir;
- const char *tmpvars[] = { "MCABBERTMPDIR", "TMP", "TMPDIR", "TEMP" };
- unsigned int i;
-
- if (tmpdir)
- return tmpdir;
-
- for (i = 0; i < (sizeof(tmpvars) / sizeof(const char *)); i++) {
- tmpdir = getenv(tmpvars[i]);
- if (tmpdir && tmpdir[0] && tmpdir[0] == '/' && tmpdir[1]) {
- // Looks ok.
- return tmpdir;
- }
- }
-
- // Default temporary directory
- tmpdir = "/tmp";
- return tmpdir;
-}
-
-// to_iso8601(dststr, timestamp)
-// Convert timestamp to iso8601 format, and store it in dststr.
-// NOTE: dststr should be at last 19 chars long.
-// Return should be 0
-int to_iso8601(char *dststr, time_t timestamp)
-{
- struct tm *tm_time;
- int ret;
-
- tm_time = gmtime(×tamp);
-
- ret = snprintf(dststr, 19, "%.4d%02d%02dT%02d:%02d:%02dZ",
- (int)(1900+tm_time->tm_year), tm_time->tm_mon+1, tm_time->tm_mday,
- tm_time->tm_hour, tm_time->tm_min, tm_time->tm_sec);
-
- return ((ret == -1) ? -1 : 0);
-}
-
-// from_iso8601(timestamp, utc)
-// This function came from the Pidgin project, gaim_str_to_time().
-// (Actually date may not be pure iso-8601)
-// Thanks, guys!
-// ** Modified by somian 10 Apr 2006 with advice from ysth.
-time_t from_iso8601(const char *timestamp, int utc)
-{
- struct tm t;
- time_t retval = 0;
- char buf[32];
- char *c;
- int tzoff = 0;
- int hms_succ = 0;
- int tmpyear;
-
- time(&retval);
- localtime_r(&retval, &t);
-
- /* Reset time to midnight (00:00:00) */
- t.tm_hour = t.tm_min = t.tm_sec = 0;
-
- snprintf(buf, sizeof(buf), "%s", timestamp);
- c = buf;
-
- /* 4 digit year */
- if (!sscanf(c, "%04d", &tmpyear)) return 0;
- t.tm_year = tmpyear;
- c+=4;
- if (*c == '-')
- c++;
-
- t.tm_year -= 1900;
-
- /* 2 digit month */
- if (!sscanf(c, "%02d", &t.tm_mon)) return 0;
- c+=2;
- if (*c == '-')
- c++;
-
- t.tm_mon -= 1;
-
- /* 2 digit day */
- if (!sscanf(c, "%02d", &t.tm_mday)) return 0;
- c+=2;
- if (*c == 'T' || *c == '.') { /* we have more than a date, keep going */
- c++; /* skip the "T" */
-
- /* 2 digit hour */
- if (sscanf(c, "%02d:%02d:%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3)
- {
- hms_succ = 1;
- c += 8;
- }
- else if (sscanf(c, "%02d%02d%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3)
- {
- hms_succ = 1;
- c += 6;
- }
-
- if (hms_succ) {
- int tzhrs, tzmins;
-
- if (*c == '.') /* dealing with precision we don't care about */
- c += 4;
-
- if ((*c == '+' || *c == '-') &&
- sscanf(c+1, "%02d:%02d", &tzhrs, &tzmins)) {
- tzoff = tzhrs*60*60 + tzmins*60;
- if (*c == '+')
- tzoff *= -1;
- }
-
- if (tzoff || utc) {
-#ifdef HAVE_TM_GMTOFF
- tzoff += t.tm_gmtoff;
-#else
-# ifdef HAVE_TIMEZONE
- tzset(); /* making sure */
- tzoff -= timezone;
-# endif
-#endif
- }
- }
- }
-
- t.tm_isdst = -1;
-
- retval = mktime(&t);
-
- retval += tzoff;
-
- return retval;
-}
-
-/**
- * Derived from libjabber/jid.c, because the libjabber version is not
- * really convenient for our usage.
- *
- * Check if the full JID is valid
- * Return 0 if it is valid, non zero otherwise
- */
-int check_jid_syntax(const char *fjid)
-{
- const char *str;
- const char *domain, *resource;
- int domlen;
-#ifdef HAVE_LIBIDN
- char *idnpp;
- int r;
-#endif
-
- if (!fjid) return 1;
-
- domain = strchr(fjid, JID_DOMAIN_SEPARATOR);
-
- /* the username is optional */
- if (!domain) {
- domain = fjid;
- } else {
- /* node identifiers may not be longer than 1023 bytes */
- if ((domain == fjid) || (domain-fjid > 1023))
- return 1;
- domain++;
-
-#ifdef HAVE_LIBIDN
- idnpp = idnprep;
- str = fjid;
- while (*str != JID_DOMAIN_SEPARATOR)
- *idnpp++ = *str++;
- *idnpp = 0;
-
- r = stringprep(idnprep, 1023, 0, stringprep_xmpp_nodeprep);
- if (r != STRINGPREP_OK || !idnprep[0])
- return 1;
- /* the username looks okay */
-#else
- /* check for low and invalid ascii characters in the username */
- for (str = fjid; *str != JID_DOMAIN_SEPARATOR; str++) {
- if (*str <= ' ' || *str == ':' || *str == JID_DOMAIN_SEPARATOR ||
- *str == '<' || *str == '>' || *str == '\'' ||
- *str == '"' || *str == '&') {
- return 1;
- }
- }
- /* the username is okay as far as we can tell without LIBIDN */
-#endif
- }
-
- resource = strchr(domain, JID_RESOURCE_SEPARATOR);
-
- /* the resource is optional */
- if (resource) {
- domlen = resource - domain;
- resource++;
- /* resources may not be longer than 1023 bytes */
- if ((*resource == '\0') || strlen(resource) > 1023)
- return 1;
-#ifdef HAVE_LIBIDN
- strncpy(idnprep, resource, sizeof(idnprep));
- r = stringprep(idnprep, 1023, 0, stringprep_xmpp_resourceprep);
- if (r != STRINGPREP_OK || !idnprep[0])
- return 1;
-#endif
- } else {
- domlen = strlen(domain);
- }
-
- /* there must be a domain identifier */
- if (domlen == 0) return 1;
-
- /* and it must not be longer than 1023 bytes */
- if (domlen > 1023) return 1;
-
-#ifdef HAVE_LIBIDN
- idnpp = idnprep;
- str = domain;
- while (*str != '\0' && *str != JID_RESOURCE_SEPARATOR)
- *idnpp++ = *str++;
- *idnpp = 0;
-
- r = stringprep_nameprep(idnprep, 1023);
- if (r != STRINGPREP_OK || !idnprep[0])
- return 1;
-
- if (idna_to_ascii_8z(idnprep, &idnpp, IDNA_USE_STD3_ASCII_RULES) !=
- IDNA_SUCCESS)
- return 1;
- else
- free(idnpp);
-#else
- /* make sure the hostname is valid characters */
- for (str = domain; *str != '\0' && *str != JID_RESOURCE_SEPARATOR; str++) {
- if (!(isalnum(*str) || *str == '.' || *str == '-' || *str == '_'))
- return 1;
- }
-#endif
-
- /* it's okay as far as we can tell */
- return 0;
-}
-
-
-inline void mc_strtolower(char *str)
-{
- if (!str) return;
- for ( ; *str; str++)
- *str = tolower(*str);
-}
-
-// strip_arg_special_chars(string)
-// Remove quotes and backslashes before an escaped quote
-// Only quotes need a backslash
-// Ex.: ["a b"] -> [a b]; [a\"b] -> [a"b]
-void strip_arg_special_chars(char *s)
-{
- int instring = FALSE;
- int escape = FALSE;
- char *p;
-
- if (!s) return;
-
- for (p = s; *p; p++) {
- if (*p == '"') {
- if (!escape) {
- instring = !instring;
- strcpy(p, p+1);
- p--;
- } else
- escape = FALSE;
- } else if (*p == '\\') {
- if (!escape) {
- strcpy(p, p+1);
- p--;
- }
- escape = !escape;
- } else
- escape = FALSE;
- }
-}
-
-// split_arg(arg, n, preservelast)
-// Split the string arg into a maximum of n pieces, taking care of
-// double quotes.
-// Return a null-terminated array of strings. This array should be freed
-// by the caller after use, for example with free_arg_lst().
-// If dontstriplast is true, the Nth argument isn't stripped (i.e. no
-// processing of quote chars)
-char **split_arg(const char *arg, unsigned int n, int dontstriplast)
-{
- char **arglst;
- const char *p, *start, *end;
- unsigned int i = 0;
- int instring = FALSE;
- int escape = FALSE;
-
- arglst = g_new0(char*, n+1);
-
- if (!arg || !n) return arglst;
-
- // Skip leading space
- for (start = arg; *start && *start == ' '; start++) ;
- // End of string pointer
- for (end = start; *end; end++) ;
- // Skip trailing space
- while (end > start+1 && *(end-1) == ' ')
- end--;
-
- for (p = start; p < end; p++) {
- if (*p == '"' && !escape)
- instring = !instring;
- if (*p == '\\' && !escape)
- escape = TRUE;
- else if (escape)
- escape = FALSE;
- if (*p == ' ' && !instring && i+1 < n) {
- // end of parameter
- *(arglst+i) = g_strndup(start, p-start);
- strip_arg_special_chars(*(arglst+i));
- for (start = p+1; *start && *start == ' '; start++) ;
- p = start-1;
- i++;
- }
- }
-
- if (start < end) {
- *(arglst+i) = g_strndup(start, end-start);
- if (!dontstriplast || i+1 < n)
- strip_arg_special_chars(*(arglst+i));
- }
-
- return arglst;
-}
-
-// free_arg_lst(arglst)
-// Free an array allocated by split_arg()
-void free_arg_lst(char **arglst)
-{
- char **arg_elt;
-
- for (arg_elt = arglst; *arg_elt; arg_elt++)
- g_free(*arg_elt);
- g_free(arglst);
-}
-
-// replace_nl_with_dots(bufstr)
-// Replace '\n' with "(...)" (or with a NUL if the string is too short)
-void replace_nl_with_dots(char *bufstr)
-{
- char *p = strchr(bufstr, '\n');
- if (p) {
- if (strlen(p) >= 5)
- strcpy(p, "(...)");
- else
- *p = 0;
- }
-}
-
-// ut_expand_tabs(text)
-// Expand tabs and filter out some bad chars in string text.
-// If there is no tab and no bad chars in the string, a pointer to text
-// is returned (be careful _not_ to free the pointer in this case).
-// If there are some tabs or bad chars, a new string with expanded chars
-// and no bad chars is returned; this is up to the caller to free this
-// string after use.
-char *ut_expand_tabs(const char *text)
-{
- char *xtext, *linestart;
- char *p, *q;
- guint n = 0, bc = 0;
-
- xtext = (char*)text;
- for (p=xtext; *p; p++)
- if (*p == '\t')
- n++;
- else if (*p == '\x0d')
- bc++;
- // XXX Are there other special chars we should filter out?
-
- if (!n && !bc)
- return (char*)text;
-
- xtext = g_new(char, strlen(text) + 1 + 8*n);
- p = (char*)text;
- q = linestart = xtext;
- do {
- if (*p == '\t') {
- do { *q++ = ' '; } while ((q-linestart)%8);
- } else if (*p != '\x0d') {
- *q++ = *p;
- if (*p =='\n')
- linestart = q;
- }
- } while (*p++);
-
- return xtext;
-}
-
-
-/* Cygwin's newlib does not have strcasestr() */
-/* The author of the code before the endif is
- * Jeffrey Stedfast <fejj@ximian.com>
- * and this code is reusable in compliance with the GPL v2. -- somian */
-
-#if !defined(HAVE_STRCASESTR)
-
-# define lowercase(c) (isupper ((int) (c)) ? tolower ((int) (c)) : (int) (c))
-# define bm_index(c, icase) ((icase) ? lowercase (c) : (int) (c))
-# define bm_equal(c1, c2, icase) ((icase) ? lowercase (c1) == lowercase (c2) : (c1) == (c2))
-
-/* FIXME: this is just a guess... should really do some performace tests to get an accurate measure */
-# define bm_optimal(hlen, nlen) (((hlen) ? (hlen) > 20 : 1) && (nlen) > 10 ? 1 : 0)
-
-static unsigned char *
-__boyer_moore (const unsigned char *haystack, size_t haystacklen,
- const unsigned char *needle, size_t needlelen, int icase)
-{
- register unsigned char *hc_ptr, *nc_ptr;
- unsigned char *he_ptr, *ne_ptr, *h_ptr;
- size_t skiptable[256], n;
- register int i;
-
-#ifdef BOYER_MOORE_CHECKS
- /* we don't need to do these checks since memmem/strstr/etc do it already */
- /* if the haystack is shorter than the needle then we can't possibly match */
- if (haystacklen < needlelen)
- return NULL;
-
- /* instant match if the pattern buffer is 0-length */
- if (needlelen == 0)
- return (unsigned char *) haystack;
-#endif /* BOYER_MOORE_CHECKS */
-
- /* set a pointer at the end of each string */
- ne_ptr = (unsigned char *) needle + needlelen - 1;
- he_ptr = (unsigned char *) haystack + haystacklen - 1;
-
- /* create our skip table */
- for (i = 0; i < 256; i++)
- skiptable[i] = needlelen;
- for (nc_ptr = (unsigned char *) needle; nc_ptr < ne_ptr; nc_ptr++)
- skiptable[bm_index (*nc_ptr, icase)] = (size_t) (ne_ptr - nc_ptr);
-
- h_ptr = (unsigned char *) haystack;
- while (haystacklen >= needlelen) {
- hc_ptr = h_ptr + needlelen - 1; /* set the haystack compare pointer */
- nc_ptr = ne_ptr; /* set the needle compare pointer */
-
- /* work our way backwards till they don't match */
- for (i = 0; nc_ptr > (unsigned char *) needle; nc_ptr--, hc_ptr--, i++)
- if (!bm_equal (*nc_ptr, *hc_ptr, icase))
- break;
-
- if (!bm_equal (*nc_ptr, *hc_ptr, icase)) {
- n = skiptable[bm_index (*hc_ptr, icase)];
- if (n == needlelen && i)
- if (bm_equal (*ne_ptr, ((unsigned char *) needle)[0], icase))
- n--;
- h_ptr += n;
- haystacklen -= n;
- } else
- return (unsigned char *) h_ptr;
- }
-
- return NULL;
-}
-
-/*
- * strcasestr:
- * @haystack: string to search
- * @needle: substring to search for
- *
- * Finds the first occurence of the substring @needle within the
- * string @haystack ignoring case.
- *
- * Returns a pointer to the beginning of the substring match within
- * @haystack, or NULL if the substring is not found.
- **/
-char *
-strcasestr (const char *haystack, const char *needle)
-{
- register unsigned char *h, *n, *hc, *nc;
- size_t needlelen;
-
- needlelen = strlen (needle);
-
- if (needlelen == 0) {
- return (char *) haystack;
- } else if (bm_optimal (0, needlelen)) {
- return (char *) __boyer_moore ((const unsigned char *) haystack,
- strlen (haystack),
- (const unsigned char *) needle,
- needlelen, 1);
- }
-
- h = (unsigned char *) haystack;
- n = (unsigned char *) needle;
-
- while (*(h + needlelen - 1)) {
- if (lowercase (*h) == lowercase (*n)) {
- for (hc = h + 1, nc = n + 1; *hc && *nc; hc++, nc++)
- if (lowercase (*hc) != lowercase (*nc))
- break;
-
- if (!*nc)
- return (char *) h;
- }
- h++;
- }
- return NULL;
-}
-#endif /* !HAVE_STRCASESTR */
-
-// startswith(str, word, ignore_case)
-// Returns TRUE if string str starts with word.
-int startswith(const char *str, const char *word, guint ignore_case)
-{
- if (ignore_case && !strncasecmp(str, word, strlen(word)))
- return TRUE;
- else if (!ignore_case && !strncmp(str, word, strlen(word)))
- return TRUE;
- return FALSE;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/utils.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-#ifndef __UTILS_H__
-#define __UTILS_H__ 1
-
-#include <config.h>
-
-extern const char *LocaleCharSet;
-
-#define to_utf8(s) ((s) ? g_locale_to_utf8((s), -1, NULL,NULL,NULL) : NULL)
-#define from_utf8(s) ((s) ? g_convert_with_fallback((s), -1, LocaleCharSet, \
- "UTF-8", NULL,NULL,NULL,NULL) : NULL)
-
-#define JID_RESOURCE_SEPARATOR '/'
-#define JID_RESOURCE_SEPARATORSTR "/"
-#define JID_DOMAIN_SEPARATOR '@'
-#define JID_DOMAIN_SEPARATORSTR "@"
-
-char *jidtodisp(const char *fjid);
-char *jid_get_username(const char *fjid);
-char *compose_jid(const char *username, const char *servername,
- const char *resource);
-gboolean jid_equal(const char *jid1, const char *jid2);
-
-void fingerprint_to_hex(const unsigned char *fpr, char hex[49]);
-gboolean hex_to_fingerprint(const char * hex, char fpr[16]);
-
-void ut_InitDebug(int level, const char *file);
-void ut_WriteLog(unsigned int flag, const char *data);
-
-char *expand_filename(const char *fname);
-
-int checkset_perm(const char *name, unsigned int setmode);
-
-const char *ut_get_tmpdir(void);
-
-int to_iso8601(char *dststr, time_t timestamp);
-time_t from_iso8601(const char *timestamp, int utc);
-
-int check_jid_syntax(const char *fjid);
-
-void mc_strtolower(char *str);
-
-void strip_arg_special_chars(char *s);
-char **split_arg(const char *arg, unsigned int n, int dontstriplast);
-void free_arg_lst(char **arglst);
-
-void replace_nl_with_dots(char *bufstr);
-char *ut_expand_tabs(const char *text);
-
-#if !defined (HAVE_STRCASESTR)
-char *strcasestr(const char *haystack, const char *needle);
-#endif
-
-int startswith(const char *str, const char *word, guint ignore_case);
-
-#endif // __UTILS_H__
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2200 +0,0 @@
-/*
- * xmpp.c -- Jabber protocol handling
- *
- * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- * Parts come from the centericq project:
- * Copyright (C) 2002-2005 by Konstantin Klyagin <konst@konst.org.ua>
- *
- * 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 <stdlib.h>
-#include <string.h>
-
-#include "xmpp.h"
-#include "xmpp_helper.h"
-#include "xmpp_iq.h"
-#include "xmpp_iqrequest.h"
-#include "xmpp_muc.h"
-#include "xmpp_s10n.h"
-#include "caps.h"
-#include "events.h"
-#include "histolog.h"
-#include "hooks.h"
-#include "otr.h"
-#include "roster.h"
-#include "screen.h"
-#include "settings.h"
-#include "utils.h"
-#include "main.h"
-
-#define RECONNECTION_TIMEOUT 60L
-
-LmConnection* lconnection;
-static guint AutoConnection;
-
-inline void update_last_use(void);
-inline gboolean xmpp_reconnect();
-
-enum imstatus mystatus = offline;
-static enum imstatus mywantedstatus = available;
-gchar *mystatusmsg;
-
-char imstatus2char[imstatus_size+1] = {
- '_', 'o', 'f', 'd', 'n', 'a', 'i', '\0'
-};
-
-char *imstatus_showmap[] = {
- "",
- "",
- "chat",
- "dnd",
- "xa",
- "away",
- ""
-};
-
-LmMessageNode *bookmarks = NULL;
-LmMessageNode *rosternotes = NULL;
-
-static struct IqHandlers
-{
- const gchar *xmlns;
- LmHandleMessageFunction handler;
-} iq_handlers[] = {
- {NS_PING, &handle_iq_ping},
- {NS_VERSION, &handle_iq_version},
- {NS_TIME, &handle_iq_time},
- {NS_ROSTER, &handle_iq_roster},
- {NS_XMPP_TIME, &handle_iq_time202},
- {NS_LAST, &handle_iq_last},
- {NS_DISCO_INFO, &handle_iq_disco_info},
- {NS_DISCO_ITEMS,&handle_iq_disco_items},
- {NS_COMMANDS, &handle_iq_commands},
- {NS_VCARD, &handle_iq_vcard},
- {NULL, NULL}
-};
-
-void update_last_use(void)
-{
- iqlast = time(NULL);
-}
-
-// Note: the caller should check the jid is correct
-void xmpp_addbuddy(const char *bjid, const char *name, const char *group)
-{
- LmMessageNode *query, *y;
- LmMessage *iq;
- char *cleanjid;
-
- if (!lm_connection_is_authenticated(lconnection)) return;
-
- cleanjid = jidtodisp(bjid); // Stripping resource, just in case...
-
- // We don't check if the jabber user already exists in the roster,
- // because it allows to re-ask for notification.
-
- iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_SET);
- query = lm_message_node_add_child(iq->node, "query", NULL);
- lm_message_node_set_attribute(query, "xmlns", NS_ROSTER);
- y = lm_message_node_add_child(query, "item", NULL);
- lm_message_node_set_attribute(y, "jid", cleanjid);
-
- if (name)
- lm_message_node_set_attribute(y, "name", name);
-
- if (group)
- lm_message_node_add_child(y, "group", group);
-
- lm_connection_send(lconnection, iq, NULL);
- lm_message_unref(iq);
-
- xmpp_send_s10n(cleanjid, LM_MESSAGE_SUB_TYPE_SUBSCRIBE);
-
- roster_add_user(cleanjid, name, group, ROSTER_TYPE_USER, sub_pending, -1);
- g_free(cleanjid);
- buddylist_build();
-
- update_roster = TRUE;
-}
-
-void xmpp_updatebuddy(const char *bjid, const char *name, const char *group)
-{
- LmMessage *iq;
- LmMessageNode *x;
- char *cleanjid;
-
- if (!lm_connection_is_authenticated(lconnection)) return;
-
- // XXX We should check name's and group's correctness
-
- cleanjid = jidtodisp(bjid); // Stripping resource, just in case...
-
- iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_SET);
- x = lm_message_node_add_child(iq->node, "query", NULL);
- lm_message_node_set_attribute(x, "xmlns", NS_ROSTER);
- x = lm_message_node_add_child(x, "item", NULL);
- lm_message_node_set_attributes(x,
- "jid", cleanjid,
- "name", name,
- NULL);
-
- if (group)
- lm_message_node_add_child(x, "group", group);
-
- lm_connection_send(lconnection, iq, NULL);
- lm_message_unref(iq);
- g_free(cleanjid);
-}
-
-void xmpp_delbuddy(const char *bjid)
-{
- LmMessageNode *y, *z;
- LmMessage *iq;
- char *cleanjid;
-
- if (!lm_connection_is_authenticated(lconnection)) return;
-
- cleanjid = jidtodisp(bjid); // Stripping resource, just in case...
-
- // If the current buddy is an agent, unsubscribe from it
- if (roster_gettype(cleanjid) == ROSTER_TYPE_AGENT) {
- scr_LogPrint(LPRINT_LOGNORM, "Unregistering from the %s agent", cleanjid);
-
- iq = lm_message_new_with_sub_type(cleanjid, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_SET);
- y = lm_message_node_add_child(iq->node, "query", NULL);
- lm_message_node_set_attribute(y, "xmlns", NS_REGISTER);
- lm_message_node_add_child(y, "remove", NULL);
- lm_connection_send(lconnection, iq, NULL);
- lm_message_unref(iq);
- }
-
- // Cancel the subscriptions
- xmpp_send_s10n(cleanjid, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED); //cancel "from"
- xmpp_send_s10n(cleanjid, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE); //cancel "to"
-
- // Ask for removal from roster
- iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_SET);
-
- y = lm_message_node_add_child(iq->node, "query", NULL);
- lm_message_node_set_attribute(y, "xmlns", NS_ROSTER);
- z = lm_message_node_add_child(y, "item", NULL);
- lm_message_node_set_attributes(z,
- "jid", cleanjid,
- "subscription", "remove",
- NULL);
- lm_connection_send(lconnection, iq, NULL);
- lm_message_unref(iq);
-
- roster_del_user(cleanjid);
- g_free(cleanjid);
- buddylist_build();
-
- update_roster = TRUE;
-}
-
-void xmpp_request(const char *fjid, enum iqreq_type reqtype)
-{
- GSList *resources, *p_res;
- GSList *roster_elt;
- const char *strreqtype, *xmlns;
-
- if (reqtype == iqreq_version) {
- xmlns = NS_VERSION;
- strreqtype = "version";
- } else if (reqtype == iqreq_time) {
- xmlns = NS_TIME;
- strreqtype = "time";
- } else if (reqtype == iqreq_last) {
- xmlns = NS_LAST;
- strreqtype = "last";
- } else if (reqtype == iqreq_vcard) {
- xmlns = NS_VCARD;
- strreqtype = "vCard";
- // Special case
- } else
- return;
-
- if (strchr(fjid, JID_RESOURCE_SEPARATOR)) {
- // This is a full JID
- xmpp_iq_request(fjid, xmlns);
- scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fjid);
- return;
- }
-
- // The resource has not been specified
- roster_elt = roster_find(fjid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_ROOM);
- if (!roster_elt) {
- scr_LogPrint(LPRINT_NORMAL, "No known resource for <%s>...", fjid);
- xmpp_iq_request(fjid, xmlns); // Let's send a request anyway...
- scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fjid);
- return;
- }
-
- // Send a request to each resource
- resources = buddy_getresources(roster_elt->data);
- if (!resources) {
- scr_LogPrint(LPRINT_NORMAL, "No known resource for <%s>...", fjid);
- xmpp_iq_request(fjid, xmlns); // Let's send a request anyway...
- scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fjid);
- }
- for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
- gchar *fulljid;
- fulljid = g_strdup_printf("%s/%s", fjid, (char*)p_res->data);
- xmpp_iq_request(fulljid, xmlns);
- scr_LogPrint(LPRINT_NORMAL, "Sent %s request to <%s>", strreqtype, fulljid);
- g_free(fulljid);
- g_free(p_res->data);
- }
- g_slist_free(resources);
-}
-
-static LmHandlerResult cb_xep184(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data)
-{
- char *from = jidtodisp(lm_message_get_from(m));
- scr_RemoveReceiptFlag(from, h);
- g_free(from);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-// xmpp_send_msg(jid, text, type, subject,
-// otrinject, *encrypted, type_overwrite)
-// When encrypted is not NULL, the function set *encrypted to 1 if the
-// message has been PGP-encrypted. If encryption enforcement is set and
-// encryption fails, *encrypted is set to -1.
-void xmpp_send_msg(const char *fjid, const char *text, int type,
- const char *subject, gboolean otrinject, gint *encrypted,
- LmMessageSubType type_overwrite, gpointer *xep184)
-{
- LmMessage *x;
- LmMessageSubType subtype;
-#ifdef HAVE_LIBOTR
- int otr_msg = 0;
-#endif
-#if defined HAVE_GPGME || defined JEP0022 || defined JEP0085
- char *rname, *barejid;
- GSList *sl_buddy;
-#endif
-#if defined JEP0022 || defined JEP0085
- LmMessageNode *event;
- guint use_jep85 = 0;
- struct jep0085 *jep85 = NULL;
-#endif
- gchar *enc = NULL;
-
- if (encrypted)
- *encrypted = 0;
-
- if (!lm_connection_is_authenticated(lconnection))
- return;
-
- if (!text && type == ROSTER_TYPE_USER)
- return;
-
- if (type_overwrite != LM_MESSAGE_SUB_TYPE_NOT_SET)
- subtype = type_overwrite;
- else {
- if (type == ROSTER_TYPE_ROOM)
- subtype = LM_MESSAGE_SUB_TYPE_GROUPCHAT;
- else
- subtype = LM_MESSAGE_SUB_TYPE_CHAT;
- }
-
-#if defined HAVE_GPGME || defined HAVE_LIBOTR || \
- defined JEP0022 || defined JEP0085
- rname = strchr(fjid, JID_RESOURCE_SEPARATOR);
- barejid = jidtodisp(fjid);
- sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
-
- // If we can get a resource name, we use it. Else we use NULL,
- // which hopefully will give us the most likely resource.
- if (rname)
- rname++;
-
-#ifdef HAVE_LIBOTR
- if (otr_enabled() && !otrinject) {
- if (type == ROSTER_TYPE_USER) {
- otr_msg = otr_send((char **)&text, barejid);
- if (!text) {
- g_free(barejid);
- if (encrypted)
- *encrypted = -1;
- return;
- }
- }
- if (otr_msg && encrypted)
- *encrypted = ENCRYPTED_OTR;
- }
-#endif
-
-#ifdef HAVE_GPGME
- if (type == ROSTER_TYPE_USER && sl_buddy && gpg_enabled()) {
- if (!settings_pgp_getdisabled(barejid)) { // not disabled for this contact?
- guint force;
- struct pgp_data *res_pgpdata;
- force = settings_pgp_getforce(barejid);
- res_pgpdata = buddy_resource_pgp(sl_buddy->data, rname);
- if (force || (res_pgpdata && res_pgpdata->sign_keyid)) {
- /* Remote client has PGP support (we have a signature)
- * OR encryption is enforced (force = TRUE).
- * If the contact has a specific KeyId, we'll use it;
- * if not, we'll use the key used for the signature.
- * Both keys should match, in theory (cf. XEP-0027). */
- const char *key;
- key = settings_pgp_getkeyid(barejid);
- if (!key && res_pgpdata)
- key = res_pgpdata->sign_keyid;
- if (key)
- enc = gpg_encrypt(text, key);
- if (!enc && force) {
- if (encrypted)
- *encrypted = -1;
- g_free(barejid);
- return;
- }
- }
- }
- }
-#endif // HAVE_GPGME
-
- g_free(barejid);
-#endif // HAVE_GPGME || defined JEP0022 || defined JEP0085
-
- x = lm_message_new_with_sub_type(fjid, LM_MESSAGE_TYPE_MESSAGE, subtype);
- lm_message_node_add_child(x->node, "body",
- enc ? "This message is PGP-encrypted." : text);
-
- if (subject)
- lm_message_node_add_child(x->node, "subject", subject);
-
- if (enc) {
- LmMessageNode *y;
- y = lm_message_node_add_child(x->node, "x", enc);
- lm_message_node_set_attribute(y, "xmlns", NS_ENCRYPTED);
- if (encrypted)
- *encrypted = ENCRYPTED_PGP;
- g_free(enc);
- }
-
- //XEP-0184: Message Receipts
- if (sl_buddy && rname && xep184 &&
- caps_has_feature(buddy_resource_getcaps(sl_buddy->data, rname),
- NS_RECEIPTS)) {
- lm_message_node_set_attribute
- (lm_message_node_add_child(x->node, "request", NULL),
- "xmlns", NS_RECEIPTS);
- *xep184 = lm_message_handler_new(cb_xep184, NULL, NULL);
- }
-
-#if defined JEP0022 || defined JEP0085
- // If typing notifications are disabled, we can skip all this stuff...
- if (chatstates_disabled || type == ROSTER_TYPE_ROOM)
- goto xmpp_send_msg_no_chatstates;
-
- if (sl_buddy)
- jep85 = buddy_resource_jep85(sl_buddy->data, rname);
-#endif
-
-#ifdef JEP0085
- /* JEP-0085 5.1
- * "Until receiving a reply to the initial content message (or a standalone
- * notification) from the Contact, the User MUST NOT send subsequent chat
- * state notifications to the Contact."
- * In our implementation support is initially "unknown", then it's "probed"
- * and can become "ok".
- */
- if (jep85 && (jep85->support == CHATSTATES_SUPPORT_OK ||
- jep85->support == CHATSTATES_SUPPORT_UNKNOWN)) {
- event = lm_message_node_add_child(x->node, "active", NULL);
- lm_message_node_set_attribute(event, "xmlns", NS_CHATSTATES);
- if (jep85->support == CHATSTATES_SUPPORT_UNKNOWN)
- jep85->support = CHATSTATES_SUPPORT_PROBED;
- else
- use_jep85 = 1;
- jep85->last_state_sent = ROSTER_EVENT_ACTIVE;
- }
-#endif
-#ifdef JEP0022
- /* JEP-22
- * If the Contact supports JEP-0085, we do not use JEP-0022.
- * If not, we try to fall back to JEP-0022.
- */
- if (!use_jep85) {
- struct jep0022 *jep22 = NULL;
- event = lm_message_node_add_child(x->node, "x", NULL);
- lm_message_node_set_attribute(event, "xmlns", NS_EVENT);
- lm_message_node_add_child(event, "composing", NULL);
-
- if (sl_buddy)
- jep22 = buddy_resource_jep22(sl_buddy->data, rname);
- if (jep22)
- jep22->last_state_sent = ROSTER_EVENT_ACTIVE;
-
- // An id is mandatory when using JEP-0022.
- if (text || subject) {
- const gchar *msgid = lm_message_get_id(x);
- // Let's update last_msgid_sent
- if (jep22) {
- g_free(jep22->last_msgid_sent);
- jep22->last_msgid_sent = g_strdup(msgid);
- }
- }
- }
-#endif
-
-xmpp_send_msg_no_chatstates:
- if (mystatus != invisible)
- update_last_use();
- if (xep184 && *xep184) {
- lm_connection_send_with_reply(lconnection, x, *xep184, NULL);
- lm_message_handler_unref(*xep184);
- } else
- lm_connection_send(lconnection, x, NULL);
- lm_message_unref(x);
-}
-
-#ifdef JEP0085
-// xmpp_send_jep85_chatstate()
-// Send a JEP-85 chatstate.
-static void xmpp_send_jep85_chatstate(const char *bjid, const char *resname,
- guint state)
-{
- LmMessage *m;
- LmMessageNode *event;
- GSList *sl_buddy;
- const char *chattag;
- char *rjid, *fjid = NULL;
- struct jep0085 *jep85 = NULL;
-
- if (!lm_connection_is_authenticated(lconnection)) return;
-
- sl_buddy = roster_find(bjid, jidsearch, ROSTER_TYPE_USER);
-
- // If we have a resource name, we use it. Else we use NULL,
- // which hopefully will give us the most likely resource.
- if (sl_buddy)
- jep85 = buddy_resource_jep85(sl_buddy->data, resname);
-
- if (!jep85 || (jep85->support != CHATSTATES_SUPPORT_OK))
- return;
-
- if (state == jep85->last_state_sent)
- return;
-
- if (state == ROSTER_EVENT_ACTIVE)
- chattag = "active";
- else if (state == ROSTER_EVENT_COMPOSING)
- chattag = "composing";
- else if (state == ROSTER_EVENT_PAUSED)
- chattag = "paused";
- else {
- scr_LogPrint(LPRINT_LOGNORM, "Error: unsupported JEP-85 state (%d)", state);
- return;
- }
-
- jep85->last_state_sent = state;
-
- if (resname)
- fjid = g_strdup_printf("%s/%s", bjid, resname);
-
- rjid = resname ? fjid : (char*)bjid;
- m = lm_message_new_with_sub_type(rjid, LM_MESSAGE_TYPE_MESSAGE,
- LM_MESSAGE_SUB_TYPE_CHAT);
-
- event = lm_message_node_add_child(m->node, chattag, NULL);
- lm_message_node_set_attribute(event, "xmlns", NS_CHATSTATES);
-
- lm_connection_send(lconnection, m, NULL);
- lm_message_unref(m);
-
- g_free(fjid);
-}
-#endif
-
-#ifdef JEP0022
-// xmpp_send_jep22_event()
-// Send a JEP-22 message event (delivered, composing...).
-static void xmpp_send_jep22_event(const char *fjid, guint type)
-{
- LmMessage *x;
- LmMessageNode *event;
- const char *msgid;
- char *rname, *barejid;
- GSList *sl_buddy;
- struct jep0022 *jep22 = NULL;
- guint jep22_state;
-
- if (!lm_connection_is_authenticated(lconnection)) return;
-
- rname = strchr(fjid, JID_RESOURCE_SEPARATOR);
- barejid = jidtodisp(fjid);
- sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
- g_free(barejid);
-
- // If we can get a resource name, we use it. Else we use NULL,
- // which hopefully will give us the most likely resource.
- if (rname)
- rname++;
- if (sl_buddy)
- jep22 = buddy_resource_jep22(sl_buddy->data, rname);
-
- if (!jep22)
- return; // XXX Maybe we could try harder (other resources?)
-
- msgid = jep22->last_msgid_rcvd;
-
- // For composing events (composing, active, inactive, paused...),
- // JEP22 only has 2 states; we'll use composing and active.
- if (type == ROSTER_EVENT_COMPOSING)
- jep22_state = ROSTER_EVENT_COMPOSING;
- else if (type == ROSTER_EVENT_ACTIVE ||
- type == ROSTER_EVENT_PAUSED)
- jep22_state = ROSTER_EVENT_ACTIVE;
- else
- jep22_state = 0; // ROSTER_EVENT_NONE
-
- if (jep22_state) {
- // Do not re-send a same event
- if (jep22_state == jep22->last_state_sent)
- return;
- jep22->last_state_sent = jep22_state;
- }
-
- x = lm_message_new_with_sub_type(fjid, LM_MESSAGE_TYPE_MESSAGE,
- LM_MESSAGE_SUB_TYPE_CHAT);
-
- event = lm_message_node_add_child(x->node, "x", NULL);
- lm_message_node_set_attribute(event, "xmlns", NS_EVENT);
- if (type == ROSTER_EVENT_DELIVERED)
- lm_message_node_add_child(event, "delivered", NULL);
- else if (type == ROSTER_EVENT_COMPOSING)
- lm_message_node_add_child(event, "composing", NULL);
- lm_message_node_add_child(event, "id", msgid);
-
- lm_connection_send(lconnection, x, NULL);
- lm_message_unref(x);
-}
-#endif
-
-// xmpp_send_chatstate(buddy, state)
-// Send a chatstate or event (JEP-22/85) according to the buddy's capabilities.
-// The message is sent to one of the resources with the highest priority.
-#if defined JEP0022 || defined JEP0085
-void xmpp_send_chatstate(gpointer buddy, guint chatstate)
-{
- const char *bjid;
-#ifdef JEP0085
- GSList *resources, *p_res, *p_next;
- struct jep0085 *jep85 = NULL;
-#endif
-#ifdef JEP0022
- struct jep0022 *jep22;
-#endif
-
- bjid = buddy_getjid(buddy);
- if (!bjid) return;
-
-#ifdef JEP0085
- /* Send the chatstate to the last resource (which should have the highest
- priority).
- If chatstate is "active", send an "active" state to all resources
- which do not curently have this state.
- */
- resources = buddy_getresources(buddy);
- for (p_res = resources ; p_res ; p_res = p_next) {
- p_next = g_slist_next(p_res);
- jep85 = buddy_resource_jep85(buddy, p_res->data);
- if (jep85 && jep85->support == CHATSTATES_SUPPORT_OK) {
- // If p_next is NULL, this is the highest (prio) resource, i.e.
- // the one we are probably writing to.
- if (!p_next || (jep85->last_state_sent != ROSTER_EVENT_ACTIVE &&
- chatstate == ROSTER_EVENT_ACTIVE))
- xmpp_send_jep85_chatstate(bjid, p_res->data, chatstate);
- }
- g_free(p_res->data);
- }
- g_slist_free(resources);
- // If the last resource had chatstates support when can return now,
- // we don't want to send a JEP22 event.
- if (jep85 && jep85->support == CHATSTATES_SUPPORT_OK)
- return;
-#endif
-#ifdef JEP0022
- jep22 = buddy_resource_jep22(buddy, NULL);
- if (jep22 && jep22->support == CHATSTATES_SUPPORT_OK) {
- xmpp_send_jep22_event(bjid, chatstate);
- }
-#endif
-}
-#endif
-
-
-// chatstates_reset_probed(fulljid)
-// If the JEP has been probed for this contact, set it back to unknown so
-// that we probe it again. The parameter must be a full jid (w/ resource).
-#if defined JEP0022 || defined JEP0085
-static void chatstates_reset_probed(const char *fulljid)
-{
- char *rname, *barejid;
- GSList *sl_buddy;
- struct jep0085 *jep85;
- struct jep0022 *jep22;
-
- rname = strchr(fulljid, JID_RESOURCE_SEPARATOR);
- if (!rname++)
- return;
-
- barejid = jidtodisp(fulljid);
- sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
- g_free(barejid);
-
- if (!sl_buddy)
- return;
-
- jep85 = buddy_resource_jep85(sl_buddy->data, rname);
- jep22 = buddy_resource_jep22(sl_buddy->data, rname);
-
- if (jep85 && jep85->support == CHATSTATES_SUPPORT_PROBED)
- jep85->support = CHATSTATES_SUPPORT_UNKNOWN;
- if (jep22 && jep22->support == CHATSTATES_SUPPORT_PROBED)
- jep22->support = CHATSTATES_SUPPORT_UNKNOWN;
-}
-#endif
-
-#ifdef HAVE_GPGME
-// keys_mismatch(key, expectedkey)
-// Return TRUE if both keys are non-null and "expectedkey" doesn't match
-// the end of "key".
-// If one of the keys is null, return FALSE.
-// If expectedkey is less than 8 bytes long, return TRUE.
-//
-// Example: keys_mismatch("C9940A9BB0B92210", "B0B92210") will return FALSE.
-static bool keys_mismatch(const char *key, const char *expectedkey)
-{
- int lk, lek;
-
- if (!expectedkey || !key)
- return FALSE;
-
- lk = strlen(key);
- lek = strlen(expectedkey);
-
- // If the expectedkey is less than 8 bytes long, this is probably a
- // user mistake so we consider it's a mismatch.
- if (lek < 8)
- return TRUE;
-
- if (lek < lk)
- key += lk - lek;
-
- return strcasecmp(key, expectedkey);
-}
-#endif
-
-// check_signature(barejid, resourcename, xmldata, text)
-// Verify the signature (in xmldata) of "text" for the contact
-// barejid/resourcename.
-// xmldata is the 'jabber:x:signed' stanza.
-// If the key id is found, the contact's PGP data are updated.
-static void check_signature(const char *barejid, const char *rname,
- LmMessageNode *node, const char *text)
-{
-#ifdef HAVE_GPGME
- const char *p, *key;
- GSList *sl_buddy;
- struct pgp_data *res_pgpdata;
- gpgme_sigsum_t sigsum;
-
- // All parameters must be valid
- if (!(node && barejid && rname && text))
- return;
-
- if (!gpg_enabled())
- return;
-
- // Get the resource PGP data structure
- sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
- if (!sl_buddy)
- return;
- res_pgpdata = buddy_resource_pgp(sl_buddy->data, rname);
- if (!res_pgpdata)
- return;
-
- if (!node->name || strcmp(node->name, "x")) //XXX: probably useless
- return; // We expect "<x xmlns='jabber:x:signed'>"
-
- // Get signature
- p = lm_message_node_get_value(node);
- if (!p)
- return;
-
- key = gpg_verify(p, text, &sigsum);
- if (key) {
- const char *expectedkey;
- char *buf;
- g_free(res_pgpdata->sign_keyid);
- res_pgpdata->sign_keyid = (char *)key;
- res_pgpdata->last_sigsum = sigsum;
- if (sigsum & GPGME_SIGSUM_RED) {
- buf = g_strdup_printf("Bad signature from <%s/%s>", barejid, rname);
- scr_WriteIncomingMessage(barejid, buf, 0, HBB_PREFIX_INFO, 0);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
- g_free(buf);
- }
- // Verify that the key id is the one we expect.
- expectedkey = settings_pgp_getkeyid(barejid);
- if (keys_mismatch(key, expectedkey)) {
- buf = g_strdup_printf("Warning: The KeyId from <%s/%s> doesn't match "
- "the key you set up", barejid, rname);
- scr_WriteIncomingMessage(barejid, buf, 0, HBB_PREFIX_INFO, 0);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
- g_free(buf);
- }
- }
-#endif
-}
-
-static LmSSLResponse ssl_cb(LmSSL *ssl, LmSSLStatus status, gpointer ud)
-{
- scr_LogPrint(LPRINT_LOGNORM, "SSL status:%d", status);
-
- switch (status) {
- case LM_SSL_STATUS_NO_CERT_FOUND:
- scr_LogPrint(LPRINT_LOGNORM, "No certificate found!");
- break;
- case LM_SSL_STATUS_UNTRUSTED_CERT:
- scr_LogPrint(LPRINT_LOGNORM, "Certificate is not trusted!");
- break;
- case LM_SSL_STATUS_CERT_EXPIRED:
- scr_LogPrint(LPRINT_LOGNORM, "Certificate has expired!");
- break;
- case LM_SSL_STATUS_CERT_NOT_ACTIVATED:
- scr_LogPrint(LPRINT_LOGNORM, "Certificate has not been activated!");
- break;
- case LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH:
- scr_LogPrint(LPRINT_LOGNORM,
- "Certificate hostname does not match expected hostname!");
- break;
- case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH: {
- char fpr[49];
- fingerprint_to_hex((const unsigned char*)lm_ssl_get_fingerprint(ssl),
- fpr);
- scr_LogPrint(LPRINT_LOGNORM,
- "Certificate fingerprint does not match expected fingerprint!");
- scr_LogPrint(LPRINT_LOGNORM, "Remote fingerprint: %s", fpr);
-
- scr_LogPrint(LPRINT_LOGNORM, "Expected fingerprint: %s",
- settings_opt_get("ssl_fingerprint"));
-
- return LM_SSL_RESPONSE_STOP;
- break;
- }
- case LM_SSL_STATUS_GENERIC_ERROR:
- scr_LogPrint(LPRINT_LOGNORM, "Generic SSL error!");
- break;
- }
-
- if (settings_opt_get_int("ssl_ignore_checks"))
- return LM_SSL_RESPONSE_CONTINUE;
- return LM_SSL_RESPONSE_STOP;
-}
-
-static void connection_auth_cb(LmConnection *connection, gboolean success,
- gpointer user_data)
-{
- if (success) {
- LmMessage *m;
-
- m = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_PRESENCE,
- LM_MESSAGE_SUB_TYPE_AVAILABLE);
- lm_connection_send(connection, m, NULL);
-
- lm_message_unref(m);
- xmpp_setprevstatus();
- xmpp_iq_request(NULL, NS_ROSTER);
- xmpp_request_storage("storage:bookmarks");
- xmpp_request_storage("storage:rosternotes");
-
- AutoConnection = TRUE;
- } else
- scr_LogPrint(LPRINT_LOGNORM, "Authentication failed");
-}
-
-gboolean xmpp_reconnect()
-{
- if (!lm_connection_is_authenticated(lconnection))
- xmpp_connect();
- return FALSE;
-}
-
-static void _try_to_reconnect(void)
-{
- if (AutoConnection)
- g_timeout_add_seconds(RECONNECTION_TIMEOUT, xmpp_reconnect, NULL);
-}
-
-static void connection_open_cb(LmConnection *connection, gboolean success,
- gpointer user_data)
-{
- GError *error = NULL;
-
- if (success) {
- const char *password, *resource;
- char *username;
- username = jid_get_username(settings_opt_get("jid"));
- password = settings_opt_get("password");
- resource = strchr(lm_connection_get_jid(connection),
- JID_RESOURCE_SEPARATOR);
- if (resource)
- resource++;
-
- if (!lm_connection_authenticate(lconnection, username, password, resource,
- connection_auth_cb, NULL, FALSE, &error)) {
- scr_LogPrint(LPRINT_LOGNORM, "Failed to authenticate: %s\n",
- error->message);
- g_error_free (error);
- _try_to_reconnect();
- }
- g_free(username);
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "There was an error while connecting.");
- _try_to_reconnect();
- }
-}
-
-static void connection_close_cb(LmConnection *connection,
- LmDisconnectReason reason,
- gpointer user_data)
-{
- const char *str;
-
- switch (reason) {
- case LM_DISCONNECT_REASON_OK:
- str = "LM_DISCONNECT_REASON_OK";
- break;
- case LM_DISCONNECT_REASON_PING_TIME_OUT:
- str = "LM_DISCONNECT_REASON_PING_TIME_OUT";
- break;
- case LM_DISCONNECT_REASON_HUP:
- str = "LM_DISCONNECT_REASON_HUP";
- break;
- case LM_DISCONNECT_REASON_ERROR:
- str = "LM_DISCONNECT_REASON_ERROR";
- break;
- case LM_DISCONNECT_REASON_UNKNOWN:
- default:
- str = "LM_DISCONNECT_REASON_UNKNOWN";
- break;
- }
-
- if (reason != LM_DISCONNECT_REASON_OK)
- _try_to_reconnect();
-
- // Free bookmarks
- if (bookmarks)
- lm_message_node_unref(bookmarks);
- bookmarks = NULL;
- // Free roster
- roster_free();
- if (rosternotes)
- lm_message_node_unref(rosternotes);
- rosternotes = NULL;
- // Update display
- update_roster = TRUE;
- scr_UpdateBuddyWindow();
-
- scr_LogPrint(LPRINT_NORMAL, "Disconnected, reason:%d->'%s'\n", reason, str);
-}
-
-static void handle_state_events(const char *from, LmMessageNode *node)
-{
-#if defined JEP0022 || defined JEP0085
- LmMessageNode *state_ns = NULL;
- const char *body;
- char *rname, *bjid;
- GSList *sl_buddy;
- guint events;
- struct jep0022 *jep22 = NULL;
- struct jep0085 *jep85 = NULL;
- enum {
- JEP_none,
- JEP_85,
- JEP_22
- } which_jep = JEP_none;
-
- rname = strchr(from, JID_RESOURCE_SEPARATOR);
- if (rname)
- ++rname;
- else
- rname = (char *)from + strlen(from);
- bjid = jidtodisp(from);
- sl_buddy = roster_find(bjid, jidsearch, ROSTER_TYPE_USER);
- g_free(bjid);
-
- /* XXX Actually that's wrong, since it filters out server "offline"
- messages (for JEP-0022). This JEP is (almost) deprecated so
- we don't really care. */
- if (!sl_buddy) {
- return;
- }
-
- /* Let's see chich JEP the contact uses. If possible, we'll use
- JEP-85, if not we'll look for JEP-22 support. */
- events = buddy_resource_getevents(sl_buddy->data, rname);
-
- jep85 = buddy_resource_jep85(sl_buddy->data, rname);
- if (jep85) {
- state_ns = lm_message_node_find_xmlns(node, NS_CHATSTATES);
- if (state_ns)
- which_jep = JEP_85;
- }
-
- if (which_jep != JEP_85) { /* Fall back to JEP-0022 */
- jep22 = buddy_resource_jep22(sl_buddy->data, rname);
- if (jep22) {
- state_ns = lm_message_node_find_xmlns(node, NS_EVENT);
- if (state_ns)
- which_jep = JEP_22;
- }
- }
-
- if (!which_jep) { /* Sender does not use chat states */
- return;
- }
-
- body = lm_message_node_get_child_value(node, "body");
-
- if (which_jep == JEP_85) { /* JEP-0085 */
- jep85->support = CHATSTATES_SUPPORT_OK;
-
- if (!strcmp(state_ns->name, "composing")) {
- jep85->last_state_rcvd = ROSTER_EVENT_COMPOSING;
- } else if (!strcmp(state_ns->name, "active")) {
- jep85->last_state_rcvd = ROSTER_EVENT_ACTIVE;
- } else if (!strcmp(state_ns->name, "paused")) {
- jep85->last_state_rcvd = ROSTER_EVENT_PAUSED;
- } else if (!strcmp(state_ns->name, "inactive")) {
- jep85->last_state_rcvd = ROSTER_EVENT_INACTIVE;
- } else if (!strcmp(state_ns->name, "gone")) {
- jep85->last_state_rcvd = ROSTER_EVENT_GONE;
- }
- events = jep85->last_state_rcvd;
- } else { /* JEP-0022 */
-#ifdef JEP0022
- const char *msgid;
- jep22->support = CHATSTATES_SUPPORT_OK;
- jep22->last_state_rcvd = ROSTER_EVENT_NONE;
-
- msgid = lm_message_node_get_attribute(node, "id");
-
- if (lm_message_node_get_child(state_ns, "composing")) {
- // Clear composing if the message contains a body
- if (body)
- events &= ~ROSTER_EVENT_COMPOSING;
- else
- events |= ROSTER_EVENT_COMPOSING;
- jep22->last_state_rcvd |= ROSTER_EVENT_COMPOSING;
-
- } else {
- events &= ~ROSTER_EVENT_COMPOSING;
- }
-
- // Cache the message id
- g_free(jep22->last_msgid_rcvd);
- if (msgid)
- jep22->last_msgid_rcvd = g_strdup(msgid);
- else
- jep22->last_msgid_rcvd = NULL;
-
- if (lm_message_node_get_child(state_ns, "delivered")) {
- jep22->last_state_rcvd |= ROSTER_EVENT_DELIVERED;
-
- // Do we have to send back an ACK?
- if (body)
- xmpp_send_jep22_event(from, ROSTER_EVENT_DELIVERED);
- }
-#endif
- }
-
- buddy_resource_setevents(sl_buddy->data, rname, events);
-
- update_roster = TRUE;
-#endif
-}
-
-static void gotmessage(LmMessageSubType type, const char *from,
- const char *body, const char *enc, const char *subject,
- time_t timestamp, LmMessageNode *node_signed)
-{
- char *bjid;
- const char *rname, *s;
- char *decrypted_pgp = NULL;
- char *decrypted_otr = NULL;
- int otr_msg = 0, free_msg = 0;
-
- bjid = jidtodisp(from);
-
- rname = strchr(from, JID_RESOURCE_SEPARATOR);
- if (rname) rname++;
-
-#ifdef HAVE_GPGME
- if (enc && gpg_enabled()) {
- decrypted_pgp = gpg_decrypt(enc);
- if (decrypted_pgp) {
- body = decrypted_pgp;
- }
- }
- // Check signature of an unencrypted message
- if (node_signed && gpg_enabled())
- check_signature(bjid, rname, node_signed, decrypted_pgp);
-#endif
-
-#ifdef HAVE_LIBOTR
- if (otr_enabled()) {
- decrypted_otr = (char*)body;
- otr_msg = otr_receive(&decrypted_otr, bjid, &free_msg);
- if (!decrypted_otr) {
- goto gotmessage_return;
- }
- body = decrypted_otr;
- }
-#endif
-
- // Check for unexpected groupchat messages
- // If we receive a groupchat message from a room we're not a member of,
- // this is probably a server issue and the best we can do is to send
- // a type unavailable.
- if (type == LM_MESSAGE_SUB_TYPE_GROUPCHAT && !roster_getnickname(bjid)) {
- // It shouldn't happen, probably a server issue
- GSList *room_elt;
- char *mbuf;
-
- mbuf = g_strdup_printf("Unexpected groupchat packet!");
- scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
- scr_WriteIncomingMessage(bjid, mbuf, 0, HBB_PREFIX_INFO, 0);
- g_free(mbuf);
-
- // Send back an unavailable packet
- xmpp_setstatus(offline, bjid, "", TRUE);
-
- // MUC
- // Make sure this is a room (it can be a conversion user->room)
- room_elt = roster_find(bjid, jidsearch, 0);
- if (!room_elt) {
- roster_add_user(bjid, NULL, NULL, ROSTER_TYPE_ROOM, sub_none, -1);
- } else {
- buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
- }
-
- buddylist_build();
- scr_DrawRoster();
- goto gotmessage_return;
- }
-
- // We don't call the message_in hook if 'block_unsubscribed' is true and
- // this is a regular message from an unsubscribed user.
- // System messages (from our server) are allowed.
- if ((!settings_opt_get_int("block_unsubscribed") ||
- (roster_getsubscription(bjid) & sub_from) ||
- (type == LM_MESSAGE_SUB_TYPE_CHAT)) ||
- ((s = settings_opt_get("server")) != NULL && !strcasecmp(bjid, s))) {
- gchar *fullbody = NULL;
- guint encrypted;
-
- if (decrypted_pgp)
- encrypted = ENCRYPTED_PGP;
- else if (otr_msg)
- encrypted = ENCRYPTED_OTR;
- else
- encrypted = 0;
-
- if (subject) {
- if (body)
- fullbody = g_strdup_printf("[%s]\n%s", subject, body);
- else
- fullbody = g_strdup_printf("[%s]\n", subject);
- body = fullbody;
- }
- hk_message_in(bjid, rname, timestamp, body, type, encrypted);
- g_free(fullbody);
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "Blocked a message from <%s>", bjid);
- }
-
-gotmessage_return:
- // Clean up and exit
- g_free(bjid);
- g_free(decrypted_pgp);
- if (free_msg)
- g_free(decrypted_otr);
-}
-
-
-static LmHandlerResult handle_messages(LmMessageHandler *handler,
- LmConnection *connection,
- LmMessage *m, gpointer user_data)
-{
- const char *p, *from=lm_message_get_from(m);
- char *r, *s;
- LmMessageNode *x;
- const char *body = NULL;
- const char *enc = NULL;
- const char *subject = NULL;
- time_t timestamp = 0L;
- LmMessageSubType mstype;
-
- mstype = lm_message_get_sub_type(m);
-
- body = lm_message_node_get_child_value(m->node, "body");
-
- x = lm_message_node_find_xmlns(m->node, NS_ENCRYPTED);
- if (x && (p = lm_message_node_get_value(x)) != NULL)
- enc = p;
-
- p = lm_message_node_get_child_value(m->node, "subject");
- if (p != NULL) {
- if (mstype != LM_MESSAGE_SUB_TYPE_GROUPCHAT) {
- // Chat message
- subject = p;
- } else { // Room topic
- GSList *roombuddy;
- gchar *mbuf;
- const gchar *subj = p;
- // Get the room (s) and the nickname (r)
- s = g_strdup(lm_message_get_from(m));
- r = strchr(s, JID_RESOURCE_SEPARATOR);
- if (r) *r++ = 0;
- else r = s;
- // Set the new topic
- roombuddy = roster_find(s, jidsearch, 0);
- if (roombuddy)
- buddy_settopic(roombuddy->data, subj);
- // Display inside the room window
- if (r == s) {
- // No specific resource (this is certainly history)
- mbuf = g_strdup_printf("The topic has been set to: %s", subj);
- } else {
- mbuf = g_strdup_printf("%s has set the topic to: %s", r, subj);
- }
- scr_WriteIncomingMessage(s, mbuf, 0,
- HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
- if (settings_opt_get_int("log_muc_conf"))
- hlog_write_message(s, 0, -1, mbuf);
- g_free(s);
- g_free(mbuf);
- // The topic is displayed in the chat status line, so refresh now.
- scr_UpdateChatStatus(TRUE);
- }
- }
-
- // Timestamp?
- timestamp = lm_message_node_get_timestamp(m->node);
-
- if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
- x = lm_message_node_get_child(m->node, "error");
- display_server_error(x);
-#if defined JEP0022 || defined JEP0085
- // If the JEP85/22 support is probed, set it back to unknown so that
- // we probe it again.
- chatstates_reset_probed(from);
-#endif
- } else {
- handle_state_events(from, m->node);
- }
- if (from && (body || subject))
- gotmessage(mstype, from, body, enc, subject, timestamp,
- lm_message_node_find_xmlns(m->node, NS_SIGNED));
- //report received message if message receipt was requested
- if (lm_message_node_get_child(m->node, "request")) {
- LmMessage *rcvd = lm_message_new(from, LM_MESSAGE_TYPE_MESSAGE);
- lm_message_node_set_attribute(rcvd->node, "id", lm_message_get_id(m));
- lm_message_node_set_attribute
- (lm_message_node_add_child(rcvd->node, "received", NULL),
- "xmlns", NS_RECEIPTS);
- lm_connection_send(connection, rcvd, NULL);
- lm_message_unref(rcvd);
- }
-
- if (from) {
- x = lm_message_node_find_xmlns(m->node,
- "http://jabber.org/protocol/muc#user");
- if (x && !strcmp(x->name, "x"))
- got_muc_message(from, x);
- }
-
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static LmHandlerResult cb_caps(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data)
-{
- char *ver = user_data;
- LmMessageSubType mstype = lm_message_get_sub_type(m);
-
- caps_add(ver);
- if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
- display_server_error(lm_message_node_get_child(m->node, "error"));
- } else if (mstype == LM_MESSAGE_SUB_TYPE_RESULT) {
- LmMessageNode *info;
- LmMessageNode *query = lm_message_node_get_child(m->node, "query");
-
- info = lm_message_node_get_child(query, "identity");
- if (info)
- caps_set_identity(ver, lm_message_node_get_attribute(info, "category"),
- lm_message_node_get_attribute(info, "name"),
- lm_message_node_get_attribute(info, "type"));
- info = lm_message_node_get_child(query, "feature");
- while (info) {
- if (!g_strcmp0(info->name, "feature"))
- caps_add_feature(ver, lm_message_node_get_attribute(info, "var"));
- info = info->next;
- }
- }
- g_free(ver);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static LmHandlerResult handle_presence(LmMessageHandler *handler,
- LmConnection *connection,
- LmMessage *m, gpointer user_data)
-{
- char *r;
- const char *from, *rname, *p=NULL, *ustmsg=NULL;
- enum imstatus ust;
- char bpprio;
- time_t timestamp = 0L;
- LmMessageNode *muc_packet, *caps;
- LmMessageSubType mstype;
-
- // Check for MUC presence packet
- muc_packet = lm_message_node_find_xmlns
- (m->node, "http://jabber.org/protocol/muc#user");
-
- from = lm_message_get_from(m);
-
- rname = strchr(from, JID_RESOURCE_SEPARATOR);
- if (rname) rname++;
-
- if (settings_opt_get_int("ignore_self_presence")) {
- const char *self_fjid = lm_connection_get_jid(connection);
- if (self_fjid && !strcasecmp(self_fjid, from)) {
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Ignoring self presence
- }
- }
-
- r = jidtodisp(from);
- mstype = lm_message_get_sub_type(m);
-
- if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
- LmMessageNode *x;
- scr_LogPrint(LPRINT_LOGNORM, "Error presence packet from <%s>", r);
- x = lm_message_node_find_child(m->node, "error");
- display_server_error(x);
- // Let's check it isn't a nickname conflict.
- // XXX Note: We should handle the <conflict/> string condition.
- if ((p = lm_message_node_get_attribute(x, "code")) != NULL) {
- if (atoi(p) == 409) {
- // 409 = conflict (nickname is in use or registered by another user)
- // If we are not inside this room, we should reset the nickname
- GSList *room_elt = roster_find(r, jidsearch, 0);
- if (room_elt && !buddy_getinsideroom(room_elt->data))
- buddy_setnickname(room_elt->data, NULL);
- }
- }
-
- g_free(r);
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
-
- p = lm_message_node_get_child_value(m->node, "priority");
- if (p && *p) bpprio = (gchar)atoi(p);
- else bpprio = 0;
-
- ust = available;
-
- p = lm_message_node_get_child_value(m->node, "show");
- if (p) {
- if (!strcmp(p, "away")) ust = away;
- else if (!strcmp(p, "dnd")) ust = dontdisturb;
- else if (!strcmp(p, "xa")) ust = notavail;
- else if (!strcmp(p, "chat")) ust = freeforchat;
- }
-
- if (mstype == LM_MESSAGE_SUB_TYPE_UNAVAILABLE)
- ust = offline;
-
- ustmsg = lm_message_node_get_child_value(m->node, "status");
-
- // Timestamp?
- timestamp = lm_message_node_get_timestamp(m->node);
-
- if (muc_packet) {
- // This is a MUC presence message
- handle_muc_presence(from, muc_packet, r, rname,
- ust, ustmsg, timestamp, bpprio);
- } else {
- // Not a MUC message, so this is a regular buddy...
- // Call hk_statuschange() if status has changed or if the
- // status message is different
- const char *msg;
- msg = roster_getstatusmsg(r, rname);
- if ((ust != roster_getstatus(r, rname)) ||
- (!ustmsg && msg && msg[0]) || (ustmsg && (!msg || strcmp(ustmsg, msg))))
- hk_statuschange(r, rname, bpprio, timestamp, ust, ustmsg);
- // Presence signature processing
- if (!ustmsg)
- ustmsg = ""; // Some clients omit the <status/> element :-(
- check_signature(r, rname, lm_message_node_find_xmlns(m->node, NS_SIGNED),
- ustmsg);
- }
-
- // XEP-0115 Entity Capabilities
- caps = lm_message_node_find_xmlns(m->node, NS_CAPS);
- if (caps && ust != offline) {
- const char *ver = lm_message_node_get_attribute(caps, "ver");
- GSList *sl_buddy = NULL;
- if (rname)
- sl_buddy = roster_find(r, jidsearch, ROSTER_TYPE_USER);
- // Only cache the caps if the user is on the roster
- if (sl_buddy && buddy_getonserverflag(sl_buddy->data)) {
- buddy_resource_setcaps(sl_buddy->data, rname, ver);
-
- if (!caps_has_hash(ver)) {
- char *node;
- LmMessageHandler *handler;
- LmMessage *iq = lm_message_new_with_sub_type(from, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_GET);
- node = g_strdup_printf("%s#%s",
- lm_message_node_get_attribute(caps, "node"),
- ver);
- lm_message_node_set_attributes
- (lm_message_node_add_child(iq->node, "query", NULL),
- "xmlns", NS_DISCO_INFO,
- "node", node,
- NULL);
- g_free(node);
- handler = lm_message_handler_new(cb_caps, g_strdup(ver), NULL);
- lm_connection_send_with_reply(connection, iq, handler, NULL);
- lm_message_unref(iq);
- lm_message_handler_unref(handler);
- }
- }
- }
-
- g_free(r);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-
-static LmHandlerResult handle_iq(LmMessageHandler *handler,
- LmConnection *connection,
- LmMessage *m, gpointer user_data)
-{
- int i;
- guint dbgflg;
- const char *xmlns = NULL;
- LmMessageNode *x;
- LmMessageSubType mstype = lm_message_get_sub_type(m);
-
- if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
- display_server_error(lm_message_node_get_child(m->node, "error"));
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
-
- for (x = m->node->children; x; x=x->next) {
- xmlns = lm_message_node_get_attribute(x, "xmlns");
- if (xmlns)
- for (i=0; iq_handlers[i].xmlns; ++i)
- if (!strcmp(iq_handlers[i].xmlns, xmlns))
- return iq_handlers[i].handler(NULL, connection, m, user_data);
- xmlns = NULL;
- }
-
- if ((mstype == LM_MESSAGE_SUB_TYPE_SET) ||
- (mstype == LM_MESSAGE_SUB_TYPE_GET))
- send_iq_error(connection, m, XMPP_ERROR_NOT_IMPLEMENTED);
-
- if (mstype == LM_MESSAGE_SUB_TYPE_RESULT)
- dbgflg = LPRINT_DEBUG;
- else
- dbgflg = LPRINT_NORMAL|LPRINT_DEBUG;
-
- scr_LogPrint(dbgflg, "Unhandled IQ: %s", lm_message_node_to_string(m->node));
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static LmHandlerResult handle_s10n(LmMessageHandler *handler,
- LmConnection *connection,
- LmMessage *m, gpointer user_data)
-{
- char *r;
- char *buf;
- int newbuddy;
- const char *from = lm_message_get_from(m);
- LmMessageSubType mstype;
-
- r = jidtodisp(from);
-
- newbuddy = !roster_find(r, jidsearch, 0);
- mstype = lm_message_get_sub_type(m);
-
- if (mstype == LM_MESSAGE_SUB_TYPE_SUBSCRIBE) {
- /* The sender wishes to subscribe to our presence */
- const char *msg;
- eviqs *evn;
-
- msg = lm_message_node_get_child_value(m->node, "status");
-
- buf = g_strdup_printf("<%s> wants to subscribe to your presence updates",
- from);
- scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
- g_free(buf);
-
- if (msg) {
- buf = g_strdup_printf("<%s> said: %s", from, msg);
- scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
- replace_nl_with_dots(buf);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
- g_free(buf);
- }
-
- // Create a new event item
- evn = evs_new(EVS_TYPE_SUBSCRIPTION, EVS_MAX_TIMEOUT);
- if (evn) {
- evn->callback = &evscallback_subscription;
- evn->data = g_strdup(r);
- evn->desc = g_strdup_printf("<%s> wants to subscribe to your "
- "presence updates", r);
- buf = g_strdup_printf("Please use /event %s accept|reject", evn->id);
- } else {
- buf = g_strdup_printf("Unable to create a new event!");
- }
- scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
- g_free(buf);
- } else if (mstype == LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE) {
- /* The sender is unsubscribing from our presence */
- xmpp_send_s10n(from, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED);
- buf = g_strdup_printf("<%s> is unsubscribing from your "
- "presence updates", from);
- scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
- g_free(buf);
- } else if (mstype == LM_MESSAGE_SUB_TYPE_SUBSCRIBED) {
- /* The sender has allowed us to receive their presence */
- buf = g_strdup_printf("<%s> has allowed you to receive their "
- "presence updates", from);
- scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
- g_free(buf);
- } else if (mstype == LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED) {
- /* The subscription request has been denied or a previously-granted
- subscription has been cancelled */
- roster_unsubscribed(from);
- update_roster = TRUE;
- buf = g_strdup_printf("<%s> has cancelled your subscription to "
- "their presence updates", from);
- scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
- g_free(buf);
- } else {
- g_free(r);
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
-
- if (newbuddy)
- update_roster = TRUE;
- g_free(r);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-//TODO: Use the enum of loudmouth, when it's included in the header...
-typedef enum {
- LM_LOG_LEVEL_VERBOSE = 1 << (G_LOG_LEVEL_USER_SHIFT),
- LM_LOG_LEVEL_NET = 1 << (G_LOG_LEVEL_USER_SHIFT + 1),
- LM_LOG_LEVEL_PARSER = 1 << (G_LOG_LEVEL_USER_SHIFT + 2),
- LM_LOG_LEVEL_SSL = 1 << (G_LOG_LEVEL_USER_SHIFT + 3),
- LM_LOG_LEVEL_SASL = 1 << (G_LOG_LEVEL_USER_SHIFT + 4),
- LM_LOG_LEVEL_ALL = (LM_LOG_LEVEL_NET |
- LM_LOG_LEVEL_VERBOSE |
- LM_LOG_LEVEL_PARSER |
- LM_LOG_LEVEL_SSL |
- LM_LOG_LEVEL_SASL)
-} LmLogLevelFlags;
-
-static void lm_debug_handler (const gchar *log_domain,
- GLogLevelFlags log_level,
- const gchar *message,
- gpointer user_data)
-{
- if (message && *message) {
- char *msg;
- int mcabber_loglevel = settings_opt_get_int("tracelog_level");
-
- if (mcabber_loglevel < 2)
- return;
-
- if (message[0] == '\n')
- msg = g_strdup(&message[1]);
- else
- msg = g_strdup(message);
-
- if (msg[strlen(msg)-1] == '\n')
- msg[strlen(msg)-1] = '\0';
-
- if (log_level & LM_LOG_LEVEL_VERBOSE) {
- scr_LogPrint(LPRINT_DEBUG, "LM-VERBOSE: %s", msg);
- }
- if (log_level & LM_LOG_LEVEL_NET) {
- if (mcabber_loglevel > 2)
- scr_LogPrint(LPRINT_DEBUG, "LM-NET: %s", msg);
- } else if (log_level & LM_LOG_LEVEL_PARSER) {
- if (mcabber_loglevel > 3)
- scr_LogPrint(LPRINT_DEBUG, "LM-PARSER: %s", msg);
- } else if (log_level & LM_LOG_LEVEL_SASL) {
- scr_LogPrint(LPRINT_DEBUG, "LM-SASL: %s", msg);
- } else if (log_level & LM_LOG_LEVEL_SSL) {
- scr_LogPrint(LPRINT_DEBUG, "LM-SSL: %s", msg);
- }
- g_free(msg);
- }
-}
-
-
-void xmpp_connect(void)
-{
- const char *userjid, *password, *resource, *servername, *ssl_fpr;
- char *dynresource = NULL;
- char fpr[16];
- const char *proxy_host;
- const char *resource_prefix = PACKAGE_NAME;
- char *fjid;
- int ssl, tls;
- LmSSL *lssl;
- unsigned int port;
- unsigned int ping;
- LmMessageHandler *handler;
- GError *error = NULL;
-
- if (lconnection && lm_connection_is_open(lconnection))
- xmpp_disconnect();
-
- servername = settings_opt_get("server");
- userjid = settings_opt_get("jid");
- password = settings_opt_get("password");
- resource = settings_opt_get("resource");
- proxy_host = settings_opt_get("proxy_host");
- ssl_fpr = settings_opt_get("ssl_fingerprint");
-
- if (!userjid) {
- scr_LogPrint(LPRINT_LOGNORM, "Your JID has not been specified!");
- return;
- }
- if (!password) {
- scr_LogPrint(LPRINT_LOGNORM, "Your password has not been specified!");
- return;
- }
-
- lconnection = lm_connection_new_with_context(NULL, main_context);
-
- g_log_set_handler("LM", LM_LOG_LEVEL_ALL, lm_debug_handler, NULL);
-
- ping = 40;
- if (settings_opt_get("pinginterval"))
- ping = (unsigned int) settings_opt_get_int("pinginterval");
- lm_connection_set_keep_alive_rate(lconnection, ping);
- scr_LogPrint(LPRINT_DEBUG, "Ping interval established: %d secs", ping);
-
- lm_connection_set_disconnect_function(lconnection, connection_close_cb,
- NULL, NULL);
-
- handler = lm_message_handler_new(handle_messages, NULL, NULL);
- lm_connection_register_message_handler(lconnection, handler,
- LM_MESSAGE_TYPE_MESSAGE,
- LM_HANDLER_PRIORITY_NORMAL);
- lm_message_handler_unref(handler);
-
- handler = lm_message_handler_new(handle_iq, NULL, NULL);
- lm_connection_register_message_handler(lconnection, handler,
- LM_MESSAGE_TYPE_IQ,
- LM_HANDLER_PRIORITY_NORMAL);
- lm_message_handler_unref(handler);
-
- handler = lm_message_handler_new(handle_presence, NULL, NULL);
- lm_connection_register_message_handler(lconnection, handler,
- LM_MESSAGE_TYPE_PRESENCE,
- LM_HANDLER_PRIORITY_LAST);
- lm_message_handler_unref(handler);
-
- handler = lm_message_handler_new(handle_s10n, NULL, NULL);
- lm_connection_register_message_handler(lconnection, handler,
- LM_MESSAGE_TYPE_PRESENCE,
- LM_HANDLER_PRIORITY_NORMAL);
- lm_message_handler_unref(handler);
-
- /* Connect to server */
- scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, "Connecting to server: %s",
- servername ? servername : "...");
- if (!resource)
- resource = resource_prefix;
-
- if (!settings_opt_get("disable_random_resource")) {
-#if HAVE_ARC4RANDOM
- dynresource = g_strdup_printf("%s.%08x", resource, arc4random());
-#else
- unsigned int tab[2];
- srand(time(NULL));
- tab[0] = (unsigned int) (0xffff * (rand() / (RAND_MAX + 1.0)));
- tab[1] = (unsigned int) (0xffff * (rand() / (RAND_MAX + 1.0)));
- dynresource = g_strdup_printf("%s.%04x%04x", resource, tab[0], tab[1]);
-#endif
- resource = dynresource;
- }
-
- port = (unsigned int) settings_opt_get_int("port");
-
- if (port)
- scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, " using port %d", port);
- scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, " resource %s", resource);
-
- if (proxy_host) {
- int proxy_port = settings_opt_get_int("proxy_port");
- if (proxy_port <= 0 || proxy_port > 65535) {
- scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, "Invalid proxy port: %d",
- proxy_port);
- } else {
- const char *proxy_user, *proxy_pass;
- LmProxy *lproxy;
- proxy_user = settings_opt_get("proxy_user");
- proxy_pass = settings_opt_get("proxy_pass");
- // Proxy initialization
- lproxy = lm_proxy_new_with_server(LM_PROXY_TYPE_HTTP,
- proxy_host, proxy_port);
- lm_proxy_set_username(lproxy, proxy_user);
- lm_proxy_set_password(lproxy, proxy_pass);
- lm_connection_set_proxy(lconnection, lproxy);
- lm_proxy_unref(lproxy);
- scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, " using proxy %s:%d",
- proxy_host, proxy_port);
- }
- }
-
- fjid = compose_jid(userjid, servername, resource);
- lm_connection_set_jid(lconnection, fjid);
- if (servername)
- lm_connection_set_server(lconnection, servername);
-#if defined(HAVE_LIBOTR)
- otr_init(fjid);
-#endif
- g_free(fjid);
- g_free(dynresource);
-
- ssl = settings_opt_get_int("ssl");
- tls = settings_opt_get_int("tls");
-
- if (!lm_ssl_is_supported()) {
- if (ssl || tls) {
- scr_LogPrint(LPRINT_LOGNORM, "** Error: SSL is NOT available, "
- "please recompile loudmouth with SSL enabled.");
- return;
- }
- }
-
- if (ssl && tls) {
- scr_LogPrint(LPRINT_LOGNORM, "You can only set ssl or tls, not both.");
- return;
- }
-
- if (!port)
- port = (ssl ? LM_CONNECTION_DEFAULT_PORT_SSL : LM_CONNECTION_DEFAULT_PORT);
- lm_connection_set_port(lconnection, port);
-
- if (ssl_fpr && (!hex_to_fingerprint(ssl_fpr, fpr))) {
- scr_LogPrint(LPRINT_LOGNORM, "** Plese set the fingerprint in the format "
- "97:5C:00:3F:1D:77:45:25:E2:C5:70:EC:83:C8:87:EE");
- return;
- }
-
- lssl = lm_ssl_new((ssl_fpr ? fpr : NULL), ssl_cb, NULL, NULL);
- if (lssl) {
- lm_ssl_use_starttls(lssl, !ssl, tls);
- lm_connection_set_ssl(lconnection, lssl);
- lm_ssl_unref(lssl);
- } else if (ssl || tls) {
- scr_LogPrint(LPRINT_LOGNORM, "** Error: Couldn't create SSL struct.");
- return;
- }
-
- if (!lm_connection_open(lconnection, connection_open_cb,
- NULL, FALSE, &error)) {
- _try_to_reconnect();
- scr_LogPrint(LPRINT_LOGNORM, "Failed to open: %s\n", error->message);
- g_error_free (error);
- }
-}
-
-// insert_entity_capabilities(presence_stanza)
-// Entity Capabilities (XEP-0115)
-static void insert_entity_capabilities(LmMessageNode *x, enum imstatus status)
-{
- LmMessageNode *y;
- const char *ver = entity_version(status);
-
- y = lm_message_node_add_child(x, "c", NULL);
- lm_message_node_set_attribute(y, "xmlns", NS_CAPS);
- lm_message_node_set_attribute(y, "hash", "sha-1");
- lm_message_node_set_attribute(y, "node", MCABBER_CAPS_NODE);
- lm_message_node_set_attribute(y, "ver", ver);
-}
-
-void xmpp_disconnect(void)
-{
- if (!lconnection || !lm_connection_is_authenticated(lconnection))
- return;
-
- // Launch pre-disconnect internal hook
- hook_execute_internal("hook-pre-disconnect");
- // Announce it to everyone else
- xmpp_setstatus(offline, NULL, "", FALSE);
- lm_connection_close(lconnection, NULL);
-}
-
-void xmpp_setstatus(enum imstatus st, const char *recipient, const char *msg,
- int do_not_sign)
-{
- LmMessage *m;
-
- if (msg) {
- // The status message has been specified. We'll use it, unless it is
- // "-" which is a special case (option meaning "no status message").
- if (!strcmp(msg, "-"))
- msg = "";
- } else {
- // No status message specified; we'll use:
- // a) the default status message (if provided by the user);
- // b) the current status message;
- // c) no status message (i.e. an empty one).
- msg = settings_get_status_msg(st);
- if (!msg) {
- if (mystatusmsg)
- msg = mystatusmsg;
- else
- msg = "";
- }
- }
-
- // Only send the packet if we're online.
- // (But we want to update internal status even when disconnected,
- // in order to avoid some problems during network failures)
- if (lm_connection_is_authenticated(lconnection)) {
- const char *s_msg = (st != invisible ? msg : NULL);
- m = lm_message_new_presence(st, recipient, s_msg);
- insert_entity_capabilities(m->node, st); // Entity Capabilities (XEP-0115)
-#ifdef HAVE_GPGME
- if (!do_not_sign && gpg_enabled()) {
- char *signature;
- signature = gpg_sign(s_msg ? s_msg : "");
- if (signature) {
- LmMessageNode *y;
- y = lm_message_node_add_child(m->node, "x", signature);
- lm_message_node_set_attribute(y, "xmlns", NS_SIGNED);
- g_free(signature);
- }
- }
-#endif
- lm_connection_send(lconnection, m, NULL);
- lm_message_unref(m);
- }
-
- // If we didn't change our _global_ status, we are done
- if (recipient) return;
-
- if (lm_connection_is_authenticated(lconnection)) {
- // Send presence to chatrooms
- if (st != invisible) {
- struct T_presence room_presence;
- room_presence.st = st;
- room_presence.msg = msg;
- foreach_buddy(ROSTER_TYPE_ROOM, &roompresence, &room_presence);
- }
-
- // We'll have to update the roster if we switch to/from offline because
- // we don't know the presences of buddies when offline...
- if (mystatus == offline || st == offline)
- update_roster = TRUE;
-
- hk_mystatuschange(0, mystatus, st, (st != invisible ? msg : ""));
- mystatus = st;
- }
-
- if (st)
- mywantedstatus = st;
-
- if (msg != mystatusmsg) {
- g_free(mystatusmsg);
- if (*msg)
- mystatusmsg = g_strdup(msg);
- else
- mystatusmsg = NULL;
- }
-
- if (!Autoaway)
- update_last_use();
-
- // Update status line
- scr_UpdateMainStatus(TRUE);
-}
-
-
-enum imstatus xmpp_getstatus(void)
-{
- return mystatus;
-}
-
-const char *xmpp_getstatusmsg(void)
-{
- return mystatusmsg;
-}
-
-// xmpp_setprevstatus()
-// Set previous status. This wrapper function is used after a disconnection.
-void xmpp_setprevstatus(void)
-{
- xmpp_setstatus(mywantedstatus, NULL, mystatusmsg, FALSE);
-}
-
-// send_storage(store)
-// Send the node "store" to update the server.
-// Note: the sender should check we're online.
-void send_storage(LmMessageNode *store)
-{
- LmMessage *iq;
- LmMessageNode *query;
-
- if (!rosternotes) return;
-
- iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_SET);
- query = lm_message_node_add_child(iq->node, "query", NULL);
- lm_message_node_set_attribute(query, "xmlns", NS_PRIVATE);
- lm_message_node_insert_childnode(query, store);
-
- lm_connection_send(lconnection, iq, NULL);
- lm_message_unref(iq);
-}
-
-
-// xmpp_is_bookmarked(roomjid)
-// Return TRUE if there's a bookmark for the given jid.
-guint xmpp_is_bookmarked(const char *bjid)
-{
- LmMessageNode *x;
-
- if (!bookmarks)
- return FALSE;
-
- // Walk through the storage bookmark tags
- for (x = bookmarks->children ; x; x = x->next) {
- // If the node is a conference item, check the jid.
- if (x->name && !strcmp(x->name, "conference")) {
- const char *fjid = lm_message_node_get_attribute(x, "jid");
- if (fjid && !strcasecmp(bjid, fjid))
- return TRUE;
- }
- }
- return FALSE;
-}
-
-// xmpp_get_bookmark_nick(roomjid)
-// Return the room nickname if it is present in a bookmark.
-const char *xmpp_get_bookmark_nick(const char *bjid)
-{
- LmMessageNode *x;
-
- if (!bookmarks || !bjid)
- return NULL;
-
- // Walk through the storage bookmark tags
- for (x = bookmarks->children ; x; x = x->next) {
- // If the node is a conference item, check the jid.
- if (x->name && !strcmp(x->name, "conference")) {
- const char *fjid = lm_message_node_get_attribute(x, "jid");
- if (fjid && !strcasecmp(bjid, fjid))
- return lm_message_node_get_child_value(x, "nick");
- }
- }
- return NULL;
-}
-
-
-// xmpp_get_all_storage_bookmarks()
-// Return a GSList with all storage bookmarks.
-// The caller should g_free the list (not the MUC jids).
-GSList *xmpp_get_all_storage_bookmarks(void)
-{
- LmMessageNode *x;
- GSList *sl_bookmarks = NULL;
-
- // If we have no bookmarks, probably the server doesn't support them.
- if (!bookmarks)
- return NULL;
-
- // Walk through the storage bookmark tags
- for (x = bookmarks->children ; x; x = x->next) {
- // If the node is a conference item, let's add the note to our list.
- if (x->name && !strcmp(x->name, "conference")) {
- struct bookmark *bm_elt;
- const char *autojoin, *name, *nick;
- const char *fjid = lm_message_node_get_attribute(x, "jid");
- if (!fjid)
- continue;
- bm_elt = g_new0(struct bookmark, 1);
- bm_elt->roomjid = g_strdup(fjid);
- autojoin = lm_message_node_get_attribute(x, "autojoin");
- nick = lm_message_node_get_attribute(x, "nick");
- name = lm_message_node_get_attribute(x, "name");
- if (autojoin && !strcmp(autojoin, "1"))
- bm_elt->autojoin = 1;
- if (nick)
- bm_elt->nick = g_strdup(nick);
- if (name)
- bm_elt->name = g_strdup(name);
- sl_bookmarks = g_slist_append(sl_bookmarks, bm_elt);
- }
- }
- return sl_bookmarks;
-}
-
-// xmpp_set_storage_bookmark(roomid, name, nick, passwd, autojoin,
-// printstatus, autowhois)
-// Update the private storage bookmarks: add a conference room.
-// If name is nil, we remove the bookmark.
-void xmpp_set_storage_bookmark(const char *roomid, const char *name,
- const char *nick, const char *passwd,
- int autojoin, enum room_printstatus pstatus,
- enum room_autowhois awhois)
-{
- LmMessageNode *x;
- bool changed = FALSE;
-
- if (!roomid)
- return;
-
- // If we have no bookmarks, probably the server doesn't support them.
- if (!bookmarks) {
- scr_LogPrint(LPRINT_NORMAL,
- "Sorry, your server doesn't seem to support private storage.");
- return;
- }
-
- // Walk through the storage tags
- for (x = bookmarks->children ; x; x = x->next) {
- // If the current node is a conference item, see if we have to replace it.
- if (x->name && !strcmp(x->name, "conference")) {
- const char *fjid = lm_message_node_get_attribute(x, "jid");
- if (!fjid)
- continue;
- if (!strcmp(fjid, roomid)) {
- // We've found a bookmark for this room. Let's hide it and we'll
- // create a new one.
- lm_message_node_hide(x);
- changed = TRUE;
- if (!name)
- scr_LogPrint(LPRINT_LOGNORM, "Deleting bookmark...");
- }
- }
- }
-
- // Let's create a node/bookmark for this roomid, if the name is not NULL.
- if (name) {
- x = lm_message_node_add_child(bookmarks, "conference", NULL);
- lm_message_node_set_attributes(x,
- "jid", roomid,
- "name", name,
- "autojoin", autojoin ? "1" : "0",
- NULL);
- if (nick)
- lm_message_node_add_child(x, "nick", nick);
- if (passwd)
- lm_message_node_add_child(x, "password", passwd);
- if (pstatus)
- lm_message_node_add_child(x, "print_status", strprintstatus[pstatus]);
- if (awhois)
- lm_message_node_set_attributes(x, "autowhois",
- (awhois == autowhois_on) ? "1" : "0",
- NULL);
- changed = TRUE;
- scr_LogPrint(LPRINT_LOGNORM, "Updating bookmarks...");
- }
-
- if (!changed)
- return;
-
- if (lm_connection_is_authenticated(lconnection))
- send_storage(bookmarks);
- else
- scr_LogPrint(LPRINT_LOGNORM,
- "Warning: you're not connected to the server.");
-}
-
-static struct annotation *parse_storage_rosternote(LmMessageNode *notenode)
-{
- const char *p;
- struct annotation *note = g_new0(struct annotation, 1);
- p = lm_message_node_get_attribute(notenode, "cdate");
- if (p)
- note->cdate = from_iso8601(p, 1);
- p = lm_message_node_get_attribute(notenode, "mdate");
- if (p)
- note->mdate = from_iso8601(p, 1);
- note->text = g_strdup(lm_message_node_get_value(notenode));
- note->jid = g_strdup(lm_message_node_get_attribute(notenode, "jid"));
- return note;
-}
-
-// xmpp_get_all_storage_rosternotes()
-// Return a GSList with all storage annotations.
-// The caller should g_free the list and its contents.
-GSList *xmpp_get_all_storage_rosternotes(void)
-{
- LmMessageNode *x;
- GSList *sl_notes = NULL;
-
- // If we have no rosternotes, probably the server doesn't support them.
- if (!rosternotes)
- return NULL;
-
- // Walk through the storage rosternotes tags
- for (x = rosternotes->children ; x; x = x->next) {
- struct annotation *note;
-
- // We want a note item
- if (!x->name || strcmp(x->name, "note"))
- continue;
- // Just in case, check the jid...
- if (!lm_message_node_get_attribute(x, "jid"))
- continue;
- // Ok, let's add the note to our list
- note = parse_storage_rosternote(x);
- sl_notes = g_slist_append(sl_notes, note);
- }
- return sl_notes;
-}
-
-// xmpp_get_storage_rosternotes(barejid, silent)
-// Return the annotation associated with this jid.
-// If silent is TRUE, no warning is displayed when rosternotes is disabled
-// The caller should g_free the string and structure after use.
-struct annotation *xmpp_get_storage_rosternotes(const char *barejid, int silent)
-{
- LmMessageNode *x;
-
- if (!barejid)
- return NULL;
-
- // If we have no rosternotes, probably the server doesn't support them.
- if (!rosternotes) {
- if (!silent)
- scr_LogPrint(LPRINT_NORMAL, "Sorry, "
- "your server doesn't seem to support private storage.");
- return NULL;
- }
-
- // Walk through the storage rosternotes tags
- for (x = rosternotes->children ; x; x = x->next) {
- const char *fjid;
- // We want a note item
- if (!x->name || strcmp(x->name, "note"))
- continue;
- // Just in case, check the jid...
- fjid = lm_message_node_get_attribute(x, "jid");
- if (fjid && !strcmp(fjid, barejid)) // We've found a note for this contact.
- return parse_storage_rosternote(x);
- }
- return NULL; // No note found
-}
-
-// xmpp_set_storage_rosternotes(barejid, note)
-// Update the private storage rosternotes: add/delete a note.
-// If note is nil, we remove the existing note.
-void xmpp_set_storage_rosternotes(const char *barejid, const char *note)
-{
- LmMessageNode *x;
- bool changed = FALSE;
- const char *cdate = NULL;
-
- if (!barejid)
- return;
-
- // If we have no rosternotes, probably the server doesn't support them.
- if (!rosternotes) {
- scr_LogPrint(LPRINT_NORMAL,
- "Sorry, your server doesn't seem to support private storage.");
- return;
- }
-
- // Walk through the storage tags
- for (x = rosternotes->children ; x; x = x->next) {
- // If the current node is a conference item, see if we have to replace it.
- if (x->name && !strcmp(x->name, "note")) {
- const char *fjid = lm_message_node_get_attribute(x, "jid");
- if (!fjid)
- continue;
- if (!strcmp(fjid, barejid)) {
- // We've found a note for this jid. Let's hide it and we'll
- // create a new one.
- cdate = lm_message_node_get_attribute(x, "cdate");
- lm_message_node_hide(x);
- changed = TRUE;
- break;
- }
- }
- }
-
- // Let's create a node for this jid, if the note is not NULL.
- if (note) {
- char mdate[20];
- time_t now;
- time(&now);
- to_iso8601(mdate, now);
- if (!cdate)
- cdate = mdate;
- x = lm_message_node_add_child(rosternotes, "note", note);
- lm_message_node_set_attributes(x,
- "jid", barejid,
- "cdate", cdate,
- "mdate", mdate,
- NULL);
- changed = TRUE;
- }
-
- if (!changed)
- return;
-
- if (lm_connection_is_authenticated(lconnection))
- send_storage(rosternotes);
- else
- scr_LogPrint(LPRINT_LOGNORM,
- "Warning: you're not connected to the server.");
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-#ifndef __XMPP_H__
-#define __XMPP_H__ 1
-
-#include <loudmouth/loudmouth.h>
-#include "roster.h"
-
-enum iqreq_type {
- iqreq_none,
- iqreq_version,
- iqreq_time,
- iqreq_last,
- iqreq_vcard
-};
-
-struct annotation {
- time_t cdate;
- time_t mdate;
- gchar *jid;
- gchar *text;
-};
-
-struct bookmark {
- gchar *roomjid;
- gchar *name;
- gchar *nick;
- guint autojoin;
- /* enum room_printstatus pstatus; */
- /* enum room_autowhois awhois; */
-};
-
-extern LmConnection* lconnection;
-extern LmSSL* lssl;
-
-void xmpp_connect(void);
-void xmpp_disconnect(void);
-
-void xmpp_room_join(const char *room, const char *nickname, const char *passwd);
-int xmpp_room_setattrib(const char *roomid, const char *fjid,
- const char *nick, struct role_affil ra,
- const char *reason);
-void xmpp_room_invite(const char *room, const char *fjid, const char *reason);
-void xmpp_room_unlock(const char *room);
-void xmpp_room_destroy(const char *room, const char *venue, const char *reason);
-
-void xmpp_addbuddy(const char *bjid, const char *name, const char *group);
-void xmpp_updatebuddy(const char *bjid, const char *name, const char *group);
-void xmpp_delbuddy(const char *bjid);
-
-void xmpp_send_msg(const char *fjid, const char *text, int type,
- const char *subject, gboolean otrinject, gint *encrypted,
- LmMessageSubType type_overwrite, gpointer *xep184);
-
-void xmpp_send_s10n(const char *bjid, LmMessageSubType type);
-
-enum imstatus xmpp_getstatus(void);
-const char *xmpp_getstatusmsg(void);
-void xmpp_setprevstatus(void);
-
-void xmpp_setstatus(enum imstatus st, const char *recipient,
- const char *msg, int do_not_sign);
-
-void xmpp_send_chatstate(gpointer buddy, guint chatstate);
-
-GSList *xmpp_get_all_storage_bookmarks(void);
-GSList *xmpp_get_all_storage_rosternotes(void);
-void xmpp_set_storage_bookmark(const char *roomid, const char *name,
- const char *nick, const char *passwd,
- int autojoin, enum room_printstatus pstatus,
- enum room_autowhois awhois);
-struct annotation *xmpp_get_storage_rosternotes(const char *barejid,
- int silent);
-void xmpp_set_storage_rosternotes(const char *barejid, const char *note);
-guint xmpp_is_bookmarked(const char *bjid);
-const char *xmpp_get_bookmark_nick(const char *bjid);
-
-void xmpp_request(const char *fjid, enum iqreq_type reqtype);
-void request_vcard(const char *bjid);
-void xmpp_request_storage(const gchar *storage);
-
-#endif /* __XMPP_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_defines.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-#ifndef __XMPP_DEFINES_H__
-#define __XMPP_DEFINES_H__ 1
-
-#define MCABBER_CAPS_NODE "http://mcabber.com/caps"
-
-#define NS_CLIENT "jabber:client"
-#define NS_SERVER "jabber:server"
-#define NS_DIALBACK "jabber:server:dialback"
-#define NS_AUTH "jabber:iq:auth"
-#define NS_AUTH_CRYPT "jabber:iq:auth:crypt"
-#define NS_REGISTER "jabber:iq:register"
-#define NS_ROSTER "jabber:iq:roster"
-#define NS_OFFLINE "jabber:x:offline"
-#define NS_AGENT "jabber:iq:agent"
-#define NS_AGENTS "jabber:iq:agents"
-#define NS_DELAY "jabber:x:delay"
-#define NS_VERSION "jabber:iq:version"
-#define NS_TIME "jabber:iq:time"
-#define NS_VCARD "vcard-temp"
-#define NS_PRIVATE "jabber:iq:private"
-#define NS_SEARCH "jabber:iq:search"
-#define NS_OOB "jabber:iq:oob"
-#define NS_XOOB "jabber:x:oob"
-#define NS_ADMIN "jabber:iq:admin"
-#define NS_FILTER "jabber:iq:filter"
-#define NS_AUTH_0K "jabber:iq:auth:0k"
-#define NS_BROWSE "jabber:iq:browse"
-#define NS_EVENT "jabber:x:event"
-#define NS_CONFERENCE "jabber:iq:conference"
-#define NS_SIGNED "jabber:x:signed"
-#define NS_ENCRYPTED "jabber:x:encrypted"
-#define NS_GATEWAY "jabber:iq:gateway"
-#define NS_LAST "jabber:iq:last"
-#define NS_ENVELOPE "jabber:x:envelope"
-#define NS_EXPIRE "jabber:x:expire"
-#define NS_XHTML "http://www.w3.org/1999/xhtml"
-#define NS_DISCO_INFO "http://jabber.org/protocol/disco#info"
-#define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items"
-#define NS_IQ_AUTH "http://jabber.org/features/iq-auth"
-#define NS_REGISTER_FEATURE "http://jabber.org/features/iq-register"
-
-#define NS_CAPS "http://jabber.org/protocol/caps"
-#define NS_CHATSTATES "http://jabber.org/protocol/chatstates"
-#define NS_COMMANDS "http://jabber.org/protocol/commands"
-#define NS_MUC "http://jabber.org/protocol/muc"
-
-#define NS_XDBGINSERT "jabber:xdb:ginsert"
-#define NS_XDBNSLIST "jabber:xdb:nslist"
-
-#define NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
-#define NS_XMPP_TLS "urn:ietf:params:xml:ns:xmpp-tls"
-#define NS_XMPP_STREAMS "urn:ietf:params:xml:ns:xmpp-streams"
-
-#define NS_XMPP_DELAY "urn:xmpp:delay"
-#define NS_XMPP_TIME "urn:xmpp:time"
-#define NS_PING "urn:xmpp:ping"
-#define NS_RECEIPTS "urn:xmpp:receipts"
-
-#define NS_JABBERD_STOREDPRESENCE "http://jabberd.org/ns/storedpresence"
-#define NS_JABBERD_HISTORY "http://jabberd.org/ns/history"
-
-#define XMPP_ERROR_REDIRECT 302
-#define XMPP_ERROR_BAD_REQUEST 400
-#define XMPP_ERROR_NOT_AUTHORIZED 401
-#define XMPP_ERROR_PAYMENT_REQUIRED 402
-#define XMPP_ERROR_FORBIDDEN 403
-#define XMPP_ERROR_NOT_FOUND 404
-#define XMPP_ERROR_NOT_ALLOWED 405
-#define XMPP_ERROR_NOT_ACCEPTABLE 406
-#define XMPP_ERROR_REGISTRATION_REQUIRED 407
-#define XMPP_ERROR_REQUEST_TIMEOUT 408
-#define XMPP_ERROR_CONFLICT 409
-#define XMPP_ERROR_INTERNAL_SERVER_ERROR 500
-#define XMPP_ERROR_NOT_IMPLEMENTED 501
-#define XMPP_ERROR_REMOTE_SERVER_ERROR 502
-#define XMPP_ERROR_SERVICE_UNAVAILABLE 503
-#define XMPP_ERROR_REMOTE_SERVER_TIMEOUT 504
-#define XMPP_ERROR_DISCONNECTED 510
-
-#endif
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_helper.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,400 +0,0 @@
-/*
- * xmpp_helper.c -- Jabber protocol helper functions
- *
- * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- * Some parts initially came from the centericq project:
- * Copyright (C) 2002-2005 by Konstantin Klyagin <konst@konst.org.ua>
- *
- * 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 <string.h>
-#include <stdlib.h>
-
-#include "xmpp_helper.h"
-#include "settings.h"
-#include "utils.h"
-#include "caps.h"
-#include "logprint.h"
-#include "config.h"
-
-time_t iqlast; // last message/status change time
-
-extern char *imstatus_showmap[];
-
-struct xmpp_error xmpp_errors[] = {
- {XMPP_ERROR_REDIRECT, "302",
- "Redirect", "redirect", "modify"},
- {XMPP_ERROR_BAD_REQUEST, "400",
- "Bad Request", "bad-request", "modify"},
- {XMPP_ERROR_NOT_AUTHORIZED, "401",
- "Not Authorized", "not-authorized", "auth"},
- {XMPP_ERROR_PAYMENT_REQUIRED, "402",
- "Payment Required", "payment-required", "auth"},
- {XMPP_ERROR_FORBIDDEN, "403",
- "Forbidden", "forbidden", "auth"},
- {XMPP_ERROR_NOT_FOUND, "404",
- "Not Found", "item-not-found", "cancel"},
- {XMPP_ERROR_NOT_ALLOWED, "405",
- "Not Allowed", "not-allowed", "cancel"},
- {XMPP_ERROR_NOT_ACCEPTABLE, "406",
- "Not Acceptable", "not-acceptable", "modify"},
- {XMPP_ERROR_REGISTRATION_REQUIRED, "407",
- "Registration required", "registration-required", "auth"},
- {XMPP_ERROR_REQUEST_TIMEOUT, "408",
- "Request Timeout", "remote-server-timeout", "wait"},
- {XMPP_ERROR_CONFLICT, "409",
- "Conflict", "conflict", "cancel"},
- {XMPP_ERROR_INTERNAL_SERVER_ERROR, "500",
- "Internal Server Error", "internal-server-error", "wait"},
- {XMPP_ERROR_NOT_IMPLEMENTED, "501",
- "Not Implemented", "feature-not-implemented", "cancel"},
- {XMPP_ERROR_REMOTE_SERVER_ERROR, "502",
- "Remote Server Error", "service-unavailable", "wait"},
- {XMPP_ERROR_SERVICE_UNAVAILABLE, "503",
- "Service Unavailable", "service-unavailable", "cancel"},
- {XMPP_ERROR_REMOTE_SERVER_TIMEOUT, "504",
- "Remote Server Timeout", "remote-server-timeout", "wait"},
- {XMPP_ERROR_DISCONNECTED, "510",
- "Disconnected", "service-unavailable", "cancel"},
- {0, NULL, NULL, NULL, NULL}
-};
-
-
-#ifdef MODULES_ENABLE
-static GSList *xmpp_additional_features = NULL;
-static char *ver, *ver_notavail;
-
-void xmpp_add_feature (const char *xmlns)
-{
- if (xmlns) {
- ver = NULL;
- ver_notavail = NULL;
- xmpp_additional_features = g_slist_append(xmpp_additional_features,
- g_strdup (xmlns));
- }
-}
-
-void xmpp_del_feature (const char *xmlns)
-{
- GSList *feature = xmpp_additional_features;
- while (feature) {
- if (!strcmp(feature->data, xmlns)) {
- ver = NULL;
- ver_notavail = NULL;
- g_free (feature->data);
- xmpp_additional_features = g_slist_delete_link(xmpp_additional_features,
- feature);
- return;
- }
- feature = g_slist_next (feature);
- }
-}
-#endif
-
-const gchar* lm_message_node_get_child_value(LmMessageNode *node,
- const gchar *child)
-{
- LmMessageNode *tmp;
- tmp = lm_message_node_find_child(node, child);
- if (tmp)
- return lm_message_node_get_value(tmp);
- else return NULL;
-}
-
-static LmMessageNode *hidden = NULL;
-
-void lm_message_node_hide(LmMessageNode *node)
-{
- LmMessageNode *parent = node->parent, *prev_sibling = node->prev;
-
- if (hidden) {
- hidden->children = hidden->next = hidden->prev = hidden->parent = NULL;
- lm_message_node_unref(hidden);
- }
-
- if (parent->children == node)
- parent->children = node->next;
- if (prev_sibling)
- prev_sibling->next = node->next;
- if (node->next)
- node->next->prev = prev_sibling;
-}
-
-//maybe not a good idea, because it uses internals of loudmouth...
-//it's used for rosternotes/bookmarks
-LmMessageNode *lm_message_node_new(const gchar *name, const gchar *xmlns)
-{
- LmMessageNode *node;
-
- node = g_new0 (LmMessageNode, 1);
- node->name = g_strdup (name);
- node->value = NULL;
- node->raw_mode = FALSE;
- node->attributes = NULL;
- node->next = NULL;
- node->prev = NULL;
- node->parent = NULL;
- node->children = NULL;
-
- node->ref_count = 1;
- lm_message_node_set_attribute(node, "xmlns", xmlns);
- return node;
-}
-
-void lm_message_node_insert_childnode(LmMessageNode *node,
- LmMessageNode *child)
-{
- LmMessageNode *x;
- lm_message_node_deep_ref(child);
-
- if (node->children == NULL)
- node->children = child;
- else {
- for (x = node->children; x->next; x = x->next)
- ;
- x->next = child;
- }
-}
-
-void lm_message_node_deep_ref(LmMessageNode *node)
-{
- if (node == NULL)
- return;
- lm_message_node_ref(node);
- lm_message_node_deep_ref(node->next);
- lm_message_node_deep_ref(node->children);
-}
-
-const gchar* lm_message_get_from(LmMessage *m)
-{
- return lm_message_node_get_attribute(m->node, "from");
-}
-
-const gchar* lm_message_get_id(LmMessage *m)
-{
- return lm_message_node_get_attribute(m->node, "id");
-}
-
-LmMessage *lm_message_new_iq_from_query(LmMessage *m,
- LmMessageSubType type)
-{
- LmMessage *new;
- const char *from = lm_message_node_get_attribute(m->node, "from");
- const char *id = lm_message_node_get_attribute(m->node, "id");
-
- new = lm_message_new_with_sub_type(from, LM_MESSAGE_TYPE_IQ,
- type);
- if (id)
- lm_message_node_set_attribute(new->node, "id", id);
-
- return new;
-}
-
-// entity_version(enum imstatus status)
-// Return a static version string for Entity Capabilities.
-// It should be specific to the client version, please change the id
-// if you alter mcabber's disco support (or add something to the version
-// number) so that it doesn't conflict with the official client.
-const char *entity_version(enum imstatus status)
-{
-#ifndef MODULES_ENABLE
- static char *ver, *ver_notavail;
-#endif
-
- if (ver && (status != notavail))
- return ver;
- if (ver_notavail)
- return ver_notavail;
-
- caps_add("");
- caps_set_identity("", "client", PACKAGE_STRING, "pc");
- caps_add_feature("", NS_DISCO_INFO);
- caps_add_feature("", NS_MUC);
- // advertise ChatStates only if they aren't disabled
- if (!settings_opt_get_int("disable_chatstates"))
- caps_add_feature("", NS_CHATSTATES);
- caps_add_feature("", NS_TIME);
- caps_add_feature("", NS_XMPP_TIME);
- caps_add_feature("", NS_VERSION);
- caps_add_feature("", NS_PING);
- caps_add_feature("", NS_COMMANDS);
- caps_add_feature("", NS_RECEIPTS);
- if (!settings_opt_get_int("iq_last_disable") &&
- (!settings_opt_get_int("iq_last_disable_when_notavail") ||
- status != notavail))
- caps_add_feature("", NS_LAST);
-#ifdef MODULES_ENABLE
- {
- GSList *el = xmpp_additional_features;
- while (el) {
- caps_add_feature("", el->data);
- el = g_slist_next (el);
- }
- }
-#endif
-
- if (status == notavail) {
- ver_notavail = caps_generate();
- return ver_notavail;
- }
-
- ver = caps_generate();
- return ver;
-}
-
-LmMessageNode *lm_message_node_find_xmlns(LmMessageNode *node,
- const char *xmlns)
-{
- LmMessageNode *x;
- const char *p;
-
- for (x = node->children ; x; x = x->next) {
- if ((p = lm_message_node_get_attribute(x, "xmlns")) && !strcmp(p, xmlns))
- break;
- }
- return x;
-}
-
-time_t lm_message_node_get_timestamp(LmMessageNode *node)
-{
- LmMessageNode *x;
- const char *p;
-
- x = lm_message_node_find_xmlns(node, NS_XMPP_DELAY);
- if (x && (!strcmp(x->name, "delay")) &&
- (p = lm_message_node_get_attribute(x, "stamp")) != NULL)
- return from_iso8601(p, 1);
- x = lm_message_node_find_xmlns(node, NS_DELAY);
- if (x && (p = lm_message_node_get_attribute(x, "stamp")) != NULL)
- return from_iso8601(p, 1);
- return 0;
-}
-
-// lm_message_new_presence(status, recipient, message)
-// Create an xmlnode with default presence attributes
-// Note: the caller must free the node after use
-LmMessage *lm_message_new_presence(enum imstatus st,
- const char *recipient,
- const char *msg)
-{
- unsigned int prio;
- LmMessage *x = lm_message_new(recipient, LM_MESSAGE_TYPE_PRESENCE);
-
- switch(st) {
- case away:
- case notavail:
- case dontdisturb:
- case freeforchat:
- lm_message_node_add_child(x->node, "show", imstatus_showmap[st]);
- break;
-
- case invisible:
- lm_message_node_set_attribute(x->node, "type", "invisible");
- break;
-
- case offline:
- lm_message_node_set_attribute(x->node, "type", "unavailable");
- break;
-
- default:
- break;
- }
-
- if (st == away || st == notavail)
- prio = settings_opt_get_int("priority_away");
- else
- prio = settings_opt_get_int("priority");
-
- if (prio) {
- char strprio[8];
- snprintf(strprio, 8, "%d", (int)prio);
- lm_message_node_add_child(x->node, "priority", strprio);
- }
-
- if (msg)
- lm_message_node_add_child(x->node, "status", msg);
-
- return x;
-}
-
-static const char *defaulterrormsg(guint code)
-{
- int i;
-
- for (i = 0; xmpp_errors[i].code; ++i) {
- if (xmpp_errors[i].code == code)
- return xmpp_errors[i].meaning;
- }
- return NULL;
-}
-
-// display_server_error(x)
-// Display the error to the user
-// x: error tag xmlnode pointer
-void display_server_error(LmMessageNode *x)
-{
- const char *desc = NULL, *p=NULL, *s;
- char *sdesc, *tmp;
- int code = 0;
-
- if (!x) return;
-
- /* RFC3920:
- * The <error/> element:
- * o MUST contain a child element corresponding to one of the defined
- * stanza error conditions specified below; this element MUST be
- * qualified by the 'urn:ietf:params:xml:ns:xmpp-stanzas' namespace.
- */
- if (x->children)
- p = x->children->name;
- if (p)
- scr_LogPrint(LPRINT_LOGNORM, "Received error packet [%s]", p);
-
- // For backward compatibility
- if ((s = lm_message_node_get_attribute(x, "code")) != NULL) {
- code = atoi(s);
- // Default message
- desc = defaulterrormsg(code);
- }
-
- // Error tag data is better, if available
- s = lm_message_node_get_value(x);
- if (s && *s) desc = s;
-
- // And sometimes there is a text message
- s = lm_message_node_get_child_value(x, "text");
-
- if (s && *s) desc = s;
-
- // If we still have no description, let's give up
- if (!desc)
- return;
-
- // Strip trailing newlines
- sdesc = g_strdup(desc);
- for (tmp = sdesc; *tmp; tmp++) ;
- if (tmp > sdesc)
- tmp--;
- while (tmp >= sdesc && (*tmp == '\n' || *tmp == '\r'))
- *tmp-- = '\0';
-
- scr_LogPrint(LPRINT_LOGNORM, "Error code from server: %d %s", code, sdesc);
- g_free(sdesc);
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_helper.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#ifndef __XMPPHELPER_H__
-#define __XMPPHELPER_H__ 1
-
-#include <time.h>
-#include <loudmouth/loudmouth.h>
-
-#include "xmpp.h"
-#include "xmpp_defines.h"
-#include "config.h"
-
-extern time_t iqlast; /* last message/status change time */
-
-struct T_presence {
- enum imstatus st;
- const char *msg;
-};
-
-struct xmpp_error {
- guint code;
- const char *code_str;
- const char *meaning;
- const char *condition;
- const char *type;
-};
-
-
-#ifdef MODULES_ENABLE
-void xmpp_add_feature (const char *xmlns);
-void xmpp_del_feature (const char *xmlns);
-#endif
-
-LmMessageNode *lm_message_node_new(const gchar *name, const gchar *xmlns);
-LmMessageNode *lm_message_node_find_xmlns(LmMessageNode *node,
- const char *xmlns);
-const gchar* lm_message_node_get_child_value(LmMessageNode *node,
- const gchar *child);
-void lm_message_node_hide(LmMessageNode *node);
-void lm_message_node_insert_childnode(LmMessageNode *node,
- LmMessageNode *child);
-void lm_message_node_deep_ref(LmMessageNode *node);
-time_t lm_message_node_get_timestamp(LmMessageNode *node);
-
-LmMessage *lm_message_new_iq_from_query(LmMessage *m, LmMessageSubType type);
-
-LmMessage *lm_message_new_presence(enum imstatus st,
- const char *recipient, const char *msg);
-
-const gchar* lm_message_get_from(LmMessage *m);
-const gchar* lm_message_get_id(LmMessage *m);
-
-void display_server_error(LmMessageNode *x);
-
-/* XEP-0115 (Entity Capabilities) node */
-const char *entity_version(enum imstatus status);
-
-#endif
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_iq.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,836 +0,0 @@
-/*
- * xmpp_iq.c -- Jabber protocol IQ-related stuff
- *
- * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- * Parts come from the centericq project:
- * Copyright (C) 2002-2005 by Konstantin Klyagin <konst@konst.org.ua>
- * Some small parts come from the Pidgin project <http://pidgin.im/>
- *
- * 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 <string.h>
-#include <sys/utsname.h>
-
-#include "xmpp_helper.h"
-#include "commands.h"
-#include "screen.h"
-#include "utils.h"
-#include "logprint.h"
-#include "settings.h"
-#include "caps.h"
-#include "main.h"
-
-extern struct xmpp_error xmpp_errors[];
-
-static LmHandlerResult handle_iq_command_set_status(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m,
- gpointer ud);
-
-static LmHandlerResult handle_iq_command_leave_groupchats(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m,
- gpointer ud);
-
-inline double seconds_since_last_use(void);
-
-struct adhoc_command {
- char *name;
- char *description;
- bool only_for_self;
- LmHandleMessageFunction callback;
-};
-
-const struct adhoc_command adhoc_command_list[] = {
- { "http://jabber.org/protocol/rc#set-status",
- "Change client status",
- 1,
- &handle_iq_command_set_status },
- { "http://jabber.org/protocol/rc#leave-groupchats",
- "Leave groupchat(s)",
- 1,
- &handle_iq_command_leave_groupchats },
- { NULL, NULL, 0, NULL },
-};
-
-struct adhoc_status {
- char *name; // the name used by adhoc
- char *description;
- char *status; // the string, used by setstus
-};
-// It has to match imstatus of roster.h!
-const struct adhoc_status adhoc_status_list[] = {
- {"offline", "Offline", "offline"},
- {"online", "Online", "avail"},
- {"chat", "Chat", "free"},
- {"dnd", "Do not disturb", "dnd"},
- {"xd", "Extended away", "notavail"},
- {"away", "Away", "away"},
- {"invisible", "Invisible", "invisible"},
- {NULL, NULL, NULL},
-};
-
-static char *generate_session_id(char *prefix)
-{
- char *result;
- static int counter = 0;
- counter++;
- // TODO better use timestamp?
- result = g_strdup_printf("%s-%i", prefix, counter);
- return result;
-}
-
-static LmMessage *lm_message_new_iq_error(LmMessage *m, guint error)
-{
- LmMessage *r;
- LmMessageNode *err;
- int i;
-
- for (i = 0; xmpp_errors[i].code; ++i)
- if (xmpp_errors[i].code == error)
- break;
- g_return_val_if_fail(xmpp_errors[i].code > 0, NULL);
-
- r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_ERROR);
- err = lm_message_node_add_child(r->node, "error", NULL);
- lm_message_node_set_attribute(err, "code", xmpp_errors[i].code_str);
- lm_message_node_set_attribute(err, "type", xmpp_errors[i].type);
- lm_message_node_set_attribute
- (lm_message_node_add_child(err,
- xmpp_errors[i].condition, NULL),
- "xmlns", NS_XMPP_STANZAS);
-
- return r;
-}
-
-void send_iq_error(LmConnection *c, LmMessage *m, guint error)
-{
- LmMessage *r;
- r = lm_message_new_iq_error(m, error);
- lm_connection_send(c, r, NULL);
- lm_message_unref(r);
-}
-
-static void lm_message_node_add_dataform_result(LmMessageNode *node,
- const char *message)
-{
- LmMessageNode *x, *field;
-
- x = lm_message_node_add_child(node, "x", NULL);
- lm_message_node_set_attributes(x,
- "type", "result",
- "xmlns", "jabber:x:data",
- NULL);
- field = lm_message_node_add_child(x, "field", NULL);
- lm_message_node_set_attributes(field,
- "type", "text-single",
- "var", "message",
- NULL);
- lm_message_node_add_child(field, "value", message);
-}
-
-static LmHandlerResult handle_iq_commands_list(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- LmMessage *iq;
- LmMessageNode *query;
- const char *requester_jid;
- const struct adhoc_command *command;
- const char *node;
- gboolean from_self;
-
- iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
- query = lm_message_node_add_child(iq->node, "query", NULL);
- lm_message_node_set_attribute(query, "xmlns", NS_COMMANDS);
- node = lm_message_node_get_attribute
- (lm_message_node_get_child(m->node, "query"),
- "node");
- if (node)
- lm_message_node_set_attribute(query, "node", node);
-
- requester_jid = lm_message_get_from(m);
- from_self = jid_equal(lm_connection_get_jid(c), requester_jid);
-
- for (command = adhoc_command_list ; command->name ; command++) {
- if (!command->only_for_self || from_self) {
- lm_message_node_set_attributes
- (lm_message_node_add_child(query, "item", NULL),
- "node", command->name,
- "name", command->description,
- "jid", lm_connection_get_jid(c),
- NULL);
- }
- }
-
- lm_connection_send(c, iq, NULL);
- lm_message_unref(iq);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static LmHandlerResult handle_iq_command_set_status(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- const char *action, *node;
- char *sessionid;
- LmMessage *iq;
- LmMessageNode *command, *x, *y;
- const struct adhoc_status *s;
-
- x = lm_message_node_get_child(m->node, "command");
- action = lm_message_node_get_attribute(x, "action");
- node = lm_message_node_get_attribute(x, "node");
- sessionid = (char *)lm_message_node_get_attribute(x, "sessionid");
-
- iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
- command = lm_message_node_add_child(iq->node, "command", NULL);
- lm_message_node_set_attribute(command, "node", node);
- lm_message_node_set_attribute(command, "xmlns", NS_COMMANDS);
-
- if (!sessionid) {
- sessionid = generate_session_id("set-status");
- lm_message_node_set_attribute(command, "sessionid", sessionid);
- g_free(sessionid);
- sessionid = NULL;
- lm_message_node_set_attribute(command, "status", "executing");
-
- x = lm_message_node_add_child(command, "x", NULL);
- lm_message_node_set_attribute(x, "type", "form");
- lm_message_node_set_attribute(x, "xmlns", "jabber:x:data");
-
- lm_message_node_add_child(x, "title", "Change Status");
-
- lm_message_node_add_child(x, "instructions",
- "Choose the status and status message");
-
- // TODO see if factorisation is possible
- y = lm_message_node_add_child(x, "field", NULL);
- lm_message_node_set_attribute(y, "type", "hidden");
- lm_message_node_set_attribute(y, "var", "FORM_TYPE");
-
- lm_message_node_add_child(y, "value", "http://jabber.org/protocol/rc");
-
- y = lm_message_node_add_child(x, "field", NULL);
- lm_message_node_set_attributes(y,
- "type", "list-single",
- "var", "status",
- "label", "Status",
- NULL);
- lm_message_node_add_child(y, "required", NULL);
-
- // XXX: ugly
- lm_message_node_add_child(y, "value",
- adhoc_status_list[xmpp_getstatus()].name);
- for (s = adhoc_status_list; s->name; s++) {
- LmMessageNode *option = lm_message_node_add_child(y, "option", NULL);
- lm_message_node_add_child(option, "value", s->name);
- lm_message_node_set_attribute(option, "label", s->description);
- }
- // TODO add priority ?
- // I do not think this is useful, user should not have to care of the
- // priority like gossip and gajim do (misc)
- lm_message_node_set_attributes
- (lm_message_node_add_child(x, "field", NULL),
- "type", "text-multi",
- "var", "status-message",
- "label", "Message",
- NULL);
- } else if (action && !strcmp(action, "cancel")) {
- lm_message_node_set_attribute(command, "status", "canceled");
- } else { // (if sessionid and not canceled)
- y = lm_message_node_find_xmlns(x, "jabber:x:data"); //x?xmlns=jabber:x:data
- if (y) {
- const char *value=NULL, *message=NULL;
- LmMessageNode *fields, *field;
- field = fields = lm_message_node_get_child(y, "field"); //field?var=status
- while (field && strcmp("status",
- lm_message_node_get_attribute(field, "var")))
- field = field->next;
- field = lm_message_node_get_child(field, "value");
- if (field)
- value = lm_message_node_get_value(field);
- field = fields; //field?var=status-message
- while (field && strcmp("status-message",
- lm_message_node_get_attribute(field, "var")))
- field = field->next;
- field = lm_message_node_get_child(field, "value");
- if (field)
- message = lm_message_node_get_value(field);
- if (value) {
- for (s = adhoc_status_list; !s->name || strcmp(s->name, value); s++);
- if (s->name) {
- char *status = g_strdup_printf("%s %s", s->status,
- message ? message : "");
- cmd_setstatus(NULL, status);
- g_free(status);
- lm_message_node_set_attribute(command, "status", "completed");
- lm_message_node_add_dataform_result(command,
- "Status has been changed");
- }
- }
- }
- }
- if (sessionid)
- lm_message_node_set_attribute(command, "sessionid", sessionid);
- lm_connection_send(c, iq, NULL);
- lm_message_unref(iq);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static void _callback_foreach_buddy_groupchat(gpointer rosterdata, void *param)
-{
- LmMessageNode *field, *option;
- const char *room_jid, *nickname;
- char *desc;
-
- room_jid = buddy_getjid(rosterdata);
- if (!room_jid) return;
- nickname = buddy_getnickname(rosterdata);
- if (!nickname) return;
- field = param;
-
- option = lm_message_node_add_child(field, "option", NULL);
- lm_message_node_add_child(option, "value", room_jid);
- desc = g_strdup_printf("%s on %s", nickname, room_jid);
- lm_message_node_set_attribute(option, "label", desc);
- g_free(desc);
-}
-
-static LmHandlerResult handle_iq_command_leave_groupchats(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m,
- gpointer ud)
-{
- const char *action, *node;
- char *sessionid;
- LmMessage *iq;
- LmMessageNode *command, *x;
-
- x = lm_message_node_get_child(m->node, "command");
- action = lm_message_node_get_attribute(x, "action");
- node = lm_message_node_get_attribute(x, "node");
- sessionid = (char*)lm_message_node_get_attribute(x, "sessionid");
-
- iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
- command = lm_message_node_add_child(iq->node, "command", NULL);
- lm_message_node_set_attributes(command,
- "node", node,
- "xmlns", NS_COMMANDS,
- NULL);
-
- if (!sessionid) {
- LmMessageNode *field;
-
- sessionid = generate_session_id("leave-groupchats");
- lm_message_node_set_attribute(command, "sessionid", sessionid);
- g_free(sessionid);
- sessionid = NULL;
- lm_message_node_set_attribute(command, "status", "executing");
-
- x = lm_message_node_add_child(command, "x", NULL);
- lm_message_node_set_attributes(x,
- "type", "form",
- "xmlns", "jabber:x:data",
- NULL);
-
- lm_message_node_add_child(x, "title", "Leave groupchat(s)");
-
- lm_message_node_add_child(x, "instructions",
- "What groupchats do you want to leave?");
-
- field = lm_message_node_add_child(x, "field", NULL);
- lm_message_node_set_attributes(field,
- "type", "hidden",
- "var", "FORM_TYPE",
- NULL);
-
- lm_message_node_add_child(field, "value",
- "http://jabber.org/protocol/rc");
-
- field = lm_message_node_add_child(x, "field", NULL);
- lm_message_node_set_attributes(field,
- "type", "list-multi",
- "var", "groupchats",
- "label", "Groupchats: ",
- NULL);
- lm_message_node_add_child(field, "required", NULL);
-
- foreach_buddy(ROSTER_TYPE_ROOM, &_callback_foreach_buddy_groupchat, field);
- //TODO: return an error if we are not connected to groupchats
- } else if (action && !strcmp(action, "cancel")) {
- lm_message_node_set_attribute(command, "status", "canceled");
- } else { // (if sessionid and not canceled)
- LmMessageNode *form = lm_message_node_find_xmlns(x, "jabber:x:data");//TODO
- if (form) {
- LmMessageNode *field;
-
- lm_message_node_set_attribute(command, "status", "completed");
- //TODO: implement sth. like "field?var=groupchats" in xmlnode...
- field = lm_message_node_get_child(form, "field");
- while (field && strcmp("groupchats",
- lm_message_node_get_attribute(field, "var")))
- field = field->next;
-
- if (field)
- for (x = field->children ; x ; x = x->next)
- {
- if (!strcmp (x->name, "value")) {
- GList* b = buddy_search_jid(lm_message_node_get_value(x));
- if (b)
- cmd_room_leave(b->data, "Requested by remote command");
- }
- }
- lm_message_node_add_dataform_result(command,
- "Groupchats have been left");
- }
- }
- if (sessionid)
- lm_message_node_set_attribute(command, "sessionid", sessionid);
- lm_connection_send(c, iq, NULL);
- lm_message_unref(iq);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-LmHandlerResult handle_iq_commands(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- const char *requester_jid = NULL;
- LmMessageNode *cmd;
- const struct adhoc_command *command;
-
- // mcabber has only partial XEP-0146 support...
- if (LM_MESSAGE_SUB_TYPE_SET != lm_message_get_sub_type(m))
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-
- requester_jid = lm_message_get_from(m);
-
- cmd = lm_message_node_get_child(m->node, "command");
- if (jid_equal(lm_connection_get_jid(c), requester_jid)) {
- const char *action, *node;
- action = lm_message_node_get_attribute(cmd, "action");
- node = lm_message_node_get_attribute(cmd, "node");
- // action can be NULL, in which case it seems to take the default,
- // ie execute
- if (!action || !strcmp(action, "execute") || !strcmp(action, "cancel")
- || !strcmp(action, "next") || !strcmp(action, "complete")) {
- for (command = adhoc_command_list; command->name; command++) {
- if (!strcmp(node, command->name))
- command->callback(h, c, m, ud);
- }
- // "prev" action will get there, as we do not implement it,
- // and do not authorize it
- } else {
- LmMessage *r;
- LmMessageNode *err;
- r = lm_message_new_iq_error(m, XMPP_ERROR_BAD_REQUEST);
- err = lm_message_node_get_child(r->node, "error");
- lm_message_node_set_attribute
- (lm_message_node_add_child(err, "malformed-action", NULL),
- "xmlns", NS_COMMANDS);
- lm_connection_send(c, r, NULL);
- lm_message_unref(r);
- }
- } else {
- send_iq_error(c, m, XMPP_ERROR_FORBIDDEN);
- }
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-
-LmHandlerResult handle_iq_disco_items(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- LmMessageNode *query;
- const char *node;
- query = lm_message_node_get_child(m->node, "query");
- node = lm_message_node_get_attribute(query, "node");
- if (node) {
- if (!strcmp(node, NS_COMMANDS)) {
- return handle_iq_commands_list(NULL, c, m, ud);
- } else {
- send_iq_error(c, m, XMPP_ERROR_NOT_IMPLEMENTED);
- }
- } else {
- // not sure about this one
- send_iq_error(c, m, XMPP_ERROR_NOT_IMPLEMENTED);
- }
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-
-void _disco_add_feature_helper(gpointer data, gpointer user_data)
-{
- LmMessageNode *node = user_data;
- lm_message_node_set_attribute
- (lm_message_node_add_child(node, "feature", NULL), "var", data);
-}
-
-// disco_info_set_caps(ansquery, entitycaps)
-// Add features attributes to ansquery. entitycaps should either be a
-// valid capabilities hash or NULL. If it is NULL, the node attribute won't
-// be added to the query child and Entity Capabilities will be announced
-// as a feature.
-// Please change the entity version string if you modify mcabber disco
-// source code, so that it doesn't conflict with the upstream client.
-static void disco_info_set_caps(LmMessageNode *ansquery,
- const char *entitycaps)
-{
- if (entitycaps) {
- char *eversion;
- eversion = g_strdup_printf("%s#%s", MCABBER_CAPS_NODE, entitycaps);
- lm_message_node_set_attribute(ansquery, "node", eversion);
- g_free(eversion);
- }
-
- lm_message_node_set_attributes
- (lm_message_node_add_child(ansquery, "identity", NULL),
- "category", "client",
- "name", PACKAGE_STRING,
- "type", "pc",
- NULL);
-
- if (entitycaps)
- caps_foreach_feature(entitycaps, _disco_add_feature_helper, ansquery);
- else {
- caps_foreach_feature(entity_version(xmpp_getstatus()),
- _disco_add_feature_helper,
- ansquery);
- lm_message_node_set_attribute
- (lm_message_node_add_child(ansquery, "feature", NULL),
- "var", NS_CAPS);
- }
-}
-
-LmHandlerResult handle_iq_disco_info(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- LmMessage *r;
- LmMessageNode *query, *tmp;
- const char *node = NULL;
- const char *param = NULL;
-
- if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_RESULT)
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-
- r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
- query = lm_message_node_add_child(r->node, "query", NULL);
- lm_message_node_set_attribute(query, "xmlns", NS_DISCO_INFO);
- tmp = lm_message_node_find_child(m->node, "query");
- if (tmp) {
- node = lm_message_node_get_attribute(tmp, "node");
- param = node+strlen(MCABBER_CAPS_NODE)+1;
- }
- if (node && startswith(node, MCABBER_CAPS_NODE "#", FALSE))
- disco_info_set_caps(query, param); // client#version
- else
- // Basic discovery request
- disco_info_set_caps(query, NULL);
-
- lm_connection_send(c, r, NULL);
- lm_message_unref(r);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-LmHandlerResult handle_iq_roster(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- LmMessageNode *y;
- const char *fjid, *name, *group, *sub, *ask;
- char *cleanalias;
- enum subscr esub;
- int need_refresh = FALSE;
- guint roster_type;
-
- for (y = lm_message_node_find_child(lm_message_node_find_xmlns
- (m->node, NS_ROSTER),
- "item");
- y;
- y = y->next) {
- char *name_tmp = NULL;
-
- fjid = lm_message_node_get_attribute(y, "jid");
- name = lm_message_node_get_attribute(y, "name");
- sub = lm_message_node_get_attribute(y, "subscription");
- ask = lm_message_node_get_attribute(y, "ask");
-
- if (lm_message_node_find_child(y, "group"))
- group = lm_message_node_get_value(lm_message_node_find_child(y, "group"));
- else
- group = NULL;
-
- if (!fjid)
- continue;
-
- cleanalias = jidtodisp(fjid);
-
- esub = sub_none;
- if (sub) {
- if (!strcmp(sub, "to")) esub = sub_to;
- else if (!strcmp(sub, "from")) esub = sub_from;
- else if (!strcmp(sub, "both")) esub = sub_both;
- else if (!strcmp(sub, "remove")) esub = sub_remove;
- }
-
- if (esub == sub_remove) {
- roster_del_user(cleanalias);
- scr_LogPrint(LPRINT_LOGNORM, "Buddy <%s> has been removed "
- "from the roster", cleanalias);
- g_free(cleanalias);
- need_refresh = TRUE;
- continue;
- }
-
- if (ask && !strcmp(ask, "subscribe"))
- esub |= sub_pending;
-
- if (!name) {
- if (!settings_opt_get_int("roster_hide_domain")) {
- name = cleanalias;
- } else {
- char *p;
- name = name_tmp = g_strdup(cleanalias);
- p = strchr(name_tmp, JID_DOMAIN_SEPARATOR);
- if (p) *p = '\0';
- }
- }
-
- // Tricky... :-\ My guess is that if there is no JID_DOMAIN_SEPARATOR,
- // this is an agent.
- if (strchr(cleanalias, JID_DOMAIN_SEPARATOR))
- roster_type = ROSTER_TYPE_USER;
- else
- roster_type = ROSTER_TYPE_AGENT;
-
- roster_add_user(cleanalias, name, group, roster_type, esub, 1);
-
- g_free(name_tmp);
- g_free(cleanalias);
- }
-
- buddylist_build();
- update_roster = TRUE;
- if (need_refresh)
- scr_UpdateBuddyWindow();
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-LmHandlerResult handle_iq_ping(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- LmMessage *r;
-
- r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
- lm_connection_send(c, r, NULL);
- lm_message_unref(r);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-double seconds_since_last_use(void)
-{
- return difftime(time(NULL), iqlast);
-}
-
-LmHandlerResult handle_iq_last(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- LmMessage *r;
- LmMessageNode *query;
- char *seconds;
-
- if (!settings_opt_get_int("iq_hide_requests")) {
- scr_LogPrint(LPRINT_LOGNORM, "Received an IQ last time request from <%s>",
- lm_message_get_from(m));
- }
-
- if (settings_opt_get_int("iq_last_disable") ||
- (settings_opt_get_int("iq_last_disable_when_notavail") &&
- xmpp_getstatus() == notavail))
- {
- send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
-
- r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
- query = lm_message_node_add_child(r->node, "query", NULL);
- lm_message_node_set_attribute(query, "xmlns", NS_LAST);
- seconds = g_strdup_printf("%.0f", seconds_since_last_use());
- lm_message_node_set_attribute(query, "seconds", seconds);
- g_free(seconds);
-
- lm_connection_send(c, r, NULL);
- lm_message_unref(r);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-LmHandlerResult handle_iq_version(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- LmMessage *r;
- LmMessageNode *query;
- char *os = NULL;
- char *ver = mcabber_version();
-
- if (!settings_opt_get_int("iq_hide_requests")) {
- scr_LogPrint(LPRINT_LOGNORM, "Received an IQ version request from <%s>",
- lm_message_get_from(m));
- }
- if (!settings_opt_get_int("iq_version_hide_os")) {
- struct utsname osinfo;
- uname(&osinfo);
- os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release,
- osinfo.machine);
- }
-
- r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
-
- query = lm_message_node_add_child(r->node, "query", NULL);
- lm_message_node_set_attribute(query, "xmlns", NS_VERSION);
-
- lm_message_node_add_child(query, "name", PACKAGE_NAME);
- lm_message_node_add_child(query, "version", ver);
- if (os) {
- lm_message_node_add_child(query, "os", os);
- g_free(os);
- }
-
- g_free(ver);
- lm_connection_send(c, r, NULL);
- lm_message_unref(r);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-// This function borrows some code from the Pidgin project
-LmHandlerResult handle_iq_time(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- LmMessage *r;
- LmMessageNode *query;
- char *buf, *utf8_buf;
- time_t now_t;
- struct tm *now;
-
- time(&now_t);
-
- if (!settings_opt_get_int("iq_hide_requests")) {
- scr_LogPrint(LPRINT_LOGNORM, "Received an IQ time request from <%s>",
- lm_message_get_from(m));
- }
-
- buf = g_new0(char, 512);
-
- r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
- query = lm_message_node_add_child(r->node, "query", NULL);
- lm_message_node_set_attribute(query, "xmlns", NS_TIME);
-
- now = gmtime(&now_t);
-
- strftime(buf, 512, "%Y%m%dT%T", now);
- lm_message_node_add_child(query, "utc", buf);
-
- now = localtime(&now_t);
-
- strftime(buf, 512, "%Z", now);
- if ((utf8_buf = to_utf8(buf))) {
- lm_message_node_add_child(query, "tz", utf8_buf);
- g_free(utf8_buf);
- }
-
- strftime(buf, 512, "%d %b %Y %T", now);
- if ((utf8_buf = to_utf8(buf))) {
- lm_message_node_add_child(query, "display", utf8_buf);
- g_free(utf8_buf);
- }
-
- lm_connection_send(c, r, NULL);
- lm_message_unref(r);
- g_free(buf);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-// This function borrows some code from the Pidgin project
-LmHandlerResult handle_iq_time202(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- LmMessage *r;
- LmMessageNode *query;
- char *buf, *utf8_buf;
- time_t now_t;
- struct tm *now;
- char const *sign;
- int diff = 0;
-
- time(&now_t);
-
- if (!settings_opt_get_int("iq_hide_requests")) {
- scr_LogPrint(LPRINT_LOGNORM, "Received an IQ time request from <%s>",
- lm_message_get_from(m));
- }
-
- buf = g_new0(char, 512);
-
- r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
- query = lm_message_node_add_child(r->node, "time", NULL);
- lm_message_node_set_attribute(query, "xmlns", NS_XMPP_TIME);
-
- now = localtime(&now_t);
-
- if (now->tm_isdst >= 0) {
-#if defined HAVE_TM_GMTOFF
- diff = now->tm_gmtoff;
-#elif defined HAVE_TIMEZONE
- tzset();
- diff = -timezone;
-#endif
- }
-
- if (diff < 0) {
- sign = "-";
- diff = -diff;
- } else {
- sign = "+";
- }
- diff /= 60;
- snprintf(buf, 512, "%c%02d:%02d", *sign, diff / 60, diff % 60);
- if ((utf8_buf = to_utf8(buf))) {
- lm_message_node_add_child(query, "tzo", utf8_buf);
- g_free(utf8_buf);
- }
-
- now = gmtime(&now_t);
-
- strftime(buf, 512, "%Y-%m-%dT%TZ", now);
- lm_message_node_add_child(query, "utc", buf);
-
- lm_connection_send(c, r, NULL);
- lm_message_unref(r);
- g_free(buf);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-LmHandlerResult handle_iq_vcard(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud)
-{
- send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_iq.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-#ifndef __XMPP_IQ_H__
-#define __XMPP_IQ_H__ 1
-
-LmHandlerResult handle_iq_commands(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m, gpointer ud);
-LmHandlerResult handle_iq_disco_items(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m, gpointer ud);
-LmHandlerResult handle_iq_disco_info(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m, gpointer ud);
-LmHandlerResult handle_iq_roster(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud);
-LmHandlerResult handle_iq_ping(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud);
-LmHandlerResult handle_iq_last(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud);
-LmHandlerResult handle_iq_version(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud);
-LmHandlerResult handle_iq_time(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud);
-LmHandlerResult handle_iq_time202(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud);
-LmHandlerResult handle_iq_vcard(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer ud);
-
-void send_iq_error(LmConnection *c, LmMessage *m, guint error);
-
-#endif /* __XMPP_IQ_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_iqrequest.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,636 +0,0 @@
-/*
- * xmpp_iqrequest.c -- Jabber IQ request handling
- *
- * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 <string.h>
-#include <stdlib.h>
-
-#include "xmpp_helper.h"
-#include "xmpp_iq.h"
-#include "screen.h"
-#include "utils.h"
-#include "settings.h"
-#include "hooks.h"
-#include "hbuf.h"
-
-extern LmMessageNode *bookmarks;
-extern LmMessageNode *rosternotes;
-
-static LmHandlerResult cb_roster(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data);
-static LmHandlerResult cb_version(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data);
-static LmHandlerResult cb_time(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data);
-static LmHandlerResult cb_last(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data);
-static LmHandlerResult cb_vcard(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data);
-
-static struct IqRequestHandlers
-{
- const gchar *xmlns;
- const gchar *querytag;
- LmHandleMessageFunction handler;
-} iq_request_handlers[] = {
- {NS_ROSTER, "query", &cb_roster},
- {NS_VERSION,"query", &cb_version},
- {NS_TIME, "query", &cb_time},
- {NS_LAST, "query", &cb_last},
- {NS_VCARD, "vCard", &cb_vcard},
- {NULL, NULL, NULL}
-};
-
-// Enum for vCard attributes
-enum vcard_attr {
- vcard_home = 1<<0,
- vcard_work = 1<<1,
- vcard_postal = 1<<2,
- vcard_voice = 1<<3,
- vcard_fax = 1<<4,
- vcard_cell = 1<<5,
- vcard_inet = 1<<6,
- vcard_pref = 1<<7,
-};
-
-// xmlns has to be a namespace from iq_request_handlers[].xmlns
-void xmpp_iq_request(const char *fulljid, const char *xmlns)
-{
- LmMessage *iq;
- LmMessageNode *query;
- LmMessageHandler *handler;
- int i;
-
- iq = lm_message_new_with_sub_type(fulljid, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_GET);
- for (i = 0; strcmp(iq_request_handlers[i].xmlns, xmlns) != 0 ; ++i)
- ;
- query = lm_message_node_add_child(iq->node,
- iq_request_handlers[i].querytag,
- NULL);
- lm_message_node_set_attribute(query, "xmlns", xmlns);
- handler = lm_message_handler_new(iq_request_handlers[i].handler,
- NULL, FALSE);
- lm_connection_send_with_reply(lconnection, iq, handler, NULL);
- lm_message_handler_unref(handler);
- lm_message_unref(iq);
-}
-
-// This callback is reached when mcabber receives the first roster update
-// after the connection.
-static LmHandlerResult cb_roster(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data)
-{
- LmMessageNode *x;
- const char *ns;
-
- // Only execute the hook if the roster has been successfully retrieved
- if (lm_message_get_sub_type(m) != LM_MESSAGE_SUB_TYPE_RESULT)
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-
- x = lm_message_node_find_child(m->node, "query");
- if (!x)
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-
- ns = lm_message_node_get_attribute(x, "xmlns");
- if (ns && !strcmp(ns, NS_ROSTER))
- handle_iq_roster(NULL, c, m, user_data);
-
- // Post-login stuff
- hook_execute_internal("hook-post-connect");
-
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static LmHandlerResult cb_version(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data)
-{
- LmMessageNode *ansqry;
- const char *p, *bjid;
- char *tmp;
- char *buf;
-
- ansqry = lm_message_node_get_child(m->node, "query");
- if (!ansqry) {
- scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result!");
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
-
- // Display IQ result sender...
- p = lm_message_get_from(m);
- if (!p) {
- scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result (no sender name).");
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
- bjid = p;
-
- buf = g_strdup_printf("Received IQ:version result from <%s>", bjid);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
-
- // bjid should now really be the "bare JID", let's strip the resource
- tmp = strchr(bjid, JID_RESOURCE_SEPARATOR);
- if (tmp) *tmp = '\0';
-
- scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0);
- g_free(buf);
-
- // Get result data...
- p = lm_message_node_get_child_value(ansqry, "name");
- if (p) {
- buf = g_strdup_printf("Name: %s", p);
- scr_WriteIncomingMessage(bjid, buf,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- g_free(buf);
- }
- p = lm_message_node_get_child_value(ansqry, "version");
- if (p) {
- buf = g_strdup_printf("Version: %s", p);
- scr_WriteIncomingMessage(bjid, buf,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- g_free(buf);
- }
- p = lm_message_node_get_child_value(ansqry, "os");
- if (p) {
- buf = g_strdup_printf("OS: %s", p);
- scr_WriteIncomingMessage(bjid, buf,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- g_free(buf);
- }
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static LmHandlerResult cb_time(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data)
-{
- LmMessageNode *ansqry;
- const char *p, *bjid;
- char *tmp;
- char *buf;
-
- ansqry = lm_message_node_get_child(m->node, "query");
- if (!ansqry) {
- scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:time result!");
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
- // Display IQ result sender...
- p = lm_message_get_from(m);
- if (!p) {
- scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:time result (no sender name).");
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
- bjid = p;
-
- buf = g_strdup_printf("Received IQ:time result from <%s>", bjid);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
-
- // bjid should now really be the "bare JID", let's strip the resource
- tmp = strchr(bjid, JID_RESOURCE_SEPARATOR);
- if (tmp) *tmp = '\0';
-
- scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0);
- g_free(buf);
-
- // Get result data...
- p = lm_message_node_get_child_value(ansqry, "utc");
- if (p) {
- buf = g_strdup_printf("UTC: %s", p);
- scr_WriteIncomingMessage(bjid, buf,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- g_free(buf);
- }
- p = lm_message_node_get_child_value(ansqry, "tz");
- if (p) {
- buf = g_strdup_printf("TZ: %s", p);
- scr_WriteIncomingMessage(bjid, buf,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- g_free(buf);
- }
- p = lm_message_node_get_child_value(ansqry, "display");
- if (p) {
- buf = g_strdup_printf("Time: %s", p);
- scr_WriteIncomingMessage(bjid, buf,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- g_free(buf);
- }
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static LmHandlerResult cb_last(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data)
-{
- LmMessageNode *ansqry;
- const char *p, *bjid;
- char *buf, *tmp;
-
- ansqry = lm_message_node_get_child(m->node, "query");
- if (!ansqry) {
- scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:last result!");
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
- // Display IQ result sender...
- p = lm_message_get_from(m);
- if (!p) {
- scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:last result (no sender name).");
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
- bjid = p;
-
- buf = g_strdup_printf("Received IQ:last result from <%s>", bjid);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
-
- // bjid should now really be the "bare JID", let's strip the resource
- tmp = strchr(bjid, JID_RESOURCE_SEPARATOR);
- if (tmp) *tmp = '\0';
-
- scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0);
- g_free(buf);
-
- // Get result data...
- p = lm_message_node_get_attribute(ansqry, "seconds");
- if (p) {
- long int s;
- GString *sbuf;
- sbuf = g_string_new("Idle time: ");
- s = atol(p);
- // Days
- if (s > 86400L) {
- g_string_append_printf(sbuf, "%ldd ", s/86400L);
- s %= 86400L;
- }
- // hh:mm:ss
- g_string_append_printf(sbuf, "%02ld:", s/3600L);
- s %= 3600L;
- g_string_append_printf(sbuf, "%02ld:%02ld", s/60L, s%60L);
- scr_WriteIncomingMessage(bjid, sbuf->str,
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- g_string_free(sbuf, TRUE);
- } else {
- scr_WriteIncomingMessage(bjid, "No idle time reported.",
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- }
- p = lm_message_node_get_value(ansqry);
- if (p) {
- buf = g_strdup_printf("Status message: %s", p);
- scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0);
- g_free(buf);
- }
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static void display_vcard_item(const char *bjid, const char *label,
- enum vcard_attr vcard_attrib, const char *text)
-{
- char *buf;
-
- if (!text || !bjid || !label)
- return;
-
- buf = g_strdup_printf("%s: %s%s%s%s%s%s%s%s%s%s", label,
- (vcard_attrib & vcard_home ? "[home]" : ""),
- (vcard_attrib & vcard_work ? "[work]" : ""),
- (vcard_attrib & vcard_postal ? "[postal]" : ""),
- (vcard_attrib & vcard_voice ? "[voice]" : ""),
- (vcard_attrib & vcard_fax ? "[fax]" : ""),
- (vcard_attrib & vcard_cell ? "[cell]" : ""),
- (vcard_attrib & vcard_inet ? "[inet]" : ""),
- (vcard_attrib & vcard_pref ? "[pref]" : ""),
- (vcard_attrib ? " " : ""),
- text);
- scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- g_free(buf);
-}
-
-static void handle_vcard_node(const char *barejid, LmMessageNode *vcardnode)
-{
- LmMessageNode *x;
- const char *p;
-
- for (x = vcardnode->children ; x; x = x->next) {
- const char *data;
- enum vcard_attr vcard_attrib = 0;
-
- p = x->name;
- data = lm_message_node_get_value(x);
- if (!p || !data)
- continue;
-
- if (!strcmp(p, "FN"))
- display_vcard_item(barejid, "Name", vcard_attrib, data);
- else if (!strcmp(p, "NICKNAME"))
- display_vcard_item(barejid, "Nickname", vcard_attrib, data);
- else if (!strcmp(p, "URL"))
- display_vcard_item(barejid, "URL", vcard_attrib, data);
- else if (!strcmp(p, "BDAY"))
- display_vcard_item(barejid, "Birthday", vcard_attrib, data);
- else if (!strcmp(p, "TZ"))
- display_vcard_item(barejid, "Timezone", vcard_attrib, data);
- else if (!strcmp(p, "TITLE"))
- display_vcard_item(barejid, "Title", vcard_attrib, data);
- else if (!strcmp(p, "ROLE"))
- display_vcard_item(barejid, "Role", vcard_attrib, data);
- else if (!strcmp(p, "DESC"))
- display_vcard_item(barejid, "Comment", vcard_attrib, data);
- else if (!strcmp(p, "N")) {
- data = lm_message_node_get_child_value(x, "FAMILY");
- display_vcard_item(barejid, "Family Name", vcard_attrib, data);
- data = lm_message_node_get_child_value(x, "GIVEN");
- display_vcard_item(barejid, "Given Name", vcard_attrib, data);
- data = lm_message_node_get_child_value(x, "MIDDLE");
- display_vcard_item(barejid, "Middle Name", vcard_attrib, data);
- } else if (!strcmp(p, "ORG")) {
- data = lm_message_node_get_child_value(x, "ORGNAME");
- display_vcard_item(barejid, "Organisation name", vcard_attrib, data);
- data = lm_message_node_get_child_value(x, "ORGUNIT");
- display_vcard_item(barejid, "Organisation unit", vcard_attrib, data);
- } else {
- // The HOME, WORK and PREF attributes are common to the remaining fields
- // (ADR, TEL & EMAIL)
- if (lm_message_node_get_child(x, "HOME"))
- vcard_attrib |= vcard_home;
- if (lm_message_node_get_child(x, "WORK"))
- vcard_attrib |= vcard_work;
- if (lm_message_node_get_child(x, "PREF"))
- vcard_attrib |= vcard_pref;
- if (!strcmp(p, "ADR")) { // Address
- if (lm_message_node_get_child(x, "POSTAL"))
- vcard_attrib |= vcard_postal;
- data = lm_message_node_get_child_value(x, "EXTADD");
- display_vcard_item(barejid, "Addr (ext)", vcard_attrib, data);
- data = lm_message_node_get_child_value(x, "STREET");
- display_vcard_item(barejid, "Street", vcard_attrib, data);
- data = lm_message_node_get_child_value(x, "LOCALITY");
- display_vcard_item(barejid, "Locality", vcard_attrib, data);
- data = lm_message_node_get_child_value(x, "REGION");
- display_vcard_item(barejid, "Region", vcard_attrib, data);
- data = lm_message_node_get_child_value(x, "PCODE");
- display_vcard_item(barejid, "Postal code", vcard_attrib, data);
- data = lm_message_node_get_child_value(x, "CTRY");
- display_vcard_item(barejid, "Country", vcard_attrib, data);
- } else if (!strcmp(p, "TEL")) { // Telephone
- data = lm_message_node_get_child_value(x, "NUMBER");
- if (data) {
- if (lm_message_node_get_child(x, "VOICE"))
- vcard_attrib |= vcard_voice;
- if (lm_message_node_get_child(x, "FAX"))
- vcard_attrib |= vcard_fax;
- if (lm_message_node_get_child(x, "CELL"))
- vcard_attrib |= vcard_cell;
- display_vcard_item(barejid, "Phone", vcard_attrib, data);
- }
- } else if (!strcmp(p, "EMAIL")) { // Email
- if (lm_message_node_get_child(x, "INTERNET"))
- vcard_attrib |= vcard_inet;
- data = lm_message_node_get_child_value(x, "USERID");
- display_vcard_item(barejid, "Email", vcard_attrib, data);
- }
- }
- }
-}
-
-static LmHandlerResult cb_vcard(LmMessageHandler *h, LmConnection *c,
- LmMessage *m, gpointer user_data)
-{
- LmMessageNode *ansqry;
- const char *p, *bjid;
- char *buf, *tmp;
-
- // Display IQ result sender...
- p = lm_message_get_from(m);
- if (!p) {
- scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:vCard result (no sender name).");
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
- bjid = p;
-
- buf = g_strdup_printf("Received IQ:vCard result from <%s>", bjid);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
-
- // Get the vCard node
- ansqry = lm_message_node_get_child(m->node, "vCard");
- if (!ansqry) {
- scr_LogPrint(LPRINT_LOGNORM, "Empty IQ:vCard result!");
- g_free(buf);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
-
- // bjid should really be the "bare JID", let's strip the resource
- tmp = strchr(bjid, JID_RESOURCE_SEPARATOR);
- if (tmp) *tmp = '\0';
-
- scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0);
- g_free(buf);
-
- // Get result data...
- handle_vcard_node(bjid, ansqry);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static void storage_bookmarks_parse_conference(LmMessageNode *node)
-{
- const char *fjid, *name, *autojoin;
- const char *pstatus, *awhois;
- char *bjid;
- GSList *room_elt;
-
- fjid = lm_message_node_get_attribute(node, "jid");
- if (!fjid)
- return;
- name = lm_message_node_get_attribute(node, "name");
- autojoin = lm_message_node_get_attribute(node, "autojoin");
- awhois = lm_message_node_get_attribute(node, "autowhois");
- pstatus = lm_message_node_get_child_value(node, "print_status");
-
- bjid = jidtodisp(fjid); // Bare jid
-
- // Make sure this is a room (it can be a conversion user->room)
- room_elt = roster_find(bjid, jidsearch, 0);
- if (!room_elt) {
- room_elt = roster_add_user(bjid, name, NULL, ROSTER_TYPE_ROOM,
- sub_none, -1);
- } else {
- buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
- /*
- // If the name is available, should we use it?
- // I don't think so, it would be confusing because this item is already
- // in the roster.
- if (name)
- buddy_setname(room_elt->data, name);
- */
- }
-
- // Set the print_status and auto_whois values
- if (pstatus) {
- enum room_printstatus i;
- for (i = status_none; i <= status_all; i++)
- if (!strcasecmp(pstatus, strprintstatus[i]))
- break;
- if (i <= status_all)
- buddy_setprintstatus(room_elt->data, i);
- }
- if (awhois) {
- enum room_autowhois i = autowhois_default;
- if (!strcmp(awhois, "1"))
- i = autowhois_on;
- else if (!strcmp(awhois, "0"))
- i = autowhois_off;
- if (i != autowhois_default)
- buddy_setautowhois(room_elt->data, i);
- }
-
- // Is autojoin set?
- // If it is, we'll look up for more information (nick? password?) and
- // try to join the room.
- if (autojoin && !strcmp(autojoin, "1")) {
- const char *nick, *passwd;
- char *tmpnick = NULL;
- nick = lm_message_node_get_child_value(node, "nick");
- passwd = lm_message_node_get_child_value(node, "password");
- if (!nick || !*nick)
- nick = tmpnick = default_muc_nickname(NULL);
- // Let's join now
- scr_LogPrint(LPRINT_LOGNORM, "Auto-join bookmark <%s>", bjid);
- xmpp_room_join(bjid, nick, passwd);
- g_free(tmpnick);
- }
- g_free(bjid);
-}
-
-static LmHandlerResult cb_storage_bookmarks(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m, gpointer user_data)
-{
- LmMessageNode *x, *ansqry;
- char *p;
-
- if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) {
- // No server support, or no bookmarks?
- p = m->node->children->name;
- if (p && !strcmp(p, "item-not-found")) {
- // item-no-found means the server has Private Storage, but it's
- // currently empty.
- if (bookmarks)
- lm_message_node_unref(bookmarks);
- bookmarks = lm_message_node_new("storage", "storage:bookmarks");
- // We return 0 so that the IQ error message be
- // not displayed, as it isn't a real error.
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Unhandled error
- }
-
- ansqry = lm_message_node_get_child(m->node, "query");
- ansqry = lm_message_node_get_child(ansqry, "storage");
- if (!ansqry) {
- scr_LogPrint(LPRINT_LOG, "Invalid IQ:private result! (storage:bookmarks)");
- return 0;
- }
-
- // Walk through the storage tags
- for (x = ansqry->children ; x; x = x->next) {
- // If the current node is a conference item, parse it and update the roster
- if (x->name && !strcmp(x->name, "conference"))
- storage_bookmarks_parse_conference(x);
- }
- // "Copy" the bookmarks node
- if (bookmarks)
- lm_message_node_unref(bookmarks);
- lm_message_node_deep_ref(ansqry);
- bookmarks = ansqry;
- return 0;
-}
-
-
-static LmHandlerResult cb_storage_rosternotes(LmMessageHandler *h,
- LmConnection *c,
- LmMessage *m, gpointer user_data)
-{
- LmMessageNode *ansqry;
-
- if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) {
- const char *p;
- // No server support, or no roster notes?
- p = m->node->children->name;
- if (p && !strcmp(p, "item-not-found")) {
- // item-no-found means the server has Private Storage, but it's
- // currently empty.
- if (rosternotes)
- lm_message_node_unref(rosternotes);
- rosternotes = lm_message_node_new("storage", "storage:rosternotes");
- // We return 0 so that the IQ error message be
- // not displayed, as it isn't a real error.
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Unhandled error
- }
-
- ansqry = lm_message_node_get_child(m->node, "query");
- ansqry = lm_message_node_get_child(ansqry, "storage");
- if (!ansqry) {
- scr_LogPrint(LPRINT_LOG, "Invalid IQ:private result! "
- "(storage:rosternotes)");
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
- // Copy the rosternotes node
- if (rosternotes)
- lm_message_node_unref(rosternotes);
- lm_message_node_deep_ref(ansqry);
- rosternotes = ansqry;
- return 0;
-}
-
-
-static struct IqRequestStorageHandlers
-{
- const gchar *storagens;
- LmHandleMessageFunction handler;
-} iq_request_storage_handlers[] = {
- {"storage:rosternotes", &cb_storage_rosternotes},
- {"storage:bookmarks", &cb_storage_bookmarks},
- {NULL, NULL}
-};
-
-void xmpp_request_storage(const gchar *storage)
-{
- LmMessage *iq;
- LmMessageNode *query;
- LmMessageHandler *handler;
- int i;
-
- iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_GET);
- query = lm_message_node_add_child(iq->node, "query", NULL);
- lm_message_node_set_attribute(query, "xmlns", NS_PRIVATE);
- lm_message_node_set_attribute(lm_message_node_add_child
- (query, "storage", NULL),
- "xmlns", storage);
-
- for (i = 0;
- strcmp(iq_request_storage_handlers[i].storagens, storage) != 0;
- ++i) ;
-
- handler = lm_message_handler_new(iq_request_storage_handlers[i].handler,
- NULL, FALSE);
- lm_connection_send_with_reply(lconnection, iq, handler, NULL);
- lm_message_handler_unref(handler);
- lm_message_unref(iq);
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_iqrequest.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-#ifndef __XMPP_IQREQUEST_H__
-#define __XMPP_IQREQUEST_H__ 1
-
-void xmpp_iq_request(const char *fulljid, const char *xmlns);
-
-#endif /* __XMPP_IQREQUEST_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_muc.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,715 +0,0 @@
-/*
- * xmpp_muc.c -- Jabber MUC protocol handling
- *
- * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 <string.h>
-#include <stdlib.h>
-
-#include "xmpp_helper.h"
-#include "events.h"
-#include "hooks.h"
-#include "screen.h"
-#include "hbuf.h"
-#include "roster.h"
-#include "commands.h"
-#include "settings.h"
-#include "utils.h"
-#include "histolog.h"
-
-extern enum imstatus mystatus;
-extern gchar *mystatusmsg;
-
-static void decline_invitation(event_muc_invitation *invitation, char *reason)
-{
- // cut and paste from xmpp_room_invite
- LmMessage *m;
- LmMessageNode *x, *y;
-
- if (!invitation) return;
- if (!invitation->to || !invitation->from) return;
-
- m = lm_message_new(invitation->to, LM_MESSAGE_TYPE_MESSAGE);
-
- x = lm_message_node_add_child(m->node, "x", NULL);
- lm_message_node_set_attribute(x, "xmlns",
- "http://jabber.org/protocol/muc#user");
-
- y = lm_message_node_add_child(x, "decline", NULL);
- lm_message_node_set_attribute(y, "to", invitation->from);
-
- if (reason)
- lm_message_node_add_child(y, "reason", reason);
-
- lm_connection_send(lconnection, m, NULL);
- lm_message_unref(m);
-}
-
-static int evscallback_invitation(eviqs *evp, guint evcontext)
-{
- event_muc_invitation *invitation = evp->data;
-
- // Sanity check
- if (!invitation) {
- // Shouldn't happen.
- scr_LogPrint(LPRINT_LOGNORM, "Error in evs callback.");
- return 0;
- }
-
- if (evcontext == EVS_CONTEXT_TIMEOUT) {
- scr_LogPrint(LPRINT_LOGNORM, "Event %s timed out, cancelled.", evp->id);
- goto evscallback_invitation_free;
- }
- if (evcontext == EVS_CONTEXT_CANCEL) {
- scr_LogPrint(LPRINT_LOGNORM, "Event %s cancelled.", evp->id);
- goto evscallback_invitation_free;
- }
- if (!(evcontext & EVS_CONTEXT_USER))
- goto evscallback_invitation_free;
- // Ok, let's work now.
- // evcontext: 0, 1 == reject, accept
-
- if (evcontext & ~EVS_CONTEXT_USER) {
- char *nickname = default_muc_nickname(invitation->to);
- xmpp_room_join(invitation->to, nickname, invitation->passwd);
- g_free(nickname);
- } else {
- scr_LogPrint(LPRINT_LOGNORM, "Invitation to %s refused.", invitation->to);
- decline_invitation(invitation, NULL);
- }
-
-evscallback_invitation_free:
- g_free(invitation->to);
- g_free(invitation->from);
- g_free(invitation->passwd);
- g_free(invitation->reason);
- g_free(invitation);
- evp->data = NULL;
- return 0;
-}
-
-// Join a MUC room
-void xmpp_room_join(const char *room, const char *nickname, const char *passwd)
-{
- LmMessage *x;
- LmMessageNode *y;
- gchar *roomid;
- GSList *room_elt;
-
- if (!lm_connection_is_authenticated(lconnection) || !room) return;
- if (!nickname) return;
-
- roomid = g_strdup_printf("%s/%s", room, nickname);
- if (check_jid_syntax(roomid)) {
- scr_LogPrint(LPRINT_NORMAL, "<%s/%s> is not a valid Jabber room", room,
- nickname);
- g_free(roomid);
- return;
- }
-
- room_elt = roster_find(room, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_ROOM);
- // Add room if it doesn't already exist
- if (!room_elt) {
- room_elt = roster_add_user(room, NULL, NULL, ROSTER_TYPE_ROOM,
- sub_none, -1);
- } else {
- // Make sure this is a room (it can be a conversion user->room)
- buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
- }
- // If insideroom is TRUE, this is a nickname change and we don't care here
- if (!buddy_getinsideroom(room_elt->data)) {
- // We're trying to enter a room
- buddy_setnickname(room_elt->data, nickname);
- }
-
- // Send the XML request
- lm_message_new(roomid, LM_MESSAGE_TYPE_PRESENCE);
-
- x = lm_message_new_presence(mystatus, roomid, mystatusmsg);
- y = lm_message_node_add_child(x->node, "x", NULL);
- lm_message_node_set_attribute(y, "xmlns", "http://jabber.org/protocol/muc");
- if (passwd)
- lm_message_node_add_child(y, "password", passwd);
-
- lm_connection_send(lconnection, x, NULL);
- lm_message_unref(x);
- g_free(roomid);
-}
-
-// Invite a user to a MUC room
-// room syntax: "room@server"
-// reason can be null.
-void xmpp_room_invite(const char *room, const char *fjid, const char *reason)
-{
- LmMessage *msg;
- LmMessageNode *x, *y;
-
- if (!lm_connection_is_authenticated(lconnection) || !room || !fjid) return;
-
- msg = lm_message_new(room, LM_MESSAGE_TYPE_MESSAGE);
-
- x = lm_message_node_add_child(msg->node, "x", NULL);
- lm_message_node_set_attribute(x, "xmlns",
- "http://jabber.org/protocol/muc#user");
-
- y = lm_message_node_add_child(x, "invite", NULL);
- lm_message_node_set_attribute(y, "to", fjid);
-
- if (reason)
- lm_message_node_add_child(y, "reason", reason);
-
- lm_connection_send(lconnection, msg, NULL);
- lm_message_unref(msg);
-}
-
-int xmpp_room_setattrib(const char *roomid, const char *fjid,
- const char *nick, struct role_affil ra,
- const char *reason)
-{
- LmMessage *iq;
- LmMessageNode *query, *x;
-
- if (!lm_connection_is_authenticated(lconnection) || !roomid) return 1;
- if (!fjid && !nick) return 1;
-
- if (check_jid_syntax((char*)roomid)) {
- scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber id", roomid);
- return 1;
- }
- if (fjid && check_jid_syntax((char*)fjid)) {
- scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber id", fjid);
- return 1;
- }
-
- if (ra.type == type_affil && ra.val.affil == affil_outcast && !fjid)
- return 1; // Shouldn't happen (jid mandatory when banning)
-
- iq = lm_message_new_with_sub_type(roomid, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_SET);
- query = lm_message_node_add_child(iq->node, "query", NULL);
- lm_message_node_set_attribute(query, "xmlns",
- "http://jabber.org/protocol/muc#admin");
- x = lm_message_node_add_child(query, "item", NULL);
-
- if (fjid) {
- lm_message_node_set_attribute(x, "jid", fjid);
- } else { // nickname
- lm_message_node_set_attribute(x, "nick", nick);
- }
-
- if (ra.type == type_affil)
- lm_message_node_set_attribute(x, "affiliation", straffil[ra.val.affil]);
- else if (ra.type == type_role)
- lm_message_node_set_attribute(x, "role", strrole[ra.val.role]);
-
- if (reason)
- lm_message_node_add_child(x, "reason", reason);
-
- lm_connection_send(lconnection, iq, NULL);
- lm_message_unref(iq);
-
- return 0;
-}
-
-// Unlock a MUC room
-// room syntax: "room@server"
-void xmpp_room_unlock(const char *room)
-{
- LmMessageNode *node;
- LmMessage *iq;
-
- if (!lm_connection_is_authenticated(lconnection) || !room) return;
-
- iq = lm_message_new_with_sub_type(room, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_SET);
-
- node = lm_message_node_add_child(iq->node, "query", NULL);
- lm_message_node_set_attribute(node, "xmlns",
- "http://jabber.org/protocol/muc#owner");
- node = lm_message_node_add_child(node, "x", NULL);
- lm_message_node_set_attributes(node, "xmlns", "jabber:x:data",
- "type", "submit", NULL);
-
- lm_connection_send(lconnection, iq, NULL);
- lm_message_unref(iq);
-}
-
-// Destroy a MUC room
-// room syntax: "room@server"
-void xmpp_room_destroy(const char *room, const char *venue, const char *reason)
-{
- LmMessage *iq;
- LmMessageNode *query, *x;
-
- if (!lm_connection_is_authenticated(lconnection) || !room) return;
-
- iq = lm_message_new_with_sub_type(room, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_SET);
- query = lm_message_node_add_child(iq->node, "query", NULL);
- lm_message_node_set_attribute(query, "xmlns",
- "http://jabber.org/protocol/muc#owner");
- x = lm_message_node_add_child(query, "destroy", NULL);
-
- if (venue && *venue)
- lm_message_node_set_attribute(x, "jid", venue);
-
- if (reason)
- lm_message_node_add_child(x, "reason", reason);
-
- lm_connection_send(lconnection, iq, NULL);
- lm_message_unref(iq);
-}
-
-// muc_get_item_info(...)
-// Get room member's information from xmlndata.
-// The variables must be initialized before calling this function,
-// because they are not touched if the relevant information is missing.
-static void muc_get_item_info(const char *from, LmMessageNode *xmldata,
- enum imrole *mbrole, enum imaffiliation *mbaffil,
- const char **mbjid, const char **mbnick,
- const char **actorjid, const char **reason)
-{
- LmMessageNode *y, *z;
- const char *p;
-
- y = lm_message_node_find_child(xmldata, "item");
- if (!y)
- return;
-
- p = lm_message_node_get_attribute(y, "affiliation");
- if (p) {
- if (!strcmp(p, "owner")) *mbaffil = affil_owner;
- else if (!strcmp(p, "admin")) *mbaffil = affil_admin;
- else if (!strcmp(p, "member")) *mbaffil = affil_member;
- else if (!strcmp(p, "outcast")) *mbaffil = affil_outcast;
- else if (!strcmp(p, "none")) *mbaffil = affil_none;
- else scr_LogPrint(LPRINT_LOGNORM, "<%s>: Unknown affiliation \"%s\"",
- from, p);
- }
- p = lm_message_node_get_attribute(y, "role");
- if (p) {
- if (!strcmp(p, "moderator")) *mbrole = role_moderator;
- else if (!strcmp(p, "participant")) *mbrole = role_participant;
- else if (!strcmp(p, "visitor")) *mbrole = role_visitor;
- else if (!strcmp(p, "none")) *mbrole = role_none;
- else scr_LogPrint(LPRINT_LOGNORM, "<%s>: Unknown role \"%s\"",
- from, p);
- }
- *mbjid = lm_message_node_get_attribute(y, "jid");
- *mbnick = lm_message_node_get_attribute(y, "nick");
- // For kick/ban, there can be actor and reason tags
- *reason = lm_message_node_get_child_value(y, "reason");
- z = lm_message_node_find_child(y, "actor");
- if (z)
- *actorjid = lm_message_node_get_attribute(z, "jid");
-}
-
-// muc_handle_join(...)
-// Handle a join event in a MUC room.
-// This function will return the new_member value TRUE if somebody else joins
-// the room (and FALSE if _we_ are joining the room).
-static bool muc_handle_join(const GSList *room_elt, const char *rname,
- const char *roomjid, const char *ournick,
- enum room_printstatus printstatus,
- time_t usttime, int log_muc_conf)
-{
- bool new_member = FALSE; // True if somebody else joins the room (not us)
- gchar *mbuf;
-
- if (!buddy_getinsideroom(room_elt->data)) {
- // We weren't inside the room yet. Now we are.
- // However, this could be a presence packet from another room member
-
- buddy_setinsideroom(room_elt->data, TRUE);
- // Set the message flag unless we're already in the room buffer window
- scr_setmsgflag_if_needed(roomjid, FALSE);
- // Add a message to the tracelog file
- mbuf = g_strdup_printf("You have joined %s as \"%s\"", roomjid, ournick);
- scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
- g_free(mbuf);
- mbuf = g_strdup_printf("You have joined as \"%s\"", ournick);
-
- // The 1st presence message could be for another room member
- if (strcmp(ournick, rname)) {
- // Display current mbuf and create a new message for the member
- // Note: the usttime timestamp is related to the other member,
- // so we use 0 here.
- scr_WriteIncomingMessage(roomjid, mbuf, 0,
- HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
- if (log_muc_conf)
- hlog_write_message(roomjid, 0, -1, mbuf);
- g_free(mbuf);
- if (printstatus != status_none)
- mbuf = g_strdup_printf("%s has joined", rname);
- else
- mbuf = NULL;
- new_member = TRUE;
- }
- } else {
- mbuf = NULL;
- if (strcmp(ournick, rname)) {
- if (printstatus != status_none)
- mbuf = g_strdup_printf("%s has joined", rname);
- new_member = TRUE;
- }
- }
-
- if (mbuf) {
- guint msgflags = HBB_PREFIX_INFO;
- if (!settings_opt_get_int("muc_flag_joins"))
- msgflags |= HBB_PREFIX_NOFLAG;
- scr_WriteIncomingMessage(roomjid, mbuf, usttime, msgflags, 0);
- if (log_muc_conf)
- hlog_write_message(roomjid, 0, -1, mbuf);
- g_free(mbuf);
- }
-
- return new_member;
-}
-
-void handle_muc_presence(const char *from, LmMessageNode *xmldata,
- const char *roomjid, const char *rname,
- enum imstatus ust, const char *ustmsg,
- time_t usttime, char bpprio)
-{
- LmMessageNode *y;
- const char *p;
- char *mbuf;
- const char *ournick;
- enum imrole mbrole = role_none;
- enum imaffiliation mbaffil = affil_none;
- enum room_printstatus printstatus;
- enum room_autowhois autowhois;
- const char *mbjid = NULL, *mbnick = NULL;
- const char *actorjid = NULL, *reason = NULL;
- bool new_member = FALSE; // True if somebody else joins the room (not us)
- guint statuscode = 0;
- guint nickchange = 0;
- GSList *room_elt;
- int log_muc_conf;
- guint msgflags;
-
- log_muc_conf = settings_opt_get_int("log_muc_conf");
-
- room_elt = roster_find(roomjid, jidsearch, 0);
- if (!room_elt) {
- // Add room if it doesn't already exist
- // It shouldn't happen, there is probably something wrong (server or
- // network issue?)
- room_elt = roster_add_user(roomjid, NULL, NULL, ROSTER_TYPE_ROOM,
- sub_none, -1);
- scr_LogPrint(LPRINT_LOGNORM, "Strange MUC presence message");
- } else {
- // Make sure this is a room (it can be a conversion user->room)
- buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
- }
-
- // Get room member's information
- muc_get_item_info(from, xmldata, &mbrole, &mbaffil, &mbjid, &mbnick,
- &actorjid, &reason);
-
- // Get our room nickname
- ournick = buddy_getnickname(room_elt->data);
-
- if (!ournick) {
- // It shouldn't happen, probably a server issue
- mbuf = g_strdup_printf("Unexpected groupchat packet!");
-
- scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
- scr_WriteIncomingMessage(roomjid, mbuf, 0, HBB_PREFIX_INFO, 0);
- g_free(mbuf);
- // Send back an unavailable packet
- xmpp_setstatus(offline, roomjid, "", TRUE);
- scr_DrawRoster();
- return;
- }
-
- // Get the status code
- // 201: a room has been created
- // 301: the user has been banned from the room
- // 303: new room nickname
- // 307: the user has been kicked from the room
- // 321,322,332: the user has been removed from the room
- y = lm_message_node_find_child(xmldata, "status");
- if (y) {
- p = lm_message_node_get_attribute(y, "code");
- if (p)
- statuscode = atoi(p);
- }
-
- // Get the room's "print_status" settings
- printstatus = buddy_getprintstatus(room_elt->data);
- if (printstatus == status_default) {
- printstatus = (guint) settings_opt_get_int("muc_print_status");
- if (printstatus > 3)
- printstatus = status_default;
- }
-
- // A new room has been created; accept MUC default config
- if (statuscode == 201)
- xmpp_room_unlock(roomjid);
-
- // Check for nickname change
- if (statuscode == 303 && mbnick) {
- mbuf = g_strdup_printf("%s is now known as %s", rname, mbnick);
- scr_WriteIncomingMessage(roomjid, mbuf, usttime,
- HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
- if (log_muc_conf)
- hlog_write_message(roomjid, 0, -1, mbuf);
- g_free(mbuf);
- buddy_resource_setname(room_elt->data, rname, mbnick);
- // Maybe it's _our_ nickname...
- if (ournick && !strcmp(rname, ournick))
- buddy_setnickname(room_elt->data, mbnick);
- nickchange = TRUE;
- }
-
- // Check for departure/arrival
- if (!mbnick && ust == offline) {
- // Somebody is leaving
- enum { leave=0, kick, ban } how = leave;
- bool we_left = FALSE;
-
- if (statuscode == 307)
- how = kick;
- else if (statuscode == 301)
- how = ban;
-
- // If this is a leave, check if it is ourself
- if (ournick && !strcmp(rname, ournick)) {
- we_left = TRUE; // _We_ have left! (kicked, banned, etc.)
- buddy_setinsideroom(room_elt->data, FALSE);
- buddy_setnickname(room_elt->data, NULL);
- buddy_del_all_resources(room_elt->data);
- buddy_settopic(room_elt->data, NULL);
- scr_UpdateChatStatus(FALSE);
- update_roster = TRUE;
- }
-
- // The message depends on _who_ left, and _how_
- if (how) {
- gchar *mbuf_end;
- // Forced leave
- if (actorjid) {
- mbuf_end = g_strdup_printf("%s from %s by <%s>.\nReason: %s",
- (how == ban ? "banned" : "kicked"),
- roomjid, actorjid, reason);
- } else {
- mbuf_end = g_strdup_printf("%s from %s.",
- (how == ban ? "banned" : "kicked"),
- roomjid);
- }
- if (we_left)
- mbuf = g_strdup_printf("You have been %s", mbuf_end);
- else
- mbuf = g_strdup_printf("%s has been %s", rname, mbuf_end);
-
- g_free(mbuf_end);
- } else {
- // Natural leave
- if (we_left) {
- LmMessageNode *destroynode = lm_message_node_find_child(xmldata,
- "destroy");
- if (destroynode) {
- if ((reason = lm_message_node_get_child_value(destroynode,
- "reason"))) {
- mbuf = g_strdup_printf("You have left %s, "
- "the room has been destroyed: %s",
- roomjid, reason);
- } else {
- mbuf = g_strdup_printf("You have left %s, "
- "the room has been destroyed", roomjid);
- }
- } else {
- mbuf = g_strdup_printf("You have left %s", roomjid);
- }
- } else {
- if (ust != offline) {
- // This can happen when a network failure occurs,
- // this isn't an official leave but the user isn't there anymore.
- mbuf = g_strdup_printf("%s has disappeared!", rname);
- ust = offline;
- } else {
- if (ustmsg)
- mbuf = g_strdup_printf("%s has left: %s", rname, ustmsg);
- else
- mbuf = g_strdup_printf("%s has left", rname);
- }
- }
- }
-
- // Display the mbuf message if we're concerned
- // or if the print_status isn't set to none.
- if (we_left || printstatus != status_none) {
- msgflags = HBB_PREFIX_INFO;
- if (!we_left && settings_opt_get_int("muc_flag_joins") != 2)
- msgflags |= HBB_PREFIX_NOFLAG;
- scr_WriteIncomingMessage(roomjid, mbuf, usttime, msgflags, 0);
- }
-
- if (log_muc_conf)
- hlog_write_message(roomjid, 0, -1, mbuf);
-
- if (we_left) {
- scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
- g_free(mbuf);
- return;
- }
- g_free(mbuf);
- } else if (buddy_getstatus(room_elt->data, rname) == offline &&
- ust != offline) {
- // Somebody is joining
- new_member = muc_handle_join(room_elt, rname, roomjid, ournick,
- printstatus, usttime, log_muc_conf);
- } else {
- // This is a simple member status change
-
- if (printstatus == status_all && !nickchange) {
- mbuf = g_strdup_printf("Member status has changed: %s [%c] %s", rname,
- imstatus2char[ust], ((ustmsg) ? ustmsg : ""));
- scr_WriteIncomingMessage(roomjid, mbuf, usttime,
- HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
- g_free(mbuf);
- }
- }
-
- // Sanity check, shouldn't happen...
- if (!rname)
- return;
-
- // Update room member status
- roster_setstatus(roomjid, rname, bpprio, ust, ustmsg, usttime,
- mbrole, mbaffil, mbjid);
-
- autowhois = buddy_getautowhois(room_elt->data);
- if (autowhois == autowhois_default)
- autowhois = (settings_opt_get_int("muc_auto_whois") ?
- autowhois_on : autowhois_off);
-
- if (new_member && autowhois == autowhois_on) {
- // FIXME: This will fail for some UTF-8 nicknames.
- gchar *joiner_nick = from_utf8(rname);
- cmd_room_whois(room_elt->data, joiner_nick, FALSE);
- g_free(joiner_nick);
- }
-
- scr_DrawRoster();
-}
-
-void roompresence(gpointer room, void *presencedata)
-{
- const char *bjid;
- const char *nickname;
- char *to;
- struct T_presence *pres = presencedata;
-
- if (!buddy_getinsideroom(room))
- return;
-
- bjid = buddy_getjid(room);
- if (!bjid) return;
- nickname = buddy_getnickname(room);
- if (!nickname) return;
-
- to = g_strdup_printf("%s/%s", bjid, nickname);
- xmpp_setstatus(pres->st, to, pres->msg, TRUE);
- g_free(to);
-}
-
-// got_invite(from, to, reason, passwd)
-// This function should be called when receiving an invitation from user
-// "from", to enter the room "to". Optional reason and room password can
-// be provided.
-static void got_invite(const char* from, const char *to, const char* reason,
- const char* passwd)
-{
- eviqs *evn;
- event_muc_invitation *invitation;
- GString *sbuf;
- char *barejid;
- GSList *room_elt;
-
- sbuf = g_string_new("");
- if (reason) {
- g_string_printf(sbuf,
- "Received an invitation to <%s>, from <%s>, reason: %s",
- to, from, reason);
- } else {
- g_string_printf(sbuf, "Received an invitation to <%s>, from <%s>",
- to, from);
- }
-
- barejid = jidtodisp(from);
- scr_WriteIncomingMessage(barejid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
- scr_LogPrint(LPRINT_LOGNORM, "%s", sbuf->str);
-
- evn = evs_new(EVS_TYPE_INVITATION, EVS_MAX_TIMEOUT);
- if (evn) {
- evn->callback = &evscallback_invitation;
- invitation = g_new(event_muc_invitation, 1);
- invitation->to = g_strdup(to);
- invitation->from = g_strdup(from);
- invitation->passwd = g_strdup(passwd);
- invitation->reason = g_strdup(reason);
- evn->data = invitation;
- evn->desc = g_strdup_printf("<%s> invites you to %s ", from, to);
- g_string_printf(sbuf, "Please use /event %s accept|reject", evn->id);
- } else {
- g_string_printf(sbuf, "Unable to create a new event!");
- }
- scr_WriteIncomingMessage(barejid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
- scr_LogPrint(LPRINT_LOGNORM, "%s", sbuf->str);
- g_string_free(sbuf, TRUE);
- g_free(barejid);
-
- // Make sure the MUC room barejid is a room in the roster
- barejid = jidtodisp(to);
- room_elt = roster_find(barejid, jidsearch, 0);
- if (room_elt)
- buddy_settype(room_elt->data, ROSTER_TYPE_ROOM);
-
- g_free(barejid);
-}
-
-
-// Specific MUC message handling (for example invitation processing)
-void got_muc_message(const char *from, LmMessageNode *x)
-{
- LmMessageNode *invite = lm_message_node_get_child(x, "invite");
- if (invite)
- {
- const char *invite_from;
- const char *reason = NULL;
- const char *password = NULL;
-
- invite_from = lm_message_node_get_attribute(invite, "from");
- reason = lm_message_node_get_child_value(invite, "reason");
- password = lm_message_node_get_child_value(invite, "password");
- if (invite_from)
- got_invite(invite_from, from, reason, password);
- }
- // TODO
- // handle status code = 100 ( not anonymous )
- // handle status code = 170 ( changement de config )
- // 10.2.1 Notification of Configuration Changes
- // declined invitation
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_muc.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-#ifndef __XMPP_MUC_H__
-#define __XMPP_MUC_H__ 1
-
-void roompresence(gpointer room, void *presencedata);
-void got_muc_message(const char *from, LmMessageNode *x);
-void handle_muc_presence(const char *from, LmMessageNode * xmldata,
- const char *roomjid, const char *rname,
- enum imstatus ust, const char *ustmsg,
- time_t usttime, char bpprio);
-
-#endif /* __XMPP_MUC_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_s10n.c Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-/*
- * xmpp_s10n.c -- Jabber presence subscription handling
- *
- * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de>
- * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
- *
- * 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 "xmpp_helper.h"
-#include "events.h"
-#include "screen.h"
-#include "hbuf.h"
-#include "settings.h"
-
-// xmpp_send_s10n(jid, subtype)
-// Send a s10n message with the passed subtype
-void xmpp_send_s10n(const char *bjid, LmMessageSubType type)
-{
- LmMessage *x = lm_message_new_with_sub_type(bjid,
- LM_MESSAGE_TYPE_PRESENCE,
- type);
- lm_connection_send(lconnection, x, NULL);
- lm_message_unref(x);
-}
-
-int evscallback_subscription(eviqs *evp, guint evcontext)
-{
- char *barejid;
- char *buf;
-
- if (evcontext == EVS_CONTEXT_TIMEOUT) {
- scr_LogPrint(LPRINT_LOGNORM, "Event %s timed out, cancelled.",
- evp->id);
- return 0;
- }
- if (evcontext == EVS_CONTEXT_CANCEL) {
- scr_LogPrint(LPRINT_LOGNORM, "Event %s cancelled.", evp->id);
- return 0;
- }
- if (!(evcontext & EVS_CONTEXT_USER))
- return 0;
-
- // Sanity check
- if (!evp->data) {
- // Shouldn't happen, data should be set to the barejid.
- scr_LogPrint(LPRINT_LOGNORM, "Error in evs callback.");
- return 0;
- }
-
- // Ok, let's work now.
- // evcontext: 0, 1 == reject, accept
-
- barejid = evp->data;
-
- if (evcontext & ~EVS_CONTEXT_USER) {
- // Accept subscription request
- xmpp_send_s10n(barejid, LM_MESSAGE_SUB_TYPE_SUBSCRIBED);
- buf = g_strdup_printf("<%s> is allowed to receive your presence updates",
- barejid);
- } else {
- // Reject subscription request
- xmpp_send_s10n(barejid, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED);
- buf = g_strdup_printf("<%s> won't receive your presence updates", barejid);
- if (settings_opt_get_int("delete_on_reject")) {
- // Remove the buddy from the roster if there is no current subscription
- if (roster_getsubscription(barejid) == sub_none)
- xmpp_delbuddy(barejid);
- }
- }
- scr_WriteIncomingMessage(barejid, buf, 0, HBB_PREFIX_INFO, 0);
- scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
- g_free(buf);
- return 0;
-}
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
--- a/mcabber/src/xmpp_s10n.h Tue Feb 02 21:27:26 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-#ifndef __XMPP_S10N_H__
-#define __XMPP_S10N_H__ 1
-
-#include "events.h"
-
-int evscallback_subscription(eviqs *evp, guint evcontext);
-
-#endif /* __XMPP_S10N_H__ */
-
-/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */