# HG changeset patch # User Myhailo Danylenko # Date 1257460995 -7200 # Node ID 29423ceb6adf4f8440951e6a24c8bb4667bc48a2 Initial commit diff -r 000000000000 -r 29423ceb6adf .gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.gitignore Fri Nov 06 00:43:15 2009 +0200 @@ -0,0 +1,25 @@ +# ignore generated by libtool files (no more necessary) +*.a +*.o +*.lo +*.la +*.so +*.lai +*.so.* +# ignore backup files +*~ +# ignore docs: they are not important and apiref is generated on the fly +*.html +# ignore generated makefile +Makefile +# ignore cmake files +*.cmake +CMake* +CPack* +# ignore generated header +config.h +# ignore resulting bundles +*.deb +*.tar.bz2 +# ignore build dir +build diff -r 000000000000 -r 29423ceb6adf CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists.txt Fri Nov 06 00:43:15 2009 +0200 @@ -0,0 +1,76 @@ +## Copyright 2009 Myhailo Danylenko +# This file is part of mcabber module writing howto examples. +# +# Examples are 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, see . + +cmake_minimum_required(VERSION 2.6) +project(avatar C) + +## Target definitions +add_library(avatar MODULE avatar.c) + +## User settable options +set(MCABBER_INCLUDE_DIR "${avatar_SOURCE_DIR}/../include" + CACHE FILEPATH "Path to mcabber headers") + +## Packaging information +set(CPACK_PACKAGE_NAME libmcabber-avatar) +set(CPACK_PACKAGE_VERSION "0.0.1") +set(CPACK_PACKAGE_VENDOR "IsBear") +set(CPACK_PACKAGE_CONTACT "Myhailo Danylenko ") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Mcabber example modularized avatar implementation") +set(CPACK_RESOURCE_FILE_LICENSE ${avatar_SOURCE_DIR}/COPYING) +set(CPACK_SOURCE_GENERATOR TBZ2) +set(CPACK_GENERATOR DEB CACHE TEXT "Binary package generator, eg DEB, RPM, TGZ, NSIS...") +set(CPACK_DEBIAN_PACKAGE_SECTION libs) +find_program(DPKG_EXECUTABLE dpkg) +if(DPKG_EXECUTABLE) + execute_process(COMMAND ${DPKG_EXECUTABLE} --print-architecture OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE) +else() + set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE i386 CACHE TEXT "Architecture of package") +endif() +set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") +set(CPACK_SOURCE_IGNORE_FILES "/\\\\..*;\\\\.swp;~$;/build/;\\\\.tar\\\\.;\\\\.deb;\\\\.so") +include(CPack) + +## Check for build dependencies +find_package(PkgConfig REQUIRED) +pkg_check_modules(GLIB REQUIRED glib-2.0) +pkg_check_modules(GMODULE REQUIRED gmodule-2.0) +pkg_check_modules(LM REQUIRED loudmouth-1.0) +pkg_check_modules(PNG REQUIRED libpng12) +set(AALIB_INCLUDE_DIRS "/usr/include" CACHE FILEPATH "Path to AAlib includes") +set(AALIB_LIBRARIES "-laa" CACHE TEXT "Libraries, necessary to link with AAlib") + +## Compiler setup +include_directories(SYSTEM ${GLIB_INCLUDE_DIRS} + ${GMODULE_INCLUDE_DIRS} + ${LM_INCLUDE_DIRS} + ${PNG_INCLUDE_DIRS} + ${AALIB_INCLUDE_DIRS}) +target_link_libraries(avatar ${GLIB_LIBRARIES} + ${GMODULE_LIBRARIES} + ${LM_LIBRARIES} + ${PNG_LIBRARIES} + ${AALIB_LIBRARIES}) +include_directories(${avatar_SOURCE_DIR} + ${avatar_BINARY_DIR} + ${MCABBER_INCLUDE_DIR}) + +## Installation +install(TARGETS avatar DESTINATION lib/mcabber) +install(FILES avatar.rc DESTINATION share/doc/${CPACK_PACKAGE_NAME}) +install(DIRECTORY help DESTINATION share/mcabber) + +## The End ## vim: se ts=4: ## diff -r 000000000000 -r 29423ceb6adf COPYING --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING Fri Nov 06 00:43:15 2009 +0200 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff -r 000000000000 -r 29423ceb6adf README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Fri Nov 06 00:43:15 2009 +0200 @@ -0,0 +1,58 @@ + +This is a module, implementing avatar publishing/retrieving via pep/pubsub. +This module informs you, when your buddies publish their avatars via pep +and provides command '/avatar' to publish your own. But this is not all. +It also will show you buddy's avatar! Yes, it is aalib :) + +This module depends on module 'pep'. You must load it prior to this module +and not unload until you unload this module. + +For this module to actually do something you should set option +avatars_directory - a place, where your buddy's avatars will be saved. + +Note, that currently only pubsub-publishing of avatars is supported +(retrieving too works only for pubsub, but there you will be at least +informed of url, that you can use to view avatar manually). Thus, as +standard states, that pubsub-published data must be image/png, your +avatar should be in png format. + +INSTALLATION + +To install it, you need: +loudmouth +glib +aalib +libpng +mcabber's headers +cmake +make +gcc + +Then do +$ mkdir build +$ cd build +$ cmake .. +$ make edit_cache +$ make +# make install + +Debian users can instead of make install do +$ fakeroot make package +# dpkg -i ...-*.deb + +Users of other distributions can select appropriate package +generator, using cache editor. + +LICENSE + +This code underlies terms of GNU GPL v2 or later. You can find it in file COPYING +from this distribution or on a GNU web-site . + +CONTACTS + +I will be happy to get feedback, patches, suggestions, etc. +You can send me email or contact via jabber . + + -- Myhailo Danylenko + + diff -r 000000000000 -r 29423ceb6adf avatar.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/avatar.c Fri Nov 06 00:43:15 2009 +0200 @@ -0,0 +1,1059 @@ +/* + * avatar.c -- Pep avatar events + * + * Copyrigth (C) 2009 Myhailo Danylenko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#define PNG_USER_MEM_SUPPORTED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compl.h" +#include "commands.h" +#include "utils.h" +#include "xmpp.h" +#include "screen.h" +#include "hbuf.h" +#include "hooks.h" +#include "settings.h" +#include "pep.h" + +#define NS_AVATAR_DATA ( "urn:xmpp:avatar:data" ) +#define NS_AVATAR_METADATA ( "urn:xmpp:avatar:metadata" ) +#define NS_AVATAR_METADATA_NOTIFY ( "urn:xmpp:avatar:metadata+notify" ) + +static guint avatar_cid = 0; + +GSList *reply_handlers = NULL; + +// png stuff + +static png_voidp png_glib_malloc (png_structp ignore, png_size_t size) +{ + return g_malloc (size); +} + +static void png_glib_free (png_structp ignore, png_voidp chunk) +{ + g_free (chunk); +} + +// reads file and returns array of pointers to image rows in +// grayscale 8-bit with alpha-channel format (i.e. two bytes per pixel) +// after use just do g_free of this pointer - all data are in one memory chunk +static png_bytep *png_read_file (const char *file, int *rheight, int *rwidth) +{ + FILE *fd = fopen (file, "rb"); + png_infop info_ptr; + png_structp png_ptr; + int rowbytes; + png_bytep *row_pointers; + int width; + int height; + + if (!fd) + return NULL; + + { // check signature + char header[8]; + + fread (header, 1, 8, fd); + + if (png_sig_cmp (header, 0, 8)) { + fclose (fd); + return NULL; + } + } + + // initialize reader + png_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL, png_glib_malloc, png_glib_free); + + if (!png_ptr) { + fclose (fd); + return NULL; + } + + info_ptr = png_create_info_struct (png_ptr); + if (!info_ptr) { + png_destroy_read_struct (&png_ptr, NULL, NULL); + fclose (fd); + return NULL; + } + + if (setjmp (png_jmpbuf (png_ptr))) { + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + fclose (fd); + return NULL; + } + + png_init_io (png_ptr, fd); + png_set_sig_bytes (png_ptr, 8); + + // get information + png_read_info (png_ptr, info_ptr); + + { // set up transformations + png_byte color_type = info_ptr->color_type; + png_byte bit_depth = info_ptr->bit_depth; + + // trying to convert anything to grayscale 8-bit color with alpha channel + if (color_type == PNG_COLOR_TYPE_PALETTE) // XXX + png_set_palette_to_rgb (png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand_gray_1_2_4_to_8 (png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_PALETTE /* ??? */) // XXX + png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER); + if (bit_depth == 16) + png_set_strip_16 (png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png_ptr); + if (color_type == PNG_COLOR_TYPE_PALETTE /* ??? */ || color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) // XXX + png_set_rgb_to_gray_fixed (png_ptr, 1, -1, -1); + + // renew information in info structure + png_read_update_info (png_ptr, info_ptr); + } + + width = info_ptr->width; + height = info_ptr->height; + rowbytes = info_ptr->rowbytes; + + { // allocate buffer for image data + png_bytep chunk = g_malloc ((sizeof(png_bytep) + rowbytes) * height); + png_bytep start = chunk + sizeof(png_bytep) * height; + int y; + + row_pointers = (png_bytep *) chunk; + + for (y = 0; y < height; ++y) + row_pointers[y] = start + y * rowbytes; + } + + // read image + if (setjmp (png_jmpbuf (png_ptr))) { + g_free (row_pointers); + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + fclose (fd); + return NULL; + } + + png_read_image (png_ptr, row_pointers); + png_read_end (png_ptr, NULL); + + // free resources + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + fclose (fd); + + *rheight = height; + *rwidth = width; + + return row_pointers; +} + +// aa stuff + +// converts image in format, returned by png_read_file into ascii, +// scaling it to fit specified max values. If font parameters are +// set properly, should result in image on screen with original png +// aspect ratio. +static gchar *aa_convert_image (png_bytep *row_pointers, int height, int width, int maxcharh, int maxcharw, int fonth, int fontw) +{ + gchar *result; + struct aa_hardware_params hwparams = { + .font = NULL, + .supported = AA_NORMAL_MASK, + .width = 0, + .height = 0, + }; + + // params for zooming + int finalcharh; + int finalcharw; + int finalh; + int finalw; + int starty; + int startx; + int finalratioh; + int finalratiow; + + // process aa parameters from environment variables + aa_parseoptions (&hwparams, NULL, NULL, NULL); + + { // calculate parameters for image zooming into aa surface + // this always makes me a hard time, thus such a mess :/ + // I hope, compiler will optimize out all these variables... + + // font size in font pixels (real?) + //int fonth = 16; + //int fontw = 8; + float fontaspect = (float) fontw / fonth; + // aa pixels per character + const int aappch = 2; + const int aappcw = 2; + const float aaaspect = (float) aappcw / aappch; + // font pixels per aa pixel + int aappph = (float) fonth / aappch; + int aapppw = (float) fontw / aappcw; + float aapixelaspect = (float) aapppw / aappph; + // original image size in real pixels + int originh = height; + int originw = width; + float originaspect = (float) originw / originh; + // maximal resulting image size in chars + //int maxcharh = res_height; + //int maxcharw = res_width; + // maximal resulting image size in aa pixels + int maxh = maxcharh * aappch; + int maxw = maxcharw * aappcw; + // maximal resulting image size in real pixels + int maxh_rp; + int maxw_rp; + if (aapixelaspect > 1) { + maxh_rp = maxh; + maxw_rp = (float) maxw * aapixelaspect; + } else { + maxh_rp = (float) maxh / aapixelaspect; + maxw_rp = maxw; + } + float maxaspect_rp = (float) maxw_rp / maxh_rp; + // resulting image size in real pixels + float ratio_rp; + int resh_rp; + int resw_rp; + if (originh > maxh_rp || originw > maxw_rp) { + // boundaries crossed, will zoom + if (originaspect > maxaspect_rp) { + // width is bigger + ratio_rp = (float) maxw_rp / originw; + resh_rp = (float) originh * ratio_rp; + resw_rp = maxw_rp; + } else { + // height is bigger + ratio_rp = (float) maxh_rp / originh; + resh_rp = maxh_rp; + resw_rp = (float) originw * ratio_rp; + } + } else { + // image fits, no zooming + ratio_rp = 1; + resh_rp = originh; + resw_rp = originw; + } + // resulting image size in aa pixels + int resh; + int resw; + if (aapixelaspect > 1) { + resh = resh_rp; + resw = (float) resw_rp / aapixelaspect; + } else { + resh = (float) resh_rp * aapixelaspect; + resw = resw_rp; + } + // number of original image pixels per one resulting image pixel + int ratioh = (float) originh / resh; + int ratiow = (float) originw / resw; + // resulting image size in chars + int rescharh = (float) resh / aappch; + int rescharw = (float) resw / aappcw; + + // re-calculate parameters, as they may change due to rounding (?) + finalcharh = rescharh; + finalcharw = rescharw; + finalh = rescharh * aappch; + finalw = rescharw * aappcw; + int finalh_rp; + int finalw_rp; + if (aapixelaspect > 1) { + finalh_rp = finalh; + finalw_rp = (float) finalw * aapixelaspect; + } else { + finalh_rp = (float) finalh / aapixelaspect; + finalw_rp = finalw; + } + finalratioh = (float) originh / finalh; + finalratiow = (float) originw / finalw; + + // center image part actually used + int areah = (float) finalh * finalratioh; + int areaw = (float) finalw * finalratiow; + int losth = originh - areah; + int lostw = originw - areaw; + starty = losth / 2; + startx = lostw / 2; + } + + // create aa image + hwparams.height = finalcharh; + hwparams.width = finalcharw; + + aa_context *context = aa_init (&mem_d, &hwparams, NULL); + + if (context == NULL) + return NULL; + + { // fill aa image from png buffer with scaling + int height, width; + const int bpp = 2; + int y; + + height = aa_imgheight (context); + width = aa_imgwidth (context); + + if (height > finalh) + height = finalh; + if (width > finalw) + width = finalw; + + for (y = 0; y < height; ++y) { + int x; + + for (x = 0; x < width; ++x) { + png_bytep *row_pointer = row_pointers + starty + y * finalratioh; + unsigned int color = 0; + unsigned int cy; + + // just arithmetic average + for (cy = 0; cy < finalratioh; ++cy) { + png_bytep row = row_pointer[cy] + (startx + x * finalratiow) * bpp; + int cx; + + for (cx = 0; cx < finalratiow; ++cx) + color += row[cx * bpp] * row[cx * bpp + 1] / 255; + } + + color /= finalratioh * finalratiow; + + aa_putpixel (context, x, y, color); + } + } + } + + // render ascii image + aa_render (context, &aa_defrenderparams, 0, 0, aa_scrwidth (context), aa_scrheight (context)); + aa_flush (context); + + { // format the result + char *text = aa_text (context); + int width = aa_scrwidth (context); + int height = aa_scrheight (context); + GString *avatar = g_string_new (NULL); + char *end = text + width * height; + + while (text < end) { + g_string_append_len (avatar, text, width); + g_string_append_c (avatar, '\n'); + text += width; + } + + // strip last '\n' + g_string_truncate (avatar, avatar->len - 1); + + result = g_string_free (avatar, FALSE); + + } + + // free resources + aa_close (context); + + return result; +} + +// avatar stuff + +// returns filename to save this avatar. NULL on error. g_free result. +static gchar *avatar_id_filename (const gchar *id) +{ + gchar *escid = g_strdup (id); + gchar *dir = (gchar *) settings_opt_get ("avatar_directory"); + gchar *file; + + if (!dir) + return NULL; + + { // neutralize id + const gchar *valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + + g_strcanon (escid, valid, '_'); + } + + dir = expand_filename (dir); + + file = g_strdup_printf ("%s/%s.png", dir, escid); + + g_free (dir); + g_free (escid); + + return file; +} + +// returns name of symlink to avatar image file for this jid +static gchar *avatar_jid_filename (const gchar *jid) +{ + gchar *dir = (gchar *) settings_opt_get ("avatar_directory"); + gchar *file = NULL; + gchar *bjid; + + if (!dir) + return NULL; + + // if jid validity alone is enough for mcabber to use as + // names for history logs, it's fine for us too. + if (check_jid_syntax (jid)) + return NULL; + + bjid = jidtodisp (jid); + mc_strtolower (bjid); + dir = expand_filename (dir); + file = g_strdup_printf ("%s/%s", dir, bjid); + + g_free (bjid); + return file; +} + +// symlinks specified jid to specified file. performs jid +// checks, but not file checks. +static void set_jid_avatar (const char *jid, const char *file) +{ + gchar *jfile; + + if (!jid || !file) + return; + + jfile = avatar_jid_filename (jid); + + if (!jfile) + return; + + unlink (jfile); + if (symlink (file, jfile) == -1) + scr_LogPrint (LPRINT_LOGNORM, "avatar: Cannot symlink jid-file to avatar: %s.", strerror (errno)); +} + +// reply handler for metadata publish request +// just prints error message if unsuccessful +LmHandlerResult avatar_publish_metadata_reply_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer userdata) +{ + reply_handlers = g_slist_remove (reply_handlers, handler); + + switch (lm_message_get_sub_type (message)) { + case LM_MESSAGE_SUB_TYPE_RESULT: + break; + + case LM_MESSAGE_SUB_TYPE_ERROR: + + { + LmMessageNode *node = lm_message_get_node (message); + const gchar *type; + const gchar *reason; + + node = lm_message_node_get_child (node, "error"); + type = lm_message_node_get_attribute (node, "type"); + if (node->children) + reason = node->children->name; + else + reason = "undefined"; + + scr_LogPrint (LPRINT_LOGNORM, "avatar: Metadata publishing failed: %s - %s.", type, reason); + } + + break; + + default: + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + break; + } + + return LM_HANDLER_RESULT_REMOVE_MESSAGE; +} + +// reply handler for data publish request +// sends metadata update request or prints error message +LmHandlerResult avatar_publish_data_reply_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer userdata) +{ + LmMessage *request = (LmMessage *) userdata; + + reply_handlers = g_slist_remove (reply_handlers, handler); + + switch (lm_message_get_sub_type (message)) { + case LM_MESSAGE_SUB_TYPE_RESULT: + + { + // XXX we may use only one instance of this... + LmMessageHandler *mhandler = lm_message_handler_new (avatar_publish_metadata_reply_handler, NULL, NULL); + + reply_handlers = g_slist_append (reply_handlers, mhandler); + + lm_connection_send_with_reply (connection, request, mhandler, NULL); + + lm_message_handler_unref (mhandler); + lm_message_unref (request); + } + + break; + + case LM_MESSAGE_SUB_TYPE_ERROR: + + { + LmMessageNode *node = lm_message_get_node (message); + const gchar *type; + const gchar *reason; + + node = lm_message_node_get_child (node, "error"); + type = lm_message_node_get_attribute (node, "type"); + if (node->children) + reason = node->children->name; + else + reason = "undefined"; + + scr_LogPrint (LPRINT_LOGNORM, "avatar: Data publishing failed: %s - %s.", type, reason); + } + + break; + + default: + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + break; + } + + return LM_HANDLER_RESULT_REMOVE_MESSAGE; +} + +// prints avatar (if available) for jid into jid's buffer +// id can be omitted +static gboolean print_avatar (const gchar *file, const gchar *jid, const gchar *id) +{ + png_bytep *row_pointers; + gchar *avatar; + int width; + int height; + + if (!file) + return FALSE; + + { // check if file exists to not trigger unnecessary error messages + struct stat buf; + + if (stat (file, &buf) == -1) { + gchar *bjid = jidtodisp (jid); + + scr_WriteIncomingMessage (bjid, "No avatar for this buddy yet.", 0, HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0); // NO conversion from utf-8 + + g_free (bjid); + } + } + + row_pointers = png_read_file (file, &width, &height); + + if (!row_pointers) { + scr_LogPrint (LPRINT_LOGNORM, "avatar: Cannot decode png data from file %s.", file); + return FALSE; + } + + { // convert to ascii + int maxcharh = settings_opt_get_int ("avatar_max_height"); + int maxcharw = settings_opt_get_int ("avatar_max_width"); + int fonth = settings_opt_get_int ("avatar_font_height"); + int fontw = settings_opt_get_int ("avatar_font_width"); + + // if not set explicitly, try to calculate available space + if (!maxcharh) { + char *var = getenv ("LINES"); + + if (var) + maxcharh = atoi (var); + else + maxcharh = 25; + + maxcharh -= settings_opt_get_int ("log_win_height") + 3 + 1; // +1 due to avatar header line + } + + if (!maxcharw) { + char *var = getenv ("COLUMNS"); + + if (var) + maxcharw = atoi (var); + else + maxcharw = 80; + + maxcharw -= settings_opt_get_int ("roster_width") + scr_getprefixwidth (); + } + + if (!fonth) + fonth = 16; + if (!fontw) + fontw = 8; + + avatar = aa_convert_image (row_pointers, width, height, maxcharh, maxcharw, fonth, fontw); + } + + g_free (row_pointers); + + if (!avatar) { + scr_LogPrint (LPRINT_LOGNORM, "avatar: Error converting image to ascii."); + return FALSE; + } + + { // print out avatar + gchar *bjid = jidtodisp (jid); + gchar *mesg; + if (id) + mesg = g_strdup_printf ("Avatar [%s]:\n%s", id, avatar); + else + mesg = g_strdup_printf ("Avatar:\n%s", avatar); + + scr_WriteIncomingMessage (bjid, mesg, 0, HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0); // NO conversion from utf-8 + + g_free (bjid); + g_free (mesg); + } + + g_free (avatar); + + return TRUE; +} + +// reply handler for image/png data request +// saves image and prints it to buddy buffer +LmHandlerResult avatar_retrieve_data_reply_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer userdata) +{ + gchar *from = (gchar *) userdata; + + reply_handlers = g_slist_remove (reply_handlers, handler); + + switch (lm_message_get_sub_type (message)) { + case LM_MESSAGE_SUB_TYPE_RESULT: + + { + LmMessageNode *node = lm_message_get_node (message); + node = lm_message_node_get_child (node, "pubsub"); + if (!node) + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + + node = lm_message_node_get_child (node, "items"); + if (!node) + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + + { + LmMessageNode *item; + + for (item = node->children; item; item = item->next) { + if (!strcmp (item->name, "item")) { + LmMessageNode *data = lm_message_node_get_child (item, "data"); + const gchar *id = lm_message_node_get_attribute (item, "id"); + + if (!data) + continue; + + { // save to file and display in ascii + const gchar *base64 = lm_message_node_get_value (data); + gchar *png; + gsize len; + gchar *file; + + if (!base64) + continue; + + png = g_base64_decode (base64, &len); + + if (!png) + continue; + + file = avatar_id_filename (id); + + if (!file) { + g_free (png); + continue; + } + + { // write image to file + int fd = open (file, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + + if (fd == -1) { + scr_LogPrint (LPRINT_LOGNORM, "avatar: Cannot create file: %s.", strerror (errno)); + g_free (png); + g_free (file); + continue; + } + + write (fd, png, len); + close (fd); + } + + // create jid symlink to avatar image + set_jid_avatar (from, file); + + g_free (png); + + // read it back :/ + print_avatar (file, from, id); + + g_free (file); + } + } + } + } + } + + break; + + case LM_MESSAGE_SUB_TYPE_ERROR: + + { + LmMessageNode *node = lm_message_get_node (message); + const gchar *type; + const gchar *reason; + + node = lm_message_node_get_child (node, "error"); + type = lm_message_node_get_attribute (node, "type"); + if (node->children) + reason = node->children->name; + else + reason = "undefined"; + + scr_LogPrint (LPRINT_LOGNORM, "avatar: Obtaining avatar data for %s failed: %s - %s.", from, type, reason); + } + + break; + + default: + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + break; + } + + return LM_HANDLER_RESULT_REMOVE_MESSAGE; +} + +// data is image/png data of length len +// call with len = 0 to publish empty avatar +static void avatar_publish (const gchar *data, gsize len) +{ + LmMessage *datarequest; + LmMessageHandler *handler; + gchar *id; + LmMessage *request; + LmMessageNode *node; + + request = lm_message_new_with_sub_type (NULL, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_SET); + node = lm_message_get_node (request); + + if (len) { + // create data publish request + lm_message_node_set_attribute (node, "from", lm_connection_get_jid (lconnection)); + + node = lm_message_node_add_child (node, "pubsub", NULL); + lm_message_node_set_attribute (node, "xmlns", NS_PUBSUB); + + node = lm_message_node_add_child (node, "publish", NULL); + lm_message_node_set_attribute (node, "node", NS_AVATAR_DATA); + + node = lm_message_node_add_child (node, "item", NULL); + id = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len); + lm_message_node_set_attribute (node, "id", id); + + { + gchar *base64 = g_base64_encode (data, len); + + node = lm_message_node_add_child (node, "data", base64); + lm_message_node_set_attribute (node, "xmlns", NS_AVATAR_DATA); + + g_free (base64); + } + + // then create metadata publish request to be supplied to reply handler as userdata + datarequest = lm_message_new_with_sub_type (NULL, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_SET); + node = lm_message_get_node (datarequest); + } + + lm_message_node_set_attribute (node, "from", lm_connection_get_jid (lconnection)); + + node = lm_message_node_add_child (node, "pubsub", NULL); + lm_message_node_set_attribute (node, "xmlns", NS_PUBSUB); + + node = lm_message_node_add_child (node, "publish", NULL); + lm_message_node_set_attribute (node, "node", NS_AVATAR_METADATA); + + node = lm_message_node_add_child (node, "item", NULL); + if (len) + lm_message_node_set_attribute (node, "id", id); + + node = lm_message_node_add_child (node, "metadata", NULL); + lm_message_node_set_attribute (node, "xmlns", NS_AVATAR_METADATA); + + if (len) { + gchar *bytes = g_strdup_printf ("%d", len); + + node = lm_message_node_add_child (node, "info", NULL); + lm_message_node_set_attributes (node, "bytes", bytes, "id", id, "type", "image/png", NULL); + + g_free (bytes); + } + + // create handler + if (len) + handler = lm_message_handler_new (avatar_publish_data_reply_handler, (gpointer) datarequest, (GDestroyNotify) lm_message_unref /* custom, remove from queue? */); + else + handler = lm_message_handler_new (avatar_publish_metadata_reply_handler, NULL, NULL); + reply_handlers = g_slist_append (reply_handlers, handler); + + // send + lm_connection_send_with_reply (lconnection, request, handler, NULL); + + lm_message_handler_unref (handler); + lm_message_unref (request); +} + +// pep node handler +// if avatar is not yet saved, sends data request, else updates jid's symlink +static void avatar_handler (const gchar *from, const gchar *enode, LmMessageNode *n, const gchar *id, gpointer ignore) +{ + LmMessageNode *node; + + // well, anyway we now do not store information about jid-avatar relationship, + // thus "no avatar" publishes are meaningless to us + for (node = n->children; node; node = node->next) { + const gchar *name = node->name; + + if (!strcmp (name, "info")) { + gchar *jid = jidtodisp (from); + const gchar *url = lm_message_node_get_attribute (node, "url"); + const gchar *type = lm_message_node_get_attribute (node, "type"); + const gchar *id = lm_message_node_get_attribute (node, "id"); + const gchar *bytes = lm_message_node_get_attribute (node, "bytes"); + const gchar *height = lm_message_node_get_attribute (node, "height"); + const gchar *width = lm_message_node_get_attribute (node, "width"); + + { // print to buddy's buffer + GString *mesg = g_string_new ("Avatar:"); + gchar *text; + + // [id] type w x h (# bytes) url + g_string_append_printf (mesg, " [%s]", id ? id : "(none)"); + + if (type) { + g_string_append_c (mesg, ' '); + g_string_append (mesg, type); + } + + if (width && height) + g_string_append_printf (mesg, " %s x %s", width, height); + + if (bytes) + g_string_append_printf (mesg, " (%s bytes)", bytes); + + if (url) { + g_string_append_c (mesg, ' '); + g_string_append (mesg, url); + } + + text = g_string_free (mesg, FALSE); + + scr_WriteIncomingMessage (jid, text, 0, HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0); // NO conversion from utf-8 + + g_free (text); + } + + if (!url) { + // it is server-published png, go ahead and request data + LmMessage *request; + LmMessageNode *node; + LmMessageHandler *dhandler; + + { // check, if file already exists + gchar *file = avatar_id_filename (id); + struct stat buf; + + if (!file) { + scr_LogPrint (LPRINT_LOGNORM, "avatar: Cannot obtain filename to save file, probably avatar_directory is not set."); + g_free (jid); + continue; + } + + if (stat (file, &buf) != -1) { + scr_WriteIncomingMessage (jid, "Avatar file exists, will not download.", 0, HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0); + // link jid to this file + set_jid_avatar (jid, file); + g_free (file); + g_free (jid); + continue; + } + + g_free (file); + } + + // create data request + request = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET); + node = lm_message_get_node (request); + lm_message_node_set_attribute (node, "from", lm_connection_get_jid (lconnection)); + + node = lm_message_node_add_child (node, "pubsub", NULL); + lm_message_node_set_attribute (node, "xmlns", NS_PUBSUB); + + node = lm_message_node_add_child (node, "items", NULL); + lm_message_node_set_attribute (node, "node", NS_AVATAR_DATA); + + node = lm_message_node_add_child (node, "item", NULL); + lm_message_node_set_attribute (node, "id", id); + + // create handler + dhandler = lm_message_handler_new (avatar_retrieve_data_reply_handler, jid, g_free); + reply_handlers = g_slist_append (reply_handlers, dhandler); + + // send + lm_connection_send_with_reply (lconnection, request, dhandler, NULL); + + lm_message_handler_unref (dhandler); + lm_message_unref (request); + + // NOT free jid here - it will be freed on handler destruction + } else + g_free (jid); + } + } +} + +// /AVATAR [filename|-] +static void do_avatar (char *arg) +{ + gchar *fname; + gchar *data; + gsize len; + + if (!*arg) { // print current byddy's avatar + const gchar *jid = CURRENT_JID; + + if (!jid) { + scr_LogPrint (LPRINT_NORMAL, "This item cannot have avatar."); + return; + } + + fname = avatar_jid_filename (jid); + + print_avatar (fname, jid, NULL); + + g_free (fname); + return; + } + + // publish + if (arg[0] == '-' && arg[1] == '\0') { + // no avatar + avatar_publish (NULL, 0); + return; + } + + fname = expand_filename (arg); + + { // read file + char buffer[1024]; + GString *datastring; + int fd = open (fname, O_RDONLY); + + if (!fd) { + scr_LogPrint (LPRINT_NORMAL, "Cannot open file '%s': %s.", fname, strerror (errno)); + g_free (fname); + return; + } + + datastring = g_string_new (NULL); + + { // read data + int ret; + while ((ret = read (fd, buffer, 1024)) > 0) + g_string_append_len (datastring, buffer, ret); + } + + close (fd); + + len = datastring->len; + data = g_string_free (datastring, FALSE); + } + + g_free (fname); + + avatar_publish (data, len); + + g_free (data); +} + +static void avatar_free_reply_handlers (void) +{ + GSList *hel; + + // let's hope, that after invalidation, lm will remove and free unreffed by us handler + for (hel = reply_handlers; hel; hel = hel->next) { + LmMessageHandler *handler = (LmMessageHandler *) hel->data; + lm_message_handler_invalidate (handler); + } + + g_slist_free (reply_handlers); + reply_handlers = NULL; +} + +// release handlers before reconnect +static void avatar_hh (guint32 hid, hk_arg_t *args, gpointer userdata) +{ + hk_arg_t *arg; + + for (arg = args; arg->name; arg++) { + if (!strcmp (arg->name, "hook") && !strcmp (arg->value, "hook-pre-disconnect")) { + avatar_free_reply_handlers (); + return; + } + } +} + +const gchar *g_module_check_init (GModule *module) +{ + pep_register_xmlns_handler (NS_AVATAR_METADATA, avatar_handler, NULL, NULL); + + cmd_add ("avatar", "", COMPL_FILENAME, 0, do_avatar, NULL); + + hk_add_handler (avatar_hh, HOOK_INTERNAL, NULL); + + xmpp_add_feature (NS_AVATAR_METADATA); + xmpp_add_feature (NS_AVATAR_METADATA_NOTIFY); + + return NULL; +} + +void g_module_unload (GModule *module) +{ + pep_unregister_xmlns_handler (NS_AVATAR_METADATA); + + avatar_free_reply_handlers (); + + hk_del_handler (avatar_hh, NULL); + + cmd_del ("avatar"); + + xmpp_del_feature (NS_AVATAR_METADATA); + xmpp_del_feature (NS_AVATAR_METADATA_NOTIFY); +} + +/* vim: se ts=4: */ diff -r 000000000000 -r 29423ceb6adf avatar.rc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/avatar.rc Fri Nov 06 00:43:15 2009 +0200 @@ -0,0 +1,25 @@ + +# All options can be changed in runtime, so, feel free to experiment + +# Directory, where avatars will be saved. Note, that without saving +# avatar current implementation cannot show it. Avatars are saved by +# their id (sha1sum of png data), and symlinked by jids. I.e. +# 12345...679.png contains png data +# foo@bar.org is symlink to 12345...679.png +set avatar_directory = ~/.mcabber/avatars + +# Set this to your terminal's font character dimensions in pixels. +# This will allow to retain real png image proportions. +set avatar_font_height = 16 +set avatar_font_width = 8 + +# Maximum width and height in characters. Can be omitted, then these +# will be autodetected based on COLUMNS and LINES variables (they +# must exist in mcabber's environment, i.e. you should export them +# or you will be limited to 80x25). +set avatar_max_width = 110 +set avatar_max_height = 40 + +load pep +load avatar + diff -r 000000000000 -r 29423ceb6adf config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.h Fri Nov 06 00:43:15 2009 +0200 @@ -0,0 +1,8 @@ + +#ifndef LOCAL_CONFIG_H +#define LOCAL_CONFIG_H + +#define MODULES_ENABLE 1 + +# endif + diff -r 000000000000 -r 29423ceb6adf help/en/hlp_avatar.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/help/en/hlp_avatar.txt Fri Nov 06 00:43:15 2009 +0200 @@ -0,0 +1,6 @@ + + /AVATAR [filename.png|-] + +Publishes filename as your avatar via pep. Specify - to remove avatar. Without argument shows current byddy's avatar. +Note: standard states, that avatar MUST be of type image/png, thus, no support for anything alse. +Note: url avatars are not supported yet.