diff options
author | gregor herrmann <gregoa@debian.org> | 2013-05-25 15:29:32 -0400 |
---|---|---|
committer | gregor herrmann <gregoa@debian.org> | 2013-05-25 15:29:32 -0400 |
commit | 5e10947726f3b3227a753406d89cfd0f02808cf9 (patch) | |
tree | 40534b2e84a7f97c27af2c3ddc769de921dac553 |
owl (2.2.2-1.2) unstable; urgency=low
* Non-maintainer upload.
* Fix "FTBFS: configure: error: No terminfo found for this system":
add build dependency on ncurses-term.
(Closes: #708840)
# imported from the archive
-rw-r--r-- | COPYING | 674 | ||||
-rw-r--r-- | ChangeLog | 1094 | ||||
-rw-r--r-- | Makefile.in | 105 | ||||
-rw-r--r-- | aclocal.m4 | 171 | ||||
-rw-r--r-- | aim.c | 2796 | ||||
-rwxr-xr-x | athstatic | 32 | ||||
-rw-r--r-- | buddy.c | 95 | ||||
-rw-r--r-- | buddylist.c | 166 | ||||
-rw-r--r-- | cmd.c | 283 | ||||
-rwxr-xr-x | codelist.pl | 40 | ||||
-rw-r--r-- | commands.c | 2623 | ||||
-rw-r--r-- | config.h | 122 | ||||
-rw-r--r-- | config.h.in | 121 | ||||
-rwxr-xr-x | configure | 6215 | ||||
-rw-r--r-- | configure.in | 139 | ||||
-rw-r--r-- | context.c | 124 | ||||
-rw-r--r-- | debian/README.Debian | 7 | ||||
-rw-r--r-- | debian/changelog | 208 | ||||
-rw-r--r-- | debian/compat | 1 | ||||
-rw-r--r-- | debian/control | 16 | ||||
-rw-r--r-- | debian/copyright | 55 | ||||
-rw-r--r-- | debian/dirs | 1 | ||||
-rwxr-xr-x | debian/rules | 93 | ||||
-rw-r--r-- | debian/shlibs.local | 1 | ||||
-rw-r--r-- | debian/watch | 2 | ||||
-rw-r--r-- | dict.c | 210 | ||||
-rw-r--r-- | doc/code.txt | 199 | ||||
-rw-r--r-- | doc/contributors | 31 | ||||
-rw-r--r-- | doc/intro.txt | 462 | ||||
-rw-r--r-- | doc/owl.1 | 83 | ||||
-rw-r--r-- | editwin.c | 968 | ||||
-rw-r--r-- | encapsulate.pl | 20 | ||||
-rw-r--r-- | errqueue.c | 47 | ||||
-rw-r--r-- | examples/owlconf.erik | 476 | ||||
-rw-r--r-- | examples/owlconf.simple | 270 | ||||
-rw-r--r-- | examples/owlconf.vtformat | 141 | ||||
-rw-r--r-- | filter.c | 626 | ||||
-rw-r--r-- | filterelement.c | 239 | ||||
-rw-r--r-- | fmtext.c | 649 | ||||
-rw-r--r-- | functions.c | 3590 | ||||
-rw-r--r-- | global.c | 914 | ||||
-rw-r--r-- | help.c | 167 | ||||
-rw-r--r-- | history.c | 118 | ||||
-rw-r--r-- | install.sh | 0 | ||||
-rw-r--r-- | keybinding.c | 163 | ||||
-rw-r--r-- | keymap.c | 319 | ||||
-rw-r--r-- | keypress.c | 235 | ||||
-rw-r--r-- | keys.c | 351 | ||||
-rw-r--r-- | libfaim/Makefile.in | 21 | ||||
-rw-r--r-- | libfaim/admin.c | 238 | ||||
-rw-r--r-- | libfaim/adverts.c | 31 | ||||
-rw-r--r-- | libfaim/aim.h | 1484 | ||||
-rw-r--r-- | libfaim/aim_cbtypes.h | 310 | ||||
-rw-r--r-- | libfaim/aim_internal.h | 226 | ||||
-rw-r--r-- | libfaim/auth.c | 558 | ||||
-rw-r--r-- | libfaim/bart.c | 165 | ||||
-rw-r--r-- | libfaim/bos.c | 167 | ||||
-rw-r--r-- | libfaim/bstream.c | 265 | ||||
-rw-r--r-- | libfaim/buddylist.c | 285 | ||||
-rw-r--r-- | libfaim/chat.c | 702 | ||||
-rw-r--r-- | libfaim/chatnav.c | 434 | ||||
-rw-r--r-- | libfaim/config.h.in | 83 | ||||
-rwxr-xr-x | libfaim/configure | 3659 | ||||
-rw-r--r-- | libfaim/configure.in | 13 | ||||
-rw-r--r-- | libfaim/conn.c | 1053 | ||||
-rw-r--r-- | libfaim/email.c | 216 | ||||
-rw-r--r-- | libfaim/faimconfig.h | 59 | ||||
-rw-r--r-- | libfaim/ft.c | 993 | ||||
-rw-r--r-- | libfaim/icq.c | 684 | ||||
-rw-r--r-- | libfaim/im.c | 2298 | ||||
-rw-r--r-- | libfaim/invite.c | 34 | ||||
-rw-r--r-- | libfaim/locate.c | 1319 | ||||
-rw-r--r-- | libfaim/md5.c | 392 | ||||
-rw-r--r-- | libfaim/md5.h | 93 | ||||
-rw-r--r-- | libfaim/misc.c | 147 | ||||
-rw-r--r-- | libfaim/msgcookie.c | 193 | ||||
-rw-r--r-- | libfaim/odir.c | 245 | ||||
-rw-r--r-- | libfaim/oscar.c | 6711 | ||||
-rw-r--r-- | libfaim/popups.c | 64 | ||||
-rw-r--r-- | libfaim/rxhandlers.c | 585 | ||||
-rw-r--r-- | libfaim/rxqueue.c | 290 | ||||
-rw-r--r-- | libfaim/search.c | 134 | ||||
-rw-r--r-- | libfaim/service.c | 1089 | ||||
-rw-r--r-- | libfaim/snac.c | 148 | ||||
-rw-r--r-- | libfaim/ssi.c | 1963 | ||||
-rw-r--r-- | libfaim/stats.c | 44 | ||||
-rw-r--r-- | libfaim/tlv.c | 823 | ||||
-rw-r--r-- | libfaim/translate.c | 27 | ||||
-rw-r--r-- | libfaim/txqueue.c | 439 | ||||
-rw-r--r-- | libfaim/util.c | 210 | ||||
-rw-r--r-- | list.c | 124 | ||||
-rw-r--r-- | logging.c | 450 | ||||
-rw-r--r-- | mainwin.c | 180 | ||||
-rw-r--r-- | message.c | 987 | ||||
-rw-r--r-- | messagelist.c | 128 | ||||
-rwxr-xr-x | mkinstalldirs | 40 | ||||
-rw-r--r-- | owl.c | 760 | ||||
-rw-r--r-- | owl.h | 562 | ||||
-rw-r--r-- | owl_prototypes.h | 1409 | ||||
-rw-r--r-- | pair.c | 46 | ||||
-rw-r--r-- | perlconfig.c | 362 | ||||
-rw-r--r-- | perlglue.c | 186 | ||||
-rw-r--r-- | perlglue.xs | 83 | ||||
-rw-r--r-- | perlwrap.c | 235 | ||||
-rw-r--r-- | perlwrap.pm | 231 | ||||
-rw-r--r-- | popexec.c | 215 | ||||
-rw-r--r-- | popwin.c | 156 | ||||
-rw-r--r-- | regex.c | 114 | ||||
-rw-r--r-- | select.c | 269 | ||||
-rwxr-xr-x | stubgen.pl | 44 | ||||
-rw-r--r-- | style.c | 128 | ||||
-rw-r--r-- | stylefunc.c | 794 | ||||
-rw-r--r-- | tester.c | 192 | ||||
-rw-r--r-- | text.c | 423 | ||||
-rw-r--r-- | timer.c | 98 | ||||
-rw-r--r-- | util.c | 830 | ||||
-rw-r--r-- | variable.c | 983 | ||||
-rw-r--r-- | varstubs.c | 460 | ||||
-rw-r--r-- | view.c | 191 | ||||
-rw-r--r-- | viewwin.c | 178 | ||||
-rw-r--r-- | zbuddylist.c | 89 | ||||
-rw-r--r-- | zcrypt.c | 795 | ||||
-rw-r--r-- | zephyr.c | 1091 | ||||
-rw-r--r-- | zwrite.c | 387 |
124 files changed, 67576 insertions, 0 deletions
@@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 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 <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU 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. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..55f8916 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1094 @@ +$Id: ChangeLog,v 1.274 2009/04/07 15:41:21 kretch Exp $ + +2.2.2 + 2.1.12 included a patch that breaks opaque resizing for some + versions of ncurses on solaris. Reverting to the original + behavior for now and will reincorporate in a future release. + +2.2.1 + Fix some strcpy's that could be vulnerable + Remove an unused function + Fix stderr redirector, now creating admin messages + +2.2.0 + Workaround to libzephyr on unnoticed zephyrs + With smartnarrow and zpunt, include .d suffix and ^un prefix as + part of the allowed regex for the relevant class or instance + Cleanup of lots of bad sprintf usage + +2.1.12 + Don't crash on mobile device AIM messages [BZ 20] + Don't crash when sending to someone not on AIM buddy list [BZ 94] + Fix overflow vulerability in zwrite.c and zcrypt.c + Add stack protector and -D_FORTIFY_SOURCE=2 where possible + Fix hang [barnowl 4c46dfdebc294ca24fef59924f01688b8ee07dee] + Fix segfault on missing subs [barnowl 99dabeed7de6d19acb50f1e73aa0bfe5f9469c02] + Fix zcrypt bufffer size bug [barnowl 559076cd86edc3a8317819814dd5877b8bc7c3cb] + Fix double free [barnowl e97c4a306ae2c9d2147d83da29fee59918198e70] + Memory leak fix [barnowl c0a90c2c7ab97b9465c9873757faf312faa0021e] + Memory leak fix [barnowl 95caa16b2e9ba10770d87d0955198c14c2d0e16a] + Memory leak fix [barnowl 1716fed8c2650e46892cf6571555eac937266c6e] + Add getstyle command [barnowl 216c73421653e3ef0e957aa9a9804e208a77c80e] + Binary search for msgid [barnowl 0c8ab5eadbb6ecc97a120c91b9a824b33538c764] + File-handle leak [barnowl e78397d6ac5da0de31a4e269c0ba7f3d691857a3] + Fix delay in jump from top to bottom [barnowl 801b7ac63b962640debbcfd422cb9a60da5fea31] + Load subs in chunks [barnowl 93e883d60051b80bf6d35391f9d76fd7dfd198e3] + Load subs in chunks [barnowl f6050ee9b0a171a5031f84409eb181062afacd18] + Better zsig logging [barnowl d857b667a5a9b108b1a2a26b4a5513bef2b53f80] + free() -> owlfree() [barnowl d524c838ac7c115628424b8ac171c3489622ea3a] + Escape AIM users in smartfilters [barnowl af9b92e3e7ccb7de276a94b5c7e5861b24e71eff] + Better regex escape chars [barnowl 80e54a7631f091be8c9762adb9746bad38104738] + Deal with quotes in smart filters [barnowl 4099cf83702763fa8d1efc4f1582a605431bdb77] + Deal with 0 length zephyr fields [barnowl 128171aaf7cefa91a4bb1eada93a19d8fd0c355c] + Deal with 0 length zephyr fields [barnowl 50e29e35c64d64e223d378d350a7bc4f038d78f5] + Deal with 0 length zephyr fields [barnowl 804ab8af8b6d00bcd7e2402df892db8fbd61a3ec] + Leave curmsg on screen after resize [barnowl c0f9e3009bc03e80a44de64cd5f2b4033290236e] + Rip out brower stuff [barnowl 8e5935d11c699a7ce5a3e6e9a47799564c696d6a] + Rip out browser stuff [barnowl 4f15e8e9ceada0d4b2cc969ebf43b0a1fb3709ea] + No passwords in command history [barnowl 6e400cc71aa59e041dce677aadf50dc1f25228e2] + Format NOC mssages [barnowl a1bb1980e4bca23b8329cc0e7c0bd5027055ea0a] + Expand ~ in loadsubs [barnowl 27d8d835dc6d58c08fae10e75aae306c49215143] + Expand ~ in source [barnowl 10d67d57cb29221f63a43a30643c697fc7b38911] + Only use resizeterm() if it's available + Debian backports + Change license to GPLv3 + general sprintf cleanup + +2.1.11 + Don't crash doing zlocate with bad tickets. [BZ 12] + Metion the path for the owlconf in intro.txt [BZ 54] + Print better error message if startup fails due to unreadable + .owlconf [BZ 57] + In load-subs: Print an error message if the file is unreadable or + doesn't exist, UNLESS load-subs is called with no arguments. In + that case only print an error if the file exists but isn't + readable. Still prints an error either way if zephyr reports a + failure. [BZ 19] + Fixed some small memory leaks in logging if files unwriteable + If the variable logfilter is set it names a filter. Any messages + matching this filter are logged. This is an independent + mechanism from the other logging variables. If you want to + control all logging with logfilter the other variables must be + set to their default (off) settings. [BZ 37] + Relatively substantial changes made under the hood to support + filter logging. Now have more consistent interfaces to + creating messages etc. Still needs more work though. + Deal gracefully with being resized as small as 1x1 [BZ 3] + +2.1.10 + Fix a new problem finding libdes425 + Don't crash on very long hostnames [BZ 52] + In 'sub' command, create .zephyr.subs if it doesn't exist [BZ 15] + A fix for certain resize crashes (partly from alexmv) [BZ 55] + Turn off ISTRIP (gildea) + +2.1.9 + Include /usr/include/kerberosIV if it's found + +2.1.8 + Do OLC formatting for anything coming from olc.matisse + Improvements to popup size algorithm (from gildea) + Improved 'show colors' with non-colored labels + +2.1.7 + The colorclass command is added, to make colorization easy + Handle MIT Athena OLC zephyrs correctly + Updated ktools website / bug address + Do ntohs() when printing zephyr port in zephyr info + Updated man page + +2.1.6 + Fixed three bugs found by Valgrind. + Fixed a case where doing "aim addbuddy" instead of "addbuddy aim" + would cause a segfault. + pexec will now incrimentally display data as it is output + by the child process. Additionally, commands running under + pexec may now be killed by quitting out of the popless window. + Added muxevents select loop dispatcher. File descriptors may + be registered with muxevents and handlers will be dispatched + to when data is available for non-blocking read/write/except. + Switched the stderr_redir stuff to use muxevents. + Print C-\ correctly (from gildea) + Dropped first brace in muxevents functions for consistency + Catch SIGHUP and SIGTERM and do a proper logout + +2.1.5 + Added a licence + The 'personalbell' variable can now be set to 'on' 'off' or + the name of a filter to match against + The 'loglogins' variable now controls whether login/logout + messages are logged. It is off by default. For now this + affects only AIM messages, later zephyr login/logout messages + will also be logged if this is set to 'on' + Added 'show license' + +2.1.4 + Normalize and downcase AIM names for logging + Fixed a bug where sending a null zsig could cause a crash + Better 'away' toggling if only one protocol is away. + +2.1.3 + Added perl filter elements. Similar to having "filter <subfilter>" + in a filter, you may also have "perl <functionname>" + where <functionname> is passed an owl::Message object and + returns 0 or 1 depending on whether the message matches + that element of the filter. + Don't print an error about loading subs if there is no + .zephyr.subs + Do the initial zephyr_buddy_check when pseduologin set to true. + Updated man page + +2.1.2 + removed unused filter_depth variable + Fixed memory bug on receiving pings + +2.1.1 + Filters of filters now work. + Removed the "possibly not readable" part of the config parsing + error + In the sepbar, reverse video the view name when it's not set to + view_home (as opposed to the static 'all'). + The '!' key (bound to 'view -r') now creates a negative version of + the current view and switches to it. i.e. "show me all the + messages that are not these" + Added the 'ignorelogins' variable + Log when outgoing personal message fails + Removed file descriptor from sigpipe catcher printer just for now, + since the field does not exist on OSX + Added an ifndef for socklen_t in libfaim/ft.c + Added the 'aim search' command. The popup on callback may be + dangerous, should switch to an admin msg for results, or add a + new event queue + First pass at AIM away messages. It is a little different from + what most clients seem to do, in that an away reply is sent for + each message received. Most clients only reply to the first one + per away-session. + Now have a set of 'aaway' commands and variables just like the + 'zaway' ones (except that changing the 'aaway' variable talks to + the server) + The new 'away' command does everything for both AIM *and* zephyr. + There is a known funkiness here, where if you turn one away on, + and then use 'away' (or 'A') to toggle, you will turn on off and + the other on. Just leaving it for now. Should do better in the + next patch. + The 'A' key is bound to 'away' + Status bar can now read AWAY, Z-AWAY or A-AWAY. + Changed C-n to scroll down just a line in popless + If the config exists but is not readable, print an error before + exiting + Only print forced AIM logout message once. + Don't bind F1 to help in edit context + Fix bug in 'getsubs' with no tickets + New code for getting users from .anyfile + Added the 'pseudologins' variable, and code to do it + new attributes 'pseudo' 'logintty' and 'loginhost' + Don't print extra new lines in popless_file + New zephyr_get_field function + +2.0.14 + Fixed missing word in startup message + Better 'status' command + Use '+' for popwin corners when 'fancylines' is off + Allow TERMINFO to be overridden in the envrionment + Command line arg -D turns on debugging and deletes previous + debugging file + Do ~ expansion in 'dump' command. + Current directory added to 'status' command + Massive changes to libfaim and aim + +2.0.13 + Changed startup message for new mailing list + blist now prints AIM info even if .anyone is unreadable + Catch SIGPIPE and print an error rather than crashing. + [It's possible that this may have some portability + issues under Solaris and we may need to add some + configure stuff around SA_SIGINFO...] + Handle the case in aim_bstream_send where aim_send returns -1, + although there is likely an underlying problem here + that would lead to this case. + Print the username on aim login failure, not something random like + the password. ;) + Un-word-wrap text when sending AIM messages. + Replace the main loop continue in the keyboard handler with an else. + +2.0.12 + Command history now doesn't allow the last entry + to be repeated + If format_msg returns "" print "<unformatted message>" + Better align oneline admin and loopback messages + Print an admin message indicating when subscriptions can + not be loaded on startup + Set aim_ignorelogin_timer to 15 by default + Admin message on login/logout of AIM + Fixed double quoting in smartzpunt + Added timestamp to login/logout messages + Fixed replies to loopback messages + Fixed smartnarrow on classes/instances with spaces + Added the 'loggingdirection' variable + All loopback messages log to 'loopback' now + Print an error message if trying an invalid color for a filter + Fixed bug causing > not to go to end of editwin every time + +2.0.11 + Updated basic help + Display CC: in outgoing CC messages + More AIM logout detection + Don't proclaim "interfaces changed" on first build. + Added the 'loopback' message type + Added the 'loopwrite' command + Added a timestamp to the default style + Zpunt now works with weird regex characters + Smart filters now work with weird regex characters + +2.0.10 + Allow 'hostname' in filters. + Fixed bug in reporting when no one is subbed to a class + Added an extral newline in logging incoming zephyrs + An admin message is displayed when you are logged out of AIM + Print an error message and admin message if an AIM send fails + +2.0.9 + Added the 'fancylines' variable. + Added the 'show startup' command. + Added feature for capturing stderr messages + from commands and displaying them in the errors buffer. + Create an admin message explaning that a zephyr couldn't + be sent + Better reporting of perl errors (both into the errqueue + and also clearing the error after displaying it). + Allow default_style to be specified in config. + Added errqueue + Added command "show errors" + Fixed bug removing newlines in backup files + +2.0.8 + Increased size of screen name field in buddy listing + Fixed bug with idle times causing broken pipes. + New libfaim + Added the 'source' command. + Make sure that a newline is always at the end of messages + returned by perl style formatting functions. + Add owl::login and owl::auth to legacy variables populated for format_msg. + Additions to intro.txt and advanced.txt documents. (Still in progress.) + Add base methods for login_host and login_tty + and others that return undef. + New API for perl message formatting functions. + Legacy variables are still supported for owl::format_msg + and owl::receive_msg, but these functions are now also + passed an owl::Message object which contains methods + for accessing the contents of the message. See perlwrap.pm + (and docs TBD) for the available methods. + *** WARNING: The exact API for owl::Message has + *** not yet stabilized. + Added "style" command for creating new styles. + Usage: style <name> perl <function_name> + Added support for "show styles". Changed global style table + from list to dictionary. + Changed AIM password prompt from "Password:" to "AIM Password:". + Messages are reformatted after a window resize to allow styles + to take into account the width of the window. + When perl throws an error, the message is put in the msgwin + if possible. + Added perl functions for: + owl::getcurmsg() -- returns an owl::Message object for + the active message + in the current view. + owl::getnumcols() -- returns the column width of the window + owl::zephyr_getrealm() -- returns the zephyr realm + owl::zephyr_getsender() -- returns the zephyr sender + Made owl::COMMAND("foo"); be syntactic sugar for + owl::command("COMMAND foo"); + Added perlwrap.pm to contain perl code to be compiled into + the binary. This is transformed into perlwrap.c by + encapsulate.pl. + Renamed readconfig.c to perlconfig.c and changed variables accordingly. + Minor bugfixes in cmd.c and commands.c + Improved intro doc + +2.0.7 + Idletimes now appear in the buddylisting + Failed AIM logins are now correctly reported + Owl will build now without zephyr, enabling it to act as a + standalone AIM client. + There is now a zcrypt command + Replies to zcrypted messages now work + Don't allow zwrite if zephyr isn't present + Cleaned up some warnings from linux gcc. + Fixed bug that can cause response stuff to crash + Improved status command + Fixed bug in buddy stuff + +2.0.6 + aimlogin will now accept the screenname without a password and ask + for the password such that it is not echo'd to the terminal + 'addbuddy aim' and 'delbuddy aim' now work + Bug fix to make zwrite -m work with -c/-i + Fixed documentation bug in aimwrite + Initialze $owl::auth + Fix in autoconf for des425 + Reformatted editwin.c and added capability of doing password-style + echoing + +2.0.5 + Fix in finding des for building zcrypt + Fixed description for alert_action variable + More detailed usage from -h + Special cased replies for webzephyr users on classes and + login notifications for webzephyr users + Fixed bug that caused a crash on zpunt with '*' for an instance + AIM logout and then login now works. + Fixed bug causing view -d not to work. + Added hostname and tty name to LOGIN/LOGOUT zephyrs on oneline + style + +2.0.4 + Made command line option -n actually work + Implemented styles, including the 'default' 'basic' and 'oneline' + styles. A 'perl' style is available if a format_msg() function + is found in .owlconf + Added the 'default_style' variable + Added the 'toggle-oneline' command + The 'o' key is bound to 'toggle-oneline' + Internally, the one view now has a name, 'main', and message + recalcuations are done in place when its filter is changed. + Added filter field 'login' which can take the values 'login' + 'logout' or 'none' + Added the perl variable $owl::login, just as above + Updated the 'login' and 'trash' filters appropriately + Fix for checking for DES in build system + Bug fix in using makemsg when no curses window is present + The variable $owl::auth now exists in perl + Use new internal function to delete zephyr subs from file + New 'sepbar_disable' variable can turn off sepbar info display + Updated contributor info + Added the 'show view' command + Bug fix in owl_regex + Fixed personal aim messages logging to class directory + Log "LOGIN" or "LOGOUT" for AIM buddy messages + zwrite -m now correctly displays an outgoing message and logs + zwrite -s now works + Strip spaces in AIM usernames on aimwrite send + Removed libfaim/config.log from CVS + Fixed some easy fixed-length buffers + Wordwrap incoming AIM messages + Fixed bug causing buddies not to be added to buddy list during + ingorelogin timer + Translate < > & " &ensp, &emsp, &endash and + &emdash + +2.0.3 + Don't ring the terminal bell on mail messages. + Nuke <FONT> + Make the build work a little better on OSX + Fixed a bug in fmtext + Expanded the size of the hostname buffer + +2.0.2 + Fixed bug in 'startup' command. + +2.0.1 + Moved newmsgproc stuff to a function procedure + Added the 'newlinestrip' variable, on by default, that strips + leading and trailing newlines from incoming messages. + Fixed a case sensitivity probelm in owl_message_is_personal and + owl_message_is_private + The message object now uses a list of attributes internally, in + prep. for supporting new messaging protocols + owl_function_info now uses fmtext instead of one staticly sized + buffer + in owl_message_get_cc() require that the colon be present after + cc. + Added some defenses against resize crashes, and put in debug + messages if they're encountered + In filters 'true' and 'false' are now valid tokens. + The 'all' filter has been redefinied to be 'true' and there is a + 'none' filter defined as 'false' + Fixed bug in 'unsub' command that could cause file corruption + In the zlist function, give a more detailed error message if + the file cannot be opened. + Renamed old instances of zsig_exec in the code to zsigproc + Don't print the stderr from zsigproc + Added a 'loadloginsubs' command to load login subscriptions from a + file + Added a 'loadsubs' command to eventually phase out the 'load-subs' + command + Made M-n work on classes and instances with spaces in them + Zaway now obeys the smart strip variable + Hacked the build system to not have the -E link problem on Athena + Added ZResetAuthentication in a number of places to fix problems + with stale tickets + Added some hooks for malloc debugging + M-p is bound to 'view personal' by default + loadsubs and loadloginsubs only print messages if in interactive + mode + added the 'alert_filter' variable, defaults to 'none'. + added the 'alert_action' variable, which is an owl command that + will be executed when new messages arive that match the + alert_filter + added the 'term' command which takes the 'raise' and 'deiconify' + options. It assumes xterm for now. + only 'make distclean' will nuke core and ~ files now + fixes to owl_function_do_newmsgproc from Stephen + converted functions.c to new code style, which I'm giving a shot + Makefile.in: define DATADIR, for default owlconf. + Makefile.in: provide "all" and "install" rules. + configure.in: try also libdes and libkrb4, for people using heimdal + configure.in: see if des_ecb_encrypt is already prototyped. + configure.in: minor changes to work with new autoconf without needing acconfig.h. + configure.in: find the install program. + configure.in: test for use_default_colors since some versions of + solaris don't have it, so we can at least compile something + vaguely working there. + keypress.c: ifdefs for keys not defined on at least some solarises. + owl.c: don't call use_default_colors if we don't have it + readconfig.c: added *commented out* code to try to find a + system-default owlconf if the user doesn't have one. Have to + ponder if I want this + zcrypt.c: don't prototype des_ecb_encrypt if there is a prototype in + des.h. + zcrypt.c: include owl.h so we get the configure-generated config.h + Change to codelist.pl to deal with new code style + Remove some ancient stuff from zcrypt.c + General cleanup to Makefile.in + CTRL and META are now OWL_CTRL and OWL_META. OWL_CTRL moved to + keypress.c + do_encrypt declaired static + if we don't have des functions, do not try to build in zcrypt + kill the newmsgproc function on exit + Added libfaim + Added basic AIM support, including the "aimlogin", "aimwrite" and + "aimlogout" commands + New built-in filters 'aim' and 'zephyr'. + Do ZResetAuthentication() before zlog_in and zlog_out as well. + Print AIM login / logout notifications + The 'alist' command prints a list of aim buddies logged in + The 'blist' command prints users from all protocols + The 'l' key is now bound to 'blist' instead of 'zlist' + Started work on 'addbuddy' and 'delbuddy' command but they DO NOT + WORK yet + Removed a bit of faim code that allowed commands to be executed. + The 'B' key is now bound to 'alist' + Added the 'startup' and 'unstartup' commands + The $HOME/.owl directory is created on startup if it does not exist + Added the 'aim_ingorelogin_timer' variable + 'addbuddy zephyr <user>' and 'delbuddy zephyr <user>' now work. + 'isloginout' and 'isprivate' are now message attributes + improved 'info' function lists seperate info for zephyr, aim and + also prints all message attributes + AIM logging (both in and out) now works + Disabled 'addbuddy' and 'delbuddy' for aim since it doesn't work yet + Hacked the Perl build stuff not to link with iconv + +1.2.8 + Class pings are displayed differently now + Updated owlconf.simple example to format outgoing messages. + +1.2.7 + Outgoing messages now go through the config for formatting + Zaway now makes an outgoing message, instead of an admin message + The 'zlocate' command can now handle multiple users + The simple user format for "To:" is in effect again + Prettyed up the zwrite line for using 'reply' on a zaway + Added a workaround for a libzephyr bug that caused zwrites to fail + if zephyrs were sent just before and just after renewing tickets + Fixed a memory bug in getsubs + Added receive support for zcrypt messages + Added the 'zcrypt' variable which controls whether or not zcrypt + messages are decrypted + 'reply' is disabled for zcrypt until sending zcrypt works + Started implementing zcrypt command + More updates to the intro doc + +1.2.6 + Started adding code for newmsgproc. It doesn't fully work yet! + Don't use it. + Added search, '/' and '?' to basic help. + Will attempt to keep the current message as close as possible + to the previous current message after an expunge. + "set <variable>" and "unset <variable>" now work for boolean variables. + Fixed a bug in owl_function_calculate_topmsg_normal that caused a + segfault + Fixed some typos in the intro doc + Removed old zlog functions from zephyr.c + Implemented the dump command + New startup message + +1.2.5 + Patch to fix memory bug in replying to CC messages + If we're on Athena and have static krb (or other) libraries, use + them + Added "athstatic" program to the release, which handles the above + Cast to an int for isspace, to make gcc -Wall quiet + Added 'zlist' and 'l' to basic help. + +1.2.4 + 'zlog in' will now take an optional thrid argument to set the + 'tty' variable before setting the zlocation + There is now a 'zlist' command that acts like 'znol -l' + 'l' is bound to 'zlist' + Fixed memory leak uninitialzed memory read in fmtext + viewwin will now say "End" instead of "More" when at the end + Added a debugging message indicating the result of topmsg + calculations + You can now use %me% in filters + The built-in personal filter is updated to do so + Fixed a bug in moving the pointer after an expunge + Fixed up the normal scrolling code. Now it should always + land on a message, but it's still not optimal. + Added the variable 'smartstrip' which will strip kerberos + instances out for the 'reply' command. + Added -R/usr/athena/lib to the build for Athena + Started updating the intro document + Small changes to help / about + The 'subscribe' and 'unsubscribe' commands (and their aliases) now + update .zephyr.subs by default. If either is given the '-t' + (for "temporary") option the .zephyr.subs will not be updated + Turned off beeping for hitting the top or bottom of the list of + messages + Made daemon.webzephyr a special case for smartstrip + Added 'out' as a default filter for outgoing messages + +1.2.3 + Added filters "ping", "auto" and "login" by default. + Added "body" as a valid field to match on in a filter. + Temporary fix to bug where C-SPACE would cause the key handler to + lock up. + Messages now have a direciton (in, out or none). Filters can + match on this direction + Outbound messages are no longer type 'admin' but are of the + appropriate message type (i.e. 'zephyr') and are direction + 'out'. + Smartnarrow now works on outgoing messages + 'info' updated to show more information for admin and outgoing + messages + Renamed pretty_sender to short_zuser and renamed long_sender to + long_zuser + Moved zsig generation to the zwrite object + Print the zsig used for outgoing messages + The tty variable now controls the zephyr location tty name + +1.2.2 + Added the 'search' command. + '/' is a keybinding for 'search' + '?' is a keybinding for 'search -r' + Fixed stristr, which was completely broken + renamed owl_fmtext_ztext_stylestrip to owl_function_ztext_styletsrip + and put it in functions.c + Attempts to stay near the current message when switching views. + When switching from an empty view to one we've previously + been in, the new current message position will attempt + to be close to the current position from the last + time we visited that view. + Fixed bug in readconfig.c that prevented building under perl 5.005. + Switched "C-x C-x" to only "startcommand quit" + 'getsubs' prints closer to the order you sub in. + Modified the behavior of last so that "> >" will clear the screen. + The new behavior of last is: + Moves the pointer to the last message in the view. + If we are already at the last message in the view, + blanks the screen and moves just past the end of the view + so that new messages will appear starting at the top + of the screen. + Fixed a typo in the help for smartzpunt. + Fixed functions to handle curmsg being past the end of the view. + +1.2.1 + New framework for command handling. + New framework for keymap handling. + Added commands for everything that is bound + to a key (do 'show commands' to get the full list). + Added 'multi' and '(' commands to allow multiple commands + to be specified on a line. + Added user keybindings with bindkey command. + Added command aliases (eg, "alias foo bar"). + Added undelete command that parallels the delete command. + Added additional options to delete command. + The reply command now takes arguments. + Added 'edit:insert-text' command. + Added 'show zpunts' to show active punt filters. + Added 'show variable <name>' and 'show variables'. + Added 'show command <name>' and 'show commands'. + Added 'show keymap <name>' and 'show keymaps'. + Added 'M-u' to undelete all messages in current view. + Fixed dotsend so that the zephyr will still send if there + is whitespace after the dot but not on the same line. + This should resolve an issue where dotsend wouldn't work + if you'd gone up and edited a zephyr. + Bug in page down fixed + C-t will transpose characters + Fix the scrolling bug where we would sometimes fail to scroll + the screen down, leaving the current message off + the bottom of the screen. + Refixed 'login or login' typo in help + Fixed M-u description + Removed 'first' and 'last' from basic command help + Added M-N to basic key help + Added M-D, M-u to basic key help + Fixed a quoting problem in configure.in + Changed top of help to use 'show' instead of M-x + Fixed a bug in the summary field for user-created aliases + Added "reply zaway" which sends a zaway response to the current msg. + Added "edit:delete-prev-word" command and bound M-BACKSPACE to it. + Some buffer overruns fixed + Variables now have a summary and a long description. + Only the summary is shown with help. + The long description is shown with "show variable foo". + Added a 'scrollmode' variable which determines how the screen + will scroll as the cursor moves. The default behaves + identically to previous versions of owl. + The following modes are supported: + normal - This is the owl default. Scrolling happens + when it needs to, and an attempt is made to + keep the current message roughly near + the middle of the screen. (default) + top - The current message will always be the + the top message displayed. + neartop - The current message will be one down + from the top message displayed, + where possible. + center - An attempt is made to keep the current + message near the center of the screen. + paged - The top message displayed only changes + when user moves the cursor to the top + or bottom of the screen. When it moves, + the screen will be paged up or down and + the cursor will be near the top or + the bottom. + pagedcenter - The top message displayed only changes + when user moves the cursor to the top + or bottom of the screen. When it moves, + the screen will be paged up or down and + the cursor will be near the center. + Added owl_sprintf which returns the formatted string, or NULL. + The caller must free this string. + This will allocate enough memory and thus + avoid potential some buffer overrun situations. + Simple implementation of 'zwrite -m' (doesn't yet log an outgoing + message as having been sent.) + The "Not logged in or subscribing to messages" error + now includes the name of the recipient. + The "disable-ctrl-d" variable may also be set to "middle" + which will result in ctrl-d only sending at the + end of the message. This is now the default. + This also added a command "editmulti:done-or-delete". + Fixed a bug in the "reply -e" command. + Always clear the command buffer before executing the command. + (So that interactive commands can sanely do start-command.) + Fixed preservation of e->dotsend across owl_editwin_clear(). + Added history for multiline edit windows (eg, for zephyr composition). + The M-n and M-p keys will cycle through the history ring. + In particular, it is now possible to edit the command line + of a zephyr being composed: C-c it and restart it + and then M-p to get the aborted composition back. + Added owl::send_zwrite(command, message) to the perl glue + to allow for the direct sending of multi-line messages. + For example: owl::send_zwrite("-c foo -i bar", "hello"); + Changed owl_fmtext_print_plain to return an alloc'd string to + avoid buffer overrun risks. + Added owl::ztext_stylestrip("...") function to perlglue + which returns the ztext with formatting stripped out. + Added colorztext variable which can be used to disable @color() + strings arriving in messages after it is set. + (Currently, changing its value won't reformat messages). + Outgoing zephyr logging now obeys the logpath variable. + The '~' character in logpath and classlogpath now gets + replaced with the user's home directory. + Added simple implementation of smartnarrow-to-admin that + creates a "type-admin" autofilter. + This was done mostly so that M-C-n and M-C-p do something + sane on admin messages. + Added opera to the allowed options to the webbrowser variable. + Fixed some buffer overruns in the "reply" command. + When repying to "all" on a message that begins with "CC:" (eg, sent + with "zwrite -C", the reply line will be constructed + from the sender and the usernames on the CC: line + of the message being replied to. + There is no such thing as C-R, so left C-r as it is but added: + M-r --- edit reply to all + M-R --- edit reply to sender + Added RCS Id strings to all files. + 'show keymaps' shows details of all keymaps after summary list. + Added --no-move option to delete command. + In particular, delete-and-always-move-down may now + be implemented with + '( delete --no-move ; next --skip-deleted )'. + Folded the nextmsg and prevmsg commands and functions together into + one command which takes arguments. + Added '--filter <name>' option (eg, for next_personal), + '--skip-deleted' option, and + '--last-if-none'/'--first-if-none' options. + Help updated accordingly. + In particular, the 'personal' filter is now used + for 'next personal'. + Added --smart-filter and --smart-filter-instance options + to the next and prev commands. + Updated examples/owlconf.erik with the above. + Made owl_function_fast*filt return a string and not do the + narrowing, to make it more general. + Added a smartfilter command that creates a filter + based on the current message and returns the name + of the filter. + Added M-C-n and M-C-p keybindings to "move to next message + matching current" and "move to previous message + matching current" + Added variables edit:maxfillcols and edit:maxwrapcols which + will limit how wide editing paragraphs may get before + they get wrapped. This is a max and may be narrower + depending on the current size of the window. + If 0, the max is unlimited. Default is 70 columns for + edit:maxfillcols and unlimited for edit:maxwrapcols. + Added smartzpunt command with key binding of "C-x k". + This starts a zpunt command filled in with + the proposed zpunt. + Fixed a memory reference bug in delete and undelete commands. + Added support for perl to call directly back into owl. + Changed the implementation of owl::command("...") to immediately + call back into owl. This allows perl to get the return + value of strings returned by owl commands. + Added the getview command which returns the name of the current + view's filter. + Added the getvar command which returns the value of a variable. + Added an example to examples/owlconf.erik which uses TAB to + narrow and restore the view. + Added an example to examples/owlconf.erik which uses M-c to + color messages matching the current one green. + Integrated change to fix problem with popup blinking on new zephyrs. + C-l and resizes will now refresh an open viewwin (eg, help). + Updated doc/code.txt to include info about filters, commands, + contexts, and keybindings. + Exec commands cleaned up to not have buffer-size limitations + and to not mess up spaces. exec also returns a string + of the output now. + Integrated changes from 1.1.3, and added docs for "zlocate -d" + and new show commands. + Show with arguments produces help on show. + Fix a bug in readconfig caught by efence (where we'd try to read before + the beginning of a string if it was empty). + The perl command doesn't do makemsg directly, but instead + returns the string and it will get printed if it + was run interactively. + +1.1.3 + 'show subs' and 'show subscriptions' are now the same as 'getsubs' + zlocate now takes an optional -d argument + 'show terminal' / 'show term' + '>' / last doesn't set the last message at the top of the screen now + implemented _followlast as an unsupported feature + include 'default' in the 'show colors' list + added help for 'zpunt' and 'zunpunt' + changed the bug address in the startup message + can now do 'show status' + can now do 'show version' + 'status' / 'show status' includes the owl version number now + 'show terminal' includes whether the terminal can change colors + fixed off by one bugs in paging / scrolling viewwin + don't downcase the sender when getting the log name for personals + support @owl::fields as well as @fields + downcase class/inst filter names in auto filters + +1.1.2 + Fixed memory mishandling bug + Fixed bug in redfining the filter attached to the current view + M-n will narrow to message, instance on non-personal, class + MESSAGE messages + M-N behavies like M-n except that on class messages it narrows + to class and instance + line wrap earlier, to account for tabbing + fixed typo in help + 'status' command now displays info on terminal color support + zephyr @ formatting is now case independant + added support for color terminals + zephyr @color(foo) now works + 'D' for deleted messages is now not bold, unless it's the current + message + F1 displays the help screen + added filter colors + added the 'colorview' command + added the 'show colors' command + users who don't have a .zephyr.subs get a simpler format for + incoming messages + If colors are available 'show filters' will show a filter in the + color associated with it. + Added the zpunt and zunpunt commands + Lines in the subs file starting with '-' are zpunted + Include login/logout messages in auto user filters + 'V' changes to the home view ('all' by default) + +1.1.1 + Fixed perl, aperl, and pperl commands to deal with quoting + and spaces in a saner manner. + Removed all owl_get_* methods for booleans and switched + cases where they were used to owl_is_* + Changes to owlconf.erik to use some new features. + Increased the size of the help buffer (as it + was overflowing and truncating the help message). + Variables prefixed with a _ are not shown in help + or by printallvars (and prefixed Not Yet Implemented + variables with this). + Fix typo in help + include stdio.h in functions.c + remove stale "q to quit" from bottom of info message + fix downward scrolling more than a page + use authentication for zlocate, by default + fixed buffer over run in info command on long messages + call 'perl <file>' from Makefile to avoid hardcoding perl paths + in Makefile don't build owl_prototypes.h unless necessary + store the time for admin messages + display admin message time in 'info' command + fixed an editwin M-> last character bug + +1.1 + reply is a normal function now + 'R' does reply to sender + 'T' tells you how many messages were marked for deletion + local realm removed from login / logout messages + added command history + better runtime / starttime reporting in 'status' command + leave the pointer near the current message after expunge + C-l recenters editwin + implemented zlocate + @italic works the same as @i + on reply only quote class / instance when necessary + C-r allows you to edit the reply line + don't use unecessary options in reply line + display 'info' errors in msgwin, not popup + impelemnted aexec, pexec commands + the zsig now goes through ztext formatting + messages have id numbers now + 'info' prints the msgid + added the 'filter' command + added the 'view' command + added the 'show filter' command + added the 'viewclass' (and 'vc') commands + added the 'viewuser' (and 'vu') commands + M-n will filter to the current class or user + 'v' starts a view command + M-D will delete all messages in current view + added the 'delete' (and 'del') command + load-subs with no argument loads the default subs file + '<truncated>' is now when the *current* message is truncated + the reply-lockout filter (with default) specifices messages that + cannot be replied to. + in the configfile owl::receive_msg is run whenever a message is + received + added the beep command + added the contributors file + declare ZGetSubscriptions and ZGetLocations since the includes + don't seem to + fixed bug in displaying last line in popwin if no final '\n' + 'T' uses the 'trash' filter now + zaway_msg, zaway_msg_default and zaway are all user variables now. + zsig variable overrides zsigproc + If there's no appendtosepbar don't interfear with the sepbar + Changed: owl_message_get_numlines will return 0 of m is NULL + Added login messages to messages marked by owl_function_delete_automsgs + Added owl_function_delete_by_id(id) which acts independent of view + Added "-id <id>" option to delete command + Fixed an arg checking bug in delete command + Added owl::id to perl namespace with message id + Fixed a memory corruption bug in readconfig.c (where right + after the strdup to "out", we'd strcat a \n onto the end. + This would be triggered whenever owl::format_msg returned + a string not ending in a newline + Added 'X' keybinding which expunges and then switches to + a view defined by the variable "view_home" which defaults + to "all" + Consolidated readconfig.c somewhat to remove duplication. + owl_config_execute now returns a string. + Added an example config file that does vt-style formatting. + (examples/owlconf.vtformat) + Added the 'perl', 'aperl', and 'pperl' commands which will + evaluate perl expressions. + Fixed bug where pclose zsigproc would cause zombies + Can set zsigproc or zsig to "" to disable + Added support for multiple browsers (galeon and none were added). + Configure with the "webbrowser" variable. + Changing typewinsize height triggers resize event. + Added zsig variable which will be used if no zsigproc and non-empty. + Added "make test" rule to Makefile which will run regression tests, + and added regression testing framework to tester + Fixed codelist.pl to ignore static declarations. + Added dict.c which contains string->ptr dictionary routines + and the owl_dict type. + These include regression tests. + Overhaul/rewrite of variable handling. Variables are now managed + in an owl_vardict (in g.vars) which contains a dictionary + of owl_variable's. Each owl_variable has dispatch functions + for validating values, setting it and getting it, + and for setting it to and from string values. + The variable.c file contains the list of variables. + Stubs for the owl_global_<varname>_get functions and friends + are generated from variable.c by stubgen.pl. + The help.c messages for variables now calls into variable.c + so all information about most variables is in one place. + Cleaned out code from global.c and command.c that was made obselete + by variable overhaul. + The set command now takes a -q option to not log a message. + Fixed a bug where set and print with no arguments would + print "Undefined variable" in addition + to running owl_function_printallvars. + debug is now a variable that can be turned on and off. + Fixed mail,inbox message parsing in examples/owlconf.erik + Made zaway_msg and zaway_msg_default into variables + Changed owl_function_makemsg and owl_function_debugmsg + to use varargs (ie, so they can now take a format + string with args). + Don't allow " and \ characters in URLs with the "w" command. + Removed lots of build warnings. + Popwins are wider by default so help messages fit better. + Added an atokenize_free function. + Fixes to work with an older version of libzephyr. + Added dependencies on header files to Makefile.in + Added pageup and pagedown key bindings to message list + Added pageup and pagedown to viewwin + Added configfile section to doc/intro.txt (from example config file) + Added appendtosepbar variable which may contain text which will + be appended to the sepbar. This allows the configfile + to put information about pings and logins into + the sepbar. (It may be worth also providing a variable + which enables this by default, but for now this allows + for experimenting with what works well.) + Added doc/code.txt which gives a brief overview of the code. + Added tags makefile rule and added TAGS to distclean rule. + +1.0.1 + fix frees in loadsubs and loadloginsubs + don't return in owl_free + +1.0 + 'print' and 'set' with no arguments prints all variables + Added the 'unsubscribe' and 'unsub' command + Renamed the 'unsub' command to 'unsuball' + Added the 'getsubs' command which is like zctl ret + Fixed bug in logging messages sent to more than one recipient + Support '-C', '-O', and '-n' options to zwrite + Fixed bug in owl_editwin_delete_char when there are no later chars + after the cursor + Make "more" and "truncated" work in the status bar + enable printing of zsigproc and loginsubs variables + only allow message scrolling if the message is actually off the + screen + 'T' will mark all automated message for deletion + 'P' will go to the next personal message + 'M-P' will go to the previous personal message + replying to a login message goes to the user now + added a status command + added the intro doc to the release + fixed off by one bug in viewwin + added complete online help + pass $owl::realm in configfile + fixed editwin wordwrapping on the last line + fixed editwin problem with key_right past the last char + print an error and quit if the configfile can't be parsed + got rid of owl_mainwin_calculate_topmsg + fixed off by one error in calculating topmsg upwards + you can now reply to an admin message + don't display an error about keypress on window resize + +0.11 + fixed bug in viewing messages longer than the screen + indicate in the sepbar if there is a non zero vert offset + send on '.' on a line by itself + added disable-ctrl-d variable + fixed bug where C-k did not delete the last \n in the buffer + make non-character meta keys work + use ZSendNotice instead of ZSendList + implemented <, >, M-< and M-> in viewwin + removed the spaces at the bottom of viewwin + added 'about' command + fixed bug using 'M' with no current message + changed message object to use char *'s to save on memory + change malloc, realloc, strdup and free to use owl hooks so that + debugging can be added + +0.10.1 + fixed a trailing space bug in the parser + impelemented the "burning ears" feature + have admin messages do ztext parsing + fixed bug in reporting which M- key was pressed + C-g will now cancel commands like C-c + +0.10 + implemented owl_function_full_redisplay(). + C-l uses owl_function_full_redisplay(). + when a popwin exists to a full redisplay. (fixes bug) + improved the owl_editwin_process_char logic + removed all unnecessary wrefresh's and replaced with wnoutrefesh + owl_editwin_redisplay now takes an argument to optionally doupdate() + improved the cut-and-paste speed by not doing a usleep the first + time through the loop after getting a keypress. + nuked typwin.c and associated stuff. It's useless now. + added viewwin code for paging windows + curly braces work for zephyr formatting + @i in zephyr formatting will be displayed as underlined text + turned off idlok + implemented viewwin + implemented viewwi in popwin for pageable popwins + help, info now use pageable popwins + bound 'M' to bring the current message up in a popwin + return, space bar, 'b' and backspace now scroll within a message + turned off resize message + C-v and M-v page the main window + implemented owl_message_is_mail + some build cleanup + + +0.9 + added owl_message_is_personal and have things use it + added owl_message_is_private + fixed 'print personalbell' and have 'set personalbell' + print a message + bold only on message_is_personal + display the realm if not local + implemented M-f, M-b, M-d, M-<, M-> in editwin + implemnted word wrapping in editwin + implemented M-q (paragraph-fill) in editwin + fixed bug that caused owl to segfault logging a 'weird' class + M-x is a keysym for ':' + added smart bolding and userclue + fixed a bug causing pings to beep even if rxping is off + +0.8.1 + fixed bug in logging code + +0.8 + implemented personal logging + implemented class logging + implemented resize of typewin + fixed the backspace problem + -v command line option prints the version number + +0.7 + load-subs will report error opening file + skip comment lines in loadsubs and loadloginsubs + changed internal references to rxping and txping + fix replying to a blank instance + added subscribe command + subscribe to login messages from .anyone by default + 'loginsubs' variarble controlls automated login messages + redisplay the editwin after a resize + leave the cursor in the editwin if active + fix problems in the build system + added displayoutgoing variable + temporarily removed error printing for zlog in / out + +0.61 + fixed bug in "message sent to <foo>" for zwrite + +0.6 + help updated + zaway key set to caps A + support zephyring other realms + rxping variable for receiving pings + txping variable for sending pings + function in place to resize typwin + C-l to refresh + personal bell variable + beta message now an admin message + +0.5 + Added the debug command and flag + Fixed bug in printing fields in info command + Added owl_fmtext_append_ztext and use it + Better formating for pings and login zephyrs + make tester depends on proto diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..0cf27fe --- /dev/null +++ b/Makefile.in @@ -0,0 +1,105 @@ +# $Id: Makefile.in,v 1.22 2009/03/28 21:00:35 kretch Exp $ + +srcdir = @srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datadir = @datadir@ +bindir = @bindir@ +mandir = @mandir@ + +CC=@CC@ +LIBS=@LIBS@ -L./libfaim -lfaim +CFLAGS=@CFLAGS@ -I. -I./libfaim -DDATADIR=\"${datadir}\" +LDFLAGS=@LDFLAGS@ +XSUBPPDIR=@XSUBPPDIR@ +INSTALL=@INSTALL@ +INSTALL_PROGRAM=@INSTALL_PROGRAM@ +INSTALL_DATA=@INSTALL_DATA@ + +BASE_SRCS=list.c message.c mainwin.c popwin.c zephyr.c messagelist.c \ + commands.c global.c text.c fmtext.c editwin.c util.c logging.c \ + perlconfig.c keys.c functions.c zwrite.c viewwin.c help.c filter.c \ + regex.c history.c view.c dict.c variable.c filterelement.c pair.c \ + keypress.c keymap.c keybinding.c cmd.c context.c zcrypt.c \ + aim.c buddy.c buddylist.c timer.c style.c stylefunc.c errqueue.c \ + zbuddylist.c popexec.c select.c +OWL_SRC = owl.c +TESTER_SRC = tester.c + +BASE_OBJS = $(BASE_SRCS:.c=.o) + +GEN_C = varstubs.c perlglue.c perlwrap.c +GEN_H = owl_prototypes.h +GEN_O = $(GEN_C:.c=.o) + +OBJS = $(BASE_OBJS) $(GEN_O) + +AUTOGEN=$(GEN_C) $(GEN_H) +#AUTOGEN=$(GEN_C) + + +owl: $(AUTOGEN) $(OBJS) owl.o libfaim + ./athstatic $(CC) -o owl owl.o $(OBJS) $(LDFLAGS) $(LIBS) + +tester: $(AUTOGEN) $(OBJS) tester.o + ./athstatic $(CC) -o tester tester.o $(OBJS) $(LDFLAGS) $(LIBS) + +test: tester + ./tester reg + +clean: libfaimclean + $(RM) owl tester *.o $(AUTOGEN) owl_prototypes.h.new + +distclean: clean libfaimdistclean + $(RM) config.cache config.log config.status Makefile config.h TAGS *~ core + +proto: owl_prototypes.h + +perlglue.c: perlglue.xs + perl $(XSUBPPDIR)/xsubpp -typemap $(XSUBPPDIR)/typemap -prototypes perlglue.xs > perlglue.c + +varstubs.c: variable.c stubgen.pl + perl stubgen.pl > varstubs.c + +perlwrap.c: perlwrap.pm encapsulate.pl + perl encapsulate.pl perlwrap.pm owl_perlwrap_codebuff > perlwrap.c + +# Only move owl_prototypes.h into place if the new one is different +owl_prototypes.h: codelist.pl varstubs.c $(BASE_SRCS) + perl codelist.pl > owl_prototypes.h.new + @cmp -s owl_prototypes.h.new $@ || { \ + test -f $@ && echo 'Interfaces changed!'; \ + echo mv -f owl_prototypes.h.new $@; \ + mv -f owl_prototypes.h.new $@; } +#owl_prototypes.h: + +.PHONY: tags clean distclean proto test + +tags: TAGS + +TAGS: $(BASE_SRCS) $(OWL_SRC) $(TESTER_SRC) $(GEN_C) owl.h $(GEN_H) + etags $(BASE_SRCS) $(OWL_SRC) $(TESTER_SRC) $(GEN_C) owl.h $(GEN_H) + +$(BASE_OBJS) varstubs.h:: owl.h config.h owl_prototypes.h +#$(BASE_OBJS) varstubs.h:: owl.h config.h + +libfaim: libfaim/libfaim.a + +libfaim/libfaim.a: + (cd libfaim; $(MAKE)) + +libfaimclean: + (cd libfaim; $(MAKE) clean) + +libfaimdistclean: + (cd libfaim; $(MAKE) distclean) + +all: owl + +install: all installdirs + ${INSTALL_PROGRAM} owl ${bindir}/owl + ${INSTALL_DATA} doc/owl.1 ${mandir}/man1/owl.1 + +installdirs: mkinstalldirs + ${srcdir}/mkinstalldirs ${bindir} ${mandir}/man1 ${datadir}/owl diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..8afa924 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,171 @@ +# generated automatically by aclocal 1.10.1 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# +# Copyright © 2004 Scott James Remnant <scott@netsplit.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. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# +# Similar to PKG_CHECK_MODULES, make sure that the first instance of +# this or PKG_CHECK_MODULES is called, or make sure to call +# PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_ifval([$2], [$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$PKG_CONFIG"; then + if test -n "$$1"; then + pkg_cv_[]$1="$$1" + else + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + fi +else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + ifelse([$4], , [AC_MSG_ERROR(dnl +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT +])], + [AC_MSG_RESULT([no]) + $4]) +elif test $pkg_failed = untried; then + ifelse([$4], , [AC_MSG_FAILURE(dnl +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see <http://pkg-config.freedesktop.org/>.])], + [$4]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + ifelse([$3], , :, [$3]) +fi[]dnl +])# PKG_CHECK_MODULES + @@ -0,0 +1,2796 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdio.h> +#include <stdio.h> +#include <sys/stat.h> +#include "owl.h" + +/**********************************************************************/ + +struct owlfaim_priv { + char *aimbinarypath; + char *screenname; + char *password; + char *server; + char *proxy; + char *proxyusername; + char *proxypass; + char *ohcaptainmycaptain; + int connected; + + FILE *listingfile; + char *listingpath; + + fu8_t *buddyicon; + int buddyiconlen; + time_t buddyiconstamp; + fu16_t buddyiconsum; +}; + +static char *msgerrreasons[] = { + "Invalid error", + "Invalid SNAC", + "Rate to host", + "Rate to client", + "Not logged on", + "Service unavailable", + "Service not defined", + "Obsolete SNAC", + "Not supported by host", + "Not supported by client", + "Refused by client", + "Reply too big", + "Responses lost", + "Request denied", + "Busted SNAC payload", + "Insufficient rights", + "In local permit/deny", + "Too evil (sender)", + "Too evil (receiver)", + "User temporarily unavailable", + "No match", + "List overflow", + "Request ambiguous", + "Queue full", + "Not while on AOL", +}; +static int msgerrreasonslen = 25; + +static void faimtest_debugcb(aim_session_t *sess, int level, const char *format, va_list va); +static int faimtest_parse_login(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_authresp(aim_session_t *sess, aim_frame_t *fr, ...); +int faimtest_flapversion(aim_session_t *sess, aim_frame_t *fr, ...); +int faimtest_conncomplete(aim_session_t *sess, aim_frame_t *fr, ...); +void addcb_bos(aim_session_t *sess, aim_conn_t *bosconn); +static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...); +static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...); +static int conninitdone_chatnav (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_chat (aim_session_t *, aim_frame_t *, ...); +int logout(aim_session_t *sess); + +static int faimtest_parse_connerr(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_accountconfirm(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_infochange(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_handleredirect(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_icbmparaminfo(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_bosrights(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_locrights(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_reportinterval(aim_session_t *sess, aim_frame_t *fr, ...); +/* static int reportinterval(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs); */ +static int faimtest_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...); +static int getaimdata(aim_session_t *sess, unsigned char **bufret, int *buflenret, unsigned long offset, unsigned long len, const char *modname); +static int faimtest_memrequest(aim_session_t *sess, aim_frame_t *fr, ...); +/* static void printuserflags(fu16_t flags); */ +static int faimtest_parse_userinfo(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_handlecmd(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, const char *tmpstr); +static int faimtest_parse_incoming_im_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args); +static int faimtest_parse_incoming_im_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args); +static int faimtest_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...); +int faimtest_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_searchreply(aim_session_t *sess, aim_frame_t *fr, ...); +static int faimtest_parse_searcherror(aim_session_t *sess, aim_frame_t *fr, ...); +static int handlepopup(aim_session_t *sess, aim_frame_t *fr, ...); +static int serverpause(aim_session_t *sess, aim_frame_t *fr, ...); +static int migrate(aim_session_t *sess, aim_frame_t *fr, ...); +static int ssirights(aim_session_t *sess, aim_frame_t *fr, ...); +static int ssidata(aim_session_t *sess, aim_frame_t *fr, ...); +static int ssidatanochange(aim_session_t *sess, aim_frame_t *fr, ...); +static int offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...); +static int offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...); +/* +static int faimtest_ssi_parseerr (aim_session_t *, aim_frame_t *, ...); +static int faimtest_ssi_parserights (aim_session_t *, aim_frame_t *, ...); +static int faimtest_ssi_parselist (aim_session_t *, aim_frame_t *, ...); +static int faimtest_ssi_parseack (aim_session_t *, aim_frame_t *, ...); +static int faimtest_ssi_authgiven (aim_session_t *, aim_frame_t *, ...); +static int faimtest_ssi_authrequest (aim_session_t *, aim_frame_t *, ...); +static int faimtest_ssi_authreply (aim_session_t *, aim_frame_t *, ...); +static int faimtest_ssi_gotadded (aim_session_t *, aim_frame_t *, ...); +*/ + +void chatnav_redirect(aim_session_t *sess, struct aim_redirect_data *redir); +void chat_redirect(aim_session_t *sess, struct aim_redirect_data *redir); + +/*****************************************************************/ + +void owl_aim_init(void) +{ + /* this has all been moved to owl_aim_login, but we'll leave the + * function here, in case there's stuff we want to init in the + * future. It's still called by Owl. + */ + +} + + +int owl_aim_login(char *screenname, char *password) +{ + struct owlfaim_priv *priv; + aim_conn_t *conn; + aim_session_t *sess; + + sess=owl_global_get_aimsess(&g); + + aim_session_init(sess, TRUE, 0); + aim_setdebuggingcb(sess, faimtest_debugcb); + aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL); + + /* this will leak, I know and just don't care right now */ + priv=owl_malloc(sizeof(struct owlfaim_priv)); + memset(priv, 0, sizeof(struct owlfaim_priv)); + + priv->screenname = owl_strdup(screenname); + priv->password = owl_strdup(password); + priv->server = owl_strdup(FAIM_LOGIN_SERVER); + sess->aux_data = priv; + + conn=aim_newconn(sess, AIM_CONN_TYPE_AUTH, priv->server ? priv->server : FAIM_LOGIN_SERVER); + /* conn=aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL); */ + if (!conn) { + owl_function_error("owl_aim_login: connection error during AIM login\n"); + owl_global_set_aimnologgedin(&g); + owl_global_set_no_doaimevents(&g); + return (-1); + } + + /* + else if (conn->fd == -1) { + if (conn->status & AIM_CONN_STATUS_RESOLVERR) { + owl_function_error("owl_aim_login: could not resolve authorize name"); + } else if (conn->status & AIM_CONN_STATUS_CONNERR) { + owl_function_error("owl_aim_login: could not connect to authorizer"); + } else { + owl_function_error("owl_aim_login: unknown connection error"); + } + owl_global_set_aimnologgedin(&g); + owl_global_set_no_doaimevents(&g); + aim_conn_kill(sess, &conn); + return(-1); + } + */ + + + aim_conn_addhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, faimtest_flapversion, 0); + aim_conn_addhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0); + + aim_conn_addhandler(sess, conn, AIM_CB_FAM_ATH, AIM_CB_ATH_AUTHRESPONSE, faimtest_parse_login, 0); + aim_conn_addhandler(sess, conn, AIM_CB_FAM_ATH, AIM_CB_ATH_LOGINRESPONSE, faimtest_parse_authresp, 0); + /* aim_conn_addhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0); */ + /* aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0); */ + /* aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0); */ + + /* start processing AIM events */ + owl_global_set_doaimevents(&g); + /* conn->status |= AIM_CONN_STATUS_INPROGRESS; */ + owl_function_debugmsg("owl_aim_login: sending login request for %s", screenname); + aim_request_login(sess, conn, screenname); + owl_function_debugmsg("owl_aim_login: connecting"); + + return(0); +} + +/* stuff to run once login has been successful */ +void owl_aim_successful_login(char *screenname) +{ + char *buff; + owl_function_debugmsg("doing owl_aim_successful_login"); + owl_global_set_aimloggedin(&g, screenname); + owl_global_set_doaimevents(&g); /* this should already be on */ + owl_function_makemsg("%s logged in", screenname); + buff=owl_sprintf("Logged in to AIM as %s", screenname); + owl_function_adminmsg("", buff); + owl_free(buff); + + owl_function_debugmsg("Successful AIM login for %s", screenname); + + /* start the ingorelogin timer */ + owl_timer_reset_newstart(owl_global_get_aim_login_timer(&g), + owl_global_get_aim_ignorelogin_timer(&g)); + + + /* aim_ssi_setpresence(owl_global_get_aimsess(&g), 0x00000400); */ + /* aim_bos_setidle(owl_global_get_aimsess(&g), owl_global_get_bosconn(&g), 5000); */ +} + +void owl_aim_logout(void) +{ + /* need to check if it's connected first, I think */ + logout(owl_global_get_aimsess(&g)); + + if (owl_global_is_aimloggedin(&g)) owl_function_adminmsg("", "Logged out of AIM"); + owl_global_set_aimnologgedin(&g); + owl_global_set_no_doaimevents(&g); +} + +void owl_aim_logged_out() +{ + if (owl_global_is_aimloggedin(&g)) owl_function_adminmsg("", "Logged out of AIM"); + owl_aim_logout(); +} + +void owl_aim_login_error(char *message) +{ + if (message) { + owl_function_error("%s", message); + } else { + owl_function_error("Authentication error on login"); + } + owl_function_beep(); + owl_global_set_aimnologgedin(&g); + owl_global_set_no_doaimevents(&g); +} + +int owl_aim_send_im(char *to, char *msg) +{ + int ret; + + ret=aim_im_sendch1(owl_global_get_aimsess(&g), to, NULL, msg); + + /* I don't know how to check for an error yet */ + return(ret); +} + +int owl_aim_send_awaymsg(char *to, char *msg) +{ + int ret; + + ret=aim_im_sendch1(owl_global_get_aimsess(&g), to, AIM_IMFLAGS_AWAY, msg); + + /* I don't know how to check for an error yet */ + return(ret); +} + +void owl_aim_addbuddy(char *name) +{ + + aim_ssi_addbuddy(owl_global_get_aimsess(&g), name, "Buddies", NULL, NULL, NULL, 0); + + /* + aim_ssi_addbuddy(owl_global_get_aimsess(&g), + name, + "Buddies", + NULL, NULL, NULL, + aim_ssi_waitingforauth(owl_global_get_aimsess(&g)->ssi.local, "Buddies", name)); + */ +} + +void owl_aim_delbuddy(char *name) +{ + aim_ssi_delbuddy(owl_global_get_aimsess(&g), name, "Buddies"); + owl_buddylist_offgoing(owl_global_get_buddylist(&g), name); +} + +void owl_aim_search(char *email) +{ + int ret; + + owl_function_debugmsg("owl_aim_search: doing search for %s", email); + ret=aim_search_address(owl_global_get_aimsess(&g), + aim_getconn_type(owl_global_get_aimsess(&g), AIM_CONN_TYPE_BOS), + email); + + if (ret) owl_function_error("owl_aim_search: aim_search_address returned %i", ret); +} + + +int owl_aim_set_awaymsg(char *msg) +{ + int len; + char *foo; + /* there is a max away message lentgh we should check against */ + + foo=owl_strdup(msg); + len=strlen(foo); + if (len>500) { + foo[500]='\0'; + len=499; + } + + aim_locate_setprofile(owl_global_get_aimsess(&g), + NULL, NULL, 0, + "us-ascii", foo, len); + owl_free(foo); + + /* + aim_bos_setprofile(owl_global_get_aimsess(&g), + owl_global_get_bosconn(&g), + NULL, NULL, 0, "us-ascii", msg, + strlen(msg), 0); + */ + return(0); +} + +void owl_aim_chat_join(char *name, int exchange) +{ + int ret; + aim_conn_t *cur; + /* + OscarData *od = (OscarData *)g->proto_data; + char *name, *exchange; + */ + + owl_function_debugmsg("Attempting to join chatroom %s exchange %i", name, exchange); + + /* + name = g_hash_table_lookup(data, "room"); + exchange = g_hash_table_lookup(data, "exchange"); + */ + if ((cur = aim_getconn_type(owl_global_get_aimsess(&g), AIM_CONN_TYPE_CHATNAV))) { + owl_function_debugmsg("owl_aim_chat_join: chatnav exists, creating room"); + aim_chatnav_createroom(owl_global_get_aimsess(&g), cur, name, exchange); + } else { + /* struct create_room *cr = g_new0(struct create_room, 1); */ + owl_function_debugmsg("owl_aim_chat_join: chatnav does not exist, opening chatnav"); + /* + cr->exchange = atoi(exchange); + cr->name = g_strdup(name); + od->create_rooms = g_slist_append(od->create_rooms, cr); + */ + aim_reqservice(owl_global_get_aimsess(&g), + aim_getconn_type(owl_global_get_aimsess(&g), AIM_CONN_TYPE_CHATNAV), + AIM_CONN_TYPE_CHATNAV); + aim_reqservice(owl_global_get_aimsess(&g), NULL, AIM_CONN_TYPE_CHATNAV); + aim_chatnav_createroom(owl_global_get_aimsess(&g), cur, name, exchange); + ret=aim_chat_join(owl_global_get_aimsess(&g), owl_global_get_bosconn(&g), exchange, name, 0x0000); + + } + return; + /******/ + + + /* ret=aim_chat_join(owl_global_get_aimsess(&g), owl_global_get_bosconn(&g), exchange, chatroom, 0x0000); */ + /* + ret=aim_chat_join(owl_global_get_aimsess(&g), + aim_getconn_type(owl_global_get_aimsess(&g), AIM_CONN_TYPE_CHATNAV), exchange, chatroom, 0x0000); + */ + + aim_reqservice(owl_global_get_aimsess(&g), owl_global_get_bosconn(&g), AIM_CONN_TYPE_CHATNAV); + ret = aim_chatnav_createroom(owl_global_get_aimsess(&g), + aim_getconn_type(owl_global_get_aimsess(&g), AIM_CONN_TYPE_CHATNAV), name, exchange); + ret=aim_chat_join(owl_global_get_aimsess(&g), owl_global_get_bosconn(&g), exchange, name, 0x0000); + +} + +void owl_aim_chat_leave(char *chatroom) +{ +} + +int owl_aim_chat_sendmsg(char *chatroom, char *msg) +{ + return(0); +} + +/* caller must free the return */ +char *owl_aim_normalize_screenname(char *in) +{ + char *out; + int i, j, k; + + j=strlen(in); + out=owl_malloc(j+30); + k=0; + for (i=0; i<j; i++) { + if (in[i]!=' ') { + out[k]=in[i]; + k++; + } + } + out[k]='\0'; + return(out); +} + +int owl_aim_process_events() +{ + aim_session_t *aimsess; + aim_conn_t *waitingconn = NULL; + struct timeval tv; + int selstat = 0; + struct owlfaim_priv *priv; + + aimsess=owl_global_get_aimsess(&g); + priv = (struct owlfaim_priv *) &(aimsess->aux_data); + + /* do a select without blocking */ + tv.tv_sec = 0; + tv.tv_usec = 0; + waitingconn = aim_select(aimsess, &tv, &selstat); + + if (owl_global_is_aimnop_time(&g)) { + aim_flap_nop(aimsess, aim_getconn_type(aimsess, AIM_CONN_TYPE_BOS)); + owl_global_aimnop_sent(&g); + } + + if (selstat == -1) { + owl_aim_logged_out(); + } else if (selstat == 0) { + /* no events pending */ + } else if (selstat == 1) { /* outgoing data pending */ + aim_tx_flushqueue(aimsess); + } else if (selstat == 2) { /* incoming data pending */ + /* printf("selstat == 2\n"); */ + + if (aim_get_command(aimsess, waitingconn) >= 0) { + aim_rxdispatch(aimsess); + } else { + /* printf("connection error (type 0x%04x:0x%04x)\n", waitingconn->type, waitingconn->subtype); */ + /* we should have callbacks for all these, else the library will do the conn_kill for us. */ + if (waitingconn->type == AIM_CONN_TYPE_RENDEZVOUS) { + if (waitingconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { + /* printf("disconnected from %s\n", aim_directim_getsn(waitingconn)); */ + aim_conn_kill(aimsess, &waitingconn); + owl_aim_logged_out(); + } + } else { + aim_conn_kill(aimsess, &waitingconn); + owl_aim_logged_out(); + } + if (!aim_getconn_type(aimsess, AIM_CONN_TYPE_BOS)) { + /* printf("major connection error\n"); */ + owl_aim_logged_out(); + /* break; */ + } + } + } + /* free(priv->buddyicon); */ + /* exit(0); */ + return(0); +} + +static void faimtest_debugcb(aim_session_t *sess, int level, const char *format, va_list va) +{ + return; +} + +static int faimtest_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) +{ + struct owlfaim_priv *priv = (struct owlfaim_priv *)sess->aux_data; + struct client_info_s info = CLIENTINFO_AIM_KNOWNGOOD; + + char *key; + va_list ap; + + va_start(ap, fr); + key = va_arg(ap, char *); + va_end(ap); + + owl_function_debugmsg("faimtest_parse_login: %s %s %s", priv->screenname, priv->password, key); + + aim_send_login(sess, fr->conn, priv->screenname, priv->password, &info, key); + + return(1); +} + + +static int faimtest_parse_authresp(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + struct aim_authresp_info *info; + aim_conn_t *bosconn; + + va_start(ap, fr); + info = va_arg(ap, struct aim_authresp_info *); + va_end(ap); + + /* printf("Screen name: %s\n", info->sn); */ + owl_function_debugmsg("doing faimtest_parse_authresp"); + owl_function_debugmsg("faimtest_parse_authresp: %s", info->sn); + + /* + * Check for error. + */ + if (info->errorcode || !info->bosip || !info->cookie) { + /* + printf("Login Error Code 0x%04x\n", info->errorcode); + printf("Error URL: %s\n", info->errorurl); + */ + if (info->errorcode==0x05) { + owl_aim_login_error("Incorrect nickname or password."); + } else if (info->errorcode==0x11) { + owl_aim_login_error("Your account is currently suspended."); + } else if (info->errorcode==0x14) { + owl_aim_login_error("The AOL Instant Messenger service is temporarily unavailable."); + } else if (info->errorcode==0x18) { + owl_aim_login_error("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."); + } else if (info->errorcode==0x1c) { + owl_aim_login_error("The client version you are using is too old."); + } else { + owl_aim_login_error(NULL); + } + aim_conn_kill(sess, &fr->conn); + return(1); + } + + /* + printf("Reg status: %d\n", info->regstatus); + printf("Email: %s\n", info->email); + printf("BOS IP: %s\n", info->bosip); + */ + + /* printf("Closing auth connection...\n"); */ + aim_conn_kill(sess, &fr->conn); + if (!(bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, info->bosip))) { + /* printf("could not connect to BOS: internal error\n"); */ + return(1); + } else if (bosconn->status & AIM_CONN_STATUS_CONNERR) { + /* printf("could not connect to BOS\n"); */ + aim_conn_kill(sess, &bosconn); + return(1); + } + owl_global_set_bossconn(&g, bosconn); + owl_aim_successful_login(info->sn); + addcb_bos(sess, bosconn); + aim_sendcookie(sess, bosconn, info->cookielen, info->cookie); + return(1); +} + +int faimtest_flapversion(aim_session_t *sess, aim_frame_t *fr, ...) +{ + owl_function_debugmsg("doing faimtest_flapversion"); + +#if 0 + /* XXX fix libfaim to support this */ + printf("using FLAP version 0x%08x\n", /* aimutil_get32(fr->data)*/ 0xffffffff); + + /* + * This is an alternate location for starting the login process. + */ + /* XXX should do more checking to make sure its really the right AUTH conn */ + if (fr->conn->type == AIM_CONN_TYPE_AUTH) { + /* do NOT send a flapversion, request_login will send it if needed */ + aim_request_login(sess, fr->conn, priv->screenname); + /* printf("faimtest: login request sent\n"); */ + } +#endif + + return 1; +} + + +int faimtest_conncomplete(aim_session_t *sess, aim_frame_t *fr, ...) +{ + owl_function_debugmsg("doing faimtest_conncomplete"); + /* owl_aim_successful_login(info->sn); */ + return 1; +} + +void addcb_bos(aim_session_t *sess, aim_conn_t *bosconn) +{ + owl_function_debugmsg("doing addcb_bos"); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0); + + aim_conn_addhandler(sess, bosconn, 0x0013, 0x0003, ssirights, 0); + aim_conn_addhandler(sess, bosconn, 0x0013, 0x0006, ssidata, 0); + aim_conn_addhandler(sess, bosconn, 0x0013, 0x000f, ssidatanochange, 0); + aim_conn_addhandler(sess, bosconn, 0x0008, 0x0002, handlepopup, 0); + aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, faimtest_bosrights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, faimtest_handleredirect, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_STS, AIM_CB_STS_SETREPORTINTERVAL, faimtest_reportinterval, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, faimtest_parse_buddyrights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, faimtest_parse_motd, 0); + aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, faimtest_icbmparaminfo, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, faimtest_parse_connerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, faimtest_locrights, 0); + aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, faimtest_memrequest, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, faimtest_parse_oncoming, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, faimtest_parse_offgoing, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, faimtest_parse_incoming_im, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, faimtest_parse_locerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, faimtest_parse_misses, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, faimtest_parse_ratechange, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, faimtest_parse_evilnotify, 0); + + aim_conn_addhandler(sess, bosconn, 0x000a, 0x0001, faimtest_parse_searcherror, 0); + aim_conn_addhandler(sess, bosconn, 0x000a, 0x0003, faimtest_parse_searchreply, 0); + + /* + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, AIM_CB_LOK_ERROR, faimtest_parse_searcherror, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, 0x0003, faimtest_parse_searchreply, 0); + */ + + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, faimtest_parse_msgerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, faimtest_parse_userinfo, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, faimtest_parse_msgack, 0); + + aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, faimtest_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, faimtest_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, 0x0009, 0x0001, faimtest_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, 0x0001, 0x000b, serverpause, 0); + aim_conn_addhandler(sess, bosconn, 0x0001, 0x0012, migrate, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, offlinemsg, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, offlinemsgdone, 0); + + /* + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0); + */ + + /* + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ERROR, faimtest_ssi_parseerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, faimtest_ssi_parserights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, faimtest_ssi_parselist, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_NOLIST, faimtest_ssi_parselist, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, faimtest_ssi_parseack, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTH, faimtest_ssi_authgiven, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREQ, faimtest_ssi_authrequest, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREP, faimtest_ssi_authreply, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ADDED, faimtest_ssi_gotadded, 0); + */ + + return; +} + +static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) +{ + owl_function_debugmsg("doing coninitdone_bos"); + + + aim_reqpersonalinfo(sess, fr->conn); + aim_ssi_reqrights(sess); + aim_ssi_reqdata(sess); + aim_locate_reqrights(sess); + aim_buddylist_reqrights(sess, fr->conn); + + aim_im_reqparams(sess); + /* aim_bos_reqrights(sess, fr->conn); */ /* XXX - Don't call this with ssi */ + + owl_function_debugmsg("conninitdone_bos: requesting rights"); + aim_bos_reqrights(sess, fr->conn); /* XXX - Don't call this with ssi */ + aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS); + aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE | AIM_PRIVFLAGS_ALLOWMEMBERSINCE); + + return(1); +} + +static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) +{ + aim_clientready(sess, fr->conn); + owl_function_debugmsg("conninitdone_admin: initializtion done for admin connection"); + return(1); +} + +int logout(aim_session_t *sess) +{ + aim_session_kill(sess); + owl_aim_init(); + + owl_function_debugmsg("libfaim logout called"); + /* + if (faimtest_init() == -1) + printf("faimtest_init failed\n"); + */ + + return(0); +} + +/**************************************************************************************************/ + +static int faimtest_parse_connerr(aim_session_t *sess, aim_frame_t *fr, ...) +{ + struct owlfaim_priv *priv = (struct owlfaim_priv *)sess->aux_data; + va_list ap; + fu16_t code; + char *msg; + + va_start(ap, fr); + code = va_arg(ap, int); + msg = va_arg(ap, char *); + va_end(ap); + + owl_function_error("faimtest_parse_connerr: Code 0x%04x: %s\n", code, msg); + aim_conn_kill(sess, &fr->conn); /* this will break the main loop */ + + priv->connected = 0; + + return 1; +} + +static int faimtest_accountconfirm(aim_session_t *sess, aim_frame_t *fr, ...) +{ + int status; + va_list ap; + + va_start(ap, fr); + status = va_arg(ap, int); /* status code of confirmation request */ + va_end(ap); + + /* owl_function_debugmsg("faimtest_accountconfirm: Code 0x%04x: %s\n", code, msg); */ + owl_function_debugmsg("faimtest_accountconfirm: account confirmation returned status 0x%04x (%s)\n", status, (status==0x0000)?"email sent":"unknown"); + + return 1; +} + +static int faimtest_infochange(aim_session_t *sess, aim_frame_t *fr, ...) +{ + fu16_t change = 0, perms, type; + int length, str; + char *val; + va_list ap; + + va_start(ap, fr); + change = va_arg(ap, int); + perms = (fu16_t)va_arg(ap, unsigned int); + type = (fu16_t)va_arg(ap, unsigned int); + length = va_arg(ap, int); + val = va_arg(ap, char *); + str = va_arg(ap, int); + va_end(ap); + + owl_function_debugmsg("faimtest_infochange: info%s: perms = %d, type = %x, length = %d, val = %s", change?" change":"", perms, type, length, str?val:"(not string)"); + + return(1); +} + + +static int faimtest_handleredirect(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + struct aim_redirect_data *redir; + + owl_function_debugmsg("faimtest_handledirect:"); + + va_start(ap, fr); + redir = va_arg(ap, struct aim_redirect_data *); + + if (redir->group == 0x0005) { /* Adverts */ + + } else if (redir->group == 0x0007) { /* Authorizer */ + aim_conn_t *tstconn; + + owl_function_debugmsg("faimtest_handledirect: autorizer"); + + tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, redir->ip); + if (!tstconn || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) { + owl_function_error("faimtest_handleredirect: unable to reconnect with authorizer"); + } else { + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, faimtest_flapversion, 0); + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0); + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_admin, 0); + aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, faimtest_accountconfirm, 0); + aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, faimtest_infochange, 0); + aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, faimtest_infochange, 0); + /* Send the cookie to the Auth */ + aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie); + owl_function_debugmsg("faimtest_handleredirect: sent cookie to authorizer host"); + } + } else if (redir->group == 0x000d) { /* ChatNav */ + owl_function_debugmsg("faimtest_handledirect: chatnav"); + chatnav_redirect(sess, redir); + } else if (redir->group == 0x000e) { /* Chat */ + owl_function_debugmsg("faimtest_handledirect: chat"); + chat_redirect(sess, redir); + } else { + owl_function_debugmsg("faimtest_handleredirect: uh oh... got redirect for unknown service 0x%04x!!", redir->group); + } + va_end(ap); + return 1; +} + +static int faimtest_icbmparaminfo(aim_session_t *sess, aim_frame_t *fr, ...) +{ + struct aim_icbmparameters *params; + va_list ap; + + va_start(ap, fr); + params = va_arg(ap, struct aim_icbmparameters *); + va_end(ap); + + owl_function_debugmsg("faimtest_icbmparaminfo: ICBM Parameters: maxchannel = %d, default flags = 0x%08lx, max msg len = %d, max sender evil = %f, max reciever evil = %f, min msg interval = %ld", + params->maxchan, params->flags, params->maxmsglen, ((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0, params->minmsginterval); + + /* + * Set these to your taste, or client medium. Setting minmsginterval + * higher is good for keeping yourself from getting flooded (esp + * if you're on a slow connection or something where that would be + * useful). + */ + params->maxmsglen = 8000; + params->minmsginterval = 0; /* in milliseconds */ + /* aim_seticbmparam(sess, params); */ + aim_im_setparams(sess, params); + + return 1; +} + +static int faimtest_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + fu16_t maxbuddies, maxwatchers; + + va_start(ap, fr); + maxbuddies = va_arg(ap, int); + maxwatchers = va_arg(ap, int); + va_end(ap); + + owl_function_debugmsg("faimtest_parse_buddyrights: Max buddies = %d / Max watchers = %d\n", maxbuddies, maxwatchers); + + /* aim_ssi_reqrights(sess, fr->conn); */ + aim_ssi_reqrights(sess); + + return 1; +} + +static int faimtest_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + fu16_t maxpermits, maxdenies; + + va_start(ap, fr); + maxpermits = va_arg(ap, int); + maxdenies = va_arg(ap, int); + va_end(ap); + + owl_function_debugmsg("faimtest_bosrights: Max permit = %d / Max deny = %d\n", maxpermits, maxdenies); + aim_clientready(sess, fr->conn); + owl_function_debugmsg("officially connected to BOS."); + aim_icq_reqofflinemsgs(sess); + return 1; +} + +static int faimtest_locrights(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + fu16_t maxsiglen; + + va_start(ap, fr); + maxsiglen = va_arg(ap, int); + va_end(ap); + + owl_function_debugmsg("faimtest_locrights: rights: max signature length = %d\n", maxsiglen); + + return(1); +} + +static int faimtest_reportinterval(aim_session_t *sess, aim_frame_t *fr, ...) +{ + struct owlfaim_priv *priv = (struct owlfaim_priv *)sess->aux_data; + va_list ap; + fu16_t interval; + + va_start(ap, fr); + interval = va_arg(ap, int); + va_end(ap); + + owl_function_debugmsg("faimtest_reportinterval: %d (seconds?)\n", interval); + + if (!priv->connected) { + priv->connected++; + } + /* aim_reqicbmparams(sess); */ + aim_im_reqparams(sess); + /* kretch */ + return 1; +} + +static int faimtest_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) +{ + char *msg; + fu16_t id; + va_list ap; + static int codeslen = 5; + static char *codes[] = { + "Unknown", + "Mandatory upgrade", + "Advisory upgrade", + "System bulletin", + "Top o' the world!" + }; + + va_start(ap, fr); + id = va_arg(ap, int); + msg = va_arg(ap, char *); + va_end(ap); + + owl_function_debugmsg("faimtest_parse_motd: %s (%d / %s)\n", msg?msg:"nomsg", id, (id < codeslen)?codes[id]:"unknown"); + + return 1; +} + +/* + * This is a little more complicated than it looks. The module + * name (proto, boscore, etc) may or may not be given. If it is + * not given, then use aim.exe. If it is given, put ".ocm" on the + * end of it. + * + * Now, if the offset or length requested would cause a read past + * the end of the file, then the request is considered invalid. Invalid + * requests are processed specially. The value hashed is the + * the request, put into little-endian (eight bytes: offset followed + * by length). + * + * Additionally, if the request is valid, the length is mod 4096. It is + * important that the length is checked for validity first before doing + * the mod. + * + * Note to Bosco's Brigade: if you'd like to break this, put the + * module name on an invalid request. + * + */ +static int getaimdata(aim_session_t *sess, unsigned char **bufret, int *buflenret, unsigned long offset, unsigned long len, const char *modname) +{ + struct owlfaim_priv *priv = (struct owlfaim_priv *)sess->aux_data; + FILE *f; + static const char defaultmod[] = "aim.exe"; + char *filename = NULL; + struct stat st; + unsigned char *buf; + int invalid = 0; + + if (!bufret || !buflenret) + return -1; + + if (modname) { + if (!(filename = owl_malloc(strlen(priv->aimbinarypath)+1+strlen(modname)+4+1))) { + /* perror("memrequest: malloc"); */ + return -1; + } + sprintf(filename, "%s/%s.ocm", priv->aimbinarypath, modname); + } else { + if (!(filename = owl_malloc(strlen(priv->aimbinarypath)+1+strlen(defaultmod)+1))) { + /* perror("memrequest: malloc"); */ + return -1; + } + sprintf(filename, "%s/%s", priv->aimbinarypath, defaultmod); + } + + if (stat(filename, &st) == -1) { + if (!modname) { + /* perror("memrequest: stat"); */ + owl_free(filename); + return -1; + } + invalid = 1; + } + + if (!invalid) { + if ((offset > st.st_size) || (len > st.st_size)) + invalid = 1; + else if ((st.st_size - offset) < len) + len = st.st_size - offset; + else if ((st.st_size - len) < len) + len = st.st_size - len; + } + + if (!invalid && len) { + len %= 4096; + } + + if (invalid) { + int i; + + owl_free(filename); /* not needed */ + owl_function_error("getaimdata memrequest: recieved invalid request for 0x%08lx bytes at 0x%08lx (file %s)\n", len, offset, modname); + i = 8; + if (modname) { + i+=strlen(modname); + } + + if (!(buf = owl_malloc(i))) { + return -1; + } + + i=0; + + if (modname) { + memcpy(buf, modname, strlen(modname)); + i+=strlen(modname); + } + + /* Damn endianness. This must be little (LSB first) endian. */ + buf[i++] = offset & 0xff; + buf[i++] = (offset >> 8) & 0xff; + buf[i++] = (offset >> 16) & 0xff; + buf[i++] = (offset >> 24) & 0xff; + buf[i++] = len & 0xff; + buf[i++] = (len >> 8) & 0xff; + buf[i++] = (len >> 16) & 0xff; + buf[i++] = (len >> 24) & 0xff; + + *bufret = buf; + *buflenret = i; + } else { + if (!(buf = owl_malloc(len))) { + owl_free(filename); + return -1; + } + /* printf("memrequest: loading %ld bytes from 0x%08lx in \"%s\"...\n", len, offset, filename); */ + if (!(f = fopen(filename, "r"))) { + /* perror("memrequest: fopen"); */ + owl_free(filename); + owl_free(buf); + return -1; + } + + owl_free(filename); + + if (fseek(f, offset, SEEK_SET) == -1) { + /* perror("memrequest: fseek"); */ + fclose(f); + owl_free(buf); + return -1; + } + + if (fread(buf, len, 1, f) != 1) { + /* perror("memrequest: fread"); */ + fclose(f); + owl_free(buf); + return -1; + } + + fclose(f); + *bufret = buf; + *buflenret = len; + } + return 0; /* success! */ +} + +/* + * This will get an offset and a length. The client should read this + * data out of whatever AIM.EXE binary the user has provided (hopefully + * it matches the client information thats sent at login) and pass a + * buffer back to libfaim so it can hash the data and send it to AOL for + * inspection by the client police. + */ +static int faimtest_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) +{ + struct owlfaim_priv *priv = (struct owlfaim_priv *)sess->aux_data; + va_list ap; + fu32_t offset, len; + char *modname; + unsigned char *buf; + int buflen; + + va_start(ap, fr); + offset = va_arg(ap, fu32_t); + len = va_arg(ap, fu32_t); + modname = va_arg(ap, char *); + va_end(ap); + + if (priv->aimbinarypath && (getaimdata(sess, &buf, &buflen, offset, len, modname) == 0)) { + aim_sendmemblock(sess, fr->conn, offset, buflen, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST); + owl_free(buf); + } else { + owl_function_debugmsg("faimtest_memrequest: unable to use AIM binary (\"%s/%s\"), sending defaults...\n", priv->aimbinarypath, modname); + aim_sendmemblock(sess, fr->conn, offset, len, NULL, AIM_SENDMEMBLOCK_FLAG_ISREQUEST); + } + return 1; +} + +/* +static void printuserflags(fu16_t flags) +{ + if (flags & AIM_FLAG_UNCONFIRMED) printf("UNCONFIRMED "); + if (flags & AIM_FLAG_ADMINISTRATOR) printf("ADMINISTRATOR "); + if (flags & AIM_FLAG_AOL) printf("AOL "); + if (flags & AIM_FLAG_OSCAR_PAY) printf("OSCAR_PAY "); + if (flags & AIM_FLAG_FREE) printf("FREE "); + if (flags & AIM_FLAG_AWAY) printf("AWAY "); + if (flags & AIM_FLAG_ICQ) printf("ICQ "); + if (flags & AIM_FLAG_WIRELESS) printf("WIRELESS "); + if (flags & AIM_FLAG_ACTIVEBUDDY) printf("ACTIVEBUDDY "); + + return; +} +*/ + +static int faimtest_parse_userinfo(aim_session_t *sess, aim_frame_t *fr, ...) +{ + aim_userinfo_t *userinfo; + char *prof_encoding = NULL; + char *prof = NULL; + fu16_t inforeq = 0; + owl_buddy *b; + va_list ap; + va_start(ap, fr); + userinfo = va_arg(ap, aim_userinfo_t *); + inforeq = (fu16_t)va_arg(ap, unsigned int); + prof_encoding = va_arg(ap, char *); + prof = va_arg(ap, char *); + va_end(ap); + + /* right now the only reason we call this is for idle times */ + owl_function_debugmsg("parse_userinfo sn: %s idle: %i", userinfo->sn, userinfo->idletime); + b=owl_buddylist_get_aim_buddy(owl_global_get_buddylist(&g), + userinfo->sn); + if (!b) return(1); + owl_buddy_set_idle_since(b, userinfo->idletime); + return(1); + + /* + printf("userinfo: sn: %s\n", userinfo->sn); + printf("userinfo: warnlevel: %f\n", aim_userinfo_warnlevel(userinfo)); + printf("userinfo: flags: 0x%04x = ", userinfo->flags); + printuserflags(userinfo->flags); + printf("\n"); + */ + + /* + printf("userinfo: membersince: %lu\n", userinfo->membersince); + printf("userinfo: onlinesince: %lu\n", userinfo->onlinesince); + printf("userinfo: idletime: 0x%04x\n", userinfo->idletime); + printf("userinfo: capabilities = %s = 0x%08lx\n", (userinfo->present & AIM_USERINFO_PRESENT_CAPABILITIES) ? "present" : "not present", userinfo->capabilities); + */ + + /* + if (inforeq == AIM_GETINFO_GENERALINFO) { + owl_function_debugmsg("userinfo: profile_encoding: %s\n", prof_encoding ? prof_encoding : "[none]"); + owl_function_debugmsg("userinfo: prof: %s\n", prof ? prof : "[none]"); + } else if (inforeq == AIM_GETINFO_AWAYMESSAGE) { + owl_function_debugmsg("userinfo: awaymsg_encoding: %s\n", prof_encoding ? prof_encoding : "[none]"); + owl_function_debugmsg("userinfo: awaymsg: %s\n", prof ? prof : "[none]"); + } else if (inforeq == AIM_GETINFO_CAPABILITIES) { + owl_function_debugmsg("userinfo: capabilities: see above\n"); + } else { + owl_function_debugmsg("userinfo: unknown info request\n"); + } + */ + return(1); +} + +#if 0 +static int faimtest_handlecmd(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, const char *tmpstr) +{ + struct owlfaim_priv *priv = (struct owlfaim_priv *)sess->aux_data; + + if (!strncmp(tmpstr, "disconnect", 10)) { + logout(sess); + } else if (strstr(tmpstr, "goodday")) { + /* aim_send_im(sess, userinfo->sn, AIM_IMFLAGS_ACK, "Good day to you too."); */ + } else if (strstr(tmpstr, "haveicon") && priv->buddyicon) { + struct aim_sendimext_args args; + /* static const char iconmsg[] = {"I have an icon"}; */ + static const char iconmsg[] = {""}; + + args.destsn = userinfo->sn; + args.flags = AIM_IMFLAGS_HASICON; + args.msg = iconmsg; + args.msglen = strlen(iconmsg); + args.iconlen = priv->buddyiconlen; + args.iconstamp = priv->buddyiconstamp; + args.iconsum = priv->buddyiconsum; + + /* aim_send_im_ext(sess, &args); */ + + } else if (strstr(tmpstr, "sendbin")) { + struct aim_sendimext_args args; + static const unsigned char data[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + }; + + /* + * I put this here as a demonstration of how to send + * arbitrary binary data via OSCAR ICBM's without the need + * for escape or baseN encoding of any sort. + * + * Apparently if you set the charset to something WinAIM + * doesn't recognize, it will completly ignore the message. + * That is, it will not display anything in the conversation + * window for the user that recieved it. + * + * HOWEVER, if they do not have a conversation window open + * for you, a new one will be created, but it will not have + * any messages in it. Therefore sending these things could + * be a great way to seemingly subliminally convince people + * to talk to you... + * + */ + args.destsn = userinfo->sn; + /* args.flags = AIM_IMFLAGS_CUSTOMCHARSET; */ + args.charset = args.charsubset = 0x4242; + args.msg = data; + args.msglen = sizeof(data); + /* aim_send_im_ext(sess, &args); */ + } else if (strstr(tmpstr, "sendmulti")) { + struct aim_sendimext_args args; + aim_mpmsg_t mpm; + static const fu16_t unidata[] = { /* "UNICODE." */ + 0x0055, 0x004e, 0x0049, 0x0043, + 0x004f, 0x0044, 0x0045, 0x002e, + }; + static const int unidatalen = 8; + + /* + * This is how multipart messages should be sent. + * + * This should render as: + * "Part 1, ASCII. UNICODE.Part 3, ASCII. " + */ + + aim_mpmsg_init(sess, &mpm); + aim_mpmsg_addascii(sess, &mpm, "Part 1, ASCII. "); + aim_mpmsg_addunicode(sess, &mpm, unidata, unidatalen); + aim_mpmsg_addascii(sess, &mpm, "Part 3, ASCII. "); + + args.destsn = userinfo->sn; + args.flags = AIM_IMFLAGS_MULTIPART; + args.mpmsg = &mpm; + + /* aim_send_im_ext(sess, &args); */ + + aim_mpmsg_free(sess, &mpm); + + } else if (strstr(tmpstr, "sendprebin")) { + static const unsigned char data[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + }; + struct aim_sendimext_args args; + aim_mpmsg_t mpm; + + /* + * This demonstrates sending a human-readable preamble, + * and then arbitrary binary data. + * + * This means that you can very inconspicuously send binary + * attachments to other users. In WinAIM, this appears as + * though it only had the ASCII portion. + * + */ + + aim_mpmsg_init(sess, &mpm); + aim_mpmsg_addascii(sess, &mpm, "This message has binary data."); + aim_mpmsg_addraw(sess, &mpm, 0x4242, 0x4242, data, sizeof(data)); + + args.destsn = userinfo->sn; + args.flags = AIM_IMFLAGS_MULTIPART; + args.mpmsg = &mpm; + + /* aim_send_im_ext(sess, &args); */ + aim_mpmsg_free(sess, &mpm); + + } else if (strstr(tmpstr, "havefeat")) { + struct aim_sendimext_args args; + static const char featmsg[] = {"I have nifty features."}; + fu8_t features[] = {0x01, 0x01, 0x01, 0x02, 0x42, 0x43, 0x44, 0x45}; + + args.destsn = userinfo->sn; + args.flags = AIM_IMFLAGS_CUSTOMFEATURES; + args.msg = featmsg; + args.msglen = strlen(featmsg); + args.features = features; + args.featureslen = sizeof(features); + + /* aim_send_im_ext(sess, &args); */ + } else if (strstr(tmpstr, "sendicon") && priv->buddyicon) { + /* aim_send_icon(sess, userinfo->sn, priv->buddyicon, priv->buddyiconlen, priv->buddyiconstamp, priv->buddyiconsum); */ + } else if (strstr(tmpstr, "warnme")) { + /* printf("icbm: sending non-anon warning\n"); */ + /* aim_send_warning(sess, conn, userinfo->sn, 0); */ + } else if (strstr(tmpstr, "anonwarn")) { + /* printf("icbm: sending anon warning\n"); */ + /* aim_send_warning(sess, conn, userinfo->sn, AIM_WARN_ANON); */ + } else if (strstr(tmpstr, "setdirectoryinfo")) { + /* printf("icbm: sending backwards profile data\n"); */ + aim_setdirectoryinfo(sess, conn, "tsrif", "elddim", "tsal", "nediam", "emankcin", "teerts", "ytic", "etats", "piz", 0, 1); + } else if (strstr(tmpstr, "setinterests")) { + /* printf("icbm: setting fun interests\n"); */ + aim_setuserinterests(sess, conn, "interest1", "interest2", "interest3", "interest4", "interest5", 1); + } else if (!strncmp(tmpstr, "open chatnav", 12)) { + aim_reqservice(sess, conn, AIM_CONN_TYPE_CHATNAV); + } else if (!strncmp(tmpstr, "create", 6)) { + aim_chatnav_createroom(sess,aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV), (strlen(tmpstr) < 7)?"WorldDomination":tmpstr+7, 0x0004); + } else if (!strncmp(tmpstr, "close chatnav", 13)) { + aim_conn_t *chatnavconn; + if ((chatnavconn = aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV))) + aim_conn_kill(sess, &chatnavconn); + } else if (!strncmp(tmpstr, "join", 4)) { + aim_chat_join(sess, conn, 0x0004, "worlddomination", 0x0000); + } else if (!strncmp(tmpstr, "leave", 5)) { + aim_chat_leaveroom(sess, "worlddomination"); + } else if (!strncmp(tmpstr, "getinfo", 7)) { + aim_getinfo(sess, conn, "midendian", AIM_GETINFO_GENERALINFO); + aim_getinfo(sess, conn, "midendian", AIM_GETINFO_AWAYMESSAGE); + aim_getinfo(sess, conn, "midendian", AIM_GETINFO_CAPABILITIES); + } else if(strstr(tmpstr, "lookup")) { + /* aim_usersearch_address(sess, conn, "mid@auk.cx"); */ + } else if (!strncmp(tmpstr, "reqsendmsg", 10)) { + /* aim_send_im(sess, priv->ohcaptainmycaptain, 0, "sendmsg 7900"); */ + } else if (!strncmp(tmpstr, "reqadmin", 8)) { + aim_reqservice(sess, conn, AIM_CONN_TYPE_AUTH); + } else if (!strncmp(tmpstr, "changenick", 10)) { + aim_admin_setnick(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "diputs8 1"); + } else if (!strncmp(tmpstr, "reqconfirm", 10)) { + aim_admin_reqconfirm(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH)); + } else if (!strncmp(tmpstr, "reqemail", 8)) { + aim_admin_getinfo(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), 0x0011); + } else if (!strncmp(tmpstr, "changepass", 8)) { + aim_admin_changepasswd(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWPASSWORD", "OLDPASSWORD"); + } else if (!strncmp(tmpstr, "setemail", 8)) { + aim_admin_setemail(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWEMAILADDRESS"); + } else if (!strncmp(tmpstr, "sendmsg", 7)) { + int i; + + i = atoi(tmpstr+8); + if (i < 10000) { + char *newbuf; + int z; + + newbuf = owl_malloc(i+1); + for (z = 0; z < i; z++) + newbuf[z] = (z % 10)+0x30; + newbuf[i] = '\0'; + /* aim_send_im(sess, userinfo->sn, AIM_IMFLAGS_ACK | AIM_IMFLAGS_AWAY, newbuf); */ + owl_free(newbuf); + } + } else if (strstr(tmpstr, "seticqstatus")) { + aim_setextstatus(sess, AIM_ICQ_STATE_DND); + } else if (strstr(tmpstr, "rtfmsg")) { + static const char rtfmsg[] = {"{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fswiss\\fcharset0 Arial;}{\\f1\\froman\\fprq2\\fcharset0 Georgia;}{\\f2\\fmodern\\fprq1\\fcharset0 MS Mincho;}{\\f3\\froman\\fprq2\\fcharset2 Symbol;}}\\viewkind4\\uc1\\pard\\f0\\fs20 Test\\f1 test\\f2\fs44 test\\f3\\fs20 test\\f0\\par}"}; + struct aim_sendrtfmsg_args rtfargs; + + memset(&rtfargs, 0, sizeof(rtfargs)); + rtfargs.destsn = userinfo->sn; + rtfargs.fgcolor = 0xffffffff; + rtfargs.bgcolor = 0x00000000; + rtfargs.rtfmsg = rtfmsg; + /* aim_send_rtfmsg(sess, &rtfargs); */ + } else { + /* printf("unknown command.\n"); */ + aim_add_buddy(sess, conn, userinfo->sn); + } + + return 0; +} +#endif + +/* + * Channel 1: Standard Message + */ +static int faimtest_parse_incoming_im_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) +{ + struct owlfaim_priv *priv = (struct owlfaim_priv *)sess->aux_data; + owl_message *m; + char *stripmsg, *nz_screenname, *wrapmsg; + char realmsg[8192+1] = {""}; + /* int clienttype = AIM_CLIENTTYPE_UNKNOWN; */ + + /* clienttype = aim_fingerprintclient(args->features, args->featureslen); */ + + /* + printf("icbm: sn = \"%s\"\n", userinfo->sn); + printf("icbm: probable client type: %d\n", clienttype); + printf("icbm: warnlevel = %f\n", aim_userinfo_warnlevel(userinfo)); + printf("icbm: flags = 0x%04x = ", userinfo->flags); + printuserflags(userinfo->flags); + printf("\n"); + */ + + /* + printf("icbm: membersince = %lu\n", userinfo->membersince); + printf("icbm: onlinesince = %lu\n", userinfo->onlinesince); + printf("icbm: idletime = 0x%04x\n", userinfo->idletime); + printf("icbm: capabilities = %s = 0x%08lx\n", (userinfo->present & AIM_USERINFO_PRESENT_CAPABILITIES) ? "present" : "not present", userinfo->capabilities); + */ + + /* + printf("icbm: icbmflags = "); + if (args->icbmflags & AIM_IMFLAGS_AWAY) printf("away "); + if (args->icbmflags & AIM_IMFLAGS_ACK) printf("ackrequest "); + if (args->icbmflags & AIM_IMFLAGS_OFFLINE) printf("offline "); + if (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) printf("buddyreq "); + if (args->icbmflags & AIM_IMFLAGS_HASICON) printf("hasicon "); + printf("\n"); + */ + + /* + if (args->icbmflags & AIM_IMFLAGS_CUSTOMCHARSET) { + printf("icbm: encoding flags = {%04x, %04x}\n", args->charset, args->charsubset); + } + */ + + /* + * Quickly convert it to eight bit format, replacing non-ASCII UNICODE + * characters with their equivelent HTML entity. + */ + if (args->icbmflags & AIM_IMFLAGS_UNICODE) { + int i; + + for (i=0; i<args->msglen; i+=2) { + fu16_t uni; + + uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff); + if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */ + snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "%c", uni); + } else { /* something else, do UNICODE entity */ + snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "&#%04x;", uni); + } + } + } else { + /* + * For non-UNICODE encodings (ASCII and ISO 8859-1), there is + * no need to do anything special here. Most + * terminals/whatever will be able to display such characters + * unmodified. + * + * Beware that PC-ASCII 128 through 159 are _not_ actually + * defined in ASCII or ISO 8859-1, and you should send them as + * UNICODE. WinAIM will send these characters in a UNICODE + * message, so you need to do so as well. + * + * You may not think it necessary to handle UNICODE messages. + * You're probably wrong. For one thing, Microsoft "Smart + * Quotes" will be sent by WinAIM as UNICODE (not HTML UNICODE, + * but real UNICODE). If you don't parse UNICODE at all, your + * users will get a blank message instead of the message + * containing Smart Quotes. + * + */ + if (!args->msg) { + if (args->mpmsg.parts->data) { + strncpy(realmsg,args->mpmsg.parts->data, sizeof(realmsg)); + } else { + strncpy(realmsg,"",1); + } + } else { + strncpy(realmsg, args->msg, sizeof(realmsg)); + } + } + + owl_function_debugmsg("faimtest_parse_incoming_im_chan1: message from: %s", userinfo->sn?userinfo->sn:""); + /* create a message, and put it on the message queue */ + stripmsg=owl_text_htmlstrip(realmsg); + wrapmsg=owl_text_wordwrap(stripmsg, 70); + nz_screenname=owl_aim_normalize_screenname(userinfo->sn); + m=owl_malloc(sizeof(owl_message)); + owl_message_create_aim(m, + nz_screenname, + owl_global_get_aim_screenname(&g), + wrapmsg, + OWL_MESSAGE_DIRECTION_IN, + 0); + if (args->icbmflags & AIM_IMFLAGS_AWAY) owl_message_set_attribute(m, "isauto", ""); + owl_global_messagequeue_addmsg(&g, m); + owl_free(stripmsg); + owl_free(wrapmsg); + owl_free(nz_screenname); + + return(1); + + owl_function_debugmsg("faimtest_parse_incoming_im_chan1: icbm: message: %s\n", realmsg); + + if (args->icbmflags & AIM_IMFLAGS_MULTIPART) { + aim_mpmsg_section_t *sec; + int z; + + owl_function_debugmsg("faimtest_parse_incoming_im_chan1: icbm: multipart: this message has %d parts\n", args->mpmsg.numparts); + + for (sec = args->mpmsg.parts, z = 0; sec; sec = sec->next, z++) { + if ((sec->charset == 0x0000) || (sec->charset == 0x0003) || (sec->charset == 0xffff)) { + owl_function_debugmsg("faimtest_parse_incoming_im_chan1: icbm: multipart: part %d: charset 0x%04x, subset 0x%04x, msg = %s\n", z, sec->charset, sec->charsubset, sec->data); + } else { + owl_function_debugmsg("faimtest_parse_incoming_im_chan1: icbm: multipart: part %d: charset 0x%04x, subset 0x%04x, binary or UNICODE data\n", z, sec->charset, sec->charsubset); + } + } + } + + if (args->icbmflags & AIM_IMFLAGS_HASICON) { + /* aim_send_im(sess, userinfo->sn, AIM_IMFLAGS_BUDDYREQ, "You have an icon"); */ + owl_function_debugmsg("faimtest_parse_incoming_im_chan1: icbm: their icon: iconstamp = %ld, iconlen = 0x%08lx, iconsum = 0x%04x\n", args->iconstamp, args->iconlen, args->iconsum); + } + + /* + if (realmsg) { + int i = 0; + while (realmsg[i] == '<') { + if (realmsg[i] == '<') { + while (realmsg[i] != '>') + i++; + i++; + } + } + tmpstr = realmsg+i; + faimtest_handlecmd(sess, conn, userinfo, tmpstr); + } + */ + + if (priv->buddyicon && (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)) { + /* aim_send_icon(sess, userinfo->sn, priv->buddyicon, priv->buddyiconlen, priv->buddyiconstamp, priv->buddyiconsum); */ + } + + return(1); +} + +/* + * Channel 2: Rendevous Request + */ +static int faimtest_parse_incoming_im_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) +{ + /* + printf("rendezvous: source sn = %s\n", userinfo->sn); + printf("rendezvous: \twarnlevel = %f\n", aim_userinfo_warnlevel(userinfo)); + printf("rendezvous: \tclass = 0x%04x = ", userinfo->flags); + printuserflags(userinfo->flags); + printf("\n"); + + printf("rendezvous: \tonlinesince = %lu\n", userinfo->onlinesince); + printf("rendezvous: \tidletime = 0x%04x\n", userinfo->idletime); + + printf("rendezvous: message/description = %s\n", args->msg); + printf("rendezvous: encoding = %s\n", args->encoding); + printf("rendezvous: language = %s\n", args->language); + */ + + if (args->reqclass == AIM_CAPS_SENDFILE) { + owl_function_debugmsg("faimtest_parse_incoming_im_chan2: send file!"); + } else if (args->reqclass == AIM_CAPS_CHAT) { + owl_function_debugmsg("faimtest_parse_incoming_im_chan2: chat invite: %s, %i, %i", args->info.chat.roominfo.name, args->info.chat.roominfo.exchange, args->info.chat.roominfo.instance); + /* + printf("chat invitation: room name = %s\n", args->info.chat.roominfo.name); + printf("chat invitation: exchange = 0x%04x\n", args->info.chat.roominfo.exchange); + printf("chat invitation: instance = 0x%04x\n", args->info.chat.roominfo.instance); + */ + /* Automatically join room... */ + /* printf("chat invitiation: autojoining %s...\n", args->info.chat.roominfo.name); */ + + /* aim_chat_join(sess, conn, args->info.chat.roominfo.exchange, args->info.chat.roominfo.name, args->info.chat.roominfo.instance); */ + } else if (args->reqclass == AIM_CAPS_BUDDYICON) { + owl_function_debugmsg("faimtest_parse_incoming_im_chan2: Buddy Icon from %s, length = %lu\n", + userinfo->sn, args->info.icon.length); + } else if (args->reqclass == AIM_CAPS_ICQRTF) { + owl_function_debugmsg("faimtest_parse_incoming_im_chan2: RTF message from %s: (fgcolor = 0x%08lx, bgcolor = 0x%08lx) %s\n", + userinfo->sn, args->info.rtfmsg.fgcolor, args->info.rtfmsg.bgcolor, args->info.rtfmsg.rtfmsg); + } else { + owl_function_debugmsg("faimtest_parse_incoming_im_chan2: icbm: unknown reqclass (%d)\n", args->reqclass); + } + return 1; +} + +static int faimtest_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) +{ + fu16_t channel; + aim_userinfo_t *userinfo; + va_list ap; + int ret = 0; + + va_start(ap, fr); + channel = (fu16_t)va_arg(ap, unsigned int); + userinfo = va_arg(ap, aim_userinfo_t *); + + if (channel == 1) { + struct aim_incomingim_ch1_args *args; + args = va_arg(ap, struct aim_incomingim_ch1_args *); + ret = faimtest_parse_incoming_im_chan1(sess, fr->conn, userinfo, args); + } else if (channel == 2) { + struct aim_incomingim_ch2_args *args; + args = va_arg(ap, struct aim_incomingim_ch2_args *); + ret = faimtest_parse_incoming_im_chan2(sess, fr->conn, userinfo, args); + } else { + owl_function_debugmsg("faimtest_parse_incoming_im: unsupported channel 0x%04x\n", channel); + } + va_end(ap); + owl_function_debugmsg("faimtest_parse_incoming_im: done with ICBM handling (ret = %d)\n", ret); + return 1; +} + +static int faimtest_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) +{ + aim_userinfo_t *userinfo; + char *nz_screenname; + owl_buddy *b; + owl_buddylist *bl; + va_list ap; + va_start(ap, fr); + userinfo = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + nz_screenname=owl_aim_normalize_screenname(userinfo->sn); + bl=owl_global_get_buddylist(&g); + + owl_buddylist_oncoming(owl_global_get_buddylist(&g), nz_screenname); + + if (userinfo->present & AIM_USERINFO_PRESENT_IDLE) { + owl_function_debugmsg("faimtest_parseoncoming: in empty part of userinfo present and present idle"); + } + + b=owl_buddylist_get_aim_buddy(owl_global_get_buddylist(&g), nz_screenname); + if (!b) { + owl_function_debugmsg("Error: parse_oncoming setting idle time with no buddy present."); + return(1); + } + if (userinfo->idletime==0) { + owl_buddy_set_unidle(b); + } else { + owl_buddy_set_idle(b); + owl_buddy_set_idle_since(b, userinfo->idletime); + } + + if (userinfo->flags & AIM_FLAG_AWAY) { + owl_function_debugmsg("parse_oncoming sn: %s away flag!", userinfo->sn); + } + + owl_function_debugmsg("parse_oncoming sn: %s idle: %i", userinfo->sn, userinfo->idletime); + + owl_free(nz_screenname); + + /* + printf("%ld %s is now online (flags: %04x = %s%s%s%s%s%s%s%s) (caps = %s = 0x%08lx)\n", + time(NULL), + userinfo->sn, userinfo->flags, + (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"", + (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"", + (userinfo->flags&AIM_FLAG_AOL)?" AOL":"", + (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"", + (userinfo->flags&AIM_FLAG_FREE)?" FREE":"", + (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"", + (userinfo->flags&AIM_FLAG_ICQ)?" ICQ":"", + (userinfo->flags&AIM_FLAG_WIRELESS)?" WIRELESS":"", + (userinfo->present & AIM_USERINFO_PRESENT_CAPABILITIES) ? "present" : "not present", + userinfo->capabilities); + */ + return(1); +} + +static int faimtest_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) +{ + aim_userinfo_t *userinfo; + char *nz_screenname; + va_list ap; + + va_start(ap, fr); + userinfo = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + nz_screenname=owl_aim_normalize_screenname(userinfo->sn); + owl_buddylist_offgoing(owl_global_get_buddylist(&g), nz_screenname); + owl_free(nz_screenname); + + if (userinfo->present & AIM_USERINFO_PRESENT_IDLE) { + owl_function_debugmsg("parse_offgoing sn: %s idle time %i", userinfo->sn, userinfo->idletime); + } + + /* + printf("%ld %s is now offline (flags: %04x = %s%s%s%s%s%s%s%s) (caps = %s = 0x%08lx)\n", + time(NULL), + userinfo->sn, userinfo->flags, + (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"", + (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"", + (userinfo->flags&AIM_FLAG_AOL)?" AOL":"", + (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"", + (userinfo->flags&AIM_FLAG_FREE)?" FREE":"", + (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"", + (userinfo->flags&AIM_FLAG_ICQ)?" ICQ":"", + (userinfo->flags&AIM_FLAG_WIRELESS)?" WIRELESS":"", + (userinfo->present & AIM_USERINFO_PRESENT_CAPABILITIES) ? "present" : "not present", + userinfo->capabilities); + */ + + return 1; +} + +/* Used by chat as well. */ +int faimtest_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + fu16_t reason; + + va_start(ap, fr); + reason = (fu16_t)va_arg(ap, unsigned int); + va_end(ap); + + /* printf("snac threw error (reason 0x%04x: %s)\n", reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown"); */ + if (reason<msgerrreasonslen) owl_function_error("%s", msgerrreasons[reason]); + + return 1; +} + +static int faimtest_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + char *destsn; + fu16_t reason; + + va_start(ap, fr); + reason = (fu16_t)va_arg(ap, unsigned int); + destsn = va_arg(ap, char *); + va_end(ap); + + /* printf("message to %s bounced (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown"); */ + if (reason<msgerrreasonslen) owl_function_error("%s", msgerrreasons[reason]); + + if (reason==4) { + owl_function_adminmsg("", "Could not send AIM message, user not logged on"); + } + + return 1; +} + +static int faimtest_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + char *destsn; + fu16_t reason; + + va_start(ap, fr); + reason = (fu16_t)va_arg(ap, unsigned int); + destsn = va_arg(ap, char *); + va_end(ap); + + /* printf("user information for %s unavailable (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown"); */ + if (reason<msgerrreasonslen) owl_function_error("%s", msgerrreasons[reason]); + + return 1; +} + +static int faimtest_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) +{ + static char *missedreasons[] = { + "Invalid (0)", + "Message too large", + "Rate exceeded", + "Evil Sender", + "Evil Receiver" + }; + static int missedreasonslen = 5; + + va_list ap; + fu16_t chan, nummissed, reason; + aim_userinfo_t *userinfo; + + va_start(ap, fr); + chan = (fu16_t)va_arg(ap, unsigned int); + userinfo = va_arg(ap, aim_userinfo_t *); + nummissed = (fu16_t)va_arg(ap, unsigned int); + reason = (fu16_t)va_arg(ap, unsigned int); + va_end(ap); + + owl_function_debugmsg("faimtest_parse_misses: missed %d messages from %s on channel %d (reason %d: %s)\n", nummissed, userinfo->sn, chan, reason, (reason<missedreasonslen)?missedreasons[reason]:"unknown"); + + return 1; +} + +/* + * Received in response to an IM sent with the AIM_IMFLAGS_ACK option. + */ +static int faimtest_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + fu16_t type; + char *sn = NULL; + + va_start(ap, fr); + type = (fu16_t)va_arg(ap, unsigned int); + sn = va_arg(ap, char *); + va_end(ap); + + owl_function_debugmsg("faimtest_parse_msgack: 0x%04x / %s\n", type, sn); + + return 1; +} + +static int faimtest_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) +{ + static char *codes[5] = { + "invalid", + "change", + "warning", + "limit", + "limit cleared" + }; + va_list ap; + fu16_t code, rateclass; + fu32_t windowsize, clear, alert, limit, disconnect; + fu32_t currentavg, maxavg; + + va_start(ap, fr); + + /* See code explanations below */ + code = (fu16_t)va_arg(ap, unsigned int); + + /* + * See comments above aim_parse_ratechange_middle() in aim_rxhandlers.c. + */ + rateclass = (fu16_t)va_arg(ap, unsigned int); + + /* + * Not sure what this is exactly. I think its the temporal + * relation factor (ie, how to make the rest of the numbers + * make sense in the real world). + */ + windowsize = va_arg(ap, fu32_t); + + /* Explained below */ + clear = va_arg(ap, fu32_t); + alert = va_arg(ap, fu32_t); + limit = va_arg(ap, fu32_t); + disconnect = va_arg(ap, fu32_t); + currentavg = va_arg(ap, fu32_t); + maxavg = va_arg(ap, fu32_t); + + va_end(ap); + + owl_function_debugmsg("faimtest_parse_ratechange: rate %s (rate class 0x%04x): curavg = %ld, maxavg = %ld, alert at %ld, clear warning at %ld, limit at %ld, disconnect at %ld (window size = %ld)", + (code < 5)?codes[code]:"invalid", + rateclass, + currentavg, maxavg, + alert, clear, + limit, disconnect, + windowsize); + return 1; +} + +static int faimtest_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + fu16_t newevil; + aim_userinfo_t *userinfo; + + va_start(ap, fr); + newevil = (fu16_t)va_arg(ap, unsigned int); + userinfo = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + /* + * Evil Notifications that are lacking userinfo->sn are anon-warns + * if they are an evil increases, but are not warnings at all if its + * a decrease (its the natural backoff happening). + * + * newevil is passed as an int representing the new evil value times + * ten. + */ + owl_function_debugmsg("faimtest_parse_evilnotify: new value = %2.1f%% (caused by %s)\n", ((float)newevil)/10, (userinfo && strlen(userinfo->sn))?userinfo->sn:"anonymous"); + + return 1; +} + +static int faimtest_parse_searchreply(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + char *address, *SNs; + int num, i; + owl_list list; + + va_start(ap, fr); + address = va_arg(ap, char *); + num = va_arg(ap, int); + SNs = va_arg(ap, char *); + va_end(ap); + + owl_list_create(&list); + + owl_function_debugmsg("faimtest_parse_searchreply: E-Mail Search Results for %s: ", address); + for (i=0; i<num; i++) { + owl_function_debugmsg(" %s", &SNs[i*(MAXSNLEN+1)]); + owl_list_append_element(&list, &SNs[i*(MAXSNLEN+1)]); + } + owl_function_aimsearch_results(address, &list); + owl_list_free_simple(&list); + return(1); +} + +static int faimtest_parse_searcherror(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + char *address; + + va_start(ap, fr); + address = va_arg(ap, char *); + va_end(ap); + + owl_function_error("No results searching for %s", address); + owl_function_debugmsg("faimtest_parse_searcherror: E-Mail Search Results for %s: No Results or Invalid Email\n", address); + + return(1); +} + +static int handlepopup(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + char *msg, *url; + fu16_t width, height, delay; + + va_start(ap, fr); + msg = va_arg(ap, char *); + url = va_arg(ap, char *); + width = va_arg(ap, unsigned int); + height = va_arg(ap, unsigned int); + delay = va_arg(ap, unsigned int); + va_end(ap); + + owl_function_debugmsg("handlepopup: (%dx%x:%d) %s (%s)\n", width, height, delay, msg, url); + + return 1; +} + +static int serverpause(aim_session_t *sess, aim_frame_t *fr, ...) +{ + aim_sendpauseack(sess, fr->conn); + return 1; +} + +static int migrate(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + aim_conn_t *bosconn; + char *bosip; + fu8_t *cookie; + + va_start(ap, fr); + bosip = va_arg(ap, char *); + cookie = va_arg(ap, fu8_t *); + va_end(ap); + + owl_function_debugmsg("migrate: migration in progress -- new BOS is %s -- disconnecting", bosip); + aim_conn_kill(sess, &fr->conn); + + if (!(bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, bosip))) { + owl_function_debugmsg("migrate: could not connect to BOS: internal error"); + return 1; + } else if (bosconn->status & AIM_CONN_STATUS_CONNERR) { + owl_function_debugmsg("migrate: could not connect to BOS"); + aim_conn_kill(sess, &bosconn); + return 1; + } + + /* Login will happen all over again. */ + addcb_bos(sess, bosconn); + /* aim_sendcookie(sess, bosconn, cookie); */ /********/ + return 1; +} + +static int ssirights(aim_session_t *sess, aim_frame_t *fr, ...) +{ + owl_function_debugmsg("ssirights: got SSI rights, requesting data\n"); + /* aim_ssi_reqdata(sess, fr->conn, 0, 0x0000); */ + aim_ssi_reqdata(sess); + + return(1); +} + +static int ssidata(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + fu8_t fmtver; + fu16_t itemcount; + fu32_t stamp; + struct aim_ssi_item *list; + /* + struct aim_ssi_item *curitem; + struct aim_ssi_item *l; + */ + + va_start(ap, fr); + fmtver = va_arg(ap, unsigned int); + itemcount = va_arg(ap, unsigned int); + stamp = va_arg(ap, fu32_t); + list = va_arg(ap, struct aim_ssi_item *); + va_end(ap); + + owl_function_debugmsg("ssiddata: got SSI data (0x%02x, %d items, %ld)", fmtver, itemcount, stamp); + /* + for (curitem=sess->ssi.local; curitem; curitem=curitem->next) { + for (l = list; l; l = l->next) { + owl_function_debugmsg("\t0x%04x (%s) - 0x%04x/0x%04x", l->type, l->name, l->gid, l->bid); + } + } + */ + aim_ssi_enable(sess); + + return 1; +} + +static int ssidatanochange(aim_session_t *sess, aim_frame_t *fr, ...) +{ + owl_function_debugmsg("ssidatanochange: server says we have the latest SSI data already"); + /* aim_ssi_enable(sess, fr->conn); */ + aim_ssi_enable(sess); + return 1; +} + +static int offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + struct aim_icq_offlinemsg *msg; + + va_start(ap, fr); + msg = va_arg(ap, struct aim_icq_offlinemsg *); + va_end(ap); + + if (msg->type == 0x0001) { + owl_function_debugmsg("offlinemsg: from %ld at %d/%d/%d %02d:%02d : %s", msg->sender, msg->year, msg->month, msg->day, msg->hour, msg->minute, msg->msg); + } else { + owl_function_debugmsg("unknown offline message type 0x%04x", msg->type); + } + return 1; +} + +static int offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...) +{ + /* Tell the server to delete them. */ + owl_function_debugmsg("offlinemsg done: "); + aim_icq_ackofflinemsgs(sess); + return 1; +} + + +/******************** chat.c **************************/ + +static int faimtest_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + aim_userinfo_t *userinfo; + int count; + /* int i; */ + + va_start(ap, fr); + count = va_arg(ap, int); + userinfo = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + owl_function_debugmsg("In faimtest_chat_join"); + /* + printf("chat: %s: New occupants have joined:\n", aim_chat_getname(fr->conn)); + for (i = 0; i < count; i++) + printf("chat: %s: \t%s\n", aim_chat_getname(fr->conn), userinfo[i].sn); + */ + return 1; +} + +static int faimtest_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) +{ + aim_userinfo_t *userinfo; + va_list ap; + int count; + /* int i; */ + + + va_start(ap, fr); + count = va_arg(ap, int); + userinfo = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + /* + printf("chat: %s: Some occupants have left:\n", aim_chat_getname(fr->conn)); + + for (i = 0; i < count; i++) + printf("chat: %s: \t%s\n", aim_chat_getname(fr->conn), userinfo[i].sn); + */ + return 1; +} + +static int faimtest_chat_infoupdate(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + aim_userinfo_t *userinfo; + struct aim_chat_roominfo *roominfo; + char *roomname; + int usercount; + char *roomdesc; + fu16_t flags, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen; + fu32_t creationtime; + const char *croomname; + /* int i; */ + + croomname = aim_chat_getname(fr->conn); + + va_start(ap, fr); + roominfo = va_arg(ap, struct aim_chat_roominfo *); + roomname = va_arg(ap, char *); + usercount = va_arg(ap, int); + userinfo = va_arg(ap, aim_userinfo_t *); + roomdesc = va_arg(ap, char *); + flags = (fu16_t)va_arg(ap, unsigned int); + creationtime = va_arg(ap, fu32_t); + maxmsglen = (fu16_t)va_arg(ap, unsigned int); + unknown_d2 = (fu16_t)va_arg(ap, unsigned int); + unknown_d5 = (fu16_t)va_arg(ap, unsigned int); + maxvisiblemsglen = (fu16_t)va_arg(ap, unsigned int); + va_end(ap); + + owl_function_debugmsg("In faimtest_chat_infoupdate"); + /* + printf("chat: %s: info update:\n", croomname); + printf("chat: %s: \tRoominfo: {%04x, %s, %04x}\n", croomname, roominfo->exchange, roominfo->name, roominfo->instance); + printf("chat: %s: \tRoomname: %s\n", croomname, roomname); + printf("chat: %s: \tRoomdesc: %s\n", croomname, roomdesc); + printf("chat: %s: \tOccupants: (%d)\n", croomname, usercount); + + for (i = 0; i < usercount; i++) + printf("chat: %s: \t\t%s\n", croomname, userinfo[i].sn); + + owl_function_debugmsg("chat: %s: \tRoom flags: 0x%04x (%s%s%s%s)\n", + croomname, flags, + (flags & AIM_CHATROOM_FLAG_EVILABLE) ? "Evilable, " : "", + (flags & AIM_CHATROOM_FLAG_NAV_ONLY) ? "Nav Only, " : "", + (flags & AIM_CHATROOM_FLAG_INSTANCING_ALLOWED) ? "Instancing allowed, " : "", + (flags & AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED) ? "Occupant peek allowed, " : ""); + printf("chat: %s: \tCreation time: %lu (time_t)\n", croomname, creationtime); + printf("chat: %s: \tUnknown_d2: 0x%04x\n", croomname, unknown_d2); + printf("chat: %s: \tUnknown_d5: 0x%02x\n", croomname, unknown_d5); + printf("chat: %s: \tMax message length: %d bytes\n", croomname, maxmsglen); + printf("chat: %s: \tMax visible message length: %d bytes\n", croomname, maxvisiblemsglen); + */ + + return(1); +} + +static int faimtest_chat_incomingmsg(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + aim_userinfo_t *userinfo; + char *msg; + char tmpbuf[1152]; + + va_start(ap, fr); + userinfo = va_arg(ap, aim_userinfo_t *); + msg = va_arg(ap, char *); + va_end(ap); + + owl_function_debugmsg("in faimtest_chat_incomingmsg"); + + /* + printf("chat: %s: incoming msg from %s: %s\n", aim_chat_getname(fr->conn), userinfo->sn, msg); + */ + + /* + * Do an echo for testing purposes. But not for ourselves ("oops!") + */ + if (strcmp(userinfo->sn, sess->sn) != 0) { + /* sprintf(tmpbuf, "(%s said \"%s\")", userinfo->sn, msg); */ + aim_chat_send_im(sess, fr->conn, 0, tmpbuf, strlen(tmpbuf)); + } + + return 1; +} + +static int faimtest_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) +{ + fu16_t type; + va_list ap; + + va_start(ap, fr); + type = (fu16_t)va_arg(ap, unsigned int); + + owl_function_debugmsg("in faimtest_chatnav_info"); + + if (type == 0x0002) { + int maxrooms; + struct aim_chat_exchangeinfo *exchanges; + int exchangecount; + /* int i; */ + + maxrooms = va_arg(ap, int); + exchangecount = va_arg(ap, int); + exchanges = va_arg(ap, struct aim_chat_exchangeinfo *); + va_end(ap); + + /* + printf("chat info: Chat Rights:\n"); + printf("chat info: \tMax Concurrent Rooms: %d\n", maxrooms); + printf("chat info: \tExchange List: (%d total)\n", exchangecount); + for (i = 0; i < exchangecount; i++) { + printf("chat info: \t\t%x: %s (%s/%s) (0x%04x = %s%s%s%s)\n", + exchanges[i].number, + exchanges[i].name, + exchanges[i].charset1, + exchanges[i].lang1, + exchanges[i].flags, + (exchanges[i].flags & AIM_CHATROOM_FLAG_EVILABLE) ? "Evilable, " : "", + (exchanges[i].flags & AIM_CHATROOM_FLAG_NAV_ONLY) ? "Nav Only, " : "", + (exchanges[i].flags & AIM_CHATROOM_FLAG_INSTANCING_ALLOWED) ? "Instancing allowed, " : "", + (exchanges[i].flags & AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED) ? "Occupant peek allowed, " : ""); + } + */ + } else if (type == 0x0008) { + char *fqcn, *name, *ck; + fu16_t instance, flags, maxmsglen, maxoccupancy, unknown, exchange; + fu8_t createperms; + fu32_t createtime; + + fqcn = va_arg(ap, char *); + instance = (fu16_t)va_arg(ap, unsigned int); + exchange = (fu16_t)va_arg(ap, unsigned int); + flags = (fu16_t)va_arg(ap, unsigned int); + createtime = va_arg(ap, fu32_t); + maxmsglen = (fu16_t)va_arg(ap, unsigned int); + maxoccupancy = (fu16_t)va_arg(ap, unsigned int); + createperms = (fu8_t)va_arg(ap, unsigned int); + unknown = (fu16_t)va_arg(ap, unsigned int); + name = va_arg(ap, char *); + ck = va_arg(ap, char *); + va_end(ap); + + /* printf("received room create reply for %s/0x%04x\n", fqcn, exchange); */ + + } else { + va_end(ap); + /* printf("chatnav info: unknown type (%04x)\n", type); */ + } + + return 1; +} + +static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) +{ + owl_function_debugmsg("faimtest_conninitdone_chat:"); + + aim_conn_addhandler(sess, fr->conn, 0x000e, 0x0001, faimtest_parse_genericerr, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, faimtest_chat_join, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, faimtest_chat_leave, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, faimtest_chat_infoupdate, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, faimtest_chat_incomingmsg, 0); + + aim_clientready(sess, fr->conn); + + owl_function_debugmsg("Chat ready"); + + /* + chatcon = find_oscar_chat_by_conn(gc, fr->conn); + chatcon->id = id; + chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show); + */ + return(1); +} + +static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) +{ + owl_function_debugmsg("faimtest_conninitdone_chatnav:"); + + /* aim_conn_addhandler(sess, fr->conn, 0x000d, 0x0001, gaim_parse_genericerr, 0); */ + /* aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0); */ + + aim_clientready(sess, fr->conn); + + aim_chatnav_reqrights(sess, fr->conn); + + return(1); +} + +void chatnav_redirect(aim_session_t *sess, struct aim_redirect_data *redir) +{ + aim_conn_t *tstconn; + + owl_function_debugmsg("in faimtest_chatnav_redirect"); + + tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, redir->ip); + if (!tstconn || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) { + /* printf("unable to connect to chat(nav) server\n"); */ + if (tstconn) + aim_conn_kill(sess, &tstconn); + return; + } + + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0); + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0); + aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie); + /* printf("chatnav: connected\n"); */ + return; +} + +/* XXX this needs instance too */ +void chat_redirect(aim_session_t *sess, struct aim_redirect_data *redir) +{ + aim_conn_t *tstconn; + + owl_function_debugmsg("in chat_redirect"); + + tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, redir->ip); + if (!tstconn || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) { + /* printf("unable to connect to chat server\n"); */ + if (tstconn) aim_conn_kill(sess, &tstconn); + return; + } + /* printf("chat: connected to %s instance %d on exchange %d\n", redir->chat.room, redir->chat.instance, redir->chat.exchange); */ + + /* + * We must do this to attach the stored name to the connection! + */ + aim_chat_attachname(tstconn, redir->chat.exchange, redir->chat.room, redir->chat.instance); + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0); + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0); + aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie); + return; +} + +void owl_process_aim() +{ + if (owl_global_is_doaimevents(&g)) { + owl_aim_process_events(); + } +} + +/**********************************************************************************/ + +#if 0 +static int faimtest_ssi_rerequestdata(aim_session_t *sess, aim_frame_t *fr, ...) +{ + aim_ssi_reqdata(sess); + return(0); +} + +static int faimtest_ssi_parseerr(aim_session_t *sess, aim_frame_t *fr, ...) +{ + /* GaimConnection *gc = sess->aux_data; */ + /* OscarData *od = gc->proto_data; */ + va_list ap; + fu16_t reason; + + va_start(ap, fr); + reason = (fu16_t)va_arg(ap, unsigned int); + va_end(ap); + + owl_function_debugmsg("faimtest_ssi_parseerr: ssi: SNAC error %hu", reason); + + if (reason == 0x0005) { + owl_function_error("faimtest_ssi_parseerr: unable to retrieve buddy list"); + } + + /* Activate SSI */ + /* Sending the enable causes other people to be able to see you, and you to see them */ + /* Make sure your privacy setting/invisibility is set how you want it before this! */ + owl_function_debugmsg("faimtest_ssi_parseerr: ssi: activating server-stored buddy list"); + aim_ssi_enable(sess); + + return(1); +} + +static int faimtest_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) +{ + /* GaimConnection *gc = sess->aux_data; */ + /* OscarData *od = (OscarData *)gc->proto_data; */ + int numtypes, i; + fu16_t *maxitems; + va_list ap; + + va_start(ap, fr); + numtypes = va_arg(ap, int); + maxitems = va_arg(ap, fu16_t *); + va_end(ap); + + owl_function_debugmsg("faimtest_ssi_parserights: "); + for (i=0; i<numtypes; i++) { + owl_function_debugmsg(" max type 0x%04x=%hd,", i, maxitems[i]); + } + + /* + if (numtypes >= 0) od->rights.maxbuddies = maxitems[0]; + if (numtypes >= 1) od->rights.maxgroups = maxitems[1]; + if (numtypes >= 2) od->rights.maxpermits = maxitems[2]; + if (numtypes >= 3) od->rights.maxdenies = maxitems[3]; + */ + + return(1); +} + +static int faimtest_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) +{ + /* GaimConnection *gc = sess->aux_data; */ + /* GaimAccount *account = gaim_connection_get_account(gc); */ + /* OscarData *od = (OscarData *)gc->proto_data; */ + struct aim_ssi_item *curitem; + int tmp; + int export = FALSE; + /* XXX - use these? + va_list ap; + + va_start(ap, fr); + fmtver = (fu16_t)va_arg(ap, int); + numitems = (fu16_t)va_arg(ap, int); + items = va_arg(ap, struct aim_ssi_item); + timestamp = va_arg(ap, fu32_t); + va_end(ap); */ + + owl_function_debugmsg("faimtest_ssi_parselist: syncing local list and server list"); + + /* Clean the buddy list */ + aim_ssi_cleanlist(sess); + + /* Add from server list to local list */ + for (curitem=sess->ssi.local; curitem; curitem=curitem->next) { + if ((curitem->name == NULL) || (g_utf8_validate(curitem->name, -1, NULL))) + switch (curitem->type) { + case 0x0000: { /* Buddy */ + if (curitem->name) { + char *gname = aim_ssi_itemlist_findparentname(sess->ssi.local, curitem->name); + char *gname_utf8 = gname ? gaim_utf8_try_convert(gname) : NULL; + char *alias = aim_ssi_getalias(sess->ssi.local, gname, curitem->name); + char *alias_utf8 = alias ? gaim_utf8_try_convert(alias) : NULL; + GaimBuddy *buddy = gaim_find_buddy(gc->account, curitem->name); + /* Should gname be freed here? -- elb */ + /* Not with the current code, but that might be cleaner -- med */ + free(alias); + if (buddy) { + /* Get server stored alias */ + if (alias_utf8) { + g_free(buddy->alias); + buddy->alias = g_strdup(alias_utf8); + } + } else { + GaimGroup *g; + buddy = gaim_buddy_new(gc->account, curitem->name, alias_utf8); + + if (!(g = gaim_find_group(gname_utf8 ? gname_utf8 : _("Orphans")))) { + g = gaim_group_new(gname_utf8 ? gname_utf8 : _("Orphans")); + gaim_blist_add_group(g, NULL); + } + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname_utf8 ? gname_utf8 : _("Orphans")); + gaim_blist_add_buddy(buddy, NULL, g, NULL); + export = TRUE; + } + g_free(gname_utf8); + g_free(alias_utf8); + } + } break; + + case 0x0001: { /* Group */ + /* Shouldn't add empty groups */ + } break; + + case 0x0002: { /* Permit buddy */ + if (curitem->name) { + /* if (!find_permdeny_by_name(gc->permit, curitem->name)) { AAA */ + GSList *list; + for (list=account->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next); + if (!list) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding permit buddy %s to local list\n", curitem->name); + gaim_privacy_permit_add(account, curitem->name, TRUE); + export = TRUE; + } + } + } break; + + case 0x0003: { /* Deny buddy */ + if (curitem->name) { + GSList *list; + for (list=account->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next); + if (!list) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding deny buddy %s to local list\n", curitem->name); + gaim_privacy_deny_add(account, curitem->name, TRUE); + export = TRUE; + } + } + } break; + + case 0x0004: { /* Permit/deny setting */ + if (curitem->data) { + fu8_t permdeny; + if ((permdeny = aim_ssi_getpermdeny(sess->ssi.local)) && (permdeny != account->perm_deny)) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, permdeny); + account->perm_deny = permdeny; + if (od->icq && account->perm_deny == 0x03) { + serv_set_away(gc, "Invisible", ""); + } + export = TRUE; + } + } + } break; + + case 0x0005: { /* Presence setting */ + /* We don't want to change Gaim's setting because it applies to all accounts */ + } break; + } /* End of switch on curitem->type */ + } /* End of for loop */ + + /* If changes were made, then flush buddy list to file */ + if (export) + gaim_blist_save(); + + { /* Add from local list to server list */ + GaimBlistNode *gnode, *cnode, *bnode; + GaimGroup *group; + GaimBuddy *buddy; + GaimBuddyList *blist; + GSList *cur; + + /* Buddies */ + if ((blist = gaim_get_blist())) + for (gnode = blist->root; gnode; gnode = gnode->next) { + if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) + continue; + group = (GaimGroup *)gnode; + for (cnode = gnode->child; cnode; cnode = cnode->next) { + if(!GAIM_BLIST_NODE_IS_CONTACT(cnode)) + continue; + for (bnode = cnode->child; bnode; bnode = bnode->next) { + if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) + continue; + buddy = (GaimBuddy *)bnode; + if (buddy->account == gc->account) { + const char *servernick = gaim_buddy_get_setting(buddy, "servernick"); + if (servernick) + serv_got_alias(gc, buddy->name, servernick); + + if (aim_ssi_itemlist_exists(sess->ssi.local, buddy->name)) { + /* Store local alias on server */ + char *alias = aim_ssi_getalias(sess->ssi.local, group->name, buddy->name); + if (!alias && buddy->alias && strlen(buddy->alias)) + aim_ssi_aliasbuddy(sess, group->name, buddy->name, buddy->alias); + free(alias); + } else { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding buddy %s from local list to server list\n", buddy->name); + aim_ssi_addbuddy(sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 0); + } + } + } + } + } + + /* Permit list */ + if (gc->account->permit) { + for (cur=gc->account->permit; cur; cur=cur->next) + if (!aim_ssi_itemlist_finditem(sess->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding permit %s from local list to server list\n", (char *)cur->data); + aim_ssi_addpermit(sess, cur->data); + } + } + + /* Deny list */ + if (gc->account->deny) { + for (cur=gc->account->deny; cur; cur=cur->next) + if (!aim_ssi_itemlist_finditem(sess->ssi.local, NULL, cur->data, AIM_SSI_TYPE_DENY)) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding deny %s from local list to server list\n", (char *)cur->data); + aim_ssi_adddeny(sess, cur->data); + } + } + /* Presence settings (idle time visibility) */ + if ((tmp = aim_ssi_getpresence(sess->ssi.local)) != 0xFFFFFFFF) + if (!(tmp & 0x400)) + aim_ssi_setpresence(sess, tmp | 0x400); + } /* end adding buddies from local list to server list */ + + /* Set our ICQ status */ + if (od->icq && !gc->away) { + aim_setextstatus(sess, AIM_ICQ_STATE_NORMAL); + } + + /* Activate SSI */ + /* Sending the enable causes other people to be able to see you, and you to see them */ + /* Make sure your privacy setting/invisibility is set how you want it before this! */ + gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: activating server-stored buddy list\n"); + aim_ssi_enable(sess); + + return 1; +} + +static int gaim_ssi_parseack(aim_session_t *sess, aim_frame_t *fr, ...) +{ + GaimConnection *gc = sess->aux_data; + va_list ap; + struct aim_ssi_tmp *retval; + + va_start(ap, fr); + retval = va_arg(ap, struct aim_ssi_tmp *); + va_end(ap); + + while (retval) { + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "ssi: status is 0x%04hx for a 0x%04hx action with name %s\n", retval->ack, retval->action, retval->item ? (retval->item->name ? retval->item->name : "no name") : "no item"); + + if (retval->ack != 0xffff) + switch (retval->ack) { + case 0x0000: { /* added successfully */ + } break; + + case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */ + gchar *buf; + buf = g_strdup_printf(_("Could not add the buddy %s because you have too many buddies in your buddy list. Please remove one and try again."), (retval->name ? retval->name : _("(no name)"))); + gaim_notify_error(gc, NULL, _("Unable To Add"), buf); + g_free(buf); + } + + case 0x000e: { /* buddy requires authorization */ + if ((retval->action == AIM_CB_SSI_ADD) && (retval->name)) + gaim_auth_sendrequest(gc, retval->name); + } break; + + default: { /* La la la */ + gchar *buf; + gaim_debug(GAIM_DEBUG_ERROR, "oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval->action, retval->ack); + buf = g_strdup_printf(_("Could not add the buddy %s for an unknown reason. The most common reason for this is that you have the maximum number of allowed buddies in your buddy list."), (retval->name ? retval->name : _("(no name)"))); + gaim_notify_error(gc, NULL, _("Unable To Add"), buf); + g_free(buf); + /* XXX - Should remove buddy from local list */ + } break; + } + + retval = retval->next; + } + + return 1; +} + +static int gaim_ssi_authgiven(aim_session_t *sess, aim_frame_t *fr, ...) +{ + GaimConnection *gc = sess->aux_data; + va_list ap; + char *sn, *msg; + gchar *dialog_msg, *nombre; + struct name_data *data; + GaimBuddy *buddy; + + va_start(ap, fr); + sn = va_arg(ap, char *); + msg = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: %s has given you permission to add him to your buddy list\n", sn); + + buddy = gaim_find_buddy(gc->account, sn); + if (buddy && (gaim_get_buddy_alias_only(buddy))) + nombre = g_strdup_printf("%s (%s)", sn, gaim_get_buddy_alias_only(buddy)); + else + nombre = g_strdup(sn); + + dialog_msg = g_strdup_printf(_("The user %s has given you permission to add you to their buddy list. Do you want to add them?"), nombre); + data = g_new(struct name_data, 1); + data->gc = gc; + data->name = g_strdup(sn); + data->nick = NULL; + + gaim_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg, + 0, data, + G_CALLBACK(gaim_icq_buddyadd), + G_CALLBACK(oscar_free_name_data)); + + g_free(dialog_msg); + g_free(nombre); + + return 1; +} + +static int gaim_ssi_authrequest(aim_session_t *sess, aim_frame_t *fr, ...) +{ + GaimConnection *gc = sess->aux_data; + va_list ap; + char *sn, *msg; + gchar *dialog_msg, *nombre; + struct name_data *data; + GaimBuddy *buddy; + + va_start(ap, fr); + sn = va_arg(ap, char *); + msg = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: received authorization request from %s\n", sn); + + buddy = gaim_find_buddy(gc->account, sn); + if (buddy && (gaim_get_buddy_alias_only(buddy))) + nombre = g_strdup_printf("%s (%s)", sn, gaim_get_buddy_alias_only(buddy)); + else + nombre = g_strdup(sn); + + dialog_msg = g_strdup_printf(_("The user %s wants to add you to their buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given.")); + data = g_new(struct name_data, 1); + data->gc = gc; + data->name = g_strdup(sn); + data->nick = NULL; + + gaim_request_action(gc, NULL, _("Authorization Request"), dialog_msg, + 0, data, 2, + _("Authorize"), G_CALLBACK(gaim_auth_grant), + _("Deny"), G_CALLBACK(gaim_auth_dontgrant_msgprompt)); + + g_free(dialog_msg); + g_free(nombre); + + return 1; +} + +static int gaim_ssi_authreply(aim_session_t *sess, aim_frame_t *fr, ...) +{ + GaimConnection *gc = sess->aux_data; + va_list ap; + char *sn, *msg; + gchar *dialog_msg, *nombre; + fu8_t reply; + GaimBuddy *buddy; + + va_start(ap, fr); + sn = va_arg(ap, char *); + reply = (fu8_t)va_arg(ap, int); + msg = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: received authorization reply from %s. Reply is 0x%04hhx\n", sn, reply); + + buddy = gaim_find_buddy(gc->account, sn); + if (buddy && (gaim_get_buddy_alias_only(buddy))) + nombre = g_strdup_printf("%s (%s)", sn, gaim_get_buddy_alias_only(buddy)); + else + nombre = g_strdup(sn); + + if (reply) { + /* Granted */ + dialog_msg = g_strdup_printf(_("The user %s has granted your request to add them to your buddy list."), nombre); + gaim_notify_info(gc, NULL, _("Authorization Granted"), dialog_msg); + } else { + /* Denied */ + dialog_msg = g_strdup_printf(_("The user %s has denied your request to add them to your buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given.")); + gaim_notify_info(gc, NULL, _("Authorization Denied"), dialog_msg); + } + g_free(dialog_msg); + g_free(nombre); + + return 1; +} + +static int gaim_ssi_gotadded(aim_session_t *sess, aim_frame_t *fr, ...) +{ + GaimConnection *gc = sess->aux_data; + va_list ap; + char *sn; + GaimBuddy *buddy; + + va_start(ap, fr); + sn = va_arg(ap, char *); + va_end(ap); + + buddy = gaim_find_buddy(gc->account, sn); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: %s added you to their buddy list\n", sn); + gaim_account_notify_added(gc->account, NULL, sn, (buddy ? gaim_get_buddy_alias_only(buddy) : NULL), NULL); + + return 1; +} +#endif diff --git a/athstatic b/athstatic new file mode 100755 index 0000000..6b26cbe --- /dev/null +++ b/athstatic @@ -0,0 +1,32 @@ +#!/bin/sh +# $Id: athstatic,v 1.2 2003/05/31 19:33:43 kretch Exp $ + +# Usage: athstatic progname options ... + +# Replaces -lfoo options with /usr/athena/lib/libfoo.a if it exists, +# thus preferring static libraries to shared libraries for stuff in +# /usr/athena/lib. + +progname=$1 +shift + +options= +for arg do + case $arg in + -l*) + # Chop off the first two characters to get the library name. + lib=`expr "$arg" : '..\(.*\)$'` + if [ -f /usr/athena/lib/lib${lib}.a ]; then + options="$options /usr/athena/lib/lib${lib}.a" + else + options="$options $arg" + fi + ;; + *) + options="$options $arg" + ;; + esac +done + +echo "$progname" $options +exec "$progname" $options @@ -0,0 +1,95 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id: buddy.c,v 1.3 2009/03/29 12:38:19 kretch Exp $"; + +void owl_buddy_create(owl_buddy *b, int proto, char *name) +{ + b->proto=proto; + b->name=owl_strdup(name); + b->idlesince=0; +} + +char *owl_buddy_get_name(owl_buddy *b) +{ + if (b->name) return(b->name); + return(""); +} + +int owl_buddy_is_idle(owl_buddy *b) +{ + if (b->isidle) return(1); + return(0); +} + +void owl_buddy_set_idle(owl_buddy *b) +{ + b->isidle=1; +} + +void owl_buddy_set_unidle(owl_buddy *b) +{ + b->isidle=0; +} + +int owl_buddy_get_proto(owl_buddy *b) +{ + return(b->proto); +} + +int owl_buddy_is_proto_aim(owl_buddy *b) +{ + if (b->proto==OWL_PROTOCOL_AIM) return(1); + return(0); +} + +/* Set the buddy to have been idle since 'diff' minutes ago + */ +void owl_buddy_set_idle_since(owl_buddy *b, int diff) +{ + time_t now; + + now=time(NULL); + b->idlesince=now-(diff*60); +} + +/* return the number of minutes the buddy has been idle + */ +int owl_buddy_get_idle_time(owl_buddy *b) +{ + time_t now; + + if (b->isidle) { + now=time(NULL); + return((now - b->idlesince)/60); + } + return(0); +} + +void owl_buddy_free(owl_buddy *b) +{ + if (b->name) owl_free(b->name); +} diff --git a/buddylist.c b/buddylist.c new file mode 100644 index 0000000..7be9691 --- /dev/null +++ b/buddylist.c @@ -0,0 +1,166 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id: buddylist.c,v 1.15 2009/03/29 12:38:19 kretch Exp $"; + +void owl_buddylist_init(owl_buddylist *bl) +{ + owl_list_create(&(bl->buddies)); +} + +/* add a (logged-in) AIM buddy to the buddy list + */ +void owl_buddylist_add_aim_buddy(owl_buddylist *bl, char *screenname) +{ + owl_buddy *b; + b=owl_malloc(sizeof(owl_buddy)); + + owl_buddy_create(b, OWL_PROTOCOL_AIM, screenname); + owl_list_append_element(&(bl->buddies), b); +} + +/* remove an AIM buddy from the buddy list + */ +int owl_buddylist_remove_aim_buddy(owl_buddylist *bl, char *name) +{ + int i, j; + owl_buddy *b; + + j=owl_list_get_size(&(bl->buddies)); + for (i=0; i<j; i++) { + b=owl_list_get_element(&(bl->buddies), i); + if (!strcasecmp(name, owl_buddy_get_name(b)) && owl_buddy_is_proto_aim(b)) { + owl_list_remove_element(&(bl->buddies), i); + owl_buddy_free(b); + return(0); + } + } + return(1); +} + +/* Deal with an "oncoming" message. This means recognizing the user + * has logged in, and displaying a message if they were not already + * logged in. + */ +void owl_buddylist_oncoming(owl_buddylist *bl, char *screenname) +{ + owl_message *m; + + if (!owl_buddylist_is_aim_buddy_loggedin(bl, screenname)) { + + owl_buddylist_add_aim_buddy(bl, screenname); + + /* are we ingoring login messages for a while? */ + if (!owl_timer_is_expired(owl_global_get_aim_login_timer(&g))) return; + + /* if not, create the login message */ + m=owl_malloc(sizeof(owl_message)); + owl_message_create_aim(m, + screenname, + owl_global_get_aim_screenname(&g), + "", + OWL_MESSAGE_DIRECTION_IN, + 1); + owl_global_messagequeue_addmsg(&g, m); + } +} + +/* Deal with an "offgoing" message. This means recognizing the user + * has logged out, and sending a message if they were logged in. + */ +void owl_buddylist_offgoing(owl_buddylist *bl, char *screenname) +{ + owl_message *m; + + if (owl_buddylist_is_aim_buddy_loggedin(bl, screenname)) { + m=owl_malloc(sizeof(owl_message)); + owl_message_create_aim(m, + screenname, + owl_global_get_aim_screenname(&g), + "", + OWL_MESSAGE_DIRECTION_IN, + -1); + owl_global_messagequeue_addmsg(&g, m); + } + + owl_buddylist_remove_aim_buddy(bl, screenname); +} + +/* return the number of logged in buddies */ +int owl_buddylist_get_size(owl_buddylist *bl) +{ + return(owl_list_get_size(&(bl->buddies))); +} + +/* return the buddy with index N. If out of range, return NULL + */ +owl_buddy *owl_buddylist_get_buddy_n(owl_buddylist *bl, int index) +{ + if (index<0) return(NULL); + if (index>(owl_buddylist_get_size(bl)-1)) return(NULL); + + return(owl_list_get_element(&(bl->buddies), index)); +} + +/* return the AIM buddy with screenname 'name'. If + * no such buddy is logged in, return NULL. + */ +owl_buddy *owl_buddylist_get_aim_buddy(owl_buddylist *bl, char *name) +{ + int i, j; + owl_buddy *b; + + j=owl_list_get_size(&(bl->buddies)); + for (i=0; i<j; i++) { + b=owl_list_get_element(&(bl->buddies), i); + if (!strcasecmp(name, owl_buddy_get_name(b))) return(b); + } + return(NULL); +} + +/* return 1 if the buddy 'screenname' is logged in, + * otherwise return 0 + */ +int owl_buddylist_is_aim_buddy_loggedin(owl_buddylist *bl, char *screenname) +{ + owl_buddy *b; + + b=owl_buddylist_get_aim_buddy(bl, screenname); + if (b==NULL) return(0); + return(1); +} + +/* remove all buddies from the list */ +void owl_buddylist_clear(owl_buddylist *bl) +{ + owl_list_free_all(&(bl->buddies), (void(*)(void*))owl_buddy_free); + owl_list_create(&(bl->buddies)); +} + +void owl_buddylist_free(owl_buddylist *bl) +{ + owl_list_free_all(&(bl->buddies), (void(*)(void*))owl_buddy_free); +} @@ -0,0 +1,283 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: cmd.c,v 1.6 2009/03/29 12:38:20 kretch Exp $"; + +extern owl_cmd commands_to_init[]; + +/**************************************************************************/ +/***************************** COMMAND DICT *******************************/ +/**************************************************************************/ + +int owl_cmddict_setup(owl_cmddict *cd) { + if (0 != owl_cmddict_init(cd)) return(-1); + if (0 != owl_cmddict_add_from_list(cd, commands_to_init)) return(-1); + return(0); +} + +int owl_cmddict_init(owl_cmddict *cd) { + if (owl_dict_create(cd)) return(-1); + return(0); +} + +/* for bulk initialization at startup */ +int owl_cmddict_add_from_list(owl_cmddict *cd, owl_cmd *cmds) { + owl_cmd *cur, *cmd; + for (cur = cmds; cur->name != NULL; cur++) { + cmd = owl_malloc(sizeof(owl_cmd)); + owl_cmd_create_from_template(cmd, cur); + owl_dict_insert_element(cd, cmd->name, (void*)cmd, NULL); + } + return 0; +} + +/* free the list with owl_cmddict_namelist_free */ +void owl_cmddict_get_names(owl_cmddict *d, owl_list *l) { + owl_dict_get_keys(d, l); +} + +owl_cmd *owl_cmddict_find(owl_cmddict *d, char *name) { + return (owl_cmd*)owl_dict_find_element(d, name); +} + +void owl_cmddict_namelist_free(owl_list *l) { + owl_list_free_all(l, owl_free); +} + +/* creates a new command alias */ +int owl_cmddict_add_alias(owl_cmddict *cd, char *alias_from, char *alias_to) { + owl_cmd *cmd; + cmd = owl_malloc(sizeof(owl_cmd)); + owl_cmd_create_alias(cmd, alias_from, alias_to); + owl_dict_insert_element(cd, cmd->name, (void*)cmd, (void(*)(void*))owl_cmd_free); + return(0); +} + +char *owl_cmddict_execute(owl_cmddict *cd, owl_context *ctx, char *cmdbuff) { + char **argv; + int argc; + char *tmpbuff; + char *retval = NULL; + owl_cmd *cmd; + + tmpbuff=strdup(cmdbuff); + argv=owl_parseline(tmpbuff, &argc); + if (argc < 0) { + owl_free(tmpbuff); + sepbar(NULL); + owl_function_makemsg("Unbalanced quotes"); + return NULL; + } + + if (argc < 1) return(NULL); + + if (!strcmp(argv[0], "")) { + } else if (NULL != (cmd = (owl_cmd*)owl_dict_find_element(cd, argv[0]))) { + retval = owl_cmd_execute(cmd, cd, ctx, argc, argv, cmdbuff); + } else { + owl_function_makemsg("Unknown command '%s'.", cmdbuff); + } + owl_parsefree(argv, argc); + owl_free(tmpbuff); + sepbar(NULL); + return retval; +} + +/*********************************************************************/ +/***************************** COMMAND *******************************/ +/*********************************************************************/ + +/* sets up a new command based on a template, copying strings */ +int owl_cmd_create_from_template(owl_cmd *cmd, owl_cmd *templ) { + *cmd = *templ; + if (!templ->name) return(-1); + cmd->name = owl_strdup(templ->name); + if (templ->summary) cmd->summary = owl_strdup(templ->summary); + if (templ->usage) cmd->usage = owl_strdup(templ->usage); + if (templ->description) cmd->description = owl_strdup(templ->description); + if (templ->cmd_aliased_to) cmd->cmd_aliased_to = owl_strdup(templ->cmd_aliased_to); + return(0); +} + +int owl_cmd_create_alias(owl_cmd *cmd, char *name, char *aliased_to) { + memset(cmd, 0, sizeof(owl_cmd)); + cmd->name = owl_strdup(name); + cmd->cmd_aliased_to = owl_strdup(aliased_to); + cmd->summary = owl_malloc(strlen(aliased_to)+strlen(OWL_CMD_ALIAS_SUMMARY_PREFIX)+2); + strcpy(cmd->summary, OWL_CMD_ALIAS_SUMMARY_PREFIX); + strcat(cmd->summary, aliased_to); + return(0); +} + +void owl_cmd_free(owl_cmd *cmd) { + if (cmd->name) owl_free(cmd->name); + if (cmd->summary) owl_free(cmd->summary); + if (cmd->usage) owl_free(cmd->usage); + if (cmd->description) owl_free(cmd->description); +} + +int owl_cmd_is_context_valid(owl_cmd *cmd, owl_context *ctx) { + if (owl_context_matches(ctx, cmd->validctx)) return 1; + else return 0; +} + +char *owl_cmd_execute(owl_cmd *cmd, owl_cmddict *cd, owl_context *ctx, int argc, char **argv, char *cmdbuff) { + static int alias_recurse_depth = 0; + int ival=0; + char *cmdbuffargs, *newcmd, *rv=NULL; + + if (argc < 1) return(NULL); + + /* Recurse if this is an alias */ + if (cmd->cmd_aliased_to) { + if (alias_recurse_depth++ > 50) { + owl_function_makemsg("Alias loop detected for '%s'.", cmdbuff); + } else { + cmdbuffargs = skiptokens(cmdbuff, 1); + newcmd = owl_malloc(strlen(cmd->cmd_aliased_to)+strlen(cmdbuffargs)+2); + strcpy(newcmd, cmd->cmd_aliased_to); + strcat(newcmd, " "); + strcat(newcmd, cmdbuffargs); + rv = owl_function_command(newcmd); + owl_free(newcmd); + } + alias_recurse_depth--; + return rv; + } + + /* Do validation and conversions */ + if (cmd->cmd_ctxargs_fn || cmd->cmd_ctxv_fn || cmd->cmd_ctxi_fn) { + if (!owl_cmd_is_context_valid(cmd, ctx)) { + owl_function_makemsg("Invalid context for command '%s'.", cmdbuff); + return NULL; + } + } + + if ((argc != 1) && (cmd->cmd_v_fn || cmd->cmd_ctxv_fn)) { + owl_function_makemsg("Wrong number of arguments for %s command.", argv[0]); + return NULL; + } + + if (cmd->cmd_i_fn || cmd->cmd_ctxi_fn) { + char *ep = "x"; + if (argc != 2) { + owl_function_makemsg("Wrong number of arguments for %s command.", argv[0]); + return NULL; + } + ival = strtol(argv[1], &ep, 10); + if (*ep || ep==argv[1]) { + owl_function_makemsg("Invalid argument '%s' for %s command.", argv[1], argv[0]); + return(NULL); + } + } + + if (cmd->cmd_args_fn) { + return cmd->cmd_args_fn(argc, argv, cmdbuff); + } else if (cmd->cmd_v_fn) { + cmd->cmd_v_fn(); + } else if (cmd->cmd_i_fn) { + cmd->cmd_i_fn(ival); + } else if (cmd->cmd_ctxargs_fn) { + return cmd->cmd_ctxargs_fn(owl_context_get_data(ctx), argc, argv, cmdbuff); + } else if (cmd->cmd_ctxv_fn) { + cmd->cmd_ctxv_fn(owl_context_get_data(ctx)); + } else if (cmd->cmd_ctxi_fn) { + cmd->cmd_ctxi_fn(owl_context_get_data(ctx), ival); + } + + return NULL; +} + +/* returns a reference */ +char *owl_cmd_get_summary(owl_cmd *cmd) { + return cmd->summary; +} + +/* returns a summary line describing this keymap. the caller must free. */ +char *owl_cmd_describe(owl_cmd *cmd) { + char *s; + int slen; + if (!cmd || !cmd->name || !cmd->summary) return NULL; + slen = strlen(cmd->name)+strlen(cmd->summary)+30; + s = owl_malloc(slen); + snprintf(s, slen-1, "%-25s - %s", cmd->name, cmd->summary); + return s; +} + + + +void owl_cmd_get_help(owl_cmddict *d, char *name, owl_fmtext *fm) { + char *indent, *s; + owl_cmd *cmd; + + if (!name || (cmd = owl_dict_find_element(d, name)) == NULL) { + owl_fmtext_append_bold(fm, "OWL HELP\n\n"); + owl_fmtext_append_normal(fm, "No such command...\n"); + return; + } + + owl_fmtext_append_bold(fm, "OWL HELP\n\n"); + owl_fmtext_append_bold(fm, "NAME\n\n"); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, cmd->name); + + if (cmd->summary && *cmd->summary) { + owl_fmtext_append_normal(fm, " - "); + owl_fmtext_append_normal(fm, cmd->summary); + } + owl_fmtext_append_normal(fm, "\n"); + + if (cmd->usage && *cmd->usage) { + s = cmd->usage; + indent = owl_malloc(strlen(s)+(owl_text_num_lines(s)+3)*OWL_TAB+1); + owl_text_indent(indent, s, OWL_TAB); + owl_fmtext_append_bold(fm, "\nSYNOPSIS\n"); + owl_fmtext_append_normal(fm, indent); + owl_fmtext_append_normal(fm, "\n"); + owl_free(indent); + } else { + owl_fmtext_append_bold(fm, "\nSYNOPSIS\n"); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, cmd->name); + owl_fmtext_append_normal(fm, "\n"); + } + + if (cmd->description && *cmd->description) { + s = cmd->description; + indent = owl_malloc(strlen(s)+(owl_text_num_lines(s)+3)*OWL_TAB+1); + owl_text_indent(indent, s, OWL_TAB); + owl_fmtext_append_bold(fm, "\nDESCRIPTION\n"); + owl_fmtext_append_normal(fm, indent); + owl_fmtext_append_normal(fm, "\n"); + owl_free(indent); + } + + owl_fmtext_append_normal(fm, "\n\n"); +} diff --git a/codelist.pl b/codelist.pl new file mode 100755 index 0000000..81da349 --- /dev/null +++ b/codelist.pl @@ -0,0 +1,40 @@ +#! /usr/bin/perl +# $Id: codelist.pl,v 1.7 2003/07/02 22:15:24 kretch Exp $ + +if ($#ARGV eq -1) { + @ARGV=`ls *.c`; + chop(@ARGV); +} + +foreach $file (@ARGV) { + open(FILE, $file); + + print "/* -------------------------------- $file -------------------------------- */\n"; + while (<FILE>) { + if (/^\S/ + && (/\{\s*$/ || /\)\s*$/) + && !/\}/ + && !/^\{/ + && !/^#/ + && !/^static/ + && !/^system/ + && !/^XS/ + && !/\/\*/ + && !/ZWRITEOPTIONS/ + && !/owlfaim_priv/) + { + + s/\s+\{/\;/; + s/\)[ \t]*$/\)\;/; + print "extern "; + print; + } elsif (/^#if/ || /^#else/ || /^#endif/) { + print; + } + + } + close(FILE); + print "\n"; +} + + diff --git a/commands.c b/commands.c new file mode 100644 index 0000000..39fb2bf --- /dev/null +++ b/commands.c @@ -0,0 +1,2623 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: commands.c,v 1.80 2009/03/29 18:58:21 kretch Exp $"; + +/* fn is "char *foo(int argc, char **argv, char *buff)" */ +#define OWLCMD_ARGS(name, fn, ctx, summary, usage, description) \ + { name, summary, usage, description, ctx, \ + NULL, fn, NULL, NULL, NULL, NULL, NULL } + +/* fn is "void foo(void)" */ +#define OWLCMD_VOID(name, fn, ctx, summary, usage, description) \ + { name, summary, usage, description, ctx, \ + NULL, NULL, fn, NULL, NULL, NULL, NULL } + +/* fn is "void foo(int)" */ +#define OWLCMD_INT(name, fn, ctx, summary, usage, description) \ + { name, summary, usage, description, ctx, \ + NULL, NULL, NULL, fn, NULL, NULL, NULL } + +#define OWLCMD_ALIAS(name, actualname) \ + { name, OWL_CMD_ALIAS_SUMMARY_PREFIX actualname, "", "", OWL_CTX_ANY, \ + actualname, NULL, NULL, NULL, NULL, NULL, NULL } + +/* fn is "char *foo(void *ctx, int argc, char **argv, char *buff)" */ +#define OWLCMD_ARGS_CTX(name, fn, ctx, summary, usage, description) \ + { name, summary, usage, description, ctx, \ + NULL, NULL, NULL, NULL, ((char*(*)(void*,int,char**,char*))fn), NULL, NULL } + +/* fn is "void foo(void)" */ +#define OWLCMD_VOID_CTX(name, fn, ctx, summary, usage, description) \ + { name, summary, usage, description, ctx, \ + NULL, NULL, NULL, NULL, NULL, ((void(*)(void*))(fn)), NULL } + +/* fn is "void foo(int)" */ +#define OWLCMD_INT_CTX(name, fn, ctx, summary, usage, description) \ + { name, summary, usage, description, ctx, \ + NULL, NULL, NULL, NULL, NULL, NULL, ((void(*)(void*,int))fn) } + + +owl_cmd commands_to_init[] + = { + OWLCMD_ARGS("zlog", owl_command_zlog, OWL_CTX_ANY, + "send a login or logout notification", + "zlog in [tty]\nzlog out", + "zlog in will send a login notification, zlog out will send a\n" + "logout notification. By default a login notification is sent\n" + "when owl is started and a logout notification is sent when owl\n" + "is exited. This behavior can be changed with the 'startuplogin'\n" + "and 'shudownlogout' variables. If a tty is specified for zlog in\n" + "then the owl variable 'tty' will be set to that string, causing\n" + "it to be used as the zephyr location tty.\n"), + + OWLCMD_VOID("quit", owl_command_quit, OWL_CTX_ANY, + "exit owl", + "", + "Exit owl and run any shutdown activities."), + OWLCMD_ALIAS("exit", "quit"), + OWLCMD_ALIAS("q", "quit"), + + OWLCMD_ARGS("term", owl_command_term, OWL_CTX_ANY, + "control the terminal", + "term raise\n" + "term deiconify\n", + ""), + + OWLCMD_VOID("nop", owl_command_nop, OWL_CTX_ANY, + "do nothing", + "", + ""), + + OWLCMD_ARGS("start-command", owl_command_start_command, OWL_CTX_INTERACTIVE, + "prompts the user to enter a command", + "start-command [initial-value]", + "Initializes the command field to initial-value."), + + OWLCMD_ARGS("start-question", owl_command_start_question, OWL_CTX_INTERACTIVE, + "prompts the user to enter a response to some question", + "start-command <question>", + ""), + + OWLCMD_ARGS("start-password", owl_command_start_password, OWL_CTX_INTERACTIVE, + "prompts the user to enter a password", + "start-password <question>", + ""), + + OWLCMD_ARGS("alias", owl_command_alias, OWL_CTX_ANY, + "creates a command alias", + "alias <new_command> <old_command>", + "Creates a command alias from new_command to old_command.\n" + "Any arguments passed to <new_command> will be appended to\n" + "<old_command> before it is executed.\n"), + + OWLCMD_ARGS("bindkey", owl_command_bindkey, OWL_CTX_ANY, + "creates a binding in a keymap", + "bindkey <keymap> <keyseq> command <command>", + "Binds a key sequence to a command within a keymap.\n" + "Use 'show keymaps' to see the existing keymaps.\n" + "Key sequences may be things like M-C-t or NPAGE.\n"), + + OWLCMD_ARGS("style", owl_command_style, OWL_CTX_ANY, + "creates a new style", + "style <name> perl <function_name>", + "Creates a new style for formatting messages.\n" + "A style named <name> will be created that will\n" + "format messages using the perl function <function_name>.\n\n" + "SEE ALSO: show styles, view -s, filter -s\n"), + + OWLCMD_ARGS("zwrite", owl_command_zwrite, OWL_CTX_INTERACTIVE, + "send a zephyr", + "zwrite [-n] [-C] [-c class] [-i instance] [-r realm] [-O opcde] [<user> ...] [-m <message...>]", + "Zwrite send a zephyr to the one or more users specified.\n\n" + "The following options are available:\n\n" + "-m Specifies a message to send without prompting.\n" + " Note that this does not yet log an outgoing message.\n" + " This must be the last argument.\n\n" + "-n Do not send a ping message.\n\n" + "-C If the message is sent to more than one user include a\n" + " \"cc:\" line in the text\n\n" + "-c class\n" + " Send to the specified zephyr class\n\n" + "-i instance\n" + " Send to the specified zephyr instance\n\n" + "-r realm\n" + " Send to a foreign realm\n" + "-O opcode\n" + " Send to the specified opcode\n"), + + OWLCMD_ARGS("aimwrite", owl_command_aimwrite, OWL_CTX_INTERACTIVE, + "send an AIM message", + "aimwrite <user> [-m <message...>]", + "Send an aim message to a user.\n\n" + "The following options are available:\n\n" + "-m Specifies a message to send without prompting.\n"), + + OWLCMD_ARGS("loopwrite", owl_command_loopwrite, OWL_CTX_INTERACTIVE, + "send a loopback message", + "loopwrite", + "Send a local message.\n"), + + OWLCMD_ARGS("zcrypt", owl_command_zcrypt, OWL_CTX_INTERACTIVE, + "send an encrypted zephyr", + "zcrypt [-n] [-C] [-c class] [-i instance] [-r realm] [-O opcde] [-m <message...>]\n", + "Behaves like zwrite but uses encryption. Not for use with\n" + "personal messages\n"), + + OWLCMD_ARGS("reply", owl_command_reply, OWL_CTX_INTERACTIVE, + "reply to the current message", + "reply [-e] [ sender | all | zaway ]", + "If -e is specified, the zwrite command line is presented to\n" + "allow editing.\n\n" + "If 'sender' is specified, reply to the sender.\n\n" + "If 'all' or no args are specified, reply publically to the\n" + "same class/instance for non-personal messages and to the\n" + "sender for personal messages.\n\n" + "If 'zaway' is specified, replies with a zaway message.\n\n"), + + OWLCMD_ARGS("set", owl_command_set, OWL_CTX_ANY, + "set a variable value", + "set [-q] [<variable>] [<value>]\n" + "set", + "Set the named variable to the specified value. If no\n" + "arguments are given, print the value of all variables.\n" + "If the value is unspecified and the variable is a boolean, it will be\n" + "set to 'on'. If -q is used, set is silent and does not print a\n" + "message.\n"), + + OWLCMD_ARGS("unset", owl_command_unset, OWL_CTX_ANY, + "unset a boolean variable value", + "set [-q] <variable>\n" + "set", + "Set the named boolean variable to off.\n" + "If -q is specified, is silent and doesn't print a message.\n"), + + OWLCMD_ARGS("print", owl_command_print, OWL_CTX_ANY, + "print a variable value", + "print <variable>\n" + "print", + "Print the value of the named variable. If no arugments\n" + "are used print the value of all variables.\n"), + + OWLCMD_ARGS("startup", owl_command_startup, OWL_CTX_ANY, + "run a command and set it to be run at every Owl startup", + "startup <commands> ...", + "Everything on the command line after the startup command\n" + "is executed as a normal owl command and is also placed in\n" + "a file so that the command is executed every time owl\n" + "is started"), + + OWLCMD_ARGS("unstartup", owl_command_unstartup, OWL_CTX_ANY, + "remove a command from the list of those to be run at Owl startup", + "unstartup <commands> ...", + ""), + + OWLCMD_VOID("version", owl_command_version, OWL_CTX_ANY, + "print the version of the running owl", "", ""), + + OWLCMD_ARGS("subscribe", owl_command_subscribe, OWL_CTX_ANY, + "subscribe to a zephyr class, instance, recipient", + "subscribe [-t] <class> <instance> [recipient]", + "Subscribe the specified class and instance. If the recipient\n" + "is not listed on the command line it defaults\n" + "to * (the wildcard recipient). If the -t option is present\n" + "the subscription will only be temporary, i.e., it will not\n" + "be written to the subscription file and will therefore not\n" + "be present the next time owl is started.\n"), + OWLCMD_ALIAS("sub", "subscribe"), + + OWLCMD_ARGS("unsubscribe", owl_command_unsubscribe, OWL_CTX_ANY, + "unsubscribe from a zephyr class, instance, recipient", + "unsubscribe [-t] <class> <instance> [recipient]", + "Unsubscribe from the specified class and instance. If the\n" + "recipient is not listed on the command line it defaults\n" + "to * (the wildcard recipient). If the -t option is present\n" + "the unsubscription will only be temporary, i.e., it will not\n" + "be updated in the subscription file and will therefore not\n" + "be in effect the next time owl is started.\n"), + OWLCMD_ALIAS("unsub", "unsubscribe"), + + OWLCMD_VOID("unsuball", owl_command_unsuball, OWL_CTX_ANY, + "unsubscribe from all zephyrs", "", ""), + + OWLCMD_VOID("getsubs", owl_command_getsubs, OWL_CTX_ANY, + "print all current subscriptions", + "getsubs", + "getsubs retrieves the current subscriptions from the server\n" + "and displays them.\n"), + + OWLCMD_ARGS("dump", owl_command_dump, OWL_CTX_ANY, + "dump messages to a file", + "dump <filename>", + "Dump messages in current view to the named file."), + + OWLCMD_ARGS("source", owl_command_source, OWL_CTX_ANY, + "execute owl commands from a file", + "source <filename>", + "Execute the owl commands in <filename>.\n"), + + OWLCMD_ARGS("aim", owl_command_aim, OWL_CTX_INTERACTIVE, + "AIM specific commands", + "aim search <email>", + ""), + + OWLCMD_ARGS("addbuddy", owl_command_addbuddy, OWL_CTX_INTERACTIVE, + "add a buddy to a buddylist", + "addbuddy aim <screenname>", + "Add the named buddy to your buddylist. Eventually other protocols,\n" + "such as zephyr, will also be able to use this command. For now the\n" + "only available protocol is 'aim', specified as the first argument."), + + OWLCMD_ARGS("delbuddy", owl_command_delbuddy, OWL_CTX_INTERACTIVE, + "delete a buddy from a buddylist", + "delbuddy aim <screenname>", + "Delete the named buddy to your buddylist. Eventually other protocols,\n" + "such as zephyr, will also be able to use this command. For now the\n" + "only available protocol is 'aim', specified as the first argument.\n"), + + OWLCMD_ARGS("join", owl_command_join, OWL_CTX_INTERACTIVE, + "join a chat group", + "join aim <groupname> [exchange]", + "Join the AIM chatroom with 'groupname'.\n"), + + OWLCMD_ARGS("smartzpunt", owl_command_smartzpunt, OWL_CTX_INTERACTIVE, + "creates a zpunt based on the current message", + "smartzpunt [-i | --instance]", + "Starts a zpunt command based on the current message's class\n" + "(and instance if -i is specified).\n"), + + OWLCMD_ARGS("zpunt", owl_command_zpunt, OWL_CTX_ANY, + "suppress a given zephyr triplet", + "zpunt <class> <instance> [recipient]\n" + "zpunt <instance>", + "The zpunt command will supress message to the specified\n" + "zephyr triplet. In the second usage messages as supressed\n" + "for class MESSAGE and the named instance.\n\n" + "SEE ALSO: zunpunt, show zpunts\n"), + + OWLCMD_ARGS("zunpunt", owl_command_zunpunt, OWL_CTX_ANY, + "undo a previous zpunt", + "zunpunt <class> <instance> [recipient]\n" + "zunpunt <instance>", + "The zunpunt command will allow messages that were previosly\n" + "suppressed to be received again.\n\n" + "SEE ALSO: zpunt, show zpunts\n"), + + OWLCMD_VOID("info", owl_command_info, OWL_CTX_INTERACTIVE, + "display detailed information about the current message", + "", ""), + + OWLCMD_ARGS("help", owl_command_help, OWL_CTX_INTERACTIVE, + "display help on using owl", + "help [command]", ""), + + OWLCMD_ARGS("zlist", owl_command_zlist, OWL_CTX_INTERACTIVE, + "List users logged in", + "znol [-f file]", + "Print a znol-style listing of users logged in"), + + OWLCMD_ARGS("alist", owl_command_alist, OWL_CTX_INTERACTIVE, + "List AIM users logged in", + "alist", + "Print a listing of AIM users logged in"), + + OWLCMD_ARGS("blist", owl_command_blist, OWL_CTX_INTERACTIVE, + "List all buddies logged in", + "blist", + "Print a listing of buddies logged in, regardless of protocol."), + + OWLCMD_ARGS("toggle-oneline", owl_command_toggleoneline, OWL_CTX_INTERACTIVE, + "Toggle the style between oneline and the default style", + "toggle-oneline", + ""), + + OWLCMD_VOID("recv:shiftleft", owl_command_shift_left, OWL_CTX_INTERACTIVE, + "scrolls receive window to the left", "", ""), + + OWLCMD_VOID("recv:shiftright", owl_command_shift_right, OWL_CTX_INTERACTIVE, + "scrolls receive window to the left", "", ""), + + OWLCMD_VOID("recv:pagedown", owl_function_mainwin_pagedown, + OWL_CTX_INTERACTIVE, + "scrolls down by a page", "", ""), + + OWLCMD_VOID("recv:pageup", owl_function_mainwin_pageup, OWL_CTX_INTERACTIVE, + "scrolls up by a page", "", ""), + + OWLCMD_INT ("recv:scroll", owl_function_page_curmsg, OWL_CTX_INTERACTIVE, + "scrolls current message up or down", + "recv:scroll <numlines>", + "Scrolls the current message up or down by <numlines>.\n" + "Scrolls up if <numlines> is negative, else scrolls down.\n"), + + OWLCMD_ARGS("next", owl_command_next, OWL_CTX_INTERACTIVE, + "move the pointer to the next message", + "recv:next [ --filter <name> ] [ --skip-deleted ] [ --last-if-none ]\n" + " [ --smart-filter | --smart-filter-instance ]", + "Moves the pointer to the next message in the current view.\n" + "If --filter is specified, will only consider messages in\n" + "the filter <name>.\n" + "If --smart-filter or --smart-filter-instance is specified,\n" + "goes to the next message that is similar to the current message.\n" + "If --skip-deleted is specified, deleted messages will\n" + "be skipped.\n" + "If --last-if-none is specified, will stop at last message\n" + "in the view if no other suitable messages are found.\n"), + OWLCMD_ALIAS("recv:next", "next"), + + OWLCMD_ARGS("prev", owl_command_prev, OWL_CTX_INTERACTIVE, + "move the pointer to the previous message", + "recv:prev [ --filter <name> ] [ --skip-deleted ] [ --first-if-none ]\n" + " [ --smart-filter | --smart-filter-instance ]", + "Moves the pointer to the next message in the current view.\n" + "If --filter is specified, will only consider messages in\n" + "the filter <name>.\n" + "If --smart-filter or --smart-filter-instance is specified,\n" + "goes to the previous message that is similar to the current message.\n" + "If --skip-deleted is specified, deleted messages will\n" + "be skipped.\n" + "If --first-if-none is specified, will stop at first message\n" + "in the view if no other suitable messages are found.\n"), + OWLCMD_ALIAS("recv:prev", "prev"), + + OWLCMD_ALIAS("recv:next-notdel", "recv:next --skip-deleted --last-if-none"), + OWLCMD_ALIAS("next-notdel", "recv:next --skip-deleted --last-if-none"), + + OWLCMD_ALIAS("recv:prev-notdel", "recv:prev --skip-deleted --first-if-none"), + OWLCMD_ALIAS("prev-notdel", "recv:prev --skip-deleted --first-if-none"), + + OWLCMD_ALIAS("recv:next-personal", "recv:next --filter personal"), + + OWLCMD_ALIAS("recv:prev-personal", "recv:prev --filter personal"), + + OWLCMD_VOID("first", owl_command_first, OWL_CTX_INTERACTIVE, + "move the pointer to the first message", "", ""), + OWLCMD_ALIAS("recv:first", "first"), + + OWLCMD_VOID("last", owl_command_last, OWL_CTX_INTERACTIVE, + "move the pointer to the last message", "", + "Moves the pointer to the last message in the view.\n" + "If we are already at the last message in the view,\n" + "blanks the screen and moves just past the end of the view\n" + "so that new messages will appear starting at the top\n" + "of the screen.\n"), + OWLCMD_ALIAS("recv:last", "last"), + + OWLCMD_VOID("expunge", owl_command_expunge, OWL_CTX_INTERACTIVE, + "remove all messages marked for deletion", "", ""), + + OWLCMD_VOID("resize", owl_command_resize, OWL_CTX_ANY, + "resize the window to the current screen size", "", ""), + + OWLCMD_VOID("redisplay", owl_command_redisplay, OWL_CTX_ANY, + "redraw the entire window", "", ""), + + OWLCMD_VOID("suspend", owl_command_suspend, OWL_CTX_ANY, + "suspend owl", "", ""), + + OWLCMD_ARGS("echo", owl_command_echo, OWL_CTX_ANY, + "pops up a message in popup window", + "echo [args .. ]\n\n", ""), + + OWLCMD_ARGS("exec", owl_command_exec, OWL_CTX_ANY, + "run a command from the shell", + "exec [args .. ]", ""), + + OWLCMD_ARGS("aexec", owl_command_aexec, OWL_CTX_INTERACTIVE, + "run a command from the shell and display in an admin message", + "aexec [args .. ]", ""), + + OWLCMD_ARGS("pexec", owl_command_pexec, OWL_CTX_INTERACTIVE, + "run a command from the shell and display in a popup window", + "pexec [args .. ]", ""), + + OWLCMD_ARGS("perl", owl_command_perl, OWL_CTX_ANY, + "run a perl expression", + "perl [args .. ]", ""), + + OWLCMD_ARGS("aperl", owl_command_aperl, OWL_CTX_INTERACTIVE, + "run a perl expression and display in an admin message", + "aperl [args .. ]", ""), + + OWLCMD_ARGS("pperl", owl_command_pperl, OWL_CTX_INTERACTIVE, + "run a perl expression and display in a popup window", + "pperl [args .. ]", ""), + + OWLCMD_ARGS("multi", owl_command_multi, OWL_CTX_ANY, + "runs multiple ;-separated commands", + "multi <command1> ( ; <command2> )*\n", + "Runs multiple semicolon-separated commands in order.\n" + "Note quoting isn't supported here yet.\n" + "If you want to do something fancy, use perl.\n"), + + OWLCMD_ARGS("(", owl_command_multi, OWL_CTX_ANY, + "runs multiple ;-separated commands", + "'(' <command1> ( ; <command2> )* ')'\n", + "Runs multiple semicolon-separated commands in order.\n" + "You must have a space before the final ')'\n" + "Note quoting isn't supported here yet.\n" + "If you want to do something fancy, use perl.\n"), + + OWLCMD_VOID("pop-message", owl_command_pop_message, OWL_CTX_RECWIN, + "pops up a message in a window", "", ""), + + OWLCMD_ARGS("zaway", owl_command_zaway, OWL_CTX_INTERACTIVE, + "Set, enable or disable zephyr away message", + "zaway [ on | off | toggle ]\n" + "zaway <message>", + "Turn on or off a zaway message. If 'message' is\n" + "specified turn on zaway with that message, otherwise\n" + "use the default.\n"), + + OWLCMD_ARGS("aaway", owl_command_aaway, OWL_CTX_INTERACTIVE, + "Set, enable or disable AIM away message", + "aaway [ on | off | toggle ]\n" + "aaway <message>", + "Turn on or off the AIM away message. If 'message' is\n" + "specified turn on aaway with that message, otherwise\n" + "use the default.\n"), + + OWLCMD_ARGS("away", owl_command_away, OWL_CTX_INTERACTIVE, + "Set, enable or disable both AIM and zephyr away messages", + "away [ on | off | toggle ]\n" + "away <message>", + "Turn on or off the AIM and zephyr away message. If\n" + "'message' is specified turn them on with that message,\n" + "otherwise use the default.\n" + "\n" + "This command really just runs the 'aaway' and 'zaway'\n" + "commands together\n" + "\n" + "SEE ALSO: aaway, zaway"), + + OWLCMD_ARGS("load-subs", owl_command_loadsubs, OWL_CTX_ANY, + "load subscriptions from a file", + "load-subs <file>\n", ""), + + OWLCMD_ARGS("loadsubs", owl_command_loadsubs, OWL_CTX_ANY, + "load subscriptions from a file", + "loadsubs <file>\n", ""), + + OWLCMD_ARGS("loadloginsubs", owl_command_loadloginsubs, OWL_CTX_ANY, + "load login subscriptions from a file", + "loadloginsubs <file>\n", + "The file should contain a list of usernames, one per line."), + + OWLCMD_VOID("about", owl_command_about, OWL_CTX_INTERACTIVE, + "print information about owl", "", ""), + + OWLCMD_VOID("status", owl_command_status, OWL_CTX_ANY, + "print status information about the running owl", "", ""), + + OWLCMD_ARGS("zlocate", owl_command_zlocate, OWL_CTX_INTERACTIVE, + "locate a user", + "zlocate [-d] <user> ...", + "Performs a zlocate on one ore more users and puts the result\n" + "int a popwin. If -d is specified, does not authenticate\n" + "the lookup request.\n"), + + OWLCMD_ARGS("filter", owl_command_filter, OWL_CTX_ANY, + "create a message filter", + "filter <name> [ -c color ] [ <expression> ... ]", + "The filter command creates a filter with the specified name,\n" + "or if one already exists it is replaced. Example filter\n" + "syntax would be:\n\n" + " filter myfilter -c red ( class ^foobar$ ) or ( class ^quux$ and instance ^bar$ )\n\n" + "Valid matching fields are:\n" + " sender - sender\n" + " recipient - recipient\n" + " class - zephyr class name\n" + " instance - zephyr instance name\n" + " opcode - zephyr opcode\n" + " realm - zephyr realm\n" + " body - message body\n" + " hostname - hostname of sending host\n" + " type - message type (zephyr, aim, admin)\n" + " direction - either 'in' 'out' or 'none'\n" + " login - either 'login' 'logout' or 'none'\n" + "Also you may match on the validity of another filter:\n" + " filter <filtername>\n" + "Also you may pass the message to a perl function returning 0 or 1,\n" + "where 1 indicates that the function matches the filter:\n" + " perl <subname>\n" + "Valid operators are:\n" + " and\n" + " or\n" + " not\n" + "And additionally you may use the static values:\n" + " true\n" + " false\n" + "Spaces must be present before and after parenthesis. If the\n" + "optional color argument is used it specifies the color that\n" + "messages matching this filter should be displayed in.\n\n" + "SEE ALSO: view, viewclass, viewuser\n"), + + OWLCMD_ARGS("colorview", owl_command_colorview, OWL_CTX_INTERACTIVE, + "change the color on the current filter", + "colorview <color>", + "The color of messages in the current filter will be changed\n" + "to <color>. Use the 'show colors' command for a list\n" + "of valid colors.\n\n" + "SEE ALSO: 'show colors'\n"), + + OWLCMD_ARGS("colorclass", owl_command_colorclass, OWL_CTX_INTERACTIVE, + "create a filter to color messages of the given class name", + "colorclass <class> <color>", + "A filter will be created to color messages in <class>" + "in <color>. Use the 'show colors' command for a list\n" + "of valid colors.\n\n" + "SEE ALSO: 'show colors'\n"), + + OWLCMD_ARGS("view", owl_command_view, OWL_CTX_INTERACTIVE, + "view messages matching a filter", + "view [<viewname>] [-f <filter> | --home | -r ] [-s <style>]\n" + "view <filter>\n" + "view -d <expression>\n" + "view --home", + "The view command sets information associated with a particular view,\n" + "such as view's filter or style. In the first general usage listed\n" + "above <viewname> is the name of the view to be changed. If not\n" + "specified the default view 'main' will be used. A filter can be set\n" + "for the view by listing a named filter after the -f argument. If\n" + "the --home argument is used the filter will be set to the filter named\n" + "by the\n 'view_home' variable. The style can be set by listing the\n" + "name style after the -s argument.\n" + "\n" + "The other usages listed above are abbreivated forms that simply set\n" + "the filter of the current view. The -d option allows you to write a\n" + "filter expression that will be dynamically created by owl and then\n" + "applied as the view's filter\n" + "SEE ALSO: filter, viewclass, viewuser\n"), + + OWLCMD_ARGS("smartnarrow", owl_command_smartnarrow, OWL_CTX_INTERACTIVE, + "view only messages similar to the current message", + "smartnarrow [-i | --instance]", + "If the curmsg is a personal message narrow\n" + " to the converstaion with that user.\n" + "If the curmsg is a class message, instance foo, recip *\n" + " message, narrow to the class, inst.\n" + "If the curmsg is a class message then narrow\n" + " to the class.\n" + "If the curmsg is a class message and '-i' is specied\n" + " then narrow to the class, instance\n"), + + OWLCMD_ARGS("smartfilter", owl_command_smartfilter, OWL_CTX_INTERACTIVE, + "returns the name of a filter based on the current message", + "smartfilter [-i | --instance]", + "If the curmsg is a personal message, the filter is\n" + " the converstaion with that user.\n" + "If the curmsg is a class message, instance foo, recip *\n" + " message, the filter is the class, inst.\n" + "If the curmsg is a class message, the filter is that class.\n" + "If the curmsg is a class message and '-i' is specied\n" + " the filter is that <class,instance> pair\n"), + + OWLCMD_ARGS("viewclass", owl_command_viewclass, OWL_CTX_INTERACTIVE, + "view messages matching a particular class", + "viewclass <class>", + "The viewclass command will automatically create a filter\n" + "matching the specified class and switch the current view\n" + "to it.\n\n" + "SEE ALSO: filter, view, viewuser\n"), + OWLCMD_ALIAS("vc", "viewclass"), + + OWLCMD_ARGS("viewuser", owl_command_viewuser, OWL_CTX_INTERACTIVE, + "view messages matching a particular user", + "viewuser <user>", + "The viewuser command will automatically create a filter\n" + "matching the specified user and switch the current\n" + "view to it.\n\n" + "SEE ALSO: filter, view, viewclass\n"), + OWLCMD_ALIAS("vu", "viewuser"), + + OWLCMD_ARGS("show", owl_command_show, OWL_CTX_INTERACTIVE, + "show information", + "show colors\n" + "show commands\n" + "show command <command>\n" + "show errors\n" + "show filters\n" + "show filter <filter>\n" + "show keymaps\n" + "show keymap <keymap>\n" + "show license\n" + "show startup\n" + "show status\n" + "show styles\n" + "show subscriptions / show subs\n" + "show terminal\n" + "show variables\n" + "show variable <variable>\n" + "show version\n" + "show view [<view>]\n" + "show zpunts\n", + + "Show colors will display a list of valid colors for the\n" + " terminal." + "Show filters will list the names of all filters.\n" + "Show filter <filter> will show the definition of a particular\n" + " filter.\n\n" + "Show startup will display the custom startup config\n\n" + "Show zpunts will show the active zpunt filters.\n\n" + "Show keymaps will list the names of all keymaps.\n" + "Show keymap <keymap> will show the key bindings in a keymap.\n\n" + "Show commands will list the names of all keymaps.\n" + "Show command <command> will provide information about a command.\n\n" + "Show styles will list the names of all styles available\n" + "for formatting messages.\n\n" + "Show variables will list the names of all variables.\n\n" + "Show errors will show a list of errors ecountered by Owl.\n\n" + "SEE ALSO: filter, view, alias, bindkey, help\n"), + + OWLCMD_ARGS("delete", owl_command_delete, OWL_CTX_INTERACTIVE, + "mark a message for deletion", + "delete [ -id msgid ] [ --no-move ]\n" + "delete view\n" + "delete trash", + "If no message id is specified the current message is marked\n" + "for deletion. Otherwise the message with the given message\n" + "id is marked for deltion.\n" + "If '--no-move' is specified, don't move after deletion.\n" + "If 'trash' is specified, deletes all trash/auto messages\n" + "in the current view.\n" + "If 'view' is specified, deletes all messages in the\n" + "current view.\n"), + OWLCMD_ALIAS("del", "delete"), + + OWLCMD_ARGS("undelete", owl_command_undelete, OWL_CTX_INTERACTIVE, + "unmark a message for deletion", + "undelete [ -id msgid ] [ --no-move ]\n" + "undelete view", + "If no message id is specified the current message is\n" + "unmarked for deletion. Otherwise the message with the\n" + "given message id is marked for undeltion.\n" + "If '--no-move' is specified, don't move after deletion.\n" + "If 'view' is specified, undeletes all messages\n" + "in the current view.\n"), + OWLCMD_ALIAS("undel", "undelete"), + + OWLCMD_VOID("beep", owl_command_beep, OWL_CTX_ANY, + "ring the terminal bell", + "beep", + "Beep will ring the terminal bell.\n" + "If the variable 'bell' has been\n" + "set to 'off' this command does nothing.\n"), + + OWLCMD_ARGS("debug", owl_command_debug, OWL_CTX_ANY, + "prints a message into the debug log", + "debug <message>", ""), + + OWLCMD_ARGS("getview", owl_command_getview, OWL_CTX_INTERACTIVE, + "returns the name of the filter for the current view", + "", ""), + + OWLCMD_ARGS("getvar", owl_command_getvar, OWL_CTX_INTERACTIVE, + "returns the value of a variable", + "getvar <varname>", ""), + + OWLCMD_ARGS("getstyle", owl_command_getstyle, OWL_CTX_INTERACTIVE, + "returns the name of the style for the current view", + "", ""), + + OWLCMD_ARGS("search", owl_command_search, OWL_CTX_INTERACTIVE, + "search messages for a particular string", + "search [-r] [<string>]", + "The search command will find messages that contain the\n" + "specified string and move the cursor there. If no string\n" + "argument is supplied then the previous one is used. By\n" + "default searches are done fowards, if -r is used the search\n" + "is performed backwards"), + + OWLCMD_ARGS("aimlogin", owl_command_aimlogin, OWL_CTX_ANY, + "login to an AIM account", + "aimlogin <screenname> [<password>]\n", + ""), + + OWLCMD_ARGS("aimlogout", owl_command_aimlogout, OWL_CTX_ANY, + "logout from AIM", + "aimlogout\n", + ""), + + + /****************************************************************/ + /************************* EDIT-SPECIFIC ************************/ + /****************************************************************/ + + OWLCMD_VOID_CTX("edit:move-next-word", owl_editwin_move_to_nextword, + OWL_CTX_EDIT, + "moves cursor forward a word", + "", ""), + + OWLCMD_VOID_CTX("edit:move-prev-word", owl_editwin_move_to_previousword, + OWL_CTX_EDIT, + "moves cursor backwards a word", + "", ""), + + OWLCMD_VOID_CTX("edit:move-to-buffer-start", owl_editwin_move_to_top, + OWL_CTX_EDIT, + "moves cursor to the top left (start) of the buffer", + "", ""), + + OWLCMD_VOID_CTX("edit:move-to-buffer-end", owl_editwin_move_to_end, + OWL_CTX_EDIT, + "moves cursor to the bottom right (end) of the buffer", + "", ""), + + OWLCMD_VOID_CTX("edit:move-to-line-end", owl_editwin_move_to_line_end, + OWL_CTX_EDIT, + "moves cursor to the end of the line", + "", ""), + + OWLCMD_VOID_CTX("edit:move-to-line-start", owl_editwin_move_to_line_start, + OWL_CTX_EDIT, + "moves cursor to the beginning of the line", + "", ""), + + OWLCMD_VOID_CTX("edit:move-left", owl_editwin_key_left, + OWL_CTX_EDIT, + "moves the cursor left by a character", + "", ""), + + OWLCMD_VOID_CTX("edit:move-right", owl_editwin_key_right, + OWL_CTX_EDIT, + "moves the cursor right by a character", + "", ""), + + OWLCMD_VOID_CTX("edit:delete-next-word", owl_editwin_delete_nextword, + OWL_CTX_EDIT, + "deletes the word to the right of the cursor", + "", ""), + + OWLCMD_VOID_CTX("edit:delete-prev-word", owl_editwin_delete_previousword, + OWL_CTX_EDIT, + "deletes the word to the left of the cursor", + "", ""), + + OWLCMD_VOID_CTX("edit:delete-prev-char", owl_editwin_backspace, + OWL_CTX_EDIT, + "deletes the character to the left of the cursor", + "", ""), + + OWLCMD_VOID_CTX("edit:delete-next-char", owl_editwin_delete_char, + OWL_CTX_EDIT, + "deletes the character to the right of the cursor", + "", ""), + + OWLCMD_VOID_CTX("edit:delete-to-line-end", owl_editwin_delete_to_endofline, + OWL_CTX_EDIT, + "deletes from the cursor to the end of the line", + "", ""), + + OWLCMD_VOID_CTX("edit:delete-all", owl_editwin_clear, + OWL_CTX_EDIT, + "deletes all of the contents of the buffer", + "", ""), + + OWLCMD_VOID_CTX("edit:transpose-chars", owl_editwin_transpose_chars, + OWL_CTX_EDIT, + "Interchange characters around point, moving forward one character.", + "", ""), + + OWLCMD_VOID_CTX("edit:fill-paragraph", owl_editwin_fill_paragraph, + OWL_CTX_EDIT, + "fills the current paragraph to line-wrap well", + "", ""), + + OWLCMD_VOID_CTX("edit:recenter", owl_editwin_recenter, + OWL_CTX_EDIT, + "recenters the buffer", + "", ""), + + OWLCMD_ARGS_CTX("edit:insert-text", owl_command_edit_insert_text, + OWL_CTX_EDIT, + "inserts text into the buffer", + "edit:insert-text <text>", ""), + + OWLCMD_VOID_CTX("edit:cancel", owl_command_edit_cancel, + OWL_CTX_EDIT, + "cancels the current command", + "", ""), + + OWLCMD_VOID_CTX("edit:history-next", owl_command_edit_history_next, + OWL_CTX_EDIT, + "replaces the text with the previous history", + "", ""), + + OWLCMD_VOID_CTX("edit:history-prev", owl_command_edit_history_prev, + OWL_CTX_EDIT, + "replaces the text with the previous history", + "", ""), + + OWLCMD_VOID_CTX("editline:done", owl_command_editline_done, + OWL_CTX_EDITLINE, + "completes the command (eg, executes command being composed)", + "", ""), + + OWLCMD_VOID_CTX("editresponse:done", owl_command_editresponse_done, + OWL_CTX_EDITRESPONSE, + "completes the response to a question", + "", ""), + + OWLCMD_VOID_CTX("editmulti:move-up-line", owl_editwin_key_up, + OWL_CTX_EDITMULTI, + "moves the cursor up one line", + "", ""), + + OWLCMD_VOID_CTX("editmulti:move-down-line", owl_editwin_key_down, + OWL_CTX_EDITMULTI, + "moves the cursor down one line", + "", ""), + + OWLCMD_VOID_CTX("editmulti:done", owl_command_editmulti_done, + OWL_CTX_EDITMULTI, + "completes the command (eg, sends message being composed)", + "", ""), + + OWLCMD_VOID_CTX("editmulti:done-or-delete", owl_command_editmulti_done_or_delete, + OWL_CTX_EDITMULTI, + "completes the command, but only if at end of message", + "", + "If only whitespace is to the right of the cursor,\n" + "runs 'editmulti:done'.\n"\ + "Otherwise runs 'edit:delete-next-char'\n"), + + /****************************************************************/ + /********************** POPLESS-SPECIFIC ************************/ + /****************************************************************/ + + OWLCMD_VOID_CTX("popless:scroll-down-page", owl_viewwin_pagedown, + OWL_CTX_POPLESS, + "scrolls down one page", + "", ""), + + OWLCMD_VOID_CTX("popless:scroll-down-line", owl_viewwin_linedown, + OWL_CTX_POPLESS, + "scrolls down one line", + "", ""), + + OWLCMD_VOID_CTX("popless:scroll-up-page", owl_viewwin_pageup, + OWL_CTX_POPLESS, + "scrolls up one page", + "", ""), + + OWLCMD_VOID_CTX("popless:scroll-up-line", owl_viewwin_lineup, + OWL_CTX_POPLESS, + "scrolls up one line", + "", ""), + + OWLCMD_VOID_CTX("popless:scroll-to-top", owl_viewwin_top, + OWL_CTX_POPLESS, + "scrolls to the top of the buffer", + "", ""), + + OWLCMD_VOID_CTX("popless:scroll-to-bottom", owl_viewwin_bottom, + OWL_CTX_POPLESS, + "scrolls to the bottom of the buffer", + "", ""), + + OWLCMD_INT_CTX ("popless:scroll-right", owl_viewwin_right, + OWL_CTX_POPLESS, + "scrolls right in the buffer", + "popless:scroll-right <num-chars>", ""), + + OWLCMD_INT_CTX ("popless:scroll-left", owl_viewwin_left, + OWL_CTX_POPLESS, + "scrolls left in the buffer", + "popless:scroll-left <num-chars>", ""), + + OWLCMD_VOID_CTX("popless:quit", owl_command_popless_quit, + OWL_CTX_POPLESS, + "exits the popless window", + "", ""), + + /* This line MUST be last! */ + { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL } + +}; + +void owl_command_info() +{ + owl_function_info(); +} + +void owl_command_nop() +{ +} + +char *owl_command_help(int argc, char **argv, char *buff) +{ + if (argc!=2) { + owl_help(); + return NULL; + } + + owl_function_help_for_command(argv[1]); + return NULL; +} + +char *owl_command_zlist(int argc, char **argv, char *buff) +{ + int elapsed=0, timesort=0; + char *file=NULL; + + argc--; + argv++; + while (argc) { + if (!strcmp(argv[0], "-e")) { + elapsed=1; + argc--; + argv++; + } else if (!strcmp(argv[0], "-t")) { + timesort=1; + argc--; + argv++; + } else if (!strcmp(argv[0], "-f")) { + if (argc==1) { + owl_function_makemsg("zlist: -f needs an argument"); + return(NULL); + } + file=argv[1]; + argc-=2; + argv+=2; + } else { + owl_function_makemsg("zlist: unknown argument"); + return(NULL); + } + } + owl_function_buddylist(0, 1, file); + return(NULL); +} + +char *owl_command_alist() +{ + owl_function_buddylist(1, 0, NULL); + return(NULL); +} + +char *owl_command_blist() +{ + owl_function_buddylist(1, 1, NULL); + return(NULL); +} + +char *owl_command_toggleoneline() +{ + owl_function_toggleoneline(); + return(NULL); +} + +void owl_command_about() +{ + owl_function_about(); +} + +void owl_command_version() +{ + owl_function_makemsg("Owl version %s", OWL_VERSION_STRING); +} + +char *owl_command_aim(int argc, char **argv, char *buff) +{ + if (argc<2) { + owl_function_makemsg("not enough arguments to aim command"); + return(NULL); + } + + if (!strcmp(argv[1], "search")) { + if (argc!=3) { + owl_function_makemsg("not enough arguments to aim search command"); + return(NULL); + } + owl_aim_search(argv[2]); + } else { + owl_function_makemsg("unknown subcommand '%s' for aim command", argv[1]); + return(NULL); + } + return(NULL); +} + +char *owl_command_addbuddy(int argc, char **argv, char *buff) +{ + if (argc!=3) { + owl_function_makemsg("usage: addbuddy <protocol> <buddyname>"); + return(NULL); + } + + if (!strcasecmp(argv[1], "aim")) { + if (!owl_global_is_aimloggedin(&g)) { + owl_function_makemsg("addbuddy: You must be logged into aim to use this command."); + return(NULL); + } + /* + owl_function_makemsg("This function is not yet operational. Stay tuned."); + return(NULL); + */ + owl_aim_addbuddy(argv[2]); + owl_function_makemsg("%s added as AIM buddy for %s", argv[2], owl_global_get_aim_screenname(&g)); + } else if (!strcasecmp(argv[1], "zephyr")) { + owl_zephyr_addbuddy(argv[2]); + owl_function_makemsg("%s added as zephyr buddy", argv[2]); + } else { + owl_function_makemsg("addbuddy: currently the only supported protocols are 'zephyr' and 'aim'"); + } + + return(NULL); +} + +char *owl_command_delbuddy(int argc, char **argv, char *buff) +{ + if (argc!=3) { + owl_function_makemsg("usage: delbuddy <protocol> <buddyname>"); + return(NULL); + } + + if (!strcasecmp(argv[1], "aim")) { + if (!owl_global_is_aimloggedin(&g)) { + owl_function_makemsg("delbuddy: You must be logged into aim to use this command."); + return(NULL); + } + owl_aim_delbuddy(argv[2]); + owl_function_makemsg("%s deleted as AIM buddy for %s", argv[2], owl_global_get_aim_screenname(&g)); + } else if (!strcasecmp(argv[1], "zephyr")) { + owl_zephyr_delbuddy(argv[2]); + owl_function_makemsg("%s deleted as zephyr buddy", argv[2]); + } else { + owl_function_makemsg("delbuddy: currently the only supported protocols are 'zephyr' and 'aim'"); + } + + return(NULL); +} + +char *owl_command_join(int argc, char **argv, char *buff) +{ + if (argc!=3 && argc!=4) { + owl_function_makemsg("usage: join <protocol> <buddyname> [exchange]"); + return(NULL); + } + + if (!strcasecmp(argv[1], "aim")) { + if (!owl_global_is_aimloggedin(&g)) { + owl_function_makemsg("join aim: You must be logged into aim to use this command."); + return(NULL); + } + if (argc==3) { + owl_aim_chat_join(argv[2], 4); + } else { + owl_aim_chat_join(argv[2], atoi(argv[3])); + } + /* owl_function_makemsg("%s deleted as AIM buddy for %s", argv[2], owl_global_get_aim_screenname(&g)); */ + } else { + owl_function_makemsg("join: currently the only supported protocol is 'aim'"); + } + return(NULL); +} + +char *owl_command_startup(int argc, char **argv, char *buff) +{ + char *ptr; + + if (argc<2) { + owl_function_makemsg("usage: %s <commands> ...", argv[0]); + return(NULL); + } + + ptr=strchr(buff, ' '); + if (!ptr) { + owl_function_makemsg("Parse error finding command for startup"); + return(NULL); + } + + owl_function_command(ptr+1); + owl_function_addstartup(ptr+1); + + return(NULL); +} + +char *owl_command_unstartup(int argc, char **argv, char *buff) +{ + char *ptr; + + if (argc<2) { + owl_function_makemsg("usage: %s <commands> ...", argv[0]); + return(NULL); + } + + ptr=strchr(buff, ' '); + if (!ptr) { + owl_function_makemsg("Parse error finding command for unstartup"); + return(NULL); + } + + owl_function_delstartup(ptr+1); + + return(NULL); +} + +char *owl_command_dump(int argc, char **argv, char *buff) +{ + char *filename; + + if (argc!=2) { + owl_function_makemsg("usage: dump <filename>"); + return(NULL); + } + filename=owl_util_makepath(argv[1]); + owl_function_dump(filename); + owl_free(filename); + return(NULL); +} + +char *owl_command_source(int argc, char **argv, char *buff) +{ + if (argc!=2) { + owl_function_makemsg("usage: source <filename>"); + return(NULL); + } + + owl_function_source(argv[1]); + return(NULL); +} + +char *owl_command_next(int argc, char **argv, char *buff) +{ + char *filter=NULL; + int skip_deleted=0, last_if_none=0; + while (argc>1) { + if (argc>=1 && !strcmp(argv[1], "--skip-deleted")) { + skip_deleted=1; + argc-=1; argv+=1; + } else if (argc>=1 && !strcmp(argv[1], "--last-if-none")) { + last_if_none=1; + argc-=1; argv+=1; + } else if (argc>=2 && !strcmp(argv[1], "--filter")) { + filter = owl_strdup(argv[2]); + argc-=2; argv+=2; + } else if (argc>=2 && !strcmp(argv[1], "--smart-filter")) { + filter = owl_function_smartfilter(0); + argc-=2; argv+=2; + } else if (argc>=2 && !strcmp(argv[1], "--smart-filter-instance")) { + filter = owl_function_smartfilter(1); + argc-=2; argv+=2; + } else { + owl_function_makemsg("Invalid arguments to command 'next'."); + return(NULL); + } + } + owl_function_nextmsg_full(filter, skip_deleted, last_if_none); + if (filter) owl_free(filter); + return(NULL); +} + +char *owl_command_prev(int argc, char **argv, char *buff) +{ + char *filter=NULL; + int skip_deleted=0, first_if_none=0; + while (argc>1) { + if (argc>=1 && !strcmp(argv[1], "--skip-deleted")) { + skip_deleted=1; + argc-=1; argv+=1; + } else if (argc>=1 && !strcmp(argv[1], "--first-if-none")) { + first_if_none=1; + argc-=1; argv+=1; + } else if (argc>=2 && !strcmp(argv[1], "--filter")) { + filter = owl_strdup(argv[2]); + argc-=2; argv+=2; + } else if (argc>=2 && !strcmp(argv[1], "--smart-filter")) { + filter = owl_function_smartfilter(0); + argc-=2; argv+=2; + } else if (argc>=2 && !strcmp(argv[1], "--smart-filter-instance")) { + filter = owl_function_smartfilter(1); + argc-=2; argv+=2; + } else { + owl_function_makemsg("Invalid arguments to command 'prev'."); + return(NULL); + } + } + owl_function_prevmsg_full(filter, skip_deleted, first_if_none); + if (filter) owl_free(filter); + return(NULL); +} + +char *owl_command_smartnarrow(int argc, char **argv, char *buff) +{ + char *filtname = NULL; + + if (argc == 1) { + filtname = owl_function_smartfilter(0); + } else if (argc == 2 && (!strcmp(argv[1], "-i") || !strcmp(argv[1], "--instance"))) { + filtname = owl_function_smartfilter(1); + } else { + owl_function_makemsg("Wrong number of arguments for %s", argv[0]); + } + if (filtname) { + owl_function_change_currentview_filter(filtname); + owl_free(filtname); + } + return NULL; +} + +char *owl_command_smartfilter(int argc, char **argv, char *buff) +{ + char *filtname = NULL; + + if (argc == 1) { + filtname = owl_function_smartfilter(0); + } else if (argc == 2 && (!strcmp(argv[1], "-i") || !strcmp(argv[1], "--instance"))) { + filtname = owl_function_smartfilter(1); + } else { + owl_function_makemsg("Wrong number of arguments for %s", argv[0]); + } + return filtname; +} + +void owl_command_expunge() +{ + owl_function_expunge(); +} + +void owl_command_first() +{ + owl_global_set_rightshift(&g, 0); + owl_function_firstmsg(); +} + +void owl_command_last() +{ + owl_function_lastmsg(); +} + +void owl_command_resize() +{ + owl_function_resize(); +} + +void owl_command_redisplay() +{ + owl_function_full_redisplay(); + owl_global_set_needrefresh(&g); +} + +void owl_command_shift_right() +{ + owl_function_shift_right(); +} + +void owl_command_shift_left() +{ + owl_function_shift_left(); +} + +void owl_command_unsuball() +{ + owl_function_unsuball(); +} + +char *owl_command_loadsubs(int argc, char **argv, char *buff) +{ + if (argc == 2) { + owl_function_loadsubs(argv[1]); + } else if (argc == 1) { + owl_function_loadsubs(NULL); + } else { + owl_function_makemsg("Wrong number of arguments for load-subs."); + return(NULL); + } + return(NULL); +} + + +char *owl_command_loadloginsubs(int argc, char **argv, char *buff) +{ + if (argc == 2) { + owl_function_loadloginsubs(argv[1]); + } else if (argc == 1) { + owl_function_loadloginsubs(NULL); + } else { + owl_function_makemsg("Wrong number of arguments for load-subs."); + return(NULL); + } + return(NULL); +} + +void owl_command_suspend() +{ + owl_function_suspend(); +} + +char *owl_command_start_command(int argc, char **argv, char *buff) +{ + buff = skiptokens(buff, 1); + owl_function_start_command(buff); + return(NULL); +} + +char *owl_command_start_question(int argc, char **argv, char *buff) +{ + buff = skiptokens(buff, 1); + owl_function_start_question(buff); + return(NULL); +} + +char *owl_command_start_password(int argc, char **argv, char *buff) +{ + buff = skiptokens(buff, 1); + owl_function_start_password(buff); + return(NULL); +} + +char *owl_command_zaway(int argc, char **argv, char *buff) +{ + if ((argc==1) || + ((argc==2) && !strcmp(argv[1], "on"))) { + owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g)); + owl_function_zaway_on(); + return NULL; + } + + if (argc==2 && !strcmp(argv[1], "off")) { + owl_function_zaway_off(); + return NULL; + } + + if (argc==2 && !strcmp(argv[1], "toggle")) { + owl_function_zaway_toggle(); + return NULL; + } + + buff = skiptokens(buff, 1); + owl_global_set_zaway_msg(&g, buff); + owl_function_zaway_on(); + return NULL; +} + + +char *owl_command_aaway(int argc, char **argv, char *buff) +{ + if ((argc==1) || + ((argc==2) && !strcmp(argv[1], "on"))) { + owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g)); + owl_function_aaway_on(); + return NULL; + } + + if (argc==2 && !strcmp(argv[1], "off")) { + owl_function_aaway_off(); + return NULL; + } + + if (argc==2 && !strcmp(argv[1], "toggle")) { + owl_function_aaway_toggle(); + return NULL; + } + + buff = skiptokens(buff, 1); + owl_global_set_aaway_msg(&g, buff); + owl_function_aaway_on(); + return NULL; +} + + +char *owl_command_away(int argc, char **argv, char *buff) +{ + if ((argc==1) || + ((argc==2) && !strcmp(argv[1], "on"))) { + owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g)); + owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g)); + owl_function_aaway_on(); + owl_function_zaway_on(); + owl_function_makemsg("Away messages set.", owl_global_get_aaway_msg_default(&g)); + return NULL; + } + + if (argc==2 && !strcmp(argv[1], "off")) { + owl_function_aaway_off(); + owl_function_zaway_off(); + return NULL; + } + + if (argc==2 && !strcmp(argv[1], "toggle")) { + /* if either one is on, turn it off, otherwise toggle both (turn + * them both on) + */ + if (!owl_global_is_zaway(&g) && !owl_global_is_aaway(&g)) { + owl_function_aaway_toggle(); + owl_function_zaway_toggle(); + owl_function_makemsg("Away messages set."); + } else { + if (owl_global_is_zaway(&g)) owl_function_zaway_toggle(); + if (owl_global_is_aaway(&g)) owl_function_aaway_toggle(); + owl_function_makemsg("Away messages off."); + } + return NULL; + } + + buff = skiptokens(buff, 1); + owl_global_set_aaway_msg(&g, buff); + owl_global_set_zaway_msg(&g, buff); + owl_function_aaway_on(); + owl_function_zaway_on(); + owl_function_makemsg("Away messages set."); + return NULL; +} + +char *owl_command_set(int argc, char **argv, char *buff) +{ + char *var, *val; + int silent=0; + int requirebool=0; + + if (argc == 1) { + owl_function_printallvars(); + return NULL; + } + + if (argc > 1 && !strcmp("-q",argv[1])) { + silent = 1; + argc--; argv++; + } + + if (argc == 2) { + var=argv[1]; + val="on"; + requirebool=1; + } else if (argc == 3) { + var=argv[1]; + val=argv[2]; + } else { + owl_function_makemsg("Wrong number of arguments for set command"); + return NULL; + } + owl_variable_set_fromstring(owl_global_get_vardict(&g), var, val, !silent, requirebool); + return NULL; +} + +char *owl_command_unset(int argc, char **argv, char *buff) +{ + char *var, *val; + int silent=0; + + if (argc > 1 && !strcmp("-q",argv[1])) { + silent = 1; + argc--; argv++; + } + if (argc == 2) { + var=argv[1]; + val="off"; + } else { + owl_function_makemsg("Wrong number of arguments for unset command"); + return NULL; + } + owl_variable_set_fromstring(owl_global_get_vardict(&g), var, val, !silent, 1); + return NULL; +} + +char *owl_command_print(int argc, char **argv, char *buff) +{ + char *var; + char valbuff[1024]; + + if (argc==1) { + owl_function_printallvars(); + return NULL; + } else if (argc!=2) { + owl_function_makemsg("Wrong number of arguments for print command"); + return NULL; + } + + var=argv[1]; + + if (0 == owl_variable_get_tostring(owl_global_get_vardict(&g), + var, valbuff, 1024)) { + owl_function_makemsg("%s = '%s'", var, valbuff); + } else { + owl_function_makemsg("Unknown variable '%s'.", var); + } + return NULL; +} + + +char *owl_command_exec(int argc, char **argv, char *buff) +{ + return owl_function_exec(argc, argv, buff, 0); +} + +char *owl_command_pexec(int argc, char **argv, char *buff) +{ + return owl_function_exec(argc, argv, buff, 1); +} + +char *owl_command_aexec(int argc, char **argv, char *buff) +{ + return owl_function_exec(argc, argv, buff, 2); +} + +char *owl_command_perl(int argc, char **argv, char *buff) +{ + return owl_function_perl(argc, argv, buff, 0); +} + +char *owl_command_pperl(int argc, char **argv, char *buff) +{ + return owl_function_perl(argc, argv, buff, 1); +} + +char *owl_command_aperl(int argc, char **argv, char *buff) +{ + return owl_function_perl(argc, argv, buff, 2); +} + +char *owl_command_multi(int argc, char **argv, char *buff) +{ + char *lastrv = NULL, *dupbuff, *newbuff; + char **commands; + int ncommands, i; + if (argc < 2) { + owl_function_makemsg("Invalid arguments to 'multi' command."); + return NULL; + } + dupbuff = owl_strdup(buff); + newbuff = skiptokens(dupbuff, 1); + if (!strcmp(argv[0], "(")) { + for (i=strlen(newbuff)-1; i>=0; i--) { + if (newbuff[i] == ')') { + newbuff[i] = '\0'; + break; + } else if (newbuff[i] != ' ') { + owl_function_makemsg("Invalid arguments to 'multi' command."); + owl_free(newbuff); + return NULL; + } + } + } + commands = atokenize(newbuff, ";", &ncommands); + for (i=0; i<ncommands; i++) { + if (lastrv) { + owl_free(lastrv); + } + lastrv = owl_function_command(commands[i]); + } + owl_free(dupbuff); + atokenize_free(commands, ncommands); + return lastrv; +} + + +char *owl_command_alias(int argc, char **argv, char *buff) +{ + if (argc < 3) { + owl_function_makemsg("Invalid arguments to 'alias' command."); + return NULL; + } + buff = skiptokens(buff, 2); + owl_function_command_alias(argv[1], buff); + return (NULL); +} + + +char *owl_command_bindkey(int argc, char **argv, char *buff) +{ + owl_keymap *km; + int ret; + + if (argc < 5 || strcmp(argv[3], "command")) { + owl_function_makemsg("Usage: bindkey <keymap> <binding> command <cmd>"); + return NULL; + } + km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), argv[1]); + if (!km) { + owl_function_makemsg("No such keymap '%s'", argv[1]); + return NULL; + } + buff = skiptokens(buff, 4); + ret = owl_keymap_create_binding(km, argv[2], buff, NULL, "*user*"); + if (ret!=0) { + owl_function_makemsg("Unable to bind '%s' in keymap '%s' to '%s'.", + argv[2], argv[1], buff); + return NULL; + } + return NULL; +} + +char *owl_command_style(int argc, char **argv, char *buff) { + owl_style *s; + + /* Usage: style <name> perl <function> */ + if (argc != 4 || strcmp(argv[2], "perl")) { + owl_function_makemsg("Usage: style <name> perl <function>"); + return NULL; + } + if (!owl_perlconfig_is_function(argv[3])) { + owl_function_makemsg("Unable to create style '%s': no perl function '%s'", + argv[1], argv[3]); + return NULL; + } + s=owl_malloc(sizeof(owl_style)); + owl_style_create_perl(s, argv[1], argv[3], NULL); + owl_global_add_style(&g, s); + + return NULL; +} + + +void owl_command_quit() +{ + owl_function_quit(); +} + +char *owl_command_debug(int argc, char **argv, char *buff) +{ + if (argc<2) { + owl_function_makemsg("Need at least one argument to debug command"); + return(NULL); + } + + if (!owl_global_is_debug_fast(&g)) { + owl_function_makemsg("Debugging is not turned on"); + return(NULL); + } + + owl_function_debugmsg(argv[1]); + return(NULL); +} + +char *owl_command_term(int argc, char **argv, char *buff) +{ + if (argc<2) { + owl_function_makemsg("Need at least one argument to the term command"); + return(NULL); + } + + if (!strcmp(argv[1], "raise")) { + owl_function_xterm_raise(); + } else if (!strcmp(argv[1], "deiconify")) { + owl_function_xterm_deiconify(); + } else { + owl_function_makemsg("Unknown terminal subcommand"); + } + return(NULL); +} + +char *owl_command_zlog(int argc, char **argv, char *buff) +{ + if ((argc<2) || (argc>3)) { + owl_function_makemsg("Wrong number of arguments for zlog command"); + return(NULL); + } + + if (!strcmp(argv[1], "in")) { + if (argc>2) { + owl_global_set_tty(&g, argv[2]); + } + owl_zephyr_zlog_in(); + } else if (!strcmp(argv[1], "out")) { + if (argc!=2) { + owl_function_makemsg("Wrong number of arguments for zlog command"); + return(NULL); + } + owl_zephyr_zlog_out(); + } else { + owl_function_makemsg("Invalid subcommand for zlog"); + } + return(NULL); +} + + +void owl_command_zlog_out(void) +{ + owl_zephyr_zlog_out(); +} + + +char *owl_command_subscribe(int argc, char **argv, char *buff) +{ + char *recip=""; + int temp=0; + + if (argc<3) { + owl_function_makemsg("Not enough arguments to the subscribe command"); + return(NULL); + } + argc--; + argv++; + + if (!strcmp(argv[0], "-t")) { + temp=1; + argc--; + argv++; + } + if (argc<2) { + owl_function_makemsg("Not enough arguments to the subscribe command"); + return(NULL); + } + + if (argc>3) { + owl_function_makemsg("Too many arguments to the subscribe command"); + return(NULL); + } + + if (argc==2) { + recip=""; + } else if (argc==3) { + recip=argv[2]; + } + + owl_function_subscribe(argv[0], argv[1], recip); + if (!temp) { + owl_zephyr_addsub(NULL, argv[0], argv[1], recip); + } + return(NULL); +} + + +char *owl_command_unsubscribe(int argc, char **argv, char *buff) +{ + char *recip=""; + int temp=0; + + if (argc<3) { + owl_function_makemsg("Not enough arguments to the unsubscribe command"); + return(NULL); + } + argc--; + argv++; + + if (!strcmp(argv[0], "-t")) { + temp=1; + argc--; + argv++; + } + if (argc<2) { + owl_function_makemsg("Not enough arguments to the subscribe command"); + return(NULL); + } + + if (argc>3) { + owl_function_makemsg("Too many arguments to the unsubscribe command"); + return(NULL); + } + + if (argc==2) { + recip=""; + } else if (argc==3) { + recip=argv[2]; + } + + owl_function_unsubscribe(argv[0], argv[1], recip); + if (!temp) { + owl_zephyr_delsub(NULL, argv[0], argv[1], recip); + } + return(NULL); +} + +char *owl_command_echo(int argc, char **argv, char *buff) +{ + buff = skiptokens(buff, 1); + owl_function_popless_text(buff); + return NULL; +} + +void owl_command_getsubs(void) +{ + owl_function_getsubs(); +} + +void owl_command_status(void) +{ + owl_function_status(); +} + +char *owl_command_zwrite(int argc, char **argv, char *buff) +{ + owl_zwrite z; + + if (!owl_global_is_havezephyr(&g)) { + owl_function_makemsg("Zephyr is not available"); + return(NULL); + } + /* check for a zwrite -m */ + owl_zwrite_create_from_line(&z, buff); + if (owl_zwrite_is_message_set(&z)) { + owl_function_zwrite(buff, NULL); + owl_zwrite_free(&z); + return (NULL); + } + + if (argc < 2) { + owl_function_makemsg("Not enough arguments to the zwrite command."); + } else { + owl_function_zwrite_setup(buff); + } + return(NULL); +} + +char *owl_command_aimwrite(int argc, char **argv, char *buff) +{ + char *newbuff, *recip, **myargv; + int i, j, myargc; + owl_message *m; + + if (!owl_global_is_aimloggedin(&g)) { + owl_function_makemsg("You are not logged in to AIM."); + return(NULL); + } + + if (argc < 2) { + owl_function_makemsg("Not enough arguments to the aimwrite command."); + return(NULL); + } + + myargv=argv; + if (argc<0) { + owl_function_error("Unbalanced quotes in aimwrite"); + return(NULL); + } + myargc=argc; + if (myargc && *(myargv[0])!='-') { + myargc--; + myargv++; + } + while (myargc) { + if (!strcmp(myargv[0], "-m")) { + if (myargc<2) { + break; + } + + /* Once we have -m, gobble up everything else on the line */ + myargv++; + myargc--; + newbuff=owl_malloc(1); + newbuff=owl_strdup(""); + while (myargc) { + newbuff=realloc(newbuff, strlen(newbuff)+strlen(myargv[0])+5); + strcat(newbuff, myargv[0]); + strcat(newbuff, " "); + myargc--; + myargv++; + } + newbuff[strlen(newbuff)-1]='\0'; /* remove last space */ + + recip=owl_malloc(strlen(argv[0])+5); + sprintf(recip, "%s ", argv[1]); + owl_aim_send_im(recip, newbuff); + m=owl_function_make_outgoing_aim(newbuff, recip); + if (m) { + owl_global_messagequeue_addmsg(&g, m); + } else { + owl_function_error("Could not create outgoing AIM message"); + } + + owl_free(recip); + owl_free(newbuff); + return(NULL); + } else { + /* we don't care */ + myargv++; + myargc--; + } + } + + /* squish arguments together to make one screenname w/o spaces for now */ + newbuff=owl_malloc(strlen(buff)+5); + sprintf(newbuff, "%s ", argv[0]); + j=argc-1; + for (i=0; i<j; i++) { + strcat(newbuff, argv[i+1]); + } + + owl_function_aimwrite_setup(newbuff); + owl_free(newbuff); + return(NULL); +} + +char *owl_command_loopwrite(int argc, char **argv, char *buff) +{ + owl_function_loopwrite_setup(); + return(NULL); +} + +char *owl_command_zcrypt(int argc, char **argv, char *buff) +{ +#ifdef OWL_ENABLE_ZCRYPT + owl_zwrite z; + + if (!owl_global_is_havezephyr(&g)) { + owl_function_makemsg("Zephyr is not available"); + return(NULL); + } + /* check for a zcrypt -m */ + owl_zwrite_create_from_line(&z, buff); + if (owl_zwrite_is_message_set(&z)) { + owl_function_zcrypt(buff, NULL); + owl_zwrite_free(&z); + return (NULL); + } + + if (argc < 2) { + owl_function_makemsg("Not enough arguments to the zcrypt command."); + } else { + owl_function_zwrite_setup(buff); + } + return(NULL); +#else + owl_function_makemsg("This Owl does not support zcrypt"); +#endif +} + +char *owl_command_reply(int argc, char **argv, char *buff) +{ + int edit=0; + + if (argc>=2 && !strcmp("-e", argv[1])) { + edit=1; + argv++; + argc--; + } + + if ((argc==1) || (argc==2 && !strcmp(argv[1], "all"))) { + owl_function_reply(0, !edit); + } else if (argc==2 && !strcmp(argv[1], "sender")) { + owl_function_reply(1, !edit); + } else if (argc==2 && !strcmp(argv[1], "zaway")) { + owl_message *m; + owl_view *v; + v = owl_global_get_current_view(&g); + m = owl_view_get_element(v, owl_global_get_curmsg(&g)); + if (m) owl_zephyr_zaway(m); + } else { + owl_function_makemsg("Invalid arguments to the reply command."); + } + return NULL; +} + +char *owl_command_filter(int argc, char **argv, char *buff) +{ + owl_function_create_filter(argc, argv); + return NULL; +} + +char *owl_command_zlocate(int argc, char **argv, char *buff) +{ + int auth; + + if (argc<2) { + owl_function_makemsg("Too few arguments for zlocate command"); + return NULL; + } + + auth=1; + if (!strcmp(argv[1], "-d")) { + if (argc>2) { + auth=0; + argc--; + argv++; + } else { + owl_function_makemsg("Missing arguments for zlocate command"); + return NULL; + } + } + + argc--; + argv++; + owl_function_zlocate(argc, argv, auth); + return NULL; +} + + +/* Backwards compatability has made this kind of complicated: + * view [<viewname>] [-f <filter> | -d <expression> | --home | -r ] [-s <style>] + * view <filter> + * view -d <expression> + * view --home + */ +char *owl_command_view(int argc, char **argv, char *buff) +{ + /* First take the 'view --home' and 'view -r' cases */ + if (argc == 2) { + if (!strcmp(argv[1], "--home")) { + owl_function_change_currentview_filter(owl_global_get_view_home(&g)); + return(NULL); + } else if (!strcmp(argv[1], "-r")) { + char *foo; + foo=owl_function_create_negative_filter(owl_view_get_filtname(owl_global_get_current_view(&g))); + owl_function_change_currentview_filter(foo); + owl_free(foo); + return(NULL); + } + } + + /* Now look for 'view <filter>' */ + if (argc==2) { + owl_function_change_currentview_filter(argv[1]); + return(NULL); + } + + /* Now get 'view -d <expression>' */ + if (argc>=3 && !strcmp(argv[1], "-d")) { + char **myargv; + int i; + + myargv=owl_malloc((argc*sizeof(char *))+50); + myargv[0]=""; + myargv[1]="owl-dynamic"; + for (i=2; i<argc; i++) { + myargv[i]=argv[i]; + } + owl_function_create_filter(argc, myargv); + owl_function_change_currentview_filter("owl-dynamic"); + owl_free(myargv); + return NULL; + } + + /* Finally handle the general case */ + if (argc<3) { + owl_function_makemsg("Too few arguments to the view command."); + return(NULL); + } + argc--; + argv++; + if (strcmp(argv[0], "-f") && + strcmp(argv[0], "-d") && + strcmp(argv[0], "--home") && + strcmp(argv[0], "-s") && + strcmp(argv[0], "-r")) { + if (strcmp(argv[0], "main")) { + owl_function_makemsg("No view named '%s'", argv[0]); + return(NULL); + } + argc--; + argv++; + } + while (argc) { + if (!strcmp(argv[0], "-f")) { + if (argc<2) { + owl_function_makemsg("Too few argments to the view command"); + return(NULL); + } + owl_function_change_currentview_filter(argv[1]); + argc-=2; + argv+=2; + } else if (!strcmp(argv[0], "--home")) { + owl_function_change_currentview_filter(owl_global_get_view_home(&g)); + argc--; + argv++; + } else if (!strcmp(argv[0], "-r")) { + char *foo; + foo=owl_function_create_negative_filter(owl_view_get_filtname(owl_global_get_current_view(&g))); + owl_function_change_currentview_filter(foo); + } else if (!strcmp(argv[0], "-s")) { + if (argc<2) { + owl_function_makemsg("Too few argments to the view command"); + return(NULL); + } + owl_function_change_style(owl_global_get_current_view(&g), argv[1]); + argc-=2; + argv+=2; + } else { + owl_function_makemsg("Too few argments to the view command"); + return(NULL); + } + + } + return(NULL); +} + +char *owl_command_show(int argc, char **argv, char *buff) +{ + if (argc<2) { + owl_function_help_for_command("show"); + return NULL; + } + + if (!strcmp(argv[1], "filter") || !strcmp(argv[1], "filters")) { + if (argc==2) { + owl_function_show_filters(); + } else { + owl_function_show_filter(argv[2]); + } + } else if (argc==2 + && (!strcmp(argv[1], "zpunts") || !strcmp(argv[1], "zpunted"))) { + owl_function_show_zpunts(); + } else if (!strcmp(argv[1], "command") || !strcmp(argv[1], "commands")) { + if (argc==2) { + owl_function_show_commands(); + } else { + owl_function_show_command(argv[2]); + } + } else if (!strcmp(argv[1], "variable") || !strcmp(argv[1], "variables")) { + if (argc==2) { + owl_function_show_variables(); + } else { + owl_function_show_variable(argv[2]); + } + } else if (!strcmp(argv[1], "keymap") || !strcmp(argv[1], "keymaps")) { + if (argc==2) { + owl_function_show_keymaps(); + } else { + owl_function_show_keymap(argv[2]); + } + } else if (!strcmp(argv[1], "view")) { + if (argc==3) { + owl_function_show_view(argv[2]); + } else { + owl_function_show_view(NULL); + } + } else if (!strcmp(argv[1], "colors")) { + owl_function_show_colors(); + } else if (!strcmp(argv[1], "styles")) { + owl_function_show_styles(); + } else if (!strcmp(argv[1], "subs") || !strcmp(argv[1], "subscriptions")) { + owl_function_getsubs(); + } else if (!strcmp(argv[1], "terminal") || !strcmp(argv[1], "term")) { + owl_function_show_term(); + } else if (!strcmp(argv[1], "version")) { + owl_function_about(); + } else if (!strcmp(argv[1], "status")) { + owl_function_status(); + } else if (!strcmp(argv[1], "license")) { + owl_function_show_license(); + } else if (!strcmp(argv[1], "startup")) { + char *filename; + + filename=owl_sprintf("%s/%s", owl_global_get_homedir(&g), OWL_STARTUP_FILE); + owl_function_popless_file(filename); + owl_free(filename); + } else if (!strcmp(argv[1], "errors")) { + owl_function_showerrs(); + } else { + owl_function_makemsg("Unknown subcommand for 'show' command (see 'help show' for allowed args)"); + return NULL; + } + return NULL; +} + +char *owl_command_viewclass(int argc, char **argv, char *buff) +{ + char *filtname; + if (argc!=2) { + owl_function_makemsg("Wrong number of arguments to viewclass command"); + return NULL; + } + filtname = owl_function_classinstfilt(argv[1], NULL); + owl_function_change_currentview_filter(filtname); + owl_free(filtname); + return NULL; +} + +char *owl_command_viewuser(int argc, char **argv, char *buff) +{ + char *filtname; + if (argc!=2) { + owl_function_makemsg("Wrong number of arguments to viewuser command"); + return NULL; + } + filtname=owl_function_zuserfilt(argv[1]); + owl_function_change_currentview_filter(filtname); + owl_free(filtname); + return NULL; +} + + +void owl_command_pop_message(void) +{ + owl_function_curmsg_to_popwin(); +} + +char *owl_command_delete(int argc, char **argv, char *buff) +{ + int move_after = 1; + + if (argc>1 && !strcmp(argv[1], "--no-move")) { + move_after = 0; + argc--; + argv++; + } + + if (argc==1) { + owl_function_deletecur(move_after); + return NULL; + } + + if (argc==2 && !strcmp(argv[1], "view")) { + owl_function_delete_curview_msgs(1); + return NULL; + } + + if (argc==2 && !strcmp(argv[1], "trash")) { + owl_function_delete_automsgs(); + return NULL; + } + + if (argc==3 && (!strcmp(argv[1], "-id") || !strcmp(argv[1], "--id"))) { + owl_function_delete_by_id(atoi(argv[2]), 1); + return NULL; + } + + owl_function_makemsg("Unknown arguments to delete command"); + return NULL; +} + +char *owl_command_undelete(int argc, char **argv, char *buff) +{ + int move_after = 1; + + if (argc>1 && !strcmp(argv[1], "--no-move")) { + move_after = 0; + argc--; + argv++; + } + + if (argc==1) { + owl_function_undeletecur(move_after); + return NULL; + } + + if (argc==2 && !strcmp(argv[1], "view")) { + owl_function_delete_curview_msgs(0); + return NULL; + } + + if (argc==3 && (!strcmp(argv[1], "-id") || !strcmp(argv[1], "--id"))) { + owl_function_delete_by_id(atoi(argv[2]), 0); + return NULL; + } + + owl_function_makemsg("Unknown arguments to delete command"); + return NULL; +} + +void owl_command_beep() +{ + owl_function_beep(); +} + +char *owl_command_colorview(int argc, char **argv, char *buff) +{ + if (argc!=2) { + owl_function_makemsg("Wrong number of arguments to colorview command"); + return NULL; + } + owl_function_color_current_filter(argv[1]); + return NULL; +} + +char *owl_command_colorclass(int argc, char **argv, char *buff) +{ + char *filtname; + + if (argc!=3) { + owl_function_makemsg("Wrong number of arguments to colorclass command"); + return NULL; + } + + filtname=owl_function_classinstfilt(argv[1], NULL); + (void) owl_function_color_filter(filtname, argv[2]); + return NULL; +} + +char *owl_command_zpunt(int argc, char **argv, char *buff) +{ + owl_command_zpunt_and_zunpunt(argc, argv, 0); + return NULL; +} + +char *owl_command_zunpunt(int argc, char **argv, char *buff) +{ + owl_command_zpunt_and_zunpunt(argc, argv, 1); + return NULL; +} + + +void owl_command_zpunt_and_zunpunt(int argc, char **argv, int type) +{ + /* if type==0 then zpunt + * if type==1 then zunpunt + */ + char *class, *inst, *recip; + + class="message"; + inst=""; + recip="*"; + + if (argc==1) { + /* show current punt filters */ + owl_function_show_zpunts(); + return; + } else if (argc==2) { + inst=argv[1]; + } else if (argc==3) { + class=argv[1]; + inst=argv[2]; + } else if (argc==4) { + class=argv[1]; + inst=argv[2]; + recip=argv[3]; + } else { + owl_function_makemsg("Wrong number of arguments to the zpunt command"); + return; + } + + owl_function_zpunt(class, inst, recip, type); + if (type==0) { + owl_function_makemsg("<%s, %s, %s> added to punt list.", class, inst, recip); + } else if (type==1) { + owl_function_makemsg("<%s, %s, %s> removed from punt list.", class, inst, recip); + } +} + +char *owl_command_smartzpunt(int argc, char **argv, char *buff) +{ + if (argc == 1) { + owl_function_smartzpunt(0); + } else if (argc == 2 && (!strcmp(argv[1], "-i") || !strcmp(argv[1], "--instance"))) { + owl_function_smartzpunt(1); + } else { + owl_function_makemsg("Wrong number of arguments for %s", argv[0]); + } + return NULL; +} + +char *owl_command_getview(int argc, char **argv, char *buff) +{ + char *filtname; + if (argc != 1) { + owl_function_makemsg("Wrong number of arguments for %s", argv[0]); + return NULL; + } + filtname = owl_view_get_filtname(owl_global_get_current_view(&g)); + if (filtname) filtname = owl_strdup(filtname); + return filtname; +} + +char *owl_command_getvar(int argc, char **argv, char *buff) +{ + char tmpbuff[1024]; + if (argc != 2) { + owl_function_makemsg("Wrong number of arguments for %s", argv[0]); + return NULL; + } + if (owl_variable_get_tostring(owl_global_get_vardict(&g), + argv[1], tmpbuff, 1024)) { + return NULL; + } + return owl_strdup(tmpbuff); +} + +char *owl_command_search(int argc, char **argv, char *buff) +{ + int direction; + char *buffstart; + + direction=OWL_DIRECTION_DOWNWARDS; + buffstart=skiptokens(buff, 1); + if (argc>1 && !strcmp(argv[1], "-r")) { + direction=OWL_DIRECTION_UPWARDS; + buffstart=skiptokens(buff, 2); + } + + if (argc==1 || (argc==2 && !strcmp(argv[1], "-r"))) { + owl_function_search_continue(direction); + } else { + owl_function_search_start(buffstart, direction); + } + + return(NULL); +} + +char *owl_command_aimlogin(int argc, char **argv, char *buff) +{ + int ret; + + if ((argc<2) || (argc>3)) { + owl_function_makemsg("Wrong number of arguments to aimlogin command"); + return(NULL); + } + + /* if we get two arguments, ask for the password */ + if (argc==2) { + owl_global_set_buffercommand(&g, buff); + owl_function_start_password("AIM Password: "); + return(NULL); + } + + /* clear the buddylist */ + owl_buddylist_clear(owl_global_get_buddylist(&g)); + + /* try to login */ + ret=owl_aim_login(argv[1], argv[2]); + if (ret) owl_function_makemsg("Warning: login for %s failed.\n", argv[1]); + + /* this is a test */ + return(NULL); +} + +char *owl_command_aimlogout(int argc, char **argv, char *buff) +{ + /* clear the buddylist */ + owl_buddylist_clear(owl_global_get_buddylist(&g)); + + owl_aim_logout(); + return(NULL); +} + +char *owl_command_getstyle(int argc, char **argv, char *buff) +{ + char *stylename; + if (argc != 1) { + owl_function_makemsg("Wrong number of arguments for %s", argv[0]); + return NULL; + } + stylename = owl_view_get_style_name(owl_global_get_current_view(&g)); + if (stylename) stylename = owl_strdup(stylename); + return stylename; +} + +/*********************************************************************/ +/************************** EDIT SPECIFIC ****************************/ +/*********************************************************************/ + +void owl_command_edit_cancel(owl_editwin *e) +{ + owl_history *hist; + + owl_function_makemsg("Command cancelled."); + + if(e->echochar == 0) { + hist=owl_editwin_get_history(e); + owl_history_store(hist, owl_editwin_get_text(e)); + owl_history_reset(hist); + } + + owl_editwin_fullclear(e); + owl_global_set_needrefresh(&g); + wnoutrefresh(owl_editwin_get_curswin(e)); + owl_global_set_typwin_inactive(&g); + owl_editwin_new_style(e, OWL_EDITWIN_STYLE_ONELINE, NULL); + + owl_function_activate_keymap("recv"); +} + +void owl_command_edit_history_prev(owl_editwin *e) +{ + owl_history *hist; + char *ptr; + + hist=owl_editwin_get_history(e); + if (!owl_history_is_touched(hist)) { + owl_history_store(hist, owl_editwin_get_text(e)); + owl_history_set_partial(hist); + } + ptr=owl_history_get_prev(hist); + if (ptr) { + owl_editwin_clear(e); + owl_editwin_insert_string(e, ptr); + owl_editwin_redisplay(e, 0); + owl_global_set_needrefresh(&g); + } else { + owl_function_beep(); + } +} + +void owl_command_edit_history_next(owl_editwin *e) +{ + owl_history *hist; + char *ptr; + + hist=owl_editwin_get_history(e); + ptr=owl_history_get_next(hist); + if (ptr) { + owl_editwin_clear(e); + owl_editwin_insert_string(e, ptr); + owl_editwin_redisplay(e, 0); + owl_global_set_needrefresh(&g); + } else { + owl_function_beep(); + } +} + +char *owl_command_edit_insert_text(owl_editwin *e, int argc, char **argv, char *buff) +{ + buff = skiptokens(buff, 1); + owl_editwin_insert_string(e, buff); + owl_editwin_redisplay(e, 0); + owl_global_set_needrefresh(&g); + return NULL; +} + +void owl_command_editline_done(owl_editwin *e) +{ + owl_history *hist=owl_editwin_get_history(e); + char *rv, *cmd; + + owl_history_store(hist, owl_editwin_get_text(e)); + owl_history_reset(hist); + owl_global_set_typwin_inactive(&g); + cmd = owl_strdup(owl_editwin_get_text(e)); + owl_editwin_fullclear(e); + rv = owl_function_command(cmd); + owl_free(cmd); + + wnoutrefresh(owl_editwin_get_curswin(e)); + owl_global_set_needrefresh(&g); + + if (rv) { + owl_function_makemsg("%s", rv); + owl_free(rv); + } +} + + +void owl_command_editresponse_done(owl_editwin *e) +{ + owl_global_set_response(&g, owl_editwin_get_text(e)); + + owl_global_set_typwin_inactive(&g); + owl_editwin_fullclear(e); + wnoutrefresh(owl_editwin_get_curswin(e)); + owl_global_set_needrefresh(&g); + + owl_function_run_buffercommand(); +} + + +void owl_command_editmulti_done(owl_editwin *e) +{ + owl_history *hist=owl_editwin_get_history(e); + + owl_history_store(hist, owl_editwin_get_text(e)); + owl_history_reset(hist); + + owl_function_run_buffercommand(); + owl_editwin_new_style(e, OWL_EDITWIN_STYLE_ONELINE, NULL); + owl_editwin_fullclear(e); + owl_global_set_typwin_inactive(&g); + owl_global_set_needrefresh(&g); + wnoutrefresh(owl_editwin_get_curswin(e)); +} + +void owl_command_editmulti_done_or_delete(owl_editwin *e) +{ + if (owl_editwin_is_at_end(e)) { + owl_command_editmulti_done(e); + } else { + owl_editwin_delete_char(e); + } +} + + +/*********************************************************************/ +/*********************** POPLESS SPECIFIC ****************************/ +/*********************************************************************/ + +void owl_command_popless_quit(owl_viewwin *vw) +{ + owl_popwin_close(owl_global_get_popwin(&g)); + owl_viewwin_free(vw); + owl_global_set_needrefresh(&g); +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..577df0f --- /dev/null +++ b/config.h @@ -0,0 +1,122 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the <com_err.h> header file. */ +#define HAVE_COM_ERR_H 1 + +/* Define to 1 if you have the `des_ecb_encrypt' function. */ +#define HAVE_DES_ECB_ENCRYPT 1 + +/* have proto for des_ecb_encrypt */ +#define HAVE_DES_ECB_ENCRYPT_PROTO + +/* Define to 1 if you have the `des_key_sched' function. */ +#define HAVE_DES_KEY_SCHED 1 + +/* Define to 1 if you have the `des_string_to_key' function. */ +#define HAVE_DES_STRING_TO_KEY 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `com_err' library (-lcom_err). */ +#define HAVE_LIBCOM_ERR 1 + +/* Define to 1 if you have the `curses' library (-lcurses). */ +/* #undef HAVE_LIBCURSES */ + +/* Define to 1 if you have the `des425' library (-ldes425). */ +#define HAVE_LIBDES425 1 + +/* Define to 1 if you have the `k5crypto' library (-lk5crypto). */ +#define HAVE_LIBK5CRYPTO 1 + +/* Define to 1 if you have the `krb' library (-lkrb). */ +/* #undef HAVE_LIBKRB */ + +/* Define to 1 if you have the `krb4' library (-lkrb4). */ +#define HAVE_LIBKRB4 1 + +/* Define to 1 if you have the `krb5' library (-lkrb5). */ +#define HAVE_LIBKRB5 1 + +/* Define to 1 if you have the `ncurses' library (-lncurses). */ +#define HAVE_LIBNCURSES 1 + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +#define HAVE_LIBNSL 1 + +/* Define to 1 if you have the `resolv' library (-lresolv). */ +#define HAVE_LIBRESOLV 1 + +/* Define to 1 if you have the `socket' library (-lsocket). */ +#define HAVE_LIBSOCKET 1 + +/* Define to 1 if you have the `ssp' library (-lssp). */ +/* #undef HAVE_LIBSSP */ + +/* Define to 1 if you have the `zephyr' library (-lzephyr). */ +#define HAVE_LIBZEPHYR 1 + +/* Have ZInitLocationInfo */ +#define HAVE_LIBZEPHYR_ZINITLOCATIONINFO + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `resizeterm' function. */ +#define HAVE_RESIZETERM 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/filio.h> header file. */ +#define HAVE_SYS_FILIO_H 1 + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `use_default_colors' function. */ +#define HAVE_USE_DEFAULT_COLORS 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +/* #undef STDC_HEADERS */ + +/* Have terminfo */ +#define TERMINFO "/usr/share/lib/terminfo" diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..4d86087 --- /dev/null +++ b/config.h.in @@ -0,0 +1,121 @@ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the <com_err.h> header file. */ +#undef HAVE_COM_ERR_H + +/* Define to 1 if you have the `des_ecb_encrypt' function. */ +#undef HAVE_DES_ECB_ENCRYPT + +/* have proto for des_ecb_encrypt */ +#undef HAVE_DES_ECB_ENCRYPT_PROTO + +/* Define to 1 if you have the `des_key_sched' function. */ +#undef HAVE_DES_KEY_SCHED + +/* Define to 1 if you have the `des_string_to_key' function. */ +#undef HAVE_DES_STRING_TO_KEY + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `com_err' library (-lcom_err). */ +#undef HAVE_LIBCOM_ERR + +/* Define to 1 if you have the `curses' library (-lcurses). */ +#undef HAVE_LIBCURSES + +/* Define to 1 if you have the `des425' library (-ldes425). */ +#undef HAVE_LIBDES425 + +/* Define to 1 if you have the `k5crypto' library (-lk5crypto). */ +#undef HAVE_LIBK5CRYPTO + +/* Define to 1 if you have the `krb' library (-lkrb). */ +#undef HAVE_LIBKRB + +/* Define to 1 if you have the `krb4' library (-lkrb4). */ +#undef HAVE_LIBKRB4 + +/* Define to 1 if you have the `krb5' library (-lkrb5). */ +#undef HAVE_LIBKRB5 + +/* Define to 1 if you have the `ncurses' library (-lncurses). */ +#undef HAVE_LIBNCURSES + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +#undef HAVE_LIBNSL + +/* Define to 1 if you have the `resolv' library (-lresolv). */ +#undef HAVE_LIBRESOLV + +/* Define to 1 if you have the `socket' library (-lsocket). */ +#undef HAVE_LIBSOCKET + +/* Define to 1 if you have the `ssp' library (-lssp). */ +#undef HAVE_LIBSSP + +/* Define to 1 if you have the `zephyr' library (-lzephyr). */ +#undef HAVE_LIBZEPHYR + +/* Have ZInitLocationInfo */ +#undef HAVE_LIBZEPHYR_ZINITLOCATIONINFO + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `resizeterm' function. */ +#undef HAVE_RESIZETERM + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/filio.h> header file. */ +#undef HAVE_SYS_FILIO_H + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `use_default_colors' function. */ +#undef HAVE_USE_DEFAULT_COLORS + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Have terminfo */ +#undef TERMINFO diff --git a/configure b/configure new file mode 100755 index 0000000..141b04e --- /dev/null +++ b/configure @@ -0,0 +1,6215 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.61. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell autoconf@gnu.org about your system, + echo including any error possibly output before this + echo message +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 </dev/null 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="owl.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='SHELL +PATH_SEPARATOR +PACKAGE_NAME +PACKAGE_TARNAME +PACKAGE_VERSION +PACKAGE_STRING +PACKAGE_BUGREPORT +exec_prefix +prefix +program_transform_name +bindir +sbindir +libexecdir +datarootdir +datadir +sysconfdir +sharedstatedir +localstatedir +includedir +oldincludedir +docdir +infodir +htmldir +dvidir +pdfdir +psdir +libdir +localedir +mandir +DEFS +ECHO_C +ECHO_N +ECHO_T +LIBS +build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +CPPFLAGS +ac_ct_CC +EXEEXT +OBJEXT +CPP +GREP +EGREP +PKG_CONFIG +GLIB_CFLAGS +GLIB_LIBS +XSUBPPDIR +INSTALL_PROGRAM +INSTALL_SCRIPT +INSTALL_DATA +subdirs +LIBOBJS +LTLIBOBJS' +ac_subst_files='' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +PKG_CONFIG +GLIB_CFLAGS +GLIB_LIBS' +ac_subdirs_all='libfaim' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` + eval enable_$ac_feature=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` + eval enable_$ac_feature=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/[-.]/_/g'` + eval with_$ac_package=\$ac_optarg ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/[-.]/_/g'` + eval with_$ac_package=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute directory names. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { echo "$as_me: error: Working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$0" || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CPP C preprocessor + PKG_CONFIG path to pkg-config utility + GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config + GLIB_LIBS linker flags for GLIB, overriding pkg-config + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.61 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.61. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -n "$CONFIG_SITE"; then + set x "$CONFIG_SITE" +elif test "x$prefix" != xNONE; then + set x "$prefix/share/config.site" "$prefix/etc/config.site" +else + set x "$ac_default_prefix/share/config.site" \ + "$ac_default_prefix/etc/config.site" +fi +shift +for ac_site_file +do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_config_headers="$ac_config_headers config.h" + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO: checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +# +# List of possible output files, starting from the most likely. +# The algorithm is not robust to junk in `.', hence go to wildcards (a.*) +# only as a last resort. b.out is created by i960 compilers. +ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' +# +# The IRIX 6 linker writes into existing files which may not be +# executable, retaining their permissions. Remove them first so a +# subsequent execution test works. +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6; } +if test -z "$ac_file"; then + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +{ echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } +{ echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6; } + +{ echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6; } ;; + xno) + { echo "$as_me:$LINENO: result: unsupported" >&5 +echo "${ECHO_T}unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +if test "$GCC" = yes; then + CFLAGS="$CFLAGS -Wall -g -D_FORTIFY_SOURCE=2"; +fi + +{ echo "$as_me:$LINENO: checking for /usr/athena/include" >&5 +echo $ECHO_N "checking for /usr/athena/include... $ECHO_C" >&6; } +if test -d /usr/athena/include; then + CFLAGS=${CFLAGS}\ -I/usr/athena/include + CPPFLAGS=${CPPFLAGS}\ -I/usr/athena/include + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi +{ echo "$as_me:$LINENO: checking for /usr/athena/lib" >&5 +echo $ECHO_N "checking for /usr/athena/lib... $ECHO_C" >&6; } +if test -d /usr/athena/lib; then + LDFLAGS=-L/usr/athena/lib\ ${LDFLAGS} + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + +{ echo "$as_me:$LINENO: checking for /usr/include/kerberosIV" >&5 +echo $ECHO_N "checking for /usr/include/kerberosIV... $ECHO_C" >&6; } +{ echo "$as_me:$LINENO: result: DEPRECATED" >&5 +echo "${ECHO_T}DEPRECATED" >&6; } +if test -d /usr/include/openssl; then + CFLAGS=${CFLAGS}\ -I/usr/include/openssl + CPPFLAGS=${CPPFLAGS}\ -I/usr/include/openssl + { echo "$as_me:$LINENO: result: OpenSSL DES found instead" >&5 +echo "${ECHO_T}OpenSSL DES found instead" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + +PROTECT_CFLAGS=${PROTECT_CFLAGS-"-fstack-protector"} +SAVE_CFLAGS=$CFLAGS +CFLAGS="$CFLAGS $PROTECT_CFLAGS" +{ echo "$as_me:$LINENO: checking whether protection cflags work" >&5 +echo $ECHO_N "checking whether protection cflags work... $ECHO_C" >&6; } + +cat >conftest.$ac_ext <<_ACEOF +int i; +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + CFLAGS=$SAVE_CFLAGS +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + +{ echo "$as_me:$LINENO: checking for __stack_chk_guard in -lssp" >&5 +echo $ECHO_N "checking for __stack_chk_guard in -lssp... $ECHO_C" >&6; } +if test "${ac_cv_lib_ssp___stack_chk_guard+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lssp $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char __stack_chk_guard (); +int +main () +{ +return __stack_chk_guard (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_ssp___stack_chk_guard=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_ssp___stack_chk_guard=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_ssp___stack_chk_guard" >&5 +echo "${ECHO_T}$ac_cv_lib_ssp___stack_chk_guard" >&6; } +if test $ac_cv_lib_ssp___stack_chk_guard = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSSP 1 +_ACEOF + + LIBS="-lssp $LIBS" + +fi + + +{ echo "$as_me:$LINENO: checking for initscr in -lncurses" >&5 +echo $ECHO_N "checking for initscr in -lncurses... $ECHO_C" >&6; } +if test "${ac_cv_lib_ncurses_initscr+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lncurses $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char initscr (); +int +main () +{ +return initscr (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_ncurses_initscr=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_ncurses_initscr=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_ncurses_initscr" >&5 +echo "${ECHO_T}$ac_cv_lib_ncurses_initscr" >&6; } +if test $ac_cv_lib_ncurses_initscr = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBNCURSES 1 +_ACEOF + + LIBS="-lncurses $LIBS" + +else + +{ echo "$as_me:$LINENO: checking for initscr in -lcurses" >&5 +echo $ECHO_N "checking for initscr in -lcurses... $ECHO_C" >&6; } +if test "${ac_cv_lib_curses_initscr+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcurses $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char initscr (); +int +main () +{ +return initscr (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_curses_initscr=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_curses_initscr=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_curses_initscr" >&5 +echo "${ECHO_T}$ac_cv_lib_curses_initscr" >&6; } +if test $ac_cv_lib_curses_initscr = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBCURSES 1 +_ACEOF + + LIBS="-lcurses $LIBS" + +else + { { echo "$as_me:$LINENO: error: No curses library found." >&5 +echo "$as_me: error: No curses library found." >&2;} + { (exit 1); exit 1; }; } +fi + +fi + + +{ echo "$as_me:$LINENO: checking for com_err in -lcom_err" >&5 +echo $ECHO_N "checking for com_err in -lcom_err... $ECHO_C" >&6; } +if test "${ac_cv_lib_com_err_com_err+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcom_err $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char com_err (); +int +main () +{ +return com_err (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_com_err_com_err=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_com_err_com_err=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_com_err_com_err" >&5 +echo "${ECHO_T}$ac_cv_lib_com_err_com_err" >&6; } +if test $ac_cv_lib_com_err_com_err = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBCOM_ERR 1 +_ACEOF + + LIBS="-lcom_err $LIBS" + +fi + + +{ echo "$as_me:$LINENO: checking for gethostbyname in -lnsl" >&5 +echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6; } +if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char gethostbyname (); +int +main () +{ +return gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_nsl_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_nsl_gethostbyname=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6; } +if test $ac_cv_lib_nsl_gethostbyname = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBNSL 1 +_ACEOF + + LIBS="-lnsl $LIBS" + +fi + + +{ echo "$as_me:$LINENO: checking for socket in -lsocket" >&5 +echo $ECHO_N "checking for socket in -lsocket... $ECHO_C" >&6; } +if test "${ac_cv_lib_socket_socket+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char socket (); +int +main () +{ +return socket (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_socket_socket=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_socket_socket=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_socket_socket" >&5 +echo "${ECHO_T}$ac_cv_lib_socket_socket" >&6; } +if test $ac_cv_lib_socket_socket = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSOCKET 1 +_ACEOF + + LIBS="-lsocket $LIBS" + +fi + + +{ echo "$as_me:$LINENO: checking for DES_cbc_encrypt in -lcrypto" >&5 +echo $ECHO_N "checking for DES_cbc_encrypt in -lcrypto... $ECHO_C" >&6; } +if test "${ac_cv_lib_crypto_DES_cbc_encrypt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcrypto $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char DES_cbc_encrypt (); +int +main () +{ +return DES_cbc_encrypt (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_crypto_DES_cbc_encrypt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_crypto_DES_cbc_encrypt=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_crypto_DES_cbc_encrypt" >&5 +echo "${ECHO_T}$ac_cv_lib_crypto_DES_cbc_encrypt" >&6; } +if test $ac_cv_lib_crypto_DES_cbc_encrypt = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBCRYPTO 1 +_ACEOF + + LIBS="-lcrypto $LIBS" + +fi + + +{ echo "$as_me:$LINENO: checking for res_search in -lresolv" >&5 +echo $ECHO_N "checking for res_search in -lresolv... $ECHO_C" >&6; } +if test "${ac_cv_lib_resolv_res_search+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lresolv $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char res_search (); +int +main () +{ +return res_search (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_resolv_res_search=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_resolv_res_search=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_resolv_res_search" >&5 +echo "${ECHO_T}$ac_cv_lib_resolv_res_search" >&6; } +if test $ac_cv_lib_resolv_res_search = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBRESOLV 1 +_ACEOF + + LIBS="-lresolv $LIBS" + +fi + + +{ echo "$as_me:$LINENO: checking for ZGetSender in -lzephyr" >&5 +echo $ECHO_N "checking for ZGetSender in -lzephyr... $ECHO_C" >&6; } +if test "${ac_cv_lib_zephyr_ZGetSender+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lzephyr $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ZGetSender (); +int +main () +{ +return ZGetSender (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_zephyr_ZGetSender=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_zephyr_ZGetSender=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_zephyr_ZGetSender" >&5 +echo "${ECHO_T}$ac_cv_lib_zephyr_ZGetSender" >&6; } +if test $ac_cv_lib_zephyr_ZGetSender = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBZEPHYR 1 +_ACEOF + + LIBS="-lzephyr $LIBS" + +fi + +{ echo "$as_me:$LINENO: checking for ZInitLocationInfo in -lzephyr" >&5 +echo $ECHO_N "checking for ZInitLocationInfo in -lzephyr... $ECHO_C" >&6; } +if test "${ac_cv_lib_zephyr_ZInitLocationInfo+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lzephyr $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ZInitLocationInfo (); +int +main () +{ +return ZInitLocationInfo (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_zephyr_ZInitLocationInfo=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_zephyr_ZInitLocationInfo=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_zephyr_ZInitLocationInfo" >&5 +echo "${ECHO_T}$ac_cv_lib_zephyr_ZInitLocationInfo" >&6; } +if test $ac_cv_lib_zephyr_ZInitLocationInfo = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBZEPHYR_ZINITLOCATIONINFO +_ACEOF + +fi + + + + + +for ac_func in DES_string_to_key DES_ecb_encrypt DES_key_sched +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + + + + +for ac_func in use_default_colors resizeterm des_string_to_key des_key_sched des_ecb_encrypt +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +{ echo "$as_me:$LINENO: checking for des_ecb_encrypt prototype" >&5 +echo $ECHO_N "checking for des_ecb_encrypt prototype... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <des.h> +int des_ecb_encrypt(char foo[], char bar[], des_key_schedule baz, int qux); +int +main () +{ +int foo = des_ecb_encrypt(0,0,0,0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_des_ecb_encrypt_proto=no +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_des_ecb_encrypt_proto=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_cv_des_ecb_encrypt_proto" >&5 +echo "${ECHO_T}$ac_cv_des_ecb_encrypt_proto" >&6; } +if test "$ac_cv_des_ecb_encrypt_proto" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DES_ECB_ENCRYPT_PROTO +_ACEOF + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 +echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; } +if test "${ac_cv_path_GREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Extract the first word of "grep ggrep" to use in msg output +if test -z "$GREP"; then +set dummy grep ggrep; ac_prog_name=$2 +if test "${ac_cv_path_GREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_path_GREP_found=false +# Loop through the user's path and test for each of PROGNAME-LIST +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue + # Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + + $ac_path_GREP_found && break 3 + done +done + +done +IFS=$as_save_IFS + + +fi + +GREP="$ac_cv_path_GREP" +if test -z "$GREP"; then + { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } +fi + +else + ac_cv_path_GREP=$GREP +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 +echo "${ECHO_T}$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + # Extract the first word of "egrep" to use in msg output +if test -z "$EGREP"; then +set dummy egrep; ac_prog_name=$2 +if test "${ac_cv_path_EGREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_path_EGREP_found=false +# Loop through the user's path and test for each of PROGNAME-LIST +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue + # Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + + $ac_path_EGREP_found && break 3 + done +done + +done +IFS=$as_save_IFS + + +fi + +EGREP="$ac_cv_path_EGREP" +if test -z "$EGREP"; then + { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } +fi + +else + ac_cv_path_EGREP=$EGREP +fi + + + fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 +echo "${ECHO_T}$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +{ echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6; } +if test "${ac_cv_header_sys_wait_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/wait.h> +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_sys_wait_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_sys_wait_h=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6; } +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + +for ac_header in strings.h sys/ioctl.h sys/filio.h unistd.h com_err.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_path_PKG_CONFIG+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5 +echo "${ECHO_T}$PKG_CONFIG" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { echo "$as_me:$LINENO: result: $ac_pt_PKG_CONFIG" >&5 +echo "${ECHO_T}$ac_pt_PKG_CONFIG" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 + { echo "$as_me:$LINENO: checking pkg-config is at least version $_pkg_min_version" >&5 +echo $ECHO_N "checking pkg-config is at least version $_pkg_min_version... $ECHO_C" >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + PKG_CONFIG="" + fi + +fi + +pkg_failed=no +{ echo "$as_me:$LINENO: checking for GLIB" >&5 +echo $ECHO_N "checking for GLIB... $ECHO_C" >&6; } + +if test -n "$PKG_CONFIG"; then + if test -n "$GLIB_CFLAGS"; then + pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS" + else + if test -n "$PKG_CONFIG" && \ + { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0\"") >&5 + ($PKG_CONFIG --exists --print-errors "glib-2.0") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0" 2>/dev/null` +else + pkg_failed=yes +fi + fi +else + pkg_failed=untried +fi +if test -n "$PKG_CONFIG"; then + if test -n "$GLIB_LIBS"; then + pkg_cv_GLIB_LIBS="$GLIB_LIBS" + else + if test -n "$PKG_CONFIG" && \ + { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0\"") >&5 + ($PKG_CONFIG --exists --print-errors "glib-2.0") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0" 2>/dev/null` +else + pkg_failed=yes +fi + fi +else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "glib-2.0"` + else + GLIB_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "glib-2.0"` + fi + # Put the nasty error message in config.log where it belongs + echo "$GLIB_PKG_ERRORS" >&5 + + { { echo "$as_me:$LINENO: error: Package requirements (glib-2.0) were not met: + +$GLIB_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +Alternatively, you may set the environment variables GLIB_CFLAGS +and GLIB_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details. +" >&5 +echo "$as_me: error: Package requirements (glib-2.0) were not met: + +$GLIB_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +Alternatively, you may set the environment variables GLIB_CFLAGS +and GLIB_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details. +" >&2;} + { (exit 1); exit 1; }; } +elif test $pkg_failed = untried; then + { { echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +Alternatively, you may set the environment variables GLIB_CFLAGS +and GLIB_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details. + +To get pkg-config, see <http://pkg-config.freedesktop.org/>. +See \`config.log' for more details." >&5 +echo "$as_me: error: The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +Alternatively, you may set the environment variables GLIB_CFLAGS +and GLIB_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details. + +To get pkg-config, see <http://pkg-config.freedesktop.org/>. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS + GLIB_LIBS=$pkg_cv_GLIB_LIBS + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + : +fi + +echo Adding glib-2.0 CFLAGS ${GLIB_CFLAGS} +CFLAGS="${GLIB_CFLAGS} ${CFLAGS}" +echo Adding glib-2.0 LDFLAGS ${GLIB_LIBS} +LDFLAGS="${GLIB_LIBS} ${LDFLAGS}" + +FOO=`perl -MExtUtils::Embed -e ccopts` +echo Adding perl CFLAGS ${FOO} +CFLAGS=${CFLAGS}\ ${FOO} + +{ echo "$as_me:$LINENO: checking for the perl xsubpp precompiler" >&5 +echo $ECHO_N "checking for the perl xsubpp precompiler... $ECHO_C" >&6; } +XSUBPPDIR="`(perl -MExtUtils::MakeMaker -e 'print ExtUtils::MakeMaker->new({NAME => qw(owl)})->tool_xsubpp;') | grep \^XSUBPPDIR | sed -e 's/XSUBPPDIR = //g;'`" +if test -n "${XSUBPPDIR}"; then + { echo "$as_me:$LINENO: result: ${XSUBPPDIR}" >&5 +echo "${ECHO_T}${XSUBPPDIR}" >&6; } +else + { { echo "$as_me:$LINENO: error: not found" >&5 +echo "$as_me: error: not found" >&2;} + { (exit 1); exit 1; }; } +fi + +FOO=`perl -MExtUtils::Embed -e ldopts | sed 's/-Wl,-E//' | sed 's/-liconv//'` +echo Adding perl LDFLAGS ${FOO} +LDFLAGS=${LDFLAGS}\ ${FOO} + +{ echo "$as_me:$LINENO: checking for /usr/share/terminfo" >&5 +echo $ECHO_N "checking for /usr/share/terminfo... $ECHO_C" >&6; } +if test "${ac_cv_file__usr_share_terminfo+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + test "$cross_compiling" = yes && + { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 +echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} + { (exit 1); exit 1; }; } +if test -r "/usr/share/terminfo"; then + ac_cv_file__usr_share_terminfo=yes +else + ac_cv_file__usr_share_terminfo=no +fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_file__usr_share_terminfo" >&5 +echo "${ECHO_T}$ac_cv_file__usr_share_terminfo" >&6; } +if test $ac_cv_file__usr_share_terminfo = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TERMINFO "/usr/share/terminfo" +_ACEOF + +else + { echo "$as_me:$LINENO: checking for /usr/share/lib/terminfo" >&5 +echo $ECHO_N "checking for /usr/share/lib/terminfo... $ECHO_C" >&6; } +if test "${ac_cv_file__usr_share_lib_terminfo+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + test "$cross_compiling" = yes && + { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 +echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} + { (exit 1); exit 1; }; } +if test -r "/usr/share/lib/terminfo"; then + ac_cv_file__usr_share_lib_terminfo=yes +else + ac_cv_file__usr_share_lib_terminfo=no +fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_file__usr_share_lib_terminfo" >&5 +echo "${ECHO_T}$ac_cv_file__usr_share_lib_terminfo" >&6; } +if test $ac_cv_file__usr_share_lib_terminfo = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TERMINFO "/usr/share/lib/terminfo" +_ACEOF + +else + { { echo "$as_me:$LINENO: error: No terminfo found for this system" >&5 +echo "$as_me: error: No terminfo found for this system" >&2;} + { (exit 1); exit 1; }; } +fi + +fi + + + + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5 +echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +{ echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done +IFS=$as_save_IFS + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +subdirs="$subdirs libfaim" + + +ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { echo "$as_me:$LINENO: updating cache $cache_file" >&5 +echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.61. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to <bug-autoconf@gnu.org>." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.61, + with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2006 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + { echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + CONFIG_SHELL=$SHELL + export CONFIG_SHELL + exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# +# Set up the sed scripts for CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "$CONFIG_FILES"; then + +_ACEOF + + + +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + cat >conf$$subs.sed <<_ACEOF +SHELL!$SHELL$ac_delim +PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim +PACKAGE_NAME!$PACKAGE_NAME$ac_delim +PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim +PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim +PACKAGE_STRING!$PACKAGE_STRING$ac_delim +PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim +exec_prefix!$exec_prefix$ac_delim +prefix!$prefix$ac_delim +program_transform_name!$program_transform_name$ac_delim +bindir!$bindir$ac_delim +sbindir!$sbindir$ac_delim +libexecdir!$libexecdir$ac_delim +datarootdir!$datarootdir$ac_delim +datadir!$datadir$ac_delim +sysconfdir!$sysconfdir$ac_delim +sharedstatedir!$sharedstatedir$ac_delim +localstatedir!$localstatedir$ac_delim +includedir!$includedir$ac_delim +oldincludedir!$oldincludedir$ac_delim +docdir!$docdir$ac_delim +infodir!$infodir$ac_delim +htmldir!$htmldir$ac_delim +dvidir!$dvidir$ac_delim +pdfdir!$pdfdir$ac_delim +psdir!$psdir$ac_delim +libdir!$libdir$ac_delim +localedir!$localedir$ac_delim +mandir!$mandir$ac_delim +DEFS!$DEFS$ac_delim +ECHO_C!$ECHO_C$ac_delim +ECHO_N!$ECHO_N$ac_delim +ECHO_T!$ECHO_T$ac_delim +LIBS!$LIBS$ac_delim +build_alias!$build_alias$ac_delim +host_alias!$host_alias$ac_delim +target_alias!$target_alias$ac_delim +CC!$CC$ac_delim +CFLAGS!$CFLAGS$ac_delim +LDFLAGS!$LDFLAGS$ac_delim +CPPFLAGS!$CPPFLAGS$ac_delim +ac_ct_CC!$ac_ct_CC$ac_delim +EXEEXT!$EXEEXT$ac_delim +OBJEXT!$OBJEXT$ac_delim +CPP!$CPP$ac_delim +GREP!$GREP$ac_delim +EGREP!$EGREP$ac_delim +PKG_CONFIG!$PKG_CONFIG$ac_delim +GLIB_CFLAGS!$GLIB_CFLAGS$ac_delim +GLIB_LIBS!$GLIB_LIBS$ac_delim +XSUBPPDIR!$XSUBPPDIR$ac_delim +INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim +INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim +INSTALL_DATA!$INSTALL_DATA$ac_delim +subdirs!$subdirs$ac_delim +LIBOBJS!$LIBOBJS$ac_delim +LTLIBOBJS!$LTLIBOBJS$ac_delim +_ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 57; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` +if test -n "$ac_eof"; then + ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` + ac_eof=`expr $ac_eof + 1` +fi + +cat >>$CONFIG_STATUS <<_ACEOF +cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end +_ACEOF +sed ' +s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g +s/^/s,@/; s/!/@,|#_!!_#|/ +:n +t n +s/'"$ac_delim"'$/,g/; t +s/$/\\/; p +N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n +' >>$CONFIG_STATUS <conf$$subs.sed +rm -f conf$$subs.sed +cat >>$CONFIG_STATUS <<_ACEOF +:end +s/|#_!!_#|//g +CEOF$ac_eof +_ACEOF + + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF +fi # test -n "$CONFIG_FILES" + + +for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 +echo "$as_me: error: Invalid tag $ac_tag." >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + ac_file_inputs="$ac_file_inputs $ac_f" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input="Generated from "`IFS=: + echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + fi + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin";; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +case `sed -n '/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' $ac_file_inputs` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s&@configure_input@&$configure_input&;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out"; rm -f "$tmp/out";; + *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; + esac + ;; + :H) + # + # CONFIG_HEADER + # +_ACEOF + +# Transform confdefs.h into a sed script `conftest.defines', that +# substitutes the proper values into config.h.in to produce config.h. +rm -f conftest.defines conftest.tail +# First, append a space to every undef/define line, to ease matching. +echo 's/$/ /' >conftest.defines +# Then, protect against being on the right side of a sed subst, or in +# an unquoted here document, in config.status. If some macros were +# called several times there might be several #defines for the same +# symbol, which is useless. But do not sort them, since the last +# AC_DEFINE must be honored. +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where +# NAME is the cpp macro being defined, VALUE is the value it is being given. +# PARAMS is the parameter list in the macro definition--in most cases, it's +# just an empty string. +ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*' +ac_dB='\\)[ (].*,\\1define\\2' +ac_dC=' ' +ac_dD=' ,' + +uniq confdefs.h | + sed -n ' + t rset + :rset + s/^[ ]*#[ ]*define[ ][ ]*// + t ok + d + :ok + s/[\\&,]/\\&/g + s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p + s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p + ' >>conftest.defines + +# Remove the space that was appended to ease matching. +# Then replace #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +# (The regexp can be short, since the line contains either #define or #undef.) +echo 's/ $// +s,^[ #]*u.*,/* & */,' >>conftest.defines + +# Break up conftest.defines: +ac_max_sed_lines=50 + +# First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1" +# Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2" +# Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1" +# et cetera. +ac_in='$ac_file_inputs' +ac_out='"$tmp/out1"' +ac_nxt='"$tmp/out2"' + +while : +do + # Write a here document: + cat >>$CONFIG_STATUS <<_ACEOF + # First, check the format of the line: + cat >"\$tmp/defines.sed" <<\\CEOF +/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def +/^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def +b +:def +_ACEOF + sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS + ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in + sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail + grep . conftest.tail >/dev/null || break + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines conftest.tail + +echo "ac_result=$ac_in" >>$CONFIG_STATUS +cat >>$CONFIG_STATUS <<\_ACEOF + if test x"$ac_file" != x-; then + echo "/* $configure_input */" >"$tmp/config.h" + cat "$ac_result" >>"$tmp/config.h" + if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f $ac_file + mv "$tmp/config.h" $ac_file + fi + else + echo "/* $configure_input */" + cat "$ac_result" + fi + rm -f "$tmp/out12" + ;; + + + esac + +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + +# +# CONFIG_SUBDIRS section. +# +if test "$no_recursion" != yes; then + + # Remove --cache-file and --srcdir arguments so they do not pile up. + ac_sub_configure_args= + ac_prev= + eval "set x $ac_configure_args" + shift + for ac_arg + do + if test -n "$ac_prev"; then + ac_prev= + continue + fi + case $ac_arg in + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \ + | --c=*) + ;; + --config-cache | -C) + ;; + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + ;; + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + ;; + *) + case $ac_arg in + *\'*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + ac_sub_configure_args="$ac_sub_configure_args '$ac_arg'" ;; + esac + done + + # Always prepend --prefix to ensure using the same prefix + # in subdir configurations. + ac_arg="--prefix=$prefix" + case $ac_arg in + *\'*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + ac_sub_configure_args="'$ac_arg' $ac_sub_configure_args" + + # Pass --silent + if test "$silent" = yes; then + ac_sub_configure_args="--silent $ac_sub_configure_args" + fi + + ac_popdir=`pwd` + for ac_dir in : $subdirs; do test "x$ac_dir" = x: && continue + + # Do not complain, so a configure script can configure whichever + # parts of a large source tree are present. + test -d "$srcdir/$ac_dir" || continue + + ac_msg="=== configuring in $ac_dir (`pwd`/$ac_dir)" + echo "$as_me:$LINENO: $ac_msg" >&5 + echo "$ac_msg" >&6 + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + cd "$ac_dir" + + # Check for guested configure; otherwise get Cygnus style configure. + if test -f "$ac_srcdir/configure.gnu"; then + ac_sub_configure=$ac_srcdir/configure.gnu + elif test -f "$ac_srcdir/configure"; then + ac_sub_configure=$ac_srcdir/configure + elif test -f "$ac_srcdir/configure.in"; then + # This should be Cygnus configure. + ac_sub_configure=$ac_aux_dir/configure + else + { echo "$as_me:$LINENO: WARNING: no configuration information is in $ac_dir" >&5 +echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2;} + ac_sub_configure= + fi + + # The recursion is here. + if test -n "$ac_sub_configure"; then + # Make the cache file name correct relative to the subdirectory. + case $cache_file in + [\\/]* | ?:[\\/]* ) ac_sub_cache_file=$cache_file ;; + *) # Relative name. + ac_sub_cache_file=$ac_top_build_prefix$cache_file ;; + esac + + { echo "$as_me:$LINENO: running $SHELL $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&5 +echo "$as_me: running $SHELL $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&6;} + # The eval makes quoting arguments work. + eval "\$SHELL \"\$ac_sub_configure\" $ac_sub_configure_args \ + --cache-file=\"\$ac_sub_cache_file\" --srcdir=\"\$ac_srcdir\"" || + { { echo "$as_me:$LINENO: error: $ac_sub_configure failed for $ac_dir" >&5 +echo "$as_me: error: $ac_sub_configure failed for $ac_dir" >&2;} + { (exit 1); exit 1; }; } + fi + + cd "$ac_popdir" + done +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..d37527a --- /dev/null +++ b/configure.in @@ -0,0 +1,139 @@ +dnl $Id: configure.in,v 1.25 2009/03/29 16:03:54 kretch Exp $ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(owl.c) + +AC_CONFIG_HEADER(config.h) + +AC_PROG_CC + +dnl If we're using GCC, enable all warnings +if test "$GCC" = yes; then + CFLAGS="$CFLAGS -Wall -g -D_FORTIFY_SOURCE=2"; +fi + +dnl Check for Athena +AC_MSG_CHECKING(for /usr/athena/include) +if test -d /usr/athena/include; then + CFLAGS=${CFLAGS}\ -I/usr/athena/include + CPPFLAGS=${CPPFLAGS}\ -I/usr/athena/include + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_MSG_CHECKING(for /usr/athena/lib) +if test -d /usr/athena/lib; then + LDFLAGS=-L/usr/athena/lib\ ${LDFLAGS} + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +dnl Check for kerberosIV include +AC_MSG_CHECKING(for /usr/include/kerberosIV) +dnl per debbugs#517019 +AC_MSG_RESULT(DEPRECATED) +dnl if test -d /usr/include/kerberosIV; then +dnl CFLAGS=${CFLAGS}\ -I/usr/include/kerberosIV +dnl CPPFLAGS=${CPPFLAGS}\ -I/usr/include/kerberosIV +dnl AC_MSG_RESULT(yes) +dnl el +if test -d /usr/include/openssl; then + CFLAGS=${CFLAGS}\ -I/usr/include/openssl + CPPFLAGS=${CPPFLAGS}\ -I/usr/include/openssl + AC_MSG_RESULT(OpenSSL DES found instead) +else + AC_MSG_RESULT(no) +fi +dnl openssl stanza per debbugs#517019 + +dnl check for stack-protector +PROTECT_CFLAGS=${PROTECT_CFLAGS-"-fstack-protector"} +SAVE_CFLAGS=$CFLAGS +CFLAGS="$CFLAGS $PROTECT_CFLAGS" +AC_MSG_CHECKING(whether protection cflags work) +AC_COMPILE_IFELSE(int i;, + [AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + CFLAGS=$SAVE_CFLAGS]) + +AC_CHECK_LIB(ssp, __stack_chk_guard,,) +AC_CHECK_LIB(ncurses, initscr,, + AC_CHECK_LIB(curses, initscr,, AC_MSG_ERROR(No curses library found.))) +AC_CHECK_LIB(com_err, com_err) +AC_CHECK_LIB(nsl, gethostbyname) +AC_CHECK_LIB(socket, socket) +dnl per debbugs#517019 +dnl AC_CHECK_LIB(k5crypto, krb5_derive_key) +dnl AC_CHECK_LIB(des425, req_act_vno) +dnl AC_CHECK_LIB(des425, des_cbc_encrypt) +dnl AC_CHECK_LIB(des425, des_cbc_encrypt,,AC_CHECK_LIB(crypto,DES_cbc_encrypt)) +AC_CHECK_LIB(crypto,DES_cbc_encrypt) +dnl AC_CHECK_LIB(des, des_quad_cksum) +AC_CHECK_LIB(resolv, res_search) +dnl per debbugs#517019 +dnl AC_CHECK_LIB(krb5, krb5_get_credentials) +dnl AC_CHECK_LIB(krb4, krb_sendauth,, +dnl AC_CHECK_LIB(krb, krb_sendauth)) +dnl AC_CHECK_LIB(zephyr, ZGetSender,, AC_MSG_ERROR(No zephyr library found.)) +AC_CHECK_LIB(zephyr, ZGetSender) +AC_CHECK_LIB(zephyr, ZInitLocationInfo, AC_DEFINE([HAVE_LIBZEPHYR_ZINITLOCATIONINFO], [], [Have ZInitLocationInfo]),) + +dnl per debbugs#517019 +AC_CHECK_FUNCS(DES_string_to_key DES_ecb_encrypt DES_key_sched) +AC_CHECK_FUNCS(use_default_colors resizeterm des_string_to_key des_key_sched des_ecb_encrypt) + +AC_MSG_CHECKING(for des_ecb_encrypt prototype) +AC_TRY_COMPILE([#include <des.h> +int des_ecb_encrypt(char foo[], char bar[], des_key_schedule baz, int qux);], +[int foo = des_ecb_encrypt(0,0,0,0);], +ac_cv_des_ecb_encrypt_proto=no, +ac_cv_des_ecb_encrypt_proto=yes) +AC_MSG_RESULT($ac_cv_des_ecb_encrypt_proto) +if test "$ac_cv_des_ecb_encrypt_proto" = yes; then + AC_DEFINE([HAVE_DES_ECB_ENCRYPT_PROTO], [], [have proto for des_ecb_encrypt]) +fi + +dnl Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS(strings.h sys/ioctl.h sys/filio.h unistd.h com_err.h) + +dnl Add CFLAGS and LDFLAGS for glib-2.0 +PKG_CHECK_MODULES([GLIB],[glib-2.0]) + +echo Adding glib-2.0 CFLAGS ${GLIB_CFLAGS} +CFLAGS="${GLIB_CFLAGS} ${CFLAGS}" +echo Adding glib-2.0 LDFLAGS ${GLIB_LIBS} +LDFLAGS="${GLIB_LIBS} ${LDFLAGS}" + +dnl Add CFLAGS for embeded perl +FOO=`perl -MExtUtils::Embed -e ccopts` +echo Adding perl CFLAGS ${FOO} +CFLAGS=${CFLAGS}\ ${FOO} + +dnl Find the location of perl XSUBPP +AC_MSG_CHECKING(for the perl xsubpp precompiler) +XSUBPPDIR="`(perl -MExtUtils::MakeMaker -e 'print ExtUtils::MakeMaker->new({NAME => qw(owl)})->tool_xsubpp;') | grep \^XSUBPPDIR | sed -e 's/XSUBPPDIR = //g;'`" +if test -n "${XSUBPPDIR}"; then + AC_MSG_RESULT(${XSUBPPDIR}) +else + AC_MSG_ERROR(not found) +fi + +dnl Add LDFLAGS for embeded perl +FOO=`perl -MExtUtils::Embed -e ldopts | sed 's/-Wl,-E//' | sed 's/-liconv//'` +echo Adding perl LDFLAGS ${FOO} +LDFLAGS=${LDFLAGS}\ ${FOO} + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_CHECK_FILE(/usr/share/terminfo, AC_DEFINE(TERMINFO, "/usr/share/terminfo", [Have terminfo]), + AC_CHECK_FILE(/usr/share/lib/terminfo, AC_DEFINE(TERMINFO, "/usr/share/lib/terminfo", [Have terminfo]), + AC_MSG_ERROR(No terminfo found for this system))) + +AC_SUBST(XSUBPPDIR) + +AC_PROG_INSTALL + +AC_CONFIG_SUBDIRS(libfaim) + +AC_OUTPUT(Makefile) diff --git a/context.c b/context.c new file mode 100644 index 0000000..33b84cb --- /dev/null +++ b/context.c @@ -0,0 +1,124 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <string.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: context.c,v 1.8 2009/03/29 12:38:20 kretch Exp $"; + +#define SET_ACTIVE(ctx, new) ctx->mode = ((ctx->mode)&~OWL_CTX_ACTIVE_BITS)|new +#define SET_MODE(ctx, new) ctx->mode = ((ctx->mode)&~OWL_CTX_MODE_BITS)|new + +int owl_context_init(owl_context *ctx) +{ + ctx->mode = OWL_CTX_STARTUP; + ctx->data = NULL; + return 0; +} + + +/* returns whether test matches the current context */ +int owl_context_matches(owl_context *ctx, int test) +{ + /*owl_function_debugmsg(", current: 0x%04x test: 0x%04x\n", ctx->mode, test);*/ + if ((((ctx->mode&OWL_CTX_MODE_BITS) & test) + || !(test&OWL_CTX_MODE_BITS)) + && + (((ctx->mode&OWL_CTX_ACTIVE_BITS) & test) + || !(test&OWL_CTX_ACTIVE_BITS))) { + return 1; + } else { + return 0; + } +} + +void *owl_context_get_data(owl_context *ctx) +{ + return ctx->data; +} + +int owl_context_get_mode(owl_context *ctx) +{ + return ctx->mode & OWL_CTX_MODE_BITS; +} + +int owl_context_get_active(owl_context *ctx) +{ + return ctx->mode & OWL_CTX_ACTIVE_BITS; +} + +int owl_context_is_startup(owl_context *ctx) +{ + return (ctx->mode & OWL_CTX_STARTUP)?1:0; +} + +int owl_context_is_interactive(owl_context *ctx) +{ + return(ctx->mode & OWL_CTX_INTERACTIVE)?1:0; +} + +void owl_context_set_startup(owl_context *ctx) +{ + SET_MODE(ctx, OWL_CTX_STARTUP); +} + +void owl_context_set_readconfig(owl_context *ctx) +{ + SET_MODE(ctx, OWL_CTX_READCONFIG); +} + +void owl_context_set_interactive(owl_context *ctx) +{ + SET_MODE(ctx, OWL_CTX_INTERACTIVE); +} + +void owl_context_set_popless(owl_context *ctx, owl_viewwin *vw) +{ + ctx->data = (void*)vw; + SET_ACTIVE(ctx, OWL_CTX_POPLESS); +} + +void owl_context_set_recv(owl_context *ctx) +{ + SET_ACTIVE(ctx, OWL_CTX_RECV); +} + +void owl_context_set_editmulti(owl_context *ctx, owl_editwin *ew) +{ + ctx->data = (void*)ew; + SET_ACTIVE(ctx, OWL_CTX_EDITMULTI); +} + +void owl_context_set_editline(owl_context *ctx, owl_editwin *ew) +{ + ctx->data = (void*)ew; + SET_ACTIVE(ctx, OWL_CTX_EDITLINE); +} + +void owl_context_set_editresponse(owl_context *ctx, owl_editwin *ew) +{ + ctx->data = (void*)ew; + SET_ACTIVE(ctx, OWL_CTX_EDITRESPONSE); +} + diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..a6d4aad --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,7 @@ +owl for Debian +-------------- + +owl is one of the simpler (while still being extensible) non-graphical +zephyr clients, which may make it simpler to get started as a user. + + -- Mark W. Eichin <eichin@thok.org>, Tue, 23 Apr 2002 01:58:14 -0400 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..9614395 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,208 @@ +owl (2.2.2-1.2) unstable; urgency=low + + * Non-maintainer upload. + * Fix "FTBFS: configure: error: No terminfo found for this system": + add build dependency on ncurses-term. + (Closes: #708840) + + -- gregor herrmann <gregoa@debian.org> Sat, 25 May 2013 21:29:32 +0200 + +owl (2.2.2-1.1) unstable; urgency=low + + * Non-maintainer upload. + * Build depend on libzephyr-dev >= 3.0; this indicates a transition from + krb4-based zephyr to krb5-based zephyr, Closes: #538053 + + -- Sam Hartman <hartmans@debian.org> Thu, 23 Jul 2009 07:37:06 -0400 + +owl (2.2.2-1) unstable; urgency=low + + * New upstream release. The upstream author has become active again and + has worked with the barnowl developers on security issues. (Closes: #515118) + * configure.in, debian.control: barnowl updates via Sam Hartman + eliminate retro libkrb4 and des425 dependencies. (Closes: #517019) + * Do not link against libkrb4 or libkrb5; we use none of their symbols + * Support openssl DES for zcrypt so that we continue to have zcrypt + after libdes425 goes away + Note: ditched the KerberosIV test entirely to force this version, + allowing build/test on lenny. + * zcrypt.c: use des.h again, so we get the openssl one above. + * from unreleased 2.1.11-3: + * debian/control: version debhelper depends (lintian + package-lacks-versioned-build-depends-on-debhelper.) + * debian/watch: New file. + * debian/control: add libglib2.0-dev, per configure.in + + -- Mark W. Eichin <eichin@thok.org> Mon, 13 Apr 2009 00:53:12 -0400 + +owl (2.1.11-2) unstable; urgency=low + + * debian/control, debian/rules, debian/compat: move to debhelper V7 + (lintian debian-rules-sets-DH_COMPAT, + package-uses-deprecated-debhelper-compat-version.) + * debian/changelog: force my original name spelling to avoid false NMU + detection (lintian changelog-should-mention-nmu, + source-nmu-has-incorrect-version-number.) + * debian/rules: conditionalize distclean more narrowly (lintian + debian-rules-ignores-make-clean-error.) + + -- Mark W. Eichin <eichin@thok.org> Wed, 01 Oct 2008 23:53:58 -0400 + +owl (2.1.11-1) unstable; urgency=low + + * New upstream release. (Closes: #454632) + * zephyr.c: report error in owl_zephyr_loadsubs too. + + -- Mark Eichin <eichin@thok.org> Sun, 28 Sep 2008 00:39:22 -0400 + +owl (2.1.8-4) unstable; urgency=low + + * debian/copyright: fix lintian complaint about + copyright-lists-upstream-authors-with-dh_make-boilerplate. + * debian/control: fix lintian complaint about + description-synopsis-might-not-be-phrased-properly. Also hint at + libzephyr3-krb, though I don't think it needs a Suggests. Since the + code includes AIM support, grudgingly mention it in the Description. + * functions.c: support modern graphical browsers; doesn't implement the + full generic configuration due to the -remote issue, but particular + new browsers could be added directly in perl extension code. + (Closes: #152405) + + -- Mark Eichin <eichin@thok.org> Sat, 27 Sep 2008 22:39:57 -0400 + +owl (2.1.8-3) unstable; urgency=low + + * Thanks to rra and dannf for the NMUs. (Closes: #477626) + * perlconfig.c: use PERL_SYS_INIT3 protocol as documented in perldoc + perlembed. (Closes: #495061) + + -- Mark Eichin <eichin@thok.org> Fri, 26 Sep 2008 02:52:32 -0400 + +owl (2.1.8-2.2) unstable; urgency=low + + * Non-maintainer upload. + * Sleep for 100ms instead of 10us when no key has been pressed to + avoid busy-waiting on input. (Closes: #433027) + * More correctly remove the -E flag from Perl's ldflags to fix build + problems with Perl 5.10. (Closes: #476013) + + -- Russ Allbery <rra@debian.org> Sun, 25 May 2008 08:00:28 -0700 + +owl (2.1.8-2.1) unstable; urgency=low + + * Non-maintainer upload. + * Include a patch from Andreas Jochens to fix compilation with gcc-4.0. + Closes: #287891 + * Add some prototypes to perlglue.xs to avoid the implicit conversion of + return pointers from owl_zephyr_get_realm() & owl_zephyr_get_sender(). + Closes: #326106 + + -- dann frazier <dannf@debian.org> Tue, 11 Oct 2005 20:47:51 -0600 + +owl (2.1.8-2) unstable; urgency=low + + * zcrypt.c: use kerberosIV/des.h, not the openafs one. Closes: #189108. + * configure.in: change it in the test, too. + + -- Mark W. Eichin <eichin@thok.org> Sun, 29 Aug 2004 16:25:03 -0400 + +owl (2.1.8-1) unstable; urgency=low + + * New upstream release. Kept debian/*, dropped all old patches + (everything that mattered was already upstream.) + * debian/copyright: new upstream location and mailing lists + * configure.in: don't look for libdes, it breaks kerberized zephyr. + * zephyr.c: actually record error_message in owl_function_error calls. + + -- Mark W. Eichin <eichin@thok.org> Sun, 29 Aug 2004 13:21:53 -0400 + +owl (1.2.9-1.1) unstable; urgency=low + + * NMU + * Fix compile error, patch in BTS. Closes: #189108 + + -- LaMont Jones <lamont@debian.org> Sun, 3 Aug 2003 18:54:07 -0600 + +owl (1.2.9-1) unstable; urgency=low + + * New upstream release + * update fixes resize problem (Closes: #182897) + * upstream also took changes for perl multithreading, thanks Bas + Zoetekouw and Lamont Jones for the NMU (Closes: #159123) + + -- Mark W. Eichin <eichin@thok.org> Sun, 13 Apr 2003 21:04:52 -0400 + +owl (1.1.3-1.1) unstable; urgency=low + + * NMU + * Fix perl 5.8 build problem. Closes: #159123 + + -- LaMont Jones <lamont@debian.org> Tue, 8 Oct 2002 18:21:44 -0600 + +owl (1.1.3-1) unstable; urgency=low + + * New upstream release + + -- Mark W. Eichin <eichin@thok.org> Fri, 5 Jul 2002 14:33:23 -0400 + +owl (1.0.1-4) unstable; urgency=low + + * debian/control: build-depend on a version of libzephyr late enough to + have #110486 fixed. (Closes: #148404) + + -- Mark W. Eichin <eichin@thok.org> Thu, 30 May 2002 14:19:18 -0400 + +owl (1.0.1-3) unstable; urgency=low + + * debian/shlibs.local: override libzephyr3-krb's shlibs file until + #148175 is fixed. (Closes: #148107) + + -- Mark W. Eichin <eichin@thok.org> Sat, 25 May 2002 18:35:09 -0400 + +owl (1.0.1-2) unstable; urgency=low + + * update README.debian from pre-release. + * configure.in: ditch the explicit krb tests, so we can get the right + libs. Crudely done, improvements sought. + + -- Mark W. Eichin <eichin@thok.org> Sat, 25 May 2002 02:19:47 -0400 + +owl (1.0.1-1) unstable; urgency=low + + * New upstream release. Includes the 1.0.1 fixes below. + * debian/watch: new file, lets us use uscan and be very lazy :-) + + -- Mark W. Eichin <eichin@thok.org> Fri, 24 May 2002 17:42:44 -0400 + +owl (1.0-1) unstable; urgency=low + + * New upstream release (first public deb.) (Closes: #147980) + * zephyr.c (loadloginsubs, loadsubs): use free correctly. (pre-1.0.1 fix) + * util.c (owl_free): void, don't return. (pre-1.0.1 fix) + + -- Mark W. Eichin <eichin@thok.org> Fri, 24 May 2002 01:27:28 -0400 + +owl (0.10-1) unstable; urgency=low + + * New upstream release. + + -- Mark W. Eichin <eichin@thok.org> Tue, 7 May 2002 17:25:28 -0400 + +owl (0.9-1) unstable; urgency=low + + * New upstream release. + + -- Mark W. Eichin <eichin@thok.org> Sun, 28 Apr 2002 22:29:02 -0400 + +owl (0.8.1-1) unstable; urgency=low + + * New upstream release. + + -- Mark W. Eichin <eichin@thok.org> Wed, 24 Apr 2002 19:19:29 -0400 + +owl (0.7-1) unstable; urgency=low + + * Initial Release. + + -- Mark W. Eichin <eichin@thok.org> Tue, 23 Apr 2002 01:58:14 -0400 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..3c921bb --- /dev/null +++ b/debian/control @@ -0,0 +1,16 @@ +Source: owl +Section: net +Priority: optional +Maintainer: Mark W. Eichin <eichin@thok.org> +Build-Depends: debhelper (>> 7), libzephyr-dev (>= 3.0~beta), libncurses5-dev, libkrb5-dev, libperl-dev, libssl-dev, libglib2.0-dev, ncurses-term +Standards-Version: 3.8.0 + +Package: owl +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, zephyr-clients +Description: A curses-based tty Zephyr client + An alternative standalone (doesn't use zwgc) Zephyr (instant + messaging) client. Has perl hooks and a curses UI; supports Kerberos + if libzephyr3-krb is present. (Also supports AIM.) + . + ktools software -- The 'k' is for Quality! diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..d103f92 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,55 @@ +This package was debianized by Mark W. Eichin <eichin@thok.org> on +Tue, 23 Apr 2002 01:58:14 -0400. + +It was downloaded from http://www.ktools.org/dist/owl/owl-2.2.2.tar.gz + +Upstream Authors: bug-ktools@ktools.org + +Owl was primarly written by James Kretchmar at the Massachusetts +Institute of Technology. Erik Nygren has also made substantial +contributions and improvemnts to the program. + +Copyright: + +from owl.c: + +/* Copyright (c) 2004 James Kretchmar. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Redistributions in any form must be accompanied by information on + * how to obtain complete source code for the Owl software and any + * accompanying software that uses the Owl software. The source code + * must either be included in the distribution or be available for no + * more than the cost of distribution plus a nominal fee, and must be + * freely redistributable under reasonable conditions. For an + * executable file, complete source code means the source code for + * all modules it contains. It does not include source code for + * modules or files that typically accompany the major components of + * the operating system on which the executable file runs. + * + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + ktools software -- The 'k' is for Quality! diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..e772481 --- /dev/null +++ b/debian/dirs @@ -0,0 +1 @@ +usr/bin diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..9ae65a6 --- /dev/null +++ b/debian/rules @@ -0,0 +1,93 @@ +#!/usr/bin/make -f +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + + +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) + CFLAGS += -g +endif +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + INSTALL_PROGRAM += -s +endif + +config.status: configure + dh_testdir + # Add here commands to configure the package. + ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info + + +build: build-stamp + +build-stamp: config.status + dh_testdir + + # Add here commands to compile the package. + $(MAKE) + #/usr/bin/docbook-to-man debian/owl.sgml > owl.1 + + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp + + # Add here commands to clean up after the build process. + [ ! -f Makefile ] || $(MAKE) distclean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/owl. + # $(MAKE) install prefix=$(CURDIR)/debian/owl/usr + cp owl $(CURDIR)/debian/owl/usr/bin/ + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot +# dh_installdebconf + dh_installdocs doc/intro.txt + dh_installexamples + dh_installmenu +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_installinit + dh_installcron + dh_installman doc/owl.1 + dh_installinfo +# dh_undocumented + dh_installchangelogs ChangeLog + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_makeshlibs + dh_installdeb +# dh_perl + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/debian/shlibs.local b/debian/shlibs.local new file mode 100644 index 0000000..c852f44 --- /dev/null +++ b/debian/shlibs.local @@ -0,0 +1 @@ +libzephyr 3 libzephyr3 diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..1d3e49c --- /dev/null +++ b/debian/watch @@ -0,0 +1,2 @@ +version=3 +http://www.ktools.org/dist/owl/owl-(.*)\.tar\.gz @@ -0,0 +1,210 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +/* Dictionary data abstraction. + * Maps from strings to pointers. + * Stores as a sorted list of key/value pairs. + * O(n) on inserts and deletes. + * O(n) on searches, although it should switch to a binary search for O(log n) + */ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: dict.c,v 1.4 2009/03/29 12:38:20 kretch Exp $"; + + +#define INITSIZE 30 +#define GROWAT 2 +#define GROWBY 1.5 + +int owl_dict_create(owl_dict *d) { + d->size=0; + d->els=(owl_dict_el *)owl_malloc(INITSIZE*sizeof(owl_dict_el)); + d->avail=INITSIZE; + if (d->els==NULL) return(-1); + return(0); +} + +int owl_dict_get_size(owl_dict *d) { + return(d->size); +} + +/* Finds the position of an element with key k, or of the element where + * this element would logically go, and stores the index in pos. + * TODO: optimize to do a binary search. + * Returns 1 if found, else 0. */ +int _owl_dict_find_pos(owl_dict *d, char *k, int *pos) { + int i; + for (i=0; (i<d->size) && strcmp(k,d->els[i].k)>0; i++); + *pos = i; + if (i>=d->size || strcmp(k,d->els[i].k)) { + return(0); + } else { + return(1); + } +} + +/* returns the value corresponding to key k */ +void *owl_dict_find_element(owl_dict *d, char *k) { + int found, pos; + found = _owl_dict_find_pos(d, k, &pos); + if (!found) { + return(NULL); + } + return(d->els[pos].v); +} + +/* creates a list and fills it in with keys. duplicates the keys, + * so they will need to be freed by the caller. */ +int owl_dict_get_keys(owl_dict *d, owl_list *l) { + int i; + char *dupk; + if (owl_list_create(l)) return(-1); + for (i=0; i<d->size; i++) { + if ((dupk = owl_strdup(d->els[i].k)) == NULL) return(-1); + owl_list_append_element(l, (void*)dupk); + } + return(0); +} + +void owl_dict_noop_free(void *x) { + return; +} + +/* Returns 0 on success. Will copy the key but make + a reference to the value. Will clobber an existing + entry with the same key iff free_on_replace!=NULL, + and will run free_on_replace on the old element. + Will return -2 if replace=NULL and match was found. +*/ +int owl_dict_insert_element(owl_dict *d, char *k, void *v, void (*free_on_replace)(void *old)) { + int pos, found; + char *dupk; + found = _owl_dict_find_pos(d, k, &pos); + if (found && free_on_replace) { + free_on_replace(d->els[pos].v); + d->els[pos].v = v; + return(0); + } else if (found && !free_on_replace) { + return(-2); + } else { + if ((d->size+1) > (d->avail/GROWAT)) { + d->els=owl_realloc(d->els, d->avail*GROWBY*sizeof(void *)); + d->avail=d->avail*GROWBY; + if (d->els==NULL) return(-1); + } + if ((dupk = owl_strdup(k)) == NULL) return(-1); + if (pos!=d->size) { + /* shift forward to leave us a slot */ + memmove((void*)(d->els+pos+1), (void*)(d->els+pos), + sizeof(owl_dict_el)*(d->size-pos)); + } + d->size++; + d->els[pos].k = dupk; + d->els[pos].v = v; + return(0); + } +} + +/* Doesn't free the value of the element, but does + * return it so the caller can free it. */ +void *owl_dict_remove_element(owl_dict *d, char *k) { + int i; + int pos, found; + void *v; + found = _owl_dict_find_pos(d, k, &pos); + if (!found) return(NULL); + owl_free(d->els[pos].k); + v = d->els[pos].v; + for (i=pos; i<d->size-1; i++) { + d->els[i]=d->els[i+1]; + } + d->size--; + return(v); +} + +/* elefree should free the value as well */ +void owl_dict_free_all(owl_dict *d, void (*elefree)(void *)) { + int i; + + for (i=0; i<d->size; i++) { + owl_free(d->els[i].k); + if (elefree) (elefree)(d->els[i].v); + } + if (d->els) owl_free(d->els); +} + +void owl_dict_free_simple(owl_dict *d) { + owl_dict_free_all(d, NULL); +} + + + +/************* REGRESSION TESTS **************/ +#ifdef OWL_INCLUDE_REG_TESTS + +#define FAIL_UNLESS(desc,pred) printf("\t%-4s: %s\n", (pred)?"ok":(numfailed++,"FAIL"), desc) + +int owl_dict_regtest(void) { + owl_dict d; + owl_list l; + int numfailed=0; + char *av="aval", *bv="bval", *cv="cval", *dv="dval"; + + printf("BEGIN testing owl_dict\n"); + FAIL_UNLESS("create", 0==owl_dict_create(&d)); + FAIL_UNLESS("insert b", 0==owl_dict_insert_element(&d, "b", (void*)bv, owl_dict_noop_free)); + FAIL_UNLESS("insert d", 0==owl_dict_insert_element(&d, "d", (void*)dv, owl_dict_noop_free)); + FAIL_UNLESS("insert a", 0==owl_dict_insert_element(&d, "a", (void*)av, owl_dict_noop_free)); + FAIL_UNLESS("insert c", 0==owl_dict_insert_element(&d, "c", (void*)cv, owl_dict_noop_free)); + FAIL_UNLESS("reinsert d (no replace)", -2==owl_dict_insert_element(&d, "d", (void*)dv, 0)); + FAIL_UNLESS("find a", (void*)av==owl_dict_find_element(&d, "a")); + FAIL_UNLESS("find b", (void*)bv==owl_dict_find_element(&d, "b")); + FAIL_UNLESS("find c", (void*)cv==owl_dict_find_element(&d, "c")); + FAIL_UNLESS("find d", (void*)dv==owl_dict_find_element(&d, "d")); + FAIL_UNLESS("find e (non-existent)", NULL==owl_dict_find_element(&d, "e")); + FAIL_UNLESS("remove d", (void*)dv==owl_dict_remove_element(&d, "d")); + FAIL_UNLESS("find d (post-removal)", NULL==owl_dict_find_element(&d, "d")); + + FAIL_UNLESS("get_size", 3==owl_dict_get_size(&d)); + FAIL_UNLESS("get_keys", 0==owl_dict_get_keys(&d, &l)); + FAIL_UNLESS("get_keys result size", 3==owl_list_get_size(&l)); + + /* these assume the returned keys are sorted */ + FAIL_UNLESS("get_keys result val",0==strcmp("a",owl_list_get_element(&l,0))); + FAIL_UNLESS("get_keys result val",0==strcmp("b",owl_list_get_element(&l,1))); + FAIL_UNLESS("get_keys result val",0==strcmp("c",owl_list_get_element(&l,2))); + + owl_list_free_all(&l, owl_free); + owl_dict_free_all(&d, NULL); + + if (numfailed) printf("*** WARNING: failures encountered with owl_dict\n"); + printf("END testing owl_dict (%d failures)\n", numfailed); + return(numfailed); +} + +#endif /* OWL_INCLUDE_REG_TESTS */ diff --git a/doc/code.txt b/doc/code.txt new file mode 100644 index 0000000..682315c --- /dev/null +++ b/doc/code.txt @@ -0,0 +1,199 @@ + +$Id: code.txt,v 1.2 2002/06/28 06:19:00 nygren Exp $ + + +TYPES / CLASSES / SOURCE FILES +------------------------------ + +cmd: Underlying implementation of command table management + and command dispatching. Also handles the implementation + of command aliases. + +commands: Dispatching for commands and handling of their arguments. + (Commands are the interface exported to the user.) + Many commands tend to be backed by functions. + In general, a command takes a string as an argument + and optionally returns a string. + At the top of the file is a table mapping + command names and help to functions implementing them. + The standard entrypoint for executing a command + is owl_function_command("foo"); + Commands are only active within specific contexts, + and attempts to call them from invalid contexts will fail. + +context: A context specifies the current state of owl, in terms + of which modal window is active and which point + in its life owl is in (eg, in startup, or running). + This is implemented as a bitmask and there is + some hierarchy. Commands may restrict themselves + to only running in a limited number of contexts + to prevent commands from being executed at points + when they not make sense. Also, the data from + the active context (eg, a pointer to an active window) + may be passed to a command. + +dict: Simple dictionary abstraction mapping from strings to pointers. + +editwin: Text editing window (both multiline and single line). + Sometimes also referred to as typewin. + +filter: Patterns which match messages. These may + contain multiple filterelements which may be + combined together (eg, by "and" and "or"). + +filterelement: An element of a filter which matches on some + attribute of a message. + +fmtext: Formatted text routines (handles things like @i{foo}). + These are particularly useful for building up + text regions that are to be rendered on-screen, + as they resize memory as needed, and they have + routines for cropping as needed. + +functions: Where most features are implemented. + Users should always interact with functions through commands. + In general, functions are abstract entrypoints into the + system and attempt to hide access to global state. + +global: Global state and variables and toplevel objects. + owl.h defines "g" as a singleton instance of owl_global. + Where possible/appropriate, most accesses to global data should + be from a limited number of files (eg, from owl.c and + functions.c). Consider whether you really need to before + adding in uses of global. + +help: Help strings for commands and key bindings. + Most of this is now in keys.c, commands.c, and variables.c, + along with the definition of commands and keybindings. + +keys: Default key binding definitions for all keymaps. + This also includes default actions for keymaps. + +keybinding: Binding between a sequence of keypresses and a command. + When executed, this executes the commands. The sequence + of keypresses is kept as a stack. Keybindings are a part + of keymaps. + +keypress: Utility routines for translating between keypress values and + and human-readable key names. + +keymap: Contains both keymap and keyhandler. A keymap is contains a + list of keybindings, a sub-keymap, and optionally a + default handler function. The sub-keymap is a more + general keymap which is consulted if the specific keymap + doesn't contain a match. (For example, the "global" + keymap is the ancestor of all other keymaps.) The + keyhandler is a collection of keymaps which handles + checking for key matches within keymaps. It maintains a + stack of keypresses and compares them against the + bindings in keymaps. It also handles ESC as a prefix for + Meta. At any one time, there is exactly one active + keymap which determines where keybindings are looked for + (along with its submaps). + +list: Simple list abstraction. (Uses realloc to resize the list.) + +logging: Interface to incoming / outgoing zephyr logging. + +mainwin: Window that displays the list of messages. + (Sometimes also referred to as recwin.) + +message: Abstraction to messages. Currently, messages + are either of type zephyr or of type admin. + +messagelist: List of messages. + +owl.c: main() and signal handlers and other initial setup. + Also contains the main loop, which is roughly: + - handle scheduled resizes, and anything that might result + - while zephyrs are pending, grab incoming zephyrs + and handle them (which includes formatting them + with either perl extension or default formatter + as part of owl_message_create_from_zephyr). + - updates mainwin display if there are new zephyrs + - displays and updates popwins and the terminal as necessary + - sends characters to the popwin, recwin/mainwin, + or typewin/editwin + + +owl.h: Prototypes for all types, as well as global constants. + +owl_prototypes.h: Autogenerated prototypes for all functions. + Created by codelist.pl. + +popwin: Modal pop-up window container. + Usually contains a viewwin for read-only scrolling text. + +readconfig: Perl extension interface. + +text: Text formatting utilities (ie, indenting, truncating, etc) + +util: Misc utility functions that don't fit anywhere yet: + - sepbar rendering + - tokenizing and parsing utilities + - downstr + - stristr + - owl_malloc/free/realloc + +variable: Interface to setting and getting variables. + Current variable types include bool, int, string, and other. + There's also an enum type which is variant of int. + Variables can be created and customized here as well. + +varstubs.c: Autogenerated headers for accessing global variables + +view: A collection of messages determined by a filter. + Many operations may be performed on the members + of a view, and a view can be narrowed-to for display. + +viewwin: Read-only scrolling text displayed in a modal popwin. + This is also sometimes called "popless". + +zephyr: Routines for interfacing to zephyr. + +zwrite: Outgoing zephyrs. Sends pings on creation, + handles command arguments, etc. + + +=========================================================================== + +CURSES WINDOWS +-------------- + +The four curses windows on the screen are + + recwin - receiving window + sepwin - seperator window + msgwin - message window + typwin - typing window + + +=========================================================================== + + +MISC THINGS +----------- + +userclue: right now userclue is just used to decide if you sub to + classes other than the default. If you don't it doesn't bother + making your personal messages bold since there's no point in + making every message bold. + + + +=========================================================================== + +Conventions and Design Criteria +------------------------------- + + There are no hard rules for memory allocation. In general I + have the caller allocate memory for objects themselves and any + memory the object creates gets freed with object_free(). + Functions should document if the caller needs to free + something, and this should be the exception to the rule. + + Owl should be generally useful out-of-the-box without + extensive configuration, for most people's needs. + People shouldn't have to spend days tweaking + with config files before being happy switching to it. + diff --git a/doc/contributors b/doc/contributors new file mode 100644 index 0000000..e529f5f --- /dev/null +++ b/doc/contributors @@ -0,0 +1,31 @@ + +$Id: contributors,v 1.4 2003/06/22 06:08:13 kretch Exp $ + +Owl was primarly written by James Kretchmar at the Massachusetts +Institute of Technogloy. Erik Nygren has also made substantial +contributions and improvemnts to the program. + +The following people have provided patches and other techincal +support: + + Stephen Gildea + Greg Hudson + David Resnick + Marc Horowitz + Jeremy Daniel + Roman Mitz + Derrick J Brashear + David Glasser + Mark Eichin + +Mark Eichin is also maintaining the debian package of Owl. + +The following people helped with beta testing the earliest versions of +Owl: + + Andy Ellis, Erin Panttaja, Gisele Proulx + Ron Hoffmann, Jag Patel, Tara Holm + Heather Wakefield, Emiliy Havens, + Matt Braun, Jeff Schiller + +The ASCII art owl was created by Anne LaVin. diff --git a/doc/intro.txt b/doc/intro.txt new file mode 100644 index 0000000..57371c4 --- /dev/null +++ b/doc/intro.txt @@ -0,0 +1,462 @@ + ======================== + Quick Guide To Using Owl + ======================== + +======================= +Section 1: INTRODUCTION +======================= + +Owl is a tty, curses-based instant messaging client. This is a quick +guide to learning how to use it. Currently Owl supports AIM & zephyr, +but other messaging protocols, including Jabber, are on the way. Some +major features of owl include: + + o) As a tty client it can be run over telnet, rlogin or text ssh + sessions + + o) It uses a perl configuration file for setting preferences and + formatting messages + + o) Emacs style editing of messages + + o) It is easy to use and runs without a configfile. + + o) Advanced sorting and coloring of messages + +========================== +Section 2: GETTING STARTED +========================== + +Owl will run happily without a configuration file, so to get started +just run the program. Owl will take over the terminal window it is +started in, so you may wish to have another terminal window available +at the same time. + +On Athena you can find owl in the ktools locker. To run it, type: + + add ktools + owl + +at the Athena% prompt. If you wish to run the latest beta release of +owl use: + + add ktools + owl-beta + +instead. The beta release will often have newer features, but is not +as tried and true as the production release. As a result it may be +less stable. + +The Screen Layout +----------------- +There are three main parts to the owl screen. The large top portion +of the screen is where messages are displayed. The status bar +separates this area from the one below and displays owl status +information. The space below that is used to type messages and is +also used by owl to give warnings and information to the user. + +On Line Help +------------ +Owl has a full on line help system. Pressing the 'h' key will bring +up the basic help screen. Further help can be obtained using the help +command, described later. + +Sending a Zephyr +---------------- +To send a zephyr press the 'z' key. This will start a zwrite command, +which you can finish by typing the name of the user you wish to send +to, followed by enter. Begin typing your message. You will notice +that most emacs-style editing is available. When you are ready to +send the message type Control-D or a dot ('.') on a line by itself. +If instead you wish to cancel the message type Control-C. + +If you wish to send to a class/instance pair simply supply -c and -i +arguments to the zwrite command as you normally would. + +Sending an AIM message +---------------------- + +Before sending an AIM message you must login to AOL Instant Messenger. +Use the 'aimlogin' command, with your screenname as an argument: + + aimlogin <screenname> + +You will be prompted for your password, which you must enter. Once +you are successfully logged in you can send an AIM message by pressing +the 'a' key, which will bring up an 'aimwrite' command: + + aimwrite <screenname> + +Supply the screen name you wish to write to as an argument and then +send the message just as you would send a zephyr, as described above. + +Manipulating Messages +--------------------- +When there are zephyrs in the message window, one of them will be the +'current' message. Owl will indicate which one it is with an arrow +that looks like this: -> The following keys will move you to different +messages: + + n move to the next non-deleted message + p move to the previous non-deleted message + C-n or down move to the next message + C-p or up move to the previous message + < move to the first message + > move to the last message + C-v page down + M-v page up + right scroll the screen to the right + left scroll the screen to the left + P move to the next personal message + M-P move to the previous personal message + +When you are ready to delete a message you can mark it for deletion +with the 'd' key, and a 'D' will appear to the left of the message. +Messages will not actually be removed until you perform an expunge. +The following keys are used to delete, undelete and expunge messages: + + d mark a message for deletion + u unmark a message for deletion + x expunge deleted messages + T mark all 'trash' messages for deletion + M-D mark all messages in the view for deletion + M-u unmark all messages in the view for deletion + +If you would like to respond to a message sent to you there is a reply +shortcut: + + r Reply. Personal messages get a personal reply, + group messages get a group reply. + R Reply to sender. Always replies personally + to the sender. + M-r Reply but allow editing of the command line. + M-R Reply to sender but allow editing of the + command line. + +In the event that the current message is too large to fit on the +screen, you can scroll within the message using the following keys: + + SPACE page down + b page up + RETURN line down + BACKSPACE line up + +The message pointer will change to indicate that the message is not +starting at the first line. + +Two other keys that relate to the current message: + + i print detailed information about the message + w instruct netscape to visit a URL in the message + +Other Functions +---------------- +Some other functions that can be performed with a single keystroke: + + A toggle zephyr zaway on or off + C-l refresh and resize the screen + C-z suspend + +Command Mode +------------ +Owl has a command mode from which you can enter more detailed commands +for Owl to process. To enter command mode press the colon (':') key: + + : begin command mode + +Owl will give you a command prompt and you can begin typing your +command. Type Enter to execute the command, Control-C to cancel. +There are many commands. The basic commands are listed on the basic +help screen (by pressing 'h'). If you'd like a list of all commands +you can use the command: + + show commands + +And for detailed information on the syntax and use of a command you +can use: + + help <command> + +For example "help zwrite" will display all the options available when +using the zwrite command. + +Variables +--------- +Owl has a number of internal variables that can be used to change the +behavior the program. The 'print' command will let you view the value +of a variable and the 'set' commmand will let you set the value of a +variable. For example: + + set personalbell on + +will set the value of the variable 'personalbell' to 'on'. The +command: + + print personalbell + +will show you the current value. The 'print' command with no +arguments: + + print + +Owl will show you the value of all variables. You can also use + + show variables + + show variable <variable> + +To display further information on owl variables. + + +================ +Section 3: VIEWS +================ + +Owl always displays a current "view" of messages. The view describes +which set of messages should be included on the display. The default +view is called "all" and includes every message. However, you can +narrow the view to a particular set of messages: + + M-n Narrow view to the selected conversation + M-N Narrow view to selected conversation by instance + V Return to the home view (the 'all' view) + X Expunge messages and return to home view + +If you press M-n while the pointer is on a personal message, the view +will be narrowed to the conversation with that user only. If used on +a group message the conversation will be narrowed to that group. + +There are also some Owl commands related to views: + + viewclass <class> Narrow the view to the named zephyr class + viewuser <user> Narrow the view to the named user + +More information on views and how they work is included in the section +on "FILTERS AND COLORS". + +============================= +Section 4: FILTERS AND COLORS +============================= + +Filters +------- +Owl will allow you to create custom message filters. A message filter +is an expression that matches a set of messages based on certain +criteria. Owl comes with a number of build-in filters already. You can +view a list of them with the command: + + show filters + +The default filters include: + + all Matches all messages + none Matches no messages + personal Only personal messages (no group messages) + login Login/Logout notifications + auto Messages generated by automated programs + out Messages sent from you to another user + aim AIM messages + zephyr Zephyr messages + trash "Trash" messages + ping Zephyr pings + reply-lockout Messages for which the reply commands + should not work + +If you wish to view the messages that match a particular filter, use +the 'view' command. For example: + + view personal + +This will display only personal messages on the screen. You can +change back to the 'all' view by pressing the 'V' key (capitalized). +Note that the 'v' key (not capitalized) is a shortcut to bring up the +'view' command. + +You can also create your own filters. For more information on this, +consult the Owl Advanced Users Guide. + +Colors +------ +Every filter can have a color associated with it. Messages matching +the filter will then be displayed in that color if your terminal +supports it. The color for a filter can be set by using the '-c' +option to the filter command. For example: + + filter personal -c white + +This cause all messages in the 'personal' filter to be displayed in +white. You can produce a list of the colors available to Owl with the +command: + + show colors + +If a message matches more than one filter it will be displayed in the +color specified in the last filter listed in the 'show filters' +command. + +If you would like your color settings to persist, such that they are +preset every time you start Owl, please read the "Saving Your +Settings" section below. + +=============================== +Section 5: SAVING YOUR SETTINGS +=============================== + +Any changes you make to Owl are lost when the program is terminated, +unless you specify otherwise. If you would like a setting to persist +such that it is available every time you start Owl you can use the +word 'startup' before any command. For example: + + startup filter personal -c white + +Will instruct Owl to color personal messages white both in the current +session and in any future Owl session. You may revert this behavior +with the 'unstartup' command: + + unstartup filter personal -c white + +which will not affect the current session, but will cause future +sessions not to take this action. + +Here is another example, this instructs Owl to display zephyr ping +messages: + + startup set rxping on + +========================== +Section 6: THE CONFIG FILE +========================== + +*** WARNING: This interface may change substantially in the near future *** + +The ~/.owlconf file is interpreted by the perl interpreter. You may +specify an alternate file by running owl with "owl -c <configfile>". + +If you wish to execute an owl command from .owlconf use the function +owl::command(). i.e.: + + owl::command('set zsigproc "/mit/kretch/bin/getzsig foo"'); + +Subroutines created with the names below will be executed at the +specified times: + + subroutine name properties + --------------- ---------- + owl::startup() run when owl first starts + owl::shutdown() run when owl exits + owl::format_msg() run to format messages when using the perl style. + The return value is used to display the message on the + screen. + owl::receive_msg() run when a message is received, and after + it has been added to the message list + +Both owl::format_msg and owl::receive_msg are passed perl owl::Message +objects which contain attributes of the message. Please see the +advanced.txt file for further documentation of the Perl extension API. + +The "appendtosepbar" variable may be set in owl::format_msg() to set +text to be appended to sepbar that separates the received message list +from the edit window. + + +========================================== +Section 4: KEYBINDINGS AND COMMAND ALIASES +========================================== + +Aliases +------- + +Command aliases allow users to create shortcuts +for commonly used commands. Aliases can be created wit +the alias command: + + alias NAME VALUE + +For example: + + alias zw zwrite + +Will make "zw" an alias for the zwrite command. As such, "zw aphacker" +will be expanded to "zwrite aphacker". If the value of an +alias is multiple words, use of the alias will result in the alias +command name being replaced by the sequence of words. +Any arguments following the alias name will be appended +after the expanded alias value. For example: + + alias vs view -s + +will result in "vs standard" being expanded to "view -s standard". +There is not yet any way to allow an alias to take arguments +that will be inserted in the middle of the expansion. + + +Separating Commands +------------------- + +Multiple commands can be grouped together with parentheses +and then separated by semicolons. For example: + + ( smartnarrow ; delete view ; expunge ; view all ) + +Will result in the four commands being executed +in sequence. This is particularly useful with key bindings +and coommands. For example: + + alias sn-delete ( smartnarrow ; delete view ) + +will create an "sn-delete" alias that will smartnarrow +to a view and them mark the view for deletion. + +Using "show commands" will list all existing aliases. + + +Key Bindings +------------ + +New key bindings may be created with the "bindkey" command. Each key +binding is associated with a particular keymap which is applicable in +a particular context/situation. When the key associated with a +binding is pressed in the right context, it will result in an owl +command being run. The syntax is: + + bindkey <keymap> <keyseq> command <command> + +For example: + + bindkey recv C-k command delete + +will bind Control-k to the delete command, but only in the +recv window context. + +Some keymaps inherit their bindings from more +general keymaps. The valid keymaps are: + + - global - owl-wide defaults (apply everywhere) + |-edit - all text editing and command windows + | |-editmulti - multi-line text editing windows + | |-editline - single-line editing and command windows + | |-editresponse - single-line responses to questions + |-popless - scrolling pop-up windows + |-recv - the main message list window + where received messages are displayed + +The existing key bindings can be shown with "show keymaps". +The use of "show commands" will list all available commands. +Note that not all commands may be used in all contexts. + +Key sequences may be surrounded by quotes and include +a sequence of keys that must be pressed in order +to execute the command. For example: + + bindkey recv "C-s v" command view -s vt + +will result in "Control-s" followed by "v" in the recv window +causing the command "view -s vt" to be run. + + + +============================== +Section 6: FURTHER INFORMATION +============================== + +$Id: intro.txt,v 1.8 2005/01/01 00:30:45 kretch Exp $ diff --git a/doc/owl.1 b/doc/owl.1 new file mode 100644 index 0000000..09a77ff --- /dev/null +++ b/doc/owl.1 @@ -0,0 +1,83 @@ +.TH owl 1 "10 Apr 2004" +.SH NAME +owl \- tty based zephyr client +.SH SYNOPSIS +.B owl +[ \-n +] +[ \-d +] +[ \-D +] +[ \-v +] +[ \-h +] +[ \-c +.I configfile +] +[ \-t +.I tty +] + +.br +.SH DESCRIPTION +.B Owl +is a fully integrated tty based instant messaging client. Currently +it supports AOL Instant Messenger and MIT Zephyr. It is curses-based, +allows for emacs style editing of outgoing messages and uses a perl +configuration language for setting options and customizing message +formatting. Owl will also run happily without a configuration file. + +Once Owl is started, typing 'h' will display a help screen. Typing +\':\' enters command mode, allowing the user to type an owl command +line. + +.PP +.SH USE +The following command line options are avilable when running owl: + +.B \-n +.IP +Do not subscribe to zephyr messages on startup. By default Owl +subscribes to the default subscriptions and to anything found in +~/.zephyr.subs. When this option is used no subscriptions are loaded. +.LP + +.B \-c \fIconfigfile\fP +.IP +Specifiy an alternate config file for Owl to use. The default +configuration file is ~/.owlconf +.LP + +.B \-t \fItty\fP +.IP +Specifiy the tty name to use for the zephyr location. +.LP + +.B \-v +.IP +Print the version number of owl and exit. +.LP + +.B \-d +.IP +Enable debugging. By default debugging information is placed in +/var/tmp/owldebug. +.LP + +.B \-D +.IP +Enable debugging, but first delete any existing debugging file. +.LP + +.B \-h +.IP +Print command line option help. +.LP + +.SH AUTHOR +Written by James Kretchmar at the Massachusetts Institute of +Technology. +Comments, questions, and bug reports may be mailed to +\fBbug-owl@ktools.org\fP. diff --git a/editwin.c b/editwin.c new file mode 100644 index 0000000..00689cd --- /dev/null +++ b/editwin.c @@ -0,0 +1,968 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> + +static const char fileIdent[] = "$Id: editwin.c,v 1.15 2009/03/29 12:38:20 kretch Exp $"; + +#define INCR 5000 + +/* initialize the editwin e. + * 'win' is an already initialzed curses window that will be used by editwin + */ +void owl_editwin_init(owl_editwin *e, WINDOW *win, int winlines, int wincols, int style, owl_history *hist) +{ + e->buff=owl_malloc(INCR); + e->buff[0]='\0'; + e->bufflen=0; + e->hist=hist; + e->allocated=INCR; + e->buffx=0; + e->buffy=0; + e->topline=0; + e->winlines=winlines; + e->wincols=wincols; + e->fillcol=owl_editwin_limit_maxcols(wincols-1, owl_global_get_edit_maxfillcols(&g)); + e->wrapcol=owl_editwin_limit_maxcols(wincols-1, owl_global_get_edit_maxwrapcols(&g)); + e->curswin=win; + e->style=style; + if ((style!=OWL_EDITWIN_STYLE_MULTILINE) && + (style!=OWL_EDITWIN_STYLE_ONELINE)) { + e->style=OWL_EDITWIN_STYLE_MULTILINE; + } + e->lock=0; + e->dotsend=0; + e->echochar='\0'; + if (win) werase(win); +} + +void owl_editwin_set_curswin(owl_editwin *e, WINDOW *w, int winlines, int wincols) +{ + e->curswin=w; + e->winlines=winlines; + e->wincols=wincols; + e->fillcol=owl_editwin_limit_maxcols(wincols-1, owl_global_get_edit_maxfillcols(&g)); + e->wrapcol=owl_editwin_limit_maxcols(wincols-1, owl_global_get_edit_maxwrapcols(&g)); +} + +/* echo the character 'ch' for each normal character keystroke, + * excepting locktext. This is useful for entering passwords etc. If + * ch=='\0' characters are echo'd normally + */ +void owl_editwin_set_echochar(owl_editwin *e, int ch) +{ + e->echochar=ch; +} + +WINDOW *owl_editwin_get_curswin(owl_editwin *e) +{ + return(e->curswin); +} + +void owl_editwin_set_history(owl_editwin *e, owl_history *h) +{ + e->hist=h; +} + +owl_history *owl_editwin_get_history(owl_editwin *e) +{ + return(e->hist); +} + +void owl_editwin_set_dotsend(owl_editwin *e) +{ + e->dotsend=1; +} + +int owl_editwin_limit_maxcols(int v, int maxv) +{ + if (maxv > 5 && v > maxv) { + return(maxv); + } else { + return(v); + } +} + +/* set text to be 'locked in' at the beginning of the buffer, any + * previous text (including locked text) will be overwritten + */ +void owl_editwin_set_locktext(owl_editwin *e, char *text) +{ + + int x, y; + + x=e->buffx; + y=e->buffy; + e->buffx=0; + e->buffy=0; + owl_editwin_overwrite_string(e, text); + e->lock=strlen(text); + /* if (text[e->lock-1]=='\n') e->lock--; */ + e->buffx=x; + e->buffy=y; + owl_editwin_adjust_for_locktext(e); + owl_editwin_redisplay(e, 0); +} + +int owl_editwin_get_style(owl_editwin *e) +{ + return(e->style); +} + +void owl_editwin_new_style(owl_editwin *e, int newstyle, owl_history *h) +{ + char *ptr; + + owl_editwin_set_history(e, h); + if (e->style==newstyle) return; + + if (newstyle==OWL_EDITWIN_STYLE_MULTILINE) { + e->style=newstyle; + } else if (newstyle==OWL_EDITWIN_STYLE_ONELINE) { + e->style=newstyle; + + /* nuke everything after the first line */ + if (e->bufflen > 0) { + ptr=strchr(e->buff, '\n')-1; + if (ptr) { + e->bufflen=ptr - e->buff; + e->buff[e->bufflen]='\0'; + e->buffx=0; + e->buffy=0; + } + } + } +} + +/* completly reinitialize the buffer */ +void owl_editwin_fullclear(owl_editwin *e) +{ + owl_free(e->buff); + owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style, e->hist); +} + +/* clear all text except for locktext and put the cursor at the + * beginning + */ +void owl_editwin_clear(owl_editwin *e) +{ + + int lock; + int dotsend=e->dotsend; + char *locktext=NULL; + + lock=0; + if (e->lock > 0) { + lock=1; + + locktext=owl_malloc(e->lock+20); + strncpy(locktext, e->buff, e->lock); + locktext[e->lock]='\0'; + } + + owl_free(e->buff); + owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style, e->hist); + + if (lock > 0) { + owl_editwin_set_locktext(e, locktext); + } + if (dotsend) { + owl_editwin_set_dotsend(e); + } + + if (locktext) owl_free(locktext); + owl_editwin_adjust_for_locktext(e); +} + +/* malloc more space for the buffer */ +void _owl_editwin_addspace(owl_editwin *e) +{ + e->buff=owl_realloc(e->buff, e->allocated+INCR); + if (!e->buff) { + /* error */ + return; + } + e->allocated+=INCR; +} + +void owl_editwin_recenter(owl_editwin *e) +{ + e->topline=e->buffy-(e->winlines/2); + if (e->topline<0) e->topline=0; + if (e->topline>owl_editwin_get_numlines(e)) e->topline=owl_editwin_get_numlines(e); +} + +/* regenerate the text on the curses window */ +/* if update == 1 then do a doupdate(), otherwise do not */ +void owl_editwin_redisplay(owl_editwin *e, int update) +{ + + char *ptr1, *ptr2, *ptr3, *buff; + int i; + + werase(e->curswin); + wmove(e->curswin, 0, 0); + + /* start at topline */ + ptr1=e->buff; + for (i=0; i<e->topline; i++) { + ptr2=strchr(ptr1, '\n'); + if (!ptr2) { + /* we're already on the last line */ + break; + } + ptr1=ptr2+1; + } + /* ptr1 now stores the starting point */ + + /* find the ending point and store it in ptr3 */ + ptr2=ptr1; + ptr3=ptr1; + for (i=0; i<e->winlines; i++) { + ptr3=strchr(ptr2, '\n'); + if (!ptr3) { + /* we've hit the last line */ + /* print everything to the end */ + ptr3=e->buff+e->bufflen-1; + ptr3--; + break; + } + ptr2=ptr3+1; + } + ptr3+=2; + + buff=owl_malloc(ptr3-ptr1+50); + strncpy(buff, ptr1, ptr3-ptr1); + buff[ptr3-ptr1]='\0'; + if (e->echochar=='\0') { + waddstr(e->curswin, buff); + } else { + /* translate to echochar, *except* for the locktext */ + int len; + int dolocklen=e->lock-(ptr1-e->buff); + + for (i=0; i<dolocklen; i++) { + waddch(e->curswin, buff[i]); + } + len=strlen(buff); + for (i=0; i<len-dolocklen; i++) { + waddch(e->curswin, e->echochar); + } + } + wmove(e->curswin, e->buffy-e->topline, e->buffx); + wnoutrefresh(e->curswin); + if (update==1) { + doupdate(); + } + owl_free(buff); +} + + +/* linewrap the word just before the cursor. + * returns 0 on success + * returns -1 if we could not wrap. + */ +int _owl_editwin_linewrap_word(owl_editwin *e) +{ + int i, z; + + z=_owl_editwin_get_index_from_xy(e); + /* move back and line wrap the previous word */ + for (i=z-1; ; i--) { + /* move back until you find a space or hit the beginning of the line */ + if (e->buff[i]==' ') { + /* replace the space with a newline */ + e->buff[i]='\n'; + e->buffy++; + e->buffx=z-i-1; + /* were we on the last line */ + return(0); + } else if (e->buff[i]=='\n' || i<=e->lock) { + /* we hit the begginning of the line or the buffer, we cannot + * wrap. + */ + return(-1); + } + } +} + +/* insert a character at the current point (shift later + * characters over) + */ +void owl_editwin_insert_char(owl_editwin *e, char c) +{ + + int z, i, ret; + + /* \r is \n */ + if (c=='\r') { + c='\n'; + } + + if (c=='\n' && e->style==OWL_EDITWIN_STYLE_ONELINE) { + /* perhaps later this will change some state that allows the string + to be read */ + return; + } + + /* make sure there is enough memory for the new text */ + if ((e->bufflen+1) > (e->allocated-5)) { + _owl_editwin_addspace(e); + } + + /* get the insertion point */ + z=_owl_editwin_get_index_from_xy(e); + + /* If we're going to insert at the last column do word wrapping, unless it's a \n */ + if ((e->buffx+1==e->wrapcol) && (c!='\n')) { + ret=_owl_editwin_linewrap_word(e); + if (ret==-1) { + /* we couldn't wrap, insert a hard newline instead */ + owl_editwin_insert_char(e, '\n'); + } + } + + z=_owl_editwin_get_index_from_xy(e); + /* shift all the other characters right */ + for (i=e->bufflen; i>z; i--) { + e->buff[i]=e->buff[i-1]; + } + + /* insert the new one */ + e->buff[z]=c; + + /* housekeeping */ + e->bufflen++; + e->buff[e->bufflen]='\0'; + + /* advance the cursor */ + if (c=='\n') { + e->buffx=0; + e->buffy++; + } else { + e->buffx++; + } +} + +/* overwrite the character at the current point with 'c' */ +void owl_editwin_overwrite_char(owl_editwin *e, char c) +{ + int z; + + /* \r is \n */ + if (c=='\r') { + c='\n'; + } + + if (c=='\n' && e->style==OWL_EDITWIN_STYLE_ONELINE) { + /* perhaps later this will change some state that allows the string + to be read */ + return; + } + + z=_owl_editwin_get_index_from_xy(e); + + /* only if we are at the end of the buffer do we create new space */ + if (z==e->bufflen) { + if ((e->bufflen+1) > (e->allocated-5)) { + _owl_editwin_addspace(e); + } + } + + e->buff[z]=c; + + /* housekeeping if we are at the end of the buffer */ + if (z==e->bufflen) { + e->bufflen++; + e->buff[e->bufflen]='\0'; + } + + /* advance the cursor */ + if (c=='\n') { + e->buffx=0; + e->buffy++; + } else { + e->buffx++; + } + +} + +/* delete the character at the current point, following chars + * shift left. + */ +void owl_editwin_delete_char(owl_editwin *e) +{ + int z, i; + + if (e->bufflen==0) return; + + /* get the deletion point */ + z=_owl_editwin_get_index_from_xy(e); + + if (z==e->bufflen) return; + + for (i=z; i<e->bufflen; i++) { + e->buff[i]=e->buff[i+1]; + } + e->bufflen--; + e->buff[e->bufflen]='\0'; +} + +/* Swap the character at point with the character at point-1 and + * advance the pointer. If point is at beginning of buffer do + * nothing. If point is after the last character swap point-1 with + * point-2. (Behaves as observed in tcsh and emacs). + */ +void owl_editwin_transpose_chars(owl_editwin *e) +{ + int z; + char tmp; + + if (e->bufflen==0) return; + + /* get the cursor point */ + z=_owl_editwin_get_index_from_xy(e); + + if (z==e->bufflen) { + /* point is after last character */ + z--; + } + + if (z-1 < e->lock) { + /* point is at beginning of buffer, do nothing */ + return; + } + + tmp=e->buff[z]; + e->buff[z]=e->buff[z-1]; + e->buff[z-1]=tmp; + owl_editwin_key_right(e); +} + +/* insert 'string' at the current point, later text is shifted + * right + */ +void owl_editwin_insert_string(owl_editwin *e, char *string) +{ + int i, j; + + j=strlen(string); + for (i=0; i<j; i++) { + owl_editwin_insert_char(e, string[i]); + } +} + +/* write 'string' at the current point, overwriting text that is + * already there + */ + +void owl_editwin_overwrite_string(owl_editwin *e, char *string) +{ + int i, j; + + j=strlen(string); + for (i=0; i<j; i++) { + owl_editwin_overwrite_char(e, string[i]); + } +} + +/* get the index into e->buff for the current cursor + * position. + */ +int _owl_editwin_get_index_from_xy(owl_editwin *e) +{ + int i; + char *ptr1, *ptr2; + + if (e->bufflen==0) return(0); + + /* first go to the yth line */ + ptr1=e->buff; + for (i=0; i<e->buffy; i++) { + ptr2=strchr(ptr1, '\n'); + if (!ptr2) { + /* we're already on the last line */ + break; + } + ptr1=ptr2+1; + } + + /* now go to the xth character */ + ptr2=strchr(ptr1, '\n'); + if (!ptr2) { + ptr2=e->buff+e->bufflen; + } + + if ((ptr2-ptr1) < e->buffx) { + ptr1=ptr2-1; + } else { + ptr1+=e->buffx; + } + + /* printf("DEBUG: index is %i\r\n", ptr1-e->buff); */ + return(ptr1-e->buff); +} + +void _owl_editwin_set_xy_by_index(owl_editwin *e, int index) +{ + int z, i; + + z=_owl_editwin_get_index_from_xy(e); + if (index>z) { + for (i=0; i<index-z; i++) { + owl_editwin_key_right(e); + } + } else if (index<z) { + for (i=0; i<z-index; i++) { + owl_editwin_key_left(e); + } + } +} + +void owl_editwin_adjust_for_locktext(owl_editwin *e) +{ + /* if we happen to have the cursor over locked text + * move it to be out of the locktext region */ + if (_owl_editwin_get_index_from_xy(e)<e->lock) { + _owl_editwin_set_xy_by_index(e, e->lock); + } +} + +void owl_editwin_backspace(owl_editwin *e) +{ + /* delete the char before the current one + * and shift later chars left + */ + if (_owl_editwin_get_index_from_xy(e) > e->lock) { + owl_editwin_key_left(e); + owl_editwin_delete_char(e); + } + owl_editwin_adjust_for_locktext(e); +} + +void owl_editwin_key_up(owl_editwin *e) +{ + if (e->buffy > 0) e->buffy--; + if (e->buffx >= owl_editwin_get_numchars_on_line(e, e->buffy)) { + e->buffx=owl_editwin_get_numchars_on_line(e, e->buffy); + } + + /* do we need to scroll? */ + if (e->buffy-e->topline < 0) { + e->topline-=e->winlines/2; + } + + owl_editwin_adjust_for_locktext(e); +} + +void owl_editwin_key_down(owl_editwin *e) +{ + /* move down if we can */ + if (e->buffy+1 < owl_editwin_get_numlines(e)) e->buffy++; + + /* if we're past the last character move back */ + if (e->buffx >= owl_editwin_get_numchars_on_line(e, e->buffy)) { + e->buffx=owl_editwin_get_numchars_on_line(e, e->buffy); + } + + /* do we need to scroll? */ + if (e->buffy-e->topline > e->winlines) { + e->topline+=e->winlines/2; + } + + /* adjust for locktext */ + owl_editwin_adjust_for_locktext(e); +} + +void owl_editwin_key_left(owl_editwin *e) +{ + /* move left if we can, and maybe up a line */ + if (e->buffx>0) { + e->buffx--; + } else if (e->buffy>0) { + e->buffy--; + e->buffx=owl_editwin_get_numchars_on_line(e, e->buffy); + } + + /* do we need to scroll up? */ + if (e->buffy-e->topline < 0) { + e->topline-=e->winlines/2; + } + + /* make sure to avoid locktext */ + owl_editwin_adjust_for_locktext(e); +} + +void owl_editwin_key_right(owl_editwin *e) +{ + int i; + + /* move right if we can, and skip down a line if needed */ + i=owl_editwin_get_numchars_on_line(e, e->buffy); + if (e->buffx < i) { + e->buffx++; + /* } else if (e->buffy+1 < owl_editwin_get_numlines(e)) { */ + } else if (_owl_editwin_get_index_from_xy(e) < e->bufflen) { + if (e->style==OWL_EDITWIN_STYLE_MULTILINE) { + e->buffx=0; + e->buffy++; + } + } + + /* do we need to scroll down? */ + if (e->buffy-e->topline >= e->winlines) { + e->topline+=e->winlines/2; + } +} + +void owl_editwin_move_to_nextword(owl_editwin *e) +{ + int i, x; + + /* if we're starting on a space, find the first non-space */ + i=_owl_editwin_get_index_from_xy(e); + if (e->buff[i]==' ') { + for (x=i; x<e->bufflen; x++) { + if (e->buff[x]!=' ' && e->buff[x]!='\n') { + _owl_editwin_set_xy_by_index(e, x); + break; + } + } + } + + /* find the next space, newline or end of line and go there, if + already at the end of the line, continue on to the next */ + i=owl_editwin_get_numchars_on_line(e, e->buffy); + if (e->buffx < i) { + /* move right till end of line */ + while (e->buffx < i) { + e->buffx++; + if (e->buff[_owl_editwin_get_index_from_xy(e)]==' ') return; + if (e->buffx == i) return; + } + } else if (e->buffx == i) { + /* try to move down */ + if (e->style==OWL_EDITWIN_STYLE_MULTILINE) { + if (e->buffy+1 < owl_editwin_get_numlines(e)) { + e->buffx=0; + e->buffy++; + owl_editwin_move_to_nextword(e); + } + } + } +} + +/* go backwards to the last non-space character + */ +void owl_editwin_move_to_previousword(owl_editwin *e) +{ + int i, x; + + /* are we already at the beginning of the word? */ + i=_owl_editwin_get_index_from_xy(e); + if ( (e->buff[i]!=' ' && e->buff[i]!='\n' && e->buff[i]!='\0') && + (e->buff[i-1]==' ' || e->buff[i-1]=='\n') ) { + owl_editwin_key_left(e); + } + + /* are we starting on a space character? */ + i=_owl_editwin_get_index_from_xy(e); + if (e->buff[i]==' ' || e->buff[i]=='\n' || e->buff[i]=='\0') { + /* find the first non-space */ + for (x=i; x>=e->lock; x--) { + if (e->buff[x]!=' ' && e->buff[x]!='\n' && e->buff[x]!='\0') { + _owl_editwin_set_xy_by_index(e, x); + break; + } + } + } + + /* find the last non-space */ + i=_owl_editwin_get_index_from_xy(e); + for (x=i; x>=e->lock; x--) { + if (e->buff[x-1]==' ' || e->buff[x-1]=='\n') { + _owl_editwin_set_xy_by_index(e, x); + break; + } + } + _owl_editwin_set_xy_by_index(e, x); +} + + +void owl_editwin_delete_nextword(owl_editwin *e) +{ + int z; + + if (e->bufflen==0) return; + + /* if we start out on a space character then gobble all the spaces + up first */ + while (1) { + z=_owl_editwin_get_index_from_xy(e); + if (e->buff[z]==' ' || e->buff[z]=='\n') { + owl_editwin_delete_char(e); + } else { + break; + } + } + + /* then nuke the next word */ + while (1) { + z=_owl_editwin_get_index_from_xy(e); + /* z == e->bufflen check added to prevent a hang I (nelhage) have + seen repeatedly while using owl. I'm not sure precisely what + conditions lead to it. */ + if (z == e->bufflen + || e->buff[z+1]==' ' || e->buff[z+1]=='\n' || e->buff[z+1]=='\0') break; + owl_editwin_delete_char(e); + } + owl_editwin_delete_char(e); +} + +void owl_editwin_delete_previousword(owl_editwin *e) +{ + /* go backwards to the last non-space character, then delete chars */ + int i, startpos, endpos; + + startpos = _owl_editwin_get_index_from_xy(e); + owl_editwin_move_to_previousword(e); + endpos = _owl_editwin_get_index_from_xy(e); + for (i=0; i<startpos-endpos; i++) { + owl_editwin_delete_char(e); + } +} + +void owl_editwin_delete_to_endofline(owl_editwin *e) +{ + int i; + + if (owl_editwin_get_numchars_on_line(e, e->buffy)>e->buffx) { + /* normal line */ + i=_owl_editwin_get_index_from_xy(e); + while(i < e->bufflen) { + if (e->buff[i]!='\n') { + owl_editwin_delete_char(e); + } else if ((e->buff[i]=='\n') && (i==e->bufflen-1)) { + owl_editwin_delete_char(e); + } else { + return; + } + } + } else if (e->buffy+1 < owl_editwin_get_numlines(e)) { + /* line with cursor at the end but not on very last line */ + owl_editwin_key_right(e); + owl_editwin_backspace(e); + } +} + +void owl_editwin_move_to_line_end(owl_editwin *e) +{ + e->buffx=owl_editwin_get_numchars_on_line(e, e->buffy); +} + +void owl_editwin_move_to_line_start(owl_editwin *e) +{ + e->buffx=0; + owl_editwin_adjust_for_locktext(e); +} + +void owl_editwin_move_to_end(owl_editwin *e) +{ + /* go to last char */ + e->buffy=owl_editwin_get_numlines(e)-1; + e->buffx=owl_editwin_get_numchars_on_line(e, e->buffy); + owl_editwin_key_right(e); + + /* do we need to scroll? */ + /* + if (e->buffy-e->topline > e->winlines) { + e->topline+=e->winlines/2; + } + */ + owl_editwin_recenter(e); +} + +void owl_editwin_move_to_top(owl_editwin *e) +{ + _owl_editwin_set_xy_by_index(e, 0); + + /* do we need to scroll? */ + e->topline=0; + + owl_editwin_adjust_for_locktext(e); +} + +void owl_editwin_fill_paragraph(owl_editwin *e) +{ + int i, save; + + /* save our starting point */ + save=_owl_editwin_get_index_from_xy(e); + + /* scan back to the beginning of this paragraph */ + for (i=save; i>=e->lock; i--) { + if ( (i<=e->lock) || + ((e->buff[i]=='\n') && (e->buff[i-1]=='\n'))) { + _owl_editwin_set_xy_by_index(e, i+1); + break; + } + } + + /* main loop */ + while (1) { + i=_owl_editwin_get_index_from_xy(e); + + /* bail if we hit the end of the buffer */ + if (i>=e->bufflen) break; + + /* bail if we hit the end of the paragraph */ + if (e->buff[i]=='\n' && e->buff[i+1]=='\n') break; + + /* if we've travelled too far, linewrap */ + if ((e->buffx) >= e->fillcol) { + _owl_editwin_linewrap_word(e); + } + + /* did we hit the end of a line too soon? */ + i=_owl_editwin_get_index_from_xy(e); + if (e->buff[i]=='\n' && e->buffx<e->fillcol-1) { + /* ********* we need to make sure we don't pull in a word that's too long ***********/ + e->buff[i]=' '; + } + + /* fix spacing */ + i=_owl_editwin_get_index_from_xy(e); + if (e->buff[i]==' ' && e->buff[i+1]==' ') { + if (e->buff[i-1]=='.' || e->buff[i-1]=='!' || e->buff[i-1]=='?') { + owl_editwin_key_right(e); + } else { + owl_editwin_delete_char(e); + /* if we did this ahead of the save point, adjust it */ + if (i<save) save--; + } + } else { + owl_editwin_key_right(e); + } + + } + + /* put cursor back at starting point */ + _owl_editwin_set_xy_by_index(e, save); + + /* do we need to scroll? */ + if (e->buffy-e->topline < 0) { + e->topline-=e->winlines/2; + } +} + +/* returns true if only whitespace remains */ +int owl_editwin_is_at_end(owl_editwin *e) +{ + int cur=_owl_editwin_get_index_from_xy(e); + return (only_whitespace(e->buff+cur)); +} + +int owl_editwin_check_dotsend(owl_editwin *e) +{ + int i; + + if (!e->dotsend) return(0); + for (i=e->bufflen-1; i>0; i--) { + if (e->buff[i] == '.' + && (e->buff[i-1] == '\n' || e->buff[i-1] == '\r') + && (e->buff[i+1] == '\n' || e->buff[i+1] == '\r')) { + e->bufflen = i; + e->buff[i] = '\0'; + return(1); + } + if (!isspace((int) e->buff[i])) { + return(0); + } + } + return(0); +} + +void owl_editwin_post_process_char(owl_editwin *e, int j) +{ + /* check if we need to scroll down */ + if (e->buffy-e->topline >= e->winlines) { + e->topline+=e->winlines/2; + } + if ((j==13 || j==10) && owl_editwin_check_dotsend(e)) { + owl_command_editmulti_done(e); + return; + } + owl_editwin_redisplay(e, 0); +} + +void owl_editwin_process_char(owl_editwin *e, int j) +{ + if (j == ERR) return; + if (j>127 || ((j<32) && (j!=10) && (j!=13))) { + return; + } else { + owl_editwin_insert_char(e, j); + } +} + +char *owl_editwin_get_text(owl_editwin *e) +{ + return(e->buff+e->lock); +} + +int owl_editwin_get_numchars_on_line(owl_editwin *e, int line) +{ + int i; + char *ptr1, *ptr2; + + if (e->bufflen==0) return(0); + + /* first go to the yth line */ + ptr1=e->buff; + for (i=0; i<line; i++) { + ptr2=strchr(ptr1, '\n'); + if (!ptr2) { + /* we're already on the last line */ + return(0); + } + ptr1=ptr2+1; + } + + /* now go to the xth character */ + ptr2=strchr(ptr1, '\n'); + if (!ptr2) { + return(e->buff + e->bufflen - ptr1); + } + return(ptr2-ptr1); /* don't count the newline for now */ +} + +int owl_editwin_get_numlines(owl_editwin *e) +{ + return(owl_text_num_lines(e->buff)); +} + diff --git a/encapsulate.pl b/encapsulate.pl new file mode 100644 index 0000000..8619fa0 --- /dev/null +++ b/encapsulate.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl +# +# $Id: encapsulate.pl,v 1.1 2003/07/06 22:42:15 nygren Exp $ +# + +my $infile = $ARGV[0] or die "Usage: $0 infile structname"; +my $structname = $ARGV[1] or die "Usage: $0 infile structname";; +open INF, "<$infile" or die "Unable to open $infile"; + +print "/* This file was autogenerated from $infile. DO NOT EDIT !!!! */\n\n"; +print "char *$structname = \n"; +for my $x (<INF>) { + chomp $x; + $x =~ s/\\/\\\\/g; + $x =~ s/"/\\"/g; + print qq( "$x\\n"\n); +} +print " ;\n"; + +close INF; diff --git a/errqueue.c b/errqueue.c new file mode 100644 index 0000000..6e000fa --- /dev/null +++ b/errqueue.c @@ -0,0 +1,47 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +void owl_errqueue_init(owl_errqueue *eq) +{ + owl_list_create(&(eq->errlist)); +} + +void owl_errqueue_append_err(owl_errqueue *eq, char *msg) +{ + owl_list_append_element(&(eq->errlist), owl_strdup(msg)); +} + +/* fmtext should already be initialized */ +void owl_errqueue_to_fmtext(owl_errqueue *eq, owl_fmtext *fm) +{ + int i, j; + + j=owl_list_get_size(&(eq->errlist)); + for (i=0; i<j; i++) { + owl_fmtext_append_normal(fm, owl_list_get_element(&(eq->errlist), i)); + owl_fmtext_append_normal(fm, "\n"); + } +} diff --git a/examples/owlconf.erik b/examples/owlconf.erik new file mode 100644 index 0000000..de522fa --- /dev/null +++ b/examples/owlconf.erik @@ -0,0 +1,476 @@ +### The owlconf config file -*- perl -*- +### $Id: owlconf.erik,v 1.7 2009/03/28 21:01:34 kretch Exp $ +### +### !!!!!WARNING!!!!! !!!!!WARNING!!!!! !!!!!WARNING!!!!! !!!!!WARNING!!!!! +### This is an example file intended to demonstrate how to use +### various features of owl. Some of the key bindings, in particular, +### are more for examples than things you may actually want to use. +### Make sure to read through it first and understand it before just using it. +### Don't blame me if anything in here ends up vaporizing your dog. +### !!!!!WARNING!!!!! !!!!!WARNING!!!!! !!!!!WARNING!!!!! !!!!!WARNING!!!!! +### +### +### This file is interpreted by the perl interpreter. +### If you wish to execute an owl command use the +### function owl::command(). i.e. +### +### owl::command("set zsigproc /mit/kretch/bin/getzsig"); +### +### will set the owl variable zsigproc. Subroutines created with +### the names below will be executed at the specified times: +### +### subroutine name properties +### --------------- ---------- +### owl::startup() run when owl first starts +### owl::shutdown() run when owl exits +### owl::format_msg() formats messages that are passed to it +### owl::receive_msg() run when a message is received, and after +### it has been added to the message list +### +### The format_msg and receive_msg functions are passed owl::Message objects. +### The message attributes may be dereferenced with $m->attribute +### +### + +# tokens for sepbar are: +# .username = ping +# >username = login +# <username = logout +# :username = personal message +# M = mail received +my @sepbartokens = (); + +# Map for mail messages from msg id to pop msg id +my %mail_id_map = (); + +# (originally from jdaniel) +sub mail_add_message ($) { + my ($m) = @_; + my $from = `from -t`; + # example value: + # You have 188 messages (667593 bytes) on PO11.MIT.EDU. + my ($msg_num) = $from =~ m/(\d+)/; + $mail_id_map{$m->id} = $msg_num; +} + + +sub mail_pop_curmsg () { + my $m = owl::getcurmsg(); + if (!$m->is_mail || !defined $mail_id_map{$m->id}) { + &owl::command("pop-message"); + } else { + &owl::command(sprintf 'pexec pop -request c -request "retr %d" -request q', + $mail_id_map{$m->id}); + } +} + +sub zlocatesender { + my $m = owl::getcurmsg(); + if ($m->{"type"} eq "zephyr") { + my $sender = $m->{"sender"}; + owl::command("zlocate $sender"); + } +} + +# adds a sepbartoken and also updates the appendtosepbar variable +sub sepbartokens_add { + my ($token) = @_; + $token =~ s/"//g; # " + unshift @sepbartokens, $token; + pop @sepbartokens if (@sepbartokens > 80); + sepbartokens_set(); +} + +# trims a sepbartoken from the list also updates the appendtosepbar variable +sub sepbartokens_trim { + my ($token) = @_; + @sepbartokens = map { if ($_ ne $token) { $_; } else { (); } } @sepbartokens; + sepbartokens_set(); +} + +my $loopctr=0; + +# trims a sepbartoken from the list also updates the appendtosepbar variable +sub sepbartokens_set { + owl::command(sprintf "set -q appendtosepbar \"%s %s %s\"", (scalar localtime), (join " ", @sepbartokens)); +} + +my $restoreview = undef; +sub swapview { + my $curview = owl::command("getview"); + if ($restoreview) { + owl::command("view $restoreview"); + $restoreview = undef; + } else { + $restoreview = $curview; + owl::command("smartnarrow"); + } +} + +my $lastcolored = undef; +sub colorthread { + if ($lastcolored) { + owl::command("filter $lastcolored -c default"); + } + my $smartfilt = owl::command("smartfilter"); + if (!$smartfilt or $smartfilt eq $lastcolored) { + owl::command("filter $lastcolored -c default"); + $lastcolored = undef; + } else { + owl::command("filter $smartfilt -c green"); + $lastcolored = $smartfilt; + } +} + +# Load in things which don't belong in an owlconf +# that people might use as an example... +sub personal_startup { + my $personalconf = $ENV{"HOME"}."/.owl/personalconf"; + if (-f $personalconf) { + my $b = ""; + open INB, $personalconf; + for (<INB>) { $b .= $_; } + close INB; + eval $b; + } +} + +sub owl::startup { + owl::command("set -q logging on"); + owl::command("set -q logpath ~/.zlog/owl/personal"); + owl::command("set -q classlogpath ~/.zlog/owl/class"); + owl::command("set -q logging on"); + owl::command("set -q startuplogin off"); + owl::command("set -q shutdownlogout off"); + #owl::command("set personalbell on"); + owl::command("set -q _burningears on"); + owl::command("set -q rxping on"); + owl::command("set -q typewinsize 5"); + owl::command("filter me recipient %me% or ( sender %me% and class message and instance personal ) or class mail or type aim"); + owl::command("filter owl instance ^owl.*"); + + owl::command(q(alias finger pperl $x=owl::getcurmsg()->hostname; `finger \@$x`;)); + owl::command("bindkey recv f command finger"); + + owl::command("alias z zwrite"); + owl::command("alias zw zwrite"); + owl::command("alias v view"); + + owl::command("alias popmail perl mail_pop_curmsg();"); + + # toggle between a view and a smartnarrow with TAB + owl::command("alias swapview perl swapview();"); + owl::command("bindkey recv C-i command swapview"); + + # color the current thread + owl::command("alias colorthread perl colorthread();"); + owl::command("bindkey recv M-c command colorthread"); + + # zlocate current sender + owl::command("bindkey recv L command perl zlocatesender();"); + + # Declare styles + &owl::command("style vt perl format_msg_vt"); + &owl::command("style brief perl format_msg_brief"); + &owl::command("style custom perl format_msg_custom"); + &owl::command("style debug perl format_msg_debug"); + &owl::command("set -q default_style custom"); + + # Change to different view styles + owl::command("bindkey recv \"C-s v\" command view -s vt"); + owl::command("bindkey recv \"C-s c\" command view -s custom"); + owl::command("bindkey recv \"C-s b\" command view -s brief"); + owl::command("bindkey recv \"C-s d\" command view -s debug"); + owl::command("bindkey recv \"C-s o\" command view -s standard"); + + # For fast-reading of zephyrs + owl::command("bindkey recv M-k command ( smartnarrow ; delete view )"); + owl::command("bindkey recv M-l command ( expunge ; view all )"); + owl::command("bindkey recv M-K command ( smartnarrow ; delete view ; expunge ; view all )"); + + # Support for scroll mice + &owl::command("bindkey recv \"M-[ A\" command recv:prev"); + &owl::command("bindkey recv \"M-[ B\" command recv:next"); + + # This overrides the default "M" keybinding + owl::command("bindkey recv M command popmail"); + + sepbartokens_add("..."); + + personal_startup(); +} + +sub owl::shutdown { +# not doing anything at the moment... +} + + +# run when a message is received, and after +# it has been added to the message list. +sub owl::receive_msg { + my ($m) = @_; + my ($out, $tmp); + + if ($m->is_admin && !$m->is_outgoing) { + $m->delete(); + return 1; + } + + return 0 if (!$m->is_zephyr && !$m->is_aim); + + my $sender = $m->pretty_sender; + + if ($m->is_ping) { + sepbartokens_add(".$sender"); + $m->delete(); + } elsif ($m->is_loginout) { + $m->delete(); + if ($m->is_login) { + sepbartokens_add(">$sender"); + sepbartokens_trim("<$sender"); + } elsif ($m->is_logout) { + sepbartokens_add("<$sender"); + sepbartokens_trim(">$sender"); + } + } elsif ($m->is_mail) { + mail_add_message($m); + $m->delete(); + sepbartokens_add("M"); + } + + if ($m->is_personal) { + sepbartokens_trim(".$sender"); + sepbartokens_add(":$sender"); + } + + return 1; +} + +sub indent4 { + my ($b) = @_; + $b=~s/^/ /g; + $b=~s/\n/\n /g; + return $b; +} + +# run to format a message +sub format_msg_custom { + my ($m) = @_; + my ($out, $tmp); + + if ($m->is_admin) { + $out = "\@bold([owl admin]) ".$m->header."\n"; + $out.=indent4($m->body); + return $out."\n"; + } + + my $sender=$m->pretty_sender; + + if (($m->is_aim or $m->is_zephyr) && $m->is_loginout) { + + if ($m->is_login) { + $out="\@bold(LOGIN)"; + } elsif ($m->is_logout) { + $out="\@bold(LOGOUT)"; + } else { + $out="\@bold(UNKNOWN)"; + } + $out.=" for \@bold($sender) on ".$m->type; + if ($m->is_zephyr) { + $out.=" at ".($m->login_host)." on ".($m->login_tty); + } + return("$out\n"); + } + + if (!$m->is_zephyr && !$m->is_aim) { + return "Unknown message type: ".$m->type."\n"; + } + + if ($m->is_outgoing) { + my $target = $m->recipient; + if ($m->is_zephyr) { + $target = $m->zwriteline; + $target =~ s/^zwrite //; + } + $out = sprintf "\@bold([outgoing %s to %s]) / %s\n", $m->type, $target, $m->time; + $out.=indent4($m->body); + return "$out\n"; + } + + if ($m->is_zephyr && $m->is_ping) { + return("\@bold(PING) from \@bold($sender)\n"); + } elsif ($m->is_zephyr && $m->is_mail) { + $out = "\@bold(MAIL) "; + if ($m->body =~ /^From:\s+(.+)\s*$/m) { $out .= "From $1 "; } + if ($m->body =~ /^To:\s+(.+)\s*$/m) { $out .= "To $1 "; } + if ($m->body =~ /^Subject:\s+(.+)\s*$/m) { $out .= "Subject $1 "; } + return("$out\n"); + } + + if ($m->is_zephyr) { + $out = sprintf "[mit,%s,%s] / %s / %s", lc($m->class), + lc($m->instance), $m->time, lc($m->host); + if ($m->opcode ne "") {$out.=" op:".$m->opcode;} + $out.="\n"; + $out.= " \@bold($sender)> "; + if ($m->zsig ne "") { + my $zsig = $m->zsig; + $zsig =~ s/(\n.*)+$/ [...]/; + if (length($zsig)+5+length($sender) > 70) { + $out.="# ..."; + } else { + $out.="# $zsig"; + } + } + $out.="\n"; + } else { + $out = sprintf "[%s] / %s\n", lc($m->type), $m->time; + $out.= " \@bold($sender)> "; + $out.="\n"; + } + + $out.=indent4($m->body); + + # make personal messages bold + if ($m->is_personal) { + $out="\@bold{".$out."}"; + } + + return($out."\n"); +} + +sub format_msg_debug { + my ($m) = @_; + return "\@bold(-------------MESSAGE-------------)\n".($m->serialize).".\n"; +} + +sub format_msg_brief { + my ($m) = @_; + my $out = format_msg_vt($m); + $out =~ s/\n/ /g; + $out =~ s/ / /g; + return($out."\n"); +} + + +sub format_msg_vt { + my ($m) = @_; + my ($out, $tmp); + + if ($m->is_admin) { + $out = sprintf "%-29s \@i(%s)", "\@bold(OWL ADMIN)", $m->header; + $tmp=$m->body; + $tmp=~s/^/ /g; + $tmp=~s/\n/\n /g; + $out.="\n".$tmp; + return $out; + } + + my $sender=$m->pretty_sender; + + if ($m->is_outgoing) { + my $target = $m->recipient; + if ($m->is_zephyr) { + $target = $m->zwriteline; + $target =~ s/^zwrite //; + } + $out = sprintf "%-15s %-13s", "\@bold(OUTGOING)", "to $target via ".$m->type.": "; + my $pagewidth = owl::getnumcols()-6; + $out .= fill_text($m->body, $pagewidth, 22, 1); + return $out; + } + + if (!$m->is_zephyr && !$m->is_aim) { + return "Unknown message type: ".$m->type."\n"; + } + + if ($m->is_zephyr && $m->is_ping) { + return (sprintf "%-15s %-13s\n", "\@bold(PING)", $sender); + } elsif ($m->is_loginout) { + my $state; + if ($m->is_login) { + $state="\@bold(LOGIN)"; + } elsif ($m->is_logout) { + $state="\@bold(LOGOUT)"; + } else { + $state="\@bold(UNKNOWN)"; + } + my $out = sprintf "%-15s %-13s ", $state, $sender; + if ($m->is_zephyr) { + $out .= sprintf "via %s on %s at %s", $m->type, $m->login_host, $m->login_tty; + } else { + $out .= sprintf "via %s", $m->type; + } + return "$out\n"; + + } elsif ($m->is_zephyr && $m->is_mail) { + my $body = $m->body; + $out = sprintf "%-15s %-13s ", "\@bold(MAIL)", ""; + if ($body =~ /^From:\s+(.+)\s*$/m) { $out .= "From $1 "; } + if ($body =~ /^To:\s+(.+)\s*$/m) { $out .= "To $1 "; } + if ($body =~ /^Subject:\s+(.+)\s*$/m) { $out .= "Subject $1 "; } + return($out."\n"); + } + + my $channel = ""; + my $body = $m->body; + + if ($m->is_zephyr) { + my $inst = $m->instance; + $channel = $m->class; + if (lc($m->class) ne "message" and lc($m->instance) eq "personal") { + $inst = ""; + } + $channel .= "[".$inst."]"; + $channel = substr($channel,0,13); + } else { + $channel = "[".$m->type."]"; + } + + $header = sprintf "%-8s %-13s ", lc($sender), lc($channel); + + if ($body =~ /=/) { + # indent it + $out.=$header."\n".indent4($body); + } else { + # fill it + my $pagewidth = owl::getnumcols()-6; + $out .= $header; + $out .= fill_text($body, $pagewidth, 22, 1); + } + + # note: no zsig added in this version + + # make personal messages bold + if ($m->is_personal) { + $out="\@bold{".$out."}"; + } + return($out); +} + +sub fill_text { + my ($in, $width, $indent, $unindent_first) = @_; + $indent = 0 if (@_ < 3); + my $unindent = $indent if ($unindent_first); + my @words = split " ", $in; + my $out = ""; + my $outline = ""; + if (!$unindent_first) { + my $outline = " "x$indent; + } + for my $w (@words) { + if (($outline ne "") + && (length($outline)+length($w) > $width-$unindent)) { + $out .= $outline."\n"; + $outline = " "x$indent; + $unindent = 0; + } + if ($outline =~ /.*\.$/) { + $outline .= " "; + } elsif ($outline ne "") { + $outline .= " "; + } + $outline .= $w; + } + $out .= $outline . "\n"; +} diff --git a/examples/owlconf.simple b/examples/owlconf.simple new file mode 100644 index 0000000..e173b88 --- /dev/null +++ b/examples/owlconf.simple @@ -0,0 +1,270 @@ +### The owlconf config file -*- perl -*- +### $Id: owlconf.simple,v 1.3 2009/03/28 21:01:35 kretch Exp $ + +### !!!!!WARNING!!!!! !!!!!WARNING!!!!! !!!!!WARNING!!!!! !!!!!WARNING!!!!! +### This is an example file intended to demonstrate how to use +### various features of owl. Some of the key bindings, in particular, +### are more for examples than things you may actually want to use. +### Make sure to read through it first and understand it before just using it. +### Don't blame me if anything in here ends up vaporizing your dog. +### !!!!!WARNING!!!!! !!!!!WARNING!!!!! !!!!!WARNING!!!!! !!!!!WARNING!!!!! + +### +### This file is interpreted by the perl interpreter. +### If you wish to execute an owl command use the +### function owl::command(). i.e. +### +### owl::command("set zsigproc /mit/kretch/bin/getzsig"); +### +### will set the owl variable zsigproc. Subroutines created with +### the names below will be executed at the specified times: +### +### subroutine name properties +### --------------- ---------- +### owl::startup() run when owl first starts +### owl::shutdown() run when owl exits +### owl::format_msg() run when a new message arrives, the return +### value is used to display the message on the +### screen +### owl::receive_msg() run when a message is received, and after +### it has been added to the message list +### +### +### The following variables will be set each time a message is recevied +### and when owl::format_msg() and owl::receive_msg() are run. +### +### $owl::class, $owl::instance, $owl::recipient, $owl::direction, +### $owl::sender, $owl::opcode, $owl::zsig, +### $owl::msg, $owl::time, $owl::host, @owl::fields, $owl::id +### + +# NOTE: Lines beginning with a "#" are perl comments. + + +# This subroutine is run whenever owl starts up. +# The owl::command("foo") lines execute the owl command "foo". +sub owl::startup { + + ################################################################ + ## The following set the values of variables. + ## Doing "help" will show you more about each variable. + ## These lines will set things to the default. + ## You can uncomment them and then change the value to get + ## a different behavior (remove the "#" to an alternate value). + ################################################################ + + ## Set this to off to disable the terminal bell. + # owl::command('set -q bell on'); + + ## Set this to off to disable the terminal bell on personal zephyrs. + # owl::command('set -q personalbell off'); + + ## Set this to on to enable logging of personal zephyrs + # owl::command('set -q logging off'); + + ## This directory must exist and is where personal zephyrs are logged. + ## By default, this is the ~/zlog/personal/ directory + # owl::command('set -q logpath '.$ENV{'HOME'}.'/zlog/personal/'); + + ## Set this to on to enable logging of classes + # owl::command('set -q classlogging off'); + + ## This directory must exist and is where class zephyrs are logged. + ## By default, this is the ~/zlog/class/ directory + # owl::command('set -q classlogpath '.$ENV{'HOME'}.'/zlog/class/'); + + ## If set to on, this will make is so that C-d doesn't + ## send zephyrs by accident. + # owl::command('set -q disable-ctrl-d off'); + + ## If set to on, outgoing messages will be displayed. + # owl::command('set -q displayoutgoing on'); + + ## Displays received pings? + # owl::command('set -q rxping off'); + + ## Send pings? + # owl::command('set -q txping on'); + + ## Size of typing window at the bottom of the screen + # owl::command('set -q typewinsize 8'); + + ## Which view to switch to after the 'V' or 'X' commands. + # owl::command('set -q view_home all'); + + + ## Default message to send when zaway is on (toggle with 'A') + # owl::command('set -q zaway_msg_default "Not here now..."'); + + ## Default zephyr signature. + # owl::command('set -q zsig "meep"'); + + ## Program to generate a zsig. + # owl::command('set -q zsigproc "/mit/foo/bin/randzsig"'); + + + ################################################################ + ## The following create filters. Filters can be used to select + ## on multiple zephyrs for operations. With 'views' you can + ## look at only the zephyrs matching a particular filter. + ## Use the 'view <filtername>' command or the 'v' key to switch views. + ## The 'V' key will switch you back to your 'view_home' view. + ## The M-D key will delete all messages in the current view + ## + ## + ## The filter command creates a filter with the specified name, + ## or if one already exists it is replaced. Example filter + ## syntax would be: + ## + ## filter myfilter -c red ( class ^foobar$ ) or ( class ^quux$ and instance ^bar$ ) + ## + ## Valid matching fields are class, instance, recipient, sender, opcode + ## and realm. Valid operations are 'and', 'or' and 'not'. Spaces must be + ## present before and after parenthesis. If the optional color argument + ## is used it specifies the color that messages matching this filter + ## should be displayed in. Do 'show colors' to see the available colors. + ## + ################################################################ + + ## This would create a shortcut to only show personal messages + ## with filter 'me' and to color them yellow. + ## Replace myusername with yours. + # owl::command('filter me -c yellow recipient myusername'); + + ## This would create a 'quiet' filter to not show messages + ## on noisy classes and instances. + # owl::command('filter quiet not ( class ^foo|bar|quux$ or instance ( ^baaz$ ) '); + + ## The 'trash' filter is used when you press 'T' to mark things + ## for autodeletion. + # owl::command('filter trash class ^mail$ or opcode ^ping$ or type ^admin$ or class ^login$'); + +} + +## This is run when owl exits. Currently this does nothing. +sub owl::shutdown { +} + +## This is run to format the contents of the message. +## It returns a string which is a formatted message. +## The following variables will be set each time before this is run: +## +## $owl::class, $owl::instance, $owl::recipient, +## $owl::sender, $owl::opcode, $owl::zsig, +## $owl::msg, $owl::time, $owl::host, @owl::fields, $owl::id + +sub owl::format_msg { + my $out, $tmp; + + ## Strip out noise from the sender string. + $owl::sender=~s/\@ATHENA\.MIT\.EDU$//; + $owl::sender=~s/\@local-realm$//; + $owl::recipient=~s/\@ATHENA\.MIT\.EDU$//; + $owl::recipient=~s/\@local-realm$//; + + ## Format ping, login, and mail messages. + ## uc(foo) upcases the string and "eq" does string comparison. + if (uc($owl::opcode) eq "PING") { + return("\@bold(PING) from \@bold($owl::sender)\n"); + } elsif (uc($owl::class) eq "LOGIN") { + if (uc($owl::opcode) eq "USER_LOGIN") { + $out="\@bold(LOGIN)"; + } elsif (uc($owl::opcode) eq "USER_LOGOUT") { + $out="\@bold(LOGOUT)"; + } else { + $out="\@bold(UNKNOWN)"; + } + $out.=" for \@bold($owl::sender) at $fields[0] on $fields[2]\n"; + return($out); + } elsif (uc($owl::class) eq "MAIL" and uc($owl::instance) eq "INBOX") { + $out = "\@bold(MAIL) "; + if ($owl::msg =~ /^From:\s+(.+)\s*$/m) { $out .= "From $1 "; } + if ($owl::msg =~ /^To:\s+(.+)\s*$/m) { $out .= "To $1 "; } + if ($owl::msg =~ /^Subject:\s+(.+)\s*$/m) { $out .= "Subject $1 "; } + return($out."\n"); + } + + ## The remainder formats normal messages (eg, to classes and instances). + ## Outgoing messages have different headers than incoming messages. + ## Note that: + ## $out .= "foo"; appends "foo" to the end of the variable $out. + ## lc(bar) will convert bar to lowercase. + ## "ne" does "not equal" for string comparison. + ## sprintf fills in the %s's with the arguments later on the line. + ## "foo"."bar" will append the two strings together. + + + if ($owl::direction eq "out") { + # Outgoing messages + + $out .= sprintf "[outgoing to %s] / %s", $owl::recipient, $owl::time; + if ($owl::opcode ne "") {$out.=" op:$owl::opcode";} + $out.="\n"; + $out.= " \@bold($owl::sender)> "; + if ($owl::zsig ne "") { + my $zsig = $owl::zsig; + $zsig =~ s/(\n.*)+$/ [...]/; + if (length($zsig)+5+length($owl::sender) > 70) { + $out.="# ..."; + } else { + $out.="# $zsig"; + } + } + } else { + # Incoming messages + + $out .= sprintf "[mit,%s,%s] / %s / %s", lc($owl::class), + lc($owl::instance), $owl::time, lc($owl::host); + if ($owl::opcode ne "") {$out.=" op:$owl::opcode";} + $out.="\n"; + $out.= " \@bold($owl::sender)> "; + if ($owl::zsig ne "") { + my $zsig = $owl::zsig; + $zsig =~ s/(\n.*)+$/ [...]/; + if (length($zsig)+5+length($owl::sender) > 70) { + $out.="# ..."; + } else { + $out.="# $zsig"; + } + } + } + $out.="\n"; + + # This indents the body of the message and then appends it on. + $tmp=$owl::msg; + $tmp=~s/^/ /g; + $tmp=~s/\n/\n /g; + $out.=$tmp; + + # This makes personal messages bold. + if (uc($owl::class) eq "MESSAGE" && + uc($owl::instance) eq "PERSONAL" && + $owl::direction eq "in") { + $out="\@bold{".$out."}"; + } + + # Finally, this appends a newline and returns the formatted message. + return($out."\n"); +} + +## This is run when a message is received, and after +## it has been added to the message list. +## In most cases you won't need anything here. +sub owl::receive_msg() { + + ## If this is uncommented, it would mark all messages + ## with opcode "PING" for immediate deletion: + # + # if (uc($owl::opcode) eq "PING") { + # owl::command("delete -id $owl::id"); + # } + + ## If this is uncommented, it would mark all messages + ## with class "LOGIN" for immediate deletion: + # + # if (uc($owl::login) eq "LOGIN") { + # owl::command("delete -id $owl::id"); + # } + + return 1; +} diff --git a/examples/owlconf.vtformat b/examples/owlconf.vtformat new file mode 100644 index 0000000..46fe2fe --- /dev/null +++ b/examples/owlconf.vtformat @@ -0,0 +1,141 @@ +### The owlconf config file -*- perl -*- +### $Id: owlconf.vtformat,v 1.1 2002/06/28 04:16:50 kretch Exp $ +### +### This file is interpreted by the perl interpreter. +### If you wish to execute an owl command use the +### function owl::command(). i.e. +### +### owl::command("set zsigproc /mit/kretch/bin/getzsig"); +### +### will set the owl variable zsigproc. Subroutines created with +### the names below will be executed at the specified times: +### +### subroutine name properties +### --------------- ---------- +### owl::startup() run when owl first starts +### owl::shutdown() run when owl exits +### owl::format_msg() run when a new message arrives, the return +### value is used to display the message on the +### screen +### +### The following variables will be set each time a message is recevied: +### +### $owl::class, $owl::instance, $owl::recipient, +### $owl::sender, $owl::opcode, $owl::zsig, +### $owl::msg, $owl::time, $owl::host, @owl::fields +### + +# classes will be appreviated this way +my %class_abbrev = ( + "message" => "", + "spleen" => "sp", + "ccare" => "cc", + "implied-consent" => "ic", + "syseng" => "se", + "install" => "i", + ); + +sub owl::startup { + owl::command("set logging off"); + #owl::command("set zsigproc /home/nygren/bin/owlzsigs"); + owl::command("set startuplogin off"); + owl::command("set shutdownlogout off"); + #owl::command("set personalbell on"); + owl::command("set rxping on"); + owl::command("set typewinsize 5"); +} + +sub owl::shutdown { +# system("/mit/kretch/bin/zkill > /dev/null 2> /dev/null"); +} + +sub owl::format_msg { + my ($out, $tmp); + + $owl::sender=~s/\@ATHENA\.MIT\.EDU$//; + $owl::sender=~s/\@local-realm$//; + + if (uc($owl::opcode) eq "PING") { + return("\@bold(PING) from \@bold($owl::sender)\n"); + } elsif (uc($owl::class) eq "LOGIN") { + if (uc($owl::opcode) eq "USER_LOGIN") { + $out="\@bold(LOGIN)"; + } elsif (uc($owl::opcode) eq "USER_LOGOUT") { + $out="\@bold(LOGOUT)"; + } else { + $out="\@bold(UNKNOWN)"; + } + $out.=" for \@bold($owl::sender) at $fields[0] on $fields[2]\n"; + return($out); + } elsif (uc($owl::class) eq "MAIL") { + $out = "\@bold(MAIL) "; + if ($owl::msg =~ /^From:\s+(.+)\s*$/m) { $out .= "From $1 "; } + if ($owl::msg =~ /^To:\s+(.+)\s*$/m) { $out .= "To $1 "; } + if ($owl::msg =~ /^Subject:\s+(.+)\s*$/m) { $out .= "Subject $1 "; } + return($out."\n"); + } + + my $channel = $owl::class; + if (defined $class_abbrev{lc($owl::class)}) { + $channel = $class_abbrev{lc($owl::class)}; + } + if (lc($owl::class) ne "message" and lc($owl::instance) eq "personal") { + $owl::instance = ""; + } + $channel .= "[".$owl::instance."]"; + $channel = substr($channel,0,13); + + $header = sprintf "%-8s %-13s ", lc($owl::sender), lc($channel); + + if ($owl::msg =~ /=/) { + # indent it + $tmp=$owl::msg; + $tmp=~s/^/ /g; + $tmp=~s/\n/\n /g; + $out.=$header."\n".$tmp; + } else { + # fill it + my $pagewidth = 74; + $out .= $header; + $out .= fill_text($owl::msg, $pagewidth, 22, 1); + } + + # note: no zsig added in this version + + # make personal messages bold + if (uc($owl::class) eq "MESSAGE" && + uc($owl::instance) eq "PERSONAL") { + $out="\@bold{".$out."}\n"; + } + + return($out); +} + + + +sub fill_text { + my ($in, $width, $indent, $unindent_first) = @_; + $indent = 0 if (@_ < 3); + my $unindent = $indent if ($unindent_first); + my @words = split " ", $in; + my $out = ""; + my $outline = ""; + if (!$unindent_first) { + my $outline = " "x$indent; + } + for my $w (@words) { + if (($outline ne "") + && (length($outline)+length($w) > $width-$unindent)) { + $out .= $outline."\n"; + $outline = " "x$indent; + $unindent = 0; + } + if ($outline =~ /.*\.$/) { + $outline .= " "; + } elsif ($outline ne "") { + $outline .= " "; + } + $outline .= $w; + } + $out .= $outline . "\n"; +} diff --git a/filter.c b/filter.c new file mode 100644 index 0000000..e682c17 --- /dev/null +++ b/filter.c @@ -0,0 +1,626 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <string.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: filter.c,v 1.24 2009/03/29 18:43:40 kretch Exp $"; + +#define OWL_FILTER_MAXRECURSE 20 + +int owl_filter_init_fromstring(owl_filter *f, char *name, char *string) +{ + char **argv; + int argc, out; + + argv=owl_parseline(string, &argc); + out=owl_filter_init(f, name, argc, argv); + owl_parsefree(argv, argc); + return(out); +} + +int owl_filter_init(owl_filter *f, char *name, int argc, char **argv) +{ + int i, j, error; + owl_filterelement *fe; + char *regexstr; + owl_list list; + + f->name=owl_strdup(name); + f->polarity=0; + f->color=OWL_COLOR_DEFAULT; + f->cachedmsgid=-1; + owl_list_create(&(f->fes)); + + /* first take arguments that have to come first */ + /* set the color */ + if (argc>=2 && !strcmp(argv[0], "-c")) { + if (owl_util_string_to_color(argv[1])==-1) { + owl_function_error("The color '%s' is not available, using default.", argv[1]); + } else { + f->color=owl_util_string_to_color(argv[1]); + } + argc-=2; + argv+=2; + } + + /* then deal with the expression */ + for (i=0; i<argc; i++) { + error=0; + fe=owl_malloc(sizeof(owl_filterelement)); + + /* all the 0 argument possibilities */ + if (!strcmp(argv[i], "(")) { + owl_filterelement_create_openbrace(fe); + } else if (!strcmp(argv[i], ")")) { + owl_filterelement_create_closebrace(fe); + } else if (!strcasecmp(argv[i], "and")) { + owl_filterelement_create_and(fe); + } else if (!strcasecmp(argv[i], "or")) { + owl_filterelement_create_or(fe); + } else if (!strcasecmp(argv[i], "not")) { + owl_filterelement_create_not(fe); + } else if (!strcasecmp(argv[i], "true")) { + owl_filterelement_create_true(fe); + } else if (!strcasecmp(argv[i], "false")) { + owl_filterelement_create_false(fe); + + } else if (i==argc-1) { /* we need more than one arg at this point */ + error=1; + } else { + if (!strcasecmp(argv[i], "class") || + !strcasecmp(argv[i], "instance") || + !strcasecmp(argv[i], "sender") || + !strcasecmp(argv[i], "recipient") || + !strcasecmp(argv[i], "body") || + !strcasecmp(argv[i], "opcode") || + !strcasecmp(argv[i], "realm") || + !strcasecmp(argv[i], "type") || + !strcasecmp(argv[i], "direction") || + !strcasecmp(argv[i], "hostname") || + !strcasecmp(argv[i], "login")) { + regexstr=owl_text_substitute(argv[i+1], "%me%", owl_zephyr_get_sender()); + owl_filterelement_create_re(fe, argv[i], regexstr); + owl_free(regexstr); + i++; + } else if (!strcasecmp(argv[i], "filter")) { + owl_filterelement_create_filter(fe, argv[i+1]); + i++; + } else if (!strcasecmp(argv[i], "perl")) { + owl_filterelement_create_perl(fe, argv[i+1]); + i++; + } else { + error=1; + } + } + + if (!error) { + owl_list_append_element(&(f->fes), fe); + } else { + owl_free(fe); + owl_filter_free(f); + return(-1); + } + } + + /* Are we trying to use the filter we're creating? Bad. */ + owl_list_create(&list); + _owl_filter_get_subfilter_names(f, &list); + j=owl_list_get_size(&list); + for (i=0; i<j; i++) { + if (!strcasecmp(name, owl_list_get_element(&list, i))) { + owl_function_error("Filter loop."); + owl_filter_free(f); + owl_list_free_all(&list, owl_free); + return(-1); + } + } + owl_list_free_all(&list, owl_free); + + /* Now check for more subtle recursion. */ + if (owl_filter_is_toodeep(f)) { + owl_function_error("Filter loop or exceeds recursion depth"); + owl_filter_free(f); + return(-1); + } + + return(0); +} + +char *owl_filter_get_name(owl_filter *f) +{ + return(f->name); +} + +void owl_filter_set_polarity_match(owl_filter *f) +{ + f->polarity=0; +} + +void owl_filter_set_polarity_unmatch(owl_filter *f) +{ + f->polarity=1; +} + +void owl_filter_set_color(owl_filter *f, int color) +{ + f->color=color; +} + +int owl_filter_get_color(owl_filter *f) +{ + return(f->color); +} + +void owl_filter_set_cachedmsgid(owl_filter *f, int cachedmsgid) +{ + f->cachedmsgid=cachedmsgid; +} + +int owl_filter_get_cachedmsgid(owl_filter *f) +{ + return(f->cachedmsgid); +} + +/* return 1 if the message matches the given filter, otherwise + * return 0. + */ +int owl_filter_message_match(owl_filter *f, owl_message *m) +{ + int i, j, tmp; + owl_list work_fes, *fes; + owl_filterelement *fe; + char *field, *match; + + /* create the working list of expression elements */ + fes=&(f->fes); + owl_list_create(&work_fes); + j=owl_list_get_size(fes); + for (i=0; i<j; i++) { + owl_list_append_element(&work_fes, owl_list_get_element(fes, i)); + } + + /* first go thru and evaluate all RE elements to true or false */ + match=""; + for (i=0; i<j; i++) { + fe=owl_list_get_element(&work_fes, i); + if (!owl_filterelement_is_re(fe)) continue; + field=owl_filterelement_get_field(fe); + if (!strcasecmp(field, "class")) { + match=owl_message_get_class(m); + } else if (!strcasecmp(field, "instance")) { + match=owl_message_get_instance(m); + } else if (!strcasecmp(field, "sender")) { + match=owl_message_get_sender(m); + } else if (!strcasecmp(field, "recipient")) { + match=owl_message_get_recipient(m); + } else if (!strcasecmp(field, "body")) { + match=owl_message_get_body(m); + } else if (!strcasecmp(field, "opcode")) { + match=owl_message_get_opcode(m); + } else if (!strcasecmp(field, "realm")) { + match=owl_message_get_realm(m); + } else if (!strcasecmp(field, "type")) { + match=owl_message_get_type(m); + } else if (!strcasecmp(field, "hostname")) { + match=owl_message_get_hostname(m); + } else if (!strcasecmp(field, "direction")) { + if (owl_message_is_direction_out(m)) { + match="out"; + } else if (owl_message_is_direction_in(m)) { + match="in"; + } else if (owl_message_is_direction_none(m)) { + match="none"; + } else { + match=""; + } + } else if (!strcasecmp(field, "login")) { + if (owl_message_is_login(m)) { + match="login"; + } else if (owl_message_is_logout(m)) { + match="logout"; + } else { + match="none"; + } + } + + tmp=owl_regex_compare(owl_filterelement_get_re(fe), match); + if (!tmp) { + owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g)); + } else { + owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g)); + } + } + + /* now subfilters and perl functions */ + for (i=0; i<j; i++) { + owl_filter *subfilter; + + fe=owl_list_get_element(&work_fes, i); + + if (owl_filterelement_is_filter(fe)) { + + subfilter=owl_global_get_filter(&g, owl_filterelement_get_filtername(fe)); + if (!subfilter) { + /* the filter does not exist, maybe because it was deleted. + * Default to not matching + */ + owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g)); + } else if (owl_filter_message_match(subfilter, m)) { + owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g)); + } else { + owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g)); + } + + } else if (owl_filterelement_is_perl(fe)) { + char *subname, *perlrv; + int tf=0; + + subname = owl_filterelement_get_filtername(fe); + if (!owl_perlconfig_is_function(subname)) { + owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g)); + continue; + } + perlrv = owl_perlconfig_call_with_message(subname, m); + if (perlrv) { + if (0 == strcmp(perlrv, "1")) { + tf=1; + } + owl_free(perlrv); + } + if (tf) { + owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g)); + } else { + owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g)); + } + } + } + + /* call the recrsive helper */ + i=_owl_filter_message_match_recurse(f, m, &work_fes, 0, owl_list_get_size(&(f->fes))-1); + + /* now there will be only one TRUE / FALSE, find it among the NULL's */ + tmp=0; + for (i=0; i<j; i++) { + fe=owl_list_get_element(&work_fes, i); + if (owl_filterelement_is_null(fe)) continue; + if (owl_filterelement_is_true(fe)) { + tmp=1; + break; + } + if (owl_filterelement_is_false(fe)) { + tmp=0; + break; + } + } + + /* reverse the answer if negative polarity is in use */ + if (f->polarity) tmp=!tmp; + + owl_list_free_simple(&work_fes); + return(tmp); +} + +int _owl_filter_message_match_recurse(owl_filter *f, owl_message *m, owl_list *fes, int start, int end) +{ + int a=0, b=0, i, x, y, z, score, ret, type; + owl_filterelement *fe, *tmpfe=NULL; + + /* Deal with parens first. */ + for (i=0; i<OWL_FILTER_MAX_DEPTH; i++) { + /* Find first open paren and matching close paren, store in x, y */ + score=x=y=0; + for (i=start; i<=end; i++) { + fe=owl_list_get_element(fes, i); + if (owl_filterelement_is_openbrace(fe)) { + if (score==0) x=i; + score++; + } else if (owl_filterelement_is_closebrace(fe)) { + score--; + if (score<0) { + /* unblanaced parens */ + return(-1); + } else if (score==0) { + y=i; /* this is the matching close paren */ + break; + } + } + } + if (score>0) { + /* unblanaced parens */ + return(-1); + } + + /* Simply the parens by removing them and evaluating what was in between */ + if (y>0) { + /* null out the parens */ + owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g)); + owl_list_replace_element(fes, y, owl_global_get_filterelement_null(&g)); + + /* evaluate expression in between */ + ret=_owl_filter_message_match_recurse(f, m, fes, x+1, y-1); + if (ret<0) return(-1); + + /* there may be more, so we continue */ + continue; + } else { + /* otherwise we're done with this part */ + break; + } + } + if (i==OWL_FILTER_MAX_DEPTH) { + /* hit the saftey limit, consider it invalid */ + return(-1); + } + + /* Find AND / OR / NOT. + * For binary expressions (AND/OR): + * "type" is 1 + * "x" will index first val, "y" the operator and "z" the second val + * For unary expressions (NOT): + * "type" is 2 + * "x" will index the operator, "y" the value + * "score" tallys how many expression elements have been found so far + */ + for (i=0; i<OWL_FILTER_MAX_DEPTH; i++) { + type=score=x=y=z=0; + for (i=start; i<=end; i++) { + fe=owl_list_get_element(fes, i); + if (owl_filterelement_is_null(fe)) continue; + if (score==0) { + if (owl_filterelement_is_value(fe)) { + x=i; + score=1; + type=1; + } else if (owl_filterelement_is_not(fe)) { + x=i; + score=1; + type=2; + } + } else if (score==1) { + if (type==1) { + if (owl_filterelement_is_and(fe) || owl_filterelement_is_or(fe)) { + score=2; + y=i; + } else { + /* it's not a valid binary expression */ + x=y=z=score=0; + } + } else if (type==2) { + if (owl_filterelement_is_value(fe)) { + /* valid unary expression, we're done */ + y=i; + break; + } + } + } else if (score==2) { + if (owl_filterelement_is_value(fe)) { + /* valid binary expression, we're done */ + z=i; + break; + } else { + x=y=z=score=0; + } + } + } + + /* simplify AND / OR */ + if ((type==1) && (z>0)) { + fe=owl_list_get_element(fes, x); + if (owl_filterelement_is_true(fe)) { + a=1; + } else if (owl_filterelement_is_false(fe)) { + a=0; + } + + fe=owl_list_get_element(fes, z); + if (owl_filterelement_is_true(fe)) { + b=1; + } else if (owl_filterelement_is_false(fe)) { + b=0; + } + + fe=owl_list_get_element(fes, y); + if (owl_filterelement_is_and(fe)) { + if (a && b) { + tmpfe=owl_global_get_filterelement_true(&g); + } else { + tmpfe=owl_global_get_filterelement_false(&g); + } + } else if (owl_filterelement_is_or(fe)) { + if (a || b) { + tmpfe=owl_global_get_filterelement_true(&g); + } else { + tmpfe=owl_global_get_filterelement_false(&g); + } + } + owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g)); + owl_list_replace_element(fes, y, tmpfe); + owl_list_replace_element(fes, z, owl_global_get_filterelement_null(&g)); + } else if ((type==2) && (y>0)) { + /* simplify NOT */ + fe=owl_list_get_element(fes, y); + owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g)); + if (owl_filterelement_is_false(fe)) { + owl_list_replace_element(fes, y, owl_global_get_filterelement_true(&g)); + } else { + owl_list_replace_element(fes, y, owl_global_get_filterelement_false(&g)); + } + } else { + break; + } + } + return(0); + +} + +char *owl_filter_print(owl_filter *f) +{ + int i, j; + owl_filterelement *fe; + GString *out = g_string_new(""); + + if (f->color!=OWL_COLOR_DEFAULT) { + g_string_append(out, "-c "); + if (f->color < 8) { + g_string_append(out, owl_util_color_to_string(f->color)); + } else { + g_string_append_printf(out, "%i",f->color); + } + g_string_append(out, " "); + } + + j=owl_list_get_size(&(f->fes)); + for (i=0; i<j; i++) { + fe=owl_list_get_element(&(f->fes), i); + g_string_append(out, owl_filterelement_to_string(fe)); + } + + return g_string_free(out, 0); +} + +/* Return 1 if the filters 'a' and 'b' are equivalent, 0 otherwise */ +int owl_filter_equiv(owl_filter *a, owl_filter *b) +{ + char *buffa, *buffb; + int ret; + + buffa = owl_filter_print(a); + buffb = owl_filter_print(b); + + ret = !strcmp(buffa, buffb); + ret = ret && !strcmp(owl_filter_get_name(a), + owl_filter_get_name(b)); + + owl_free(buffa); + owl_free(buffb); + + return ret; +} + +/* Private + * 'list' should already be allocated and initialized + * This function places into list the string names of all filters + * used in the filter expression for 'f'. + * Caller must do a full free on 'list', including elements. + */ +void _owl_filter_get_subfilter_names(owl_filter *f, owl_list *list) +{ + int i, j; + owl_filterelement *fe; + + j=owl_list_get_size(&(f->fes)); + for (i=0; i<j; i++) { + fe=owl_list_get_element(&(f->fes), i); + if (owl_filterelement_is_filter(fe)) { + owl_list_append_element(list, owl_strdup(owl_filterelement_get_filtername(fe))); + } + } +} + +int owl_filter_is_toodeep(owl_filter *f) +{ + owl_list seen, tocheck, tmp; + int i, j, x, y; + owl_filter *subfilter; + + owl_list_create(&seen); + owl_list_create(&tocheck); + owl_list_create(&tmp); + + /* seed 'tocheck' with the first set of filters */ + _owl_filter_get_subfilter_names(f, &tmp); + j=owl_list_get_size(&tmp); + for (i=0; i<j; i++) { + owl_util_list_add_unique_string(&tocheck, owl_list_get_element(&tmp, i)); + } + owl_list_free_all(&tmp, owl_free); + owl_list_create(&tmp); + + /* add this list to the 'seen' list */ + owl_list_append_element(&seen, owl_strdup(owl_filter_get_name(f))); + + for (i=0; i<OWL_FILTER_MAXRECURSE; i++) { + /* if anything in 'tocheck' is in 'seen' we have a loop */ + if (owl_util_common_strings_in_lists(&tocheck, &seen)) { + owl_list_free_all(&tmp, owl_free); + owl_list_free_all(&tocheck, owl_free); + owl_list_free_all(&seen, owl_free); + return(1); + } + + /* if there's nothing to check, we're done */ + y=owl_list_get_size(&tocheck); + if (y==0) { + owl_list_free_all(&tmp, owl_free); + owl_list_free_all(&tocheck, owl_free); + owl_list_free_all(&seen, owl_free); + return(0); + } + + /* add everything in 'tocheck' to the 'seen' list */ + /* y=owl_list_get_size(&tocheck); */ + for (x=0; x<y; x++) { + owl_list_append_element(&seen, owl_strdup(owl_list_get_element(&tocheck, x))); + } + + /* make a new list into 'tmp' with the children of 'tocheck' */ + /* y=owl_list_get_size(&tocheck); */ + for (x=0; x<y; x++) { + subfilter=owl_global_get_filter(&g, owl_list_get_element(&tocheck, x)); + if (!subfilter) return(0); + _owl_filter_get_subfilter_names(subfilter, &tmp); + } + + /* clean out 'tocheck' */ + owl_list_free_all(&tocheck, owl_free); + owl_list_create(&tocheck); + + /* put 'tmp' into 'tocheck' */ + y=owl_list_get_size(&tmp); + for (x=0; x<y; x++) { + owl_util_list_add_unique_string(&tocheck, owl_list_get_element(&tmp, x)); + } + + /* clean out 'tmp' */ + owl_list_free_all(&tmp, owl_free); + owl_list_create(&tmp); + } + + owl_list_free_all(&tmp, owl_free); + owl_list_free_all(&tocheck, owl_free); + owl_list_free_all(&seen, owl_free); + + return(1); +} + +void owl_filter_free(owl_filter *f) +{ + void (*func)(); + + func=&owl_filterelement_free; + + if (f->name) owl_free(f->name); + owl_list_free_all(&(f->fes), func); +} diff --git a/filterelement.c b/filterelement.c new file mode 100644 index 0000000..10f3208 --- /dev/null +++ b/filterelement.c @@ -0,0 +1,239 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id: filterelement.c,v 1.7 2009/03/29 12:38:20 kretch Exp $"; + +#define OWL_FILTERELEMENT_NULL 0 +#define OWL_FILTERELEMENT_TRUE 1 +#define OWL_FILTERELEMENT_FALSE 2 +#define OWL_FILTERELEMENT_OPENBRACE 3 +#define OWL_FILTERELEMENT_CLOSEBRACE 4 +#define OWL_FILTERELEMENT_AND 5 +#define OWL_FILTERELEMENT_OR 6 +#define OWL_FILTERELEMENT_NOT 7 +#define OWL_FILTERELEMENT_RE 8 +#define OWL_FILTERELEMENT_FILTER 9 +#define OWL_FILTERELEMENT_PERL 10 + +void owl_filterelement_create_null(owl_filterelement *fe) +{ + fe->type=OWL_FILTERELEMENT_NULL; + fe->field=NULL; + fe->filtername=NULL; +} + +void owl_filterelement_create_openbrace(owl_filterelement *fe) +{ + owl_filterelement_create_null(fe); + fe->type=OWL_FILTERELEMENT_OPENBRACE; +} + +void owl_filterelement_create_closebrace(owl_filterelement *fe) +{ + owl_filterelement_create_null(fe); + fe->type=OWL_FILTERELEMENT_CLOSEBRACE; +} + +void owl_filterelement_create_and(owl_filterelement *fe) +{ + owl_filterelement_create_null(fe); + fe->type=OWL_FILTERELEMENT_AND; +} + +void owl_filterelement_create_or(owl_filterelement *fe) +{ + owl_filterelement_create_null(fe); + fe->type=OWL_FILTERELEMENT_OR; +} + +void owl_filterelement_create_not(owl_filterelement *fe) +{ + owl_filterelement_create_null(fe); + fe->type=OWL_FILTERELEMENT_NOT; +} + +void owl_filterelement_create_true(owl_filterelement *fe) +{ + owl_filterelement_create_null(fe); + fe->type=OWL_FILTERELEMENT_TRUE; +} + +void owl_filterelement_create_false(owl_filterelement *fe) +{ + owl_filterelement_create_null(fe); + fe->type=OWL_FILTERELEMENT_FALSE; +} + +void owl_filterelement_create_re(owl_filterelement *fe, char *field, char *re) +{ + owl_filterelement_create_null(fe); + fe->type=OWL_FILTERELEMENT_RE; + fe->field=owl_strdup(field); + owl_regex_create(&(fe->re), re); +} + +void owl_filterelement_create_filter(owl_filterelement *fe, char *name) +{ + owl_filterelement_create_null(fe); + fe->type=OWL_FILTERELEMENT_FILTER; + fe->filtername=owl_strdup(name); +} + +void owl_filterelement_create_perl(owl_filterelement *fe, char *name) +{ + owl_filterelement_create_null(fe); + fe->type=OWL_FILTERELEMENT_PERL; + fe->filtername=owl_strdup(name); +} + +void owl_filterelement_free(owl_filterelement *fe) +{ + if (fe->field) owl_free(fe->field); + if (fe->filtername) owl_free(fe->filtername); +} + +int owl_filterelement_is_null(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_NULL) return(1); + return(0); +} + +int owl_filterelement_is_openbrace(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_OPENBRACE) return(1); + return(0); +} + +int owl_filterelement_is_closebrace(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_CLOSEBRACE) return(1); + return(0); +} + +int owl_filterelement_is_and(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_AND) return(1); + return(0); +} + +int owl_filterelement_is_or(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_OR) return(1); + return(0); +} + +int owl_filterelement_is_not(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_NOT) return(1); + return(0); +} + +int owl_filterelement_is_true(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_TRUE) return(1); + return(0); +} + +int owl_filterelement_is_false(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_FALSE) return(1); + return(0); +} + +int owl_filterelement_is_re(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_RE) return(1); + return(0); +} + +int owl_filterelement_is_perl(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_PERL) return(1); + return(0); +} + +owl_regex *owl_filterelement_get_re(owl_filterelement *fe) +{ + return(&(fe->re)); +} + +int owl_filterelement_is_filter(owl_filterelement *fe) +{ + if (fe->type==OWL_FILTERELEMENT_FILTER) return(1); + return(0); +} + +char *owl_filterelement_get_field(owl_filterelement *fe) +{ + if (fe->field) return(fe->field); + return("unknown-field"); +} + +char *owl_filterelement_get_filtername(owl_filterelement *fe) +{ + if (fe->filtername) return(fe->filtername); + return("unknown-filter"); +} + +int owl_filterelement_is_value(owl_filterelement *fe) +{ + if ( (fe->type==OWL_FILTERELEMENT_TRUE) || + (fe->type==OWL_FILTERELEMENT_FALSE) || + (fe->type==OWL_FILTERELEMENT_RE) || + (fe->type==OWL_FILTERELEMENT_PERL) || + (fe->type==OWL_FILTERELEMENT_FILTER)) { + return(1); + } + return(0); +} + +/* caller must free the return */ +char *owl_filterelement_to_string(owl_filterelement *fe) +{ + if (owl_filterelement_is_openbrace(fe)) { + return(owl_strdup("( ")); + } else if (owl_filterelement_is_closebrace(fe)) { + return(owl_strdup(") ")); + } else if (owl_filterelement_is_and(fe)) { + return(owl_strdup("and ")); + } else if (owl_filterelement_is_or(fe)) { + return(owl_strdup("or ")); + } else if (owl_filterelement_is_not(fe)) { + return(owl_strdup("not ")); + } else if (owl_filterelement_is_true(fe)) { + return(owl_strdup("true ")); + } else if (owl_filterelement_is_false(fe)) { + return(owl_strdup("false ")); + } else if (owl_filterelement_is_re(fe)) { + return(owl_sprintf("%s %s ", fe->field, owl_regex_get_string(&(fe->re)))); + } else if (owl_filterelement_is_filter(fe)) { + return(owl_sprintf("filter %s ", fe->filtername)); + } else if (owl_filterelement_is_perl(fe)) { + return(owl_sprintf("perl %s ", fe->filtername)); + } + + return(owl_strdup("?")); +} diff --git a/fmtext.c b/fmtext.c new file mode 100644 index 0000000..0c207e8 --- /dev/null +++ b/fmtext.c @@ -0,0 +1,649 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" +#include <stdlib.h> +#include <string.h> + +static const char fileIdent[] = "$Id: fmtext.c,v 1.17 2009/03/29 12:38:20 kretch Exp $"; + +/* initialize an fmtext with no data */ +void owl_fmtext_init_null(owl_fmtext *f) +{ + f->textlen=0; + f->textbuff=owl_strdup(""); + f->fmbuff=owl_malloc(5); + f->colorbuff=owl_malloc(5); + f->fmbuff[0]=OWL_FMTEXT_ATTR_NONE; + f->colorbuff[0]=OWL_COLOR_DEFAULT; +} + +/* Internal function. Set the attribute 'attr' from index 'first' to + * index 'last' + */ +void _owl_fmtext_set_attr(owl_fmtext *f, int attr, int first, int last) +{ + int i; + for (i=first; i<=last; i++) { + f->fmbuff[i]=(unsigned char) attr; + } +} + +/* Internal function. Add the attribute 'attr' to the existing + * attributes from index 'first' to index 'last' + */ +void _owl_fmtext_add_attr(owl_fmtext *f, int attr, int first, int last) +{ + int i; + for (i=first; i<=last; i++) { + f->fmbuff[i]|=(unsigned char) attr; + } +} + +/* Internal function. Set the color to be 'color' from index 'first' + * to index 'last + */ +void _owl_fmtext_set_color(owl_fmtext *f, int color, int first, int last) +{ + int i; + for (i=first; i<=last; i++) { + f->colorbuff[i]=(unsigned char) color; + } +} + +/* append text to the end of 'f' with attribute 'attr' and color + * 'color' + */ +void owl_fmtext_append_attr(owl_fmtext *f, char *text, int attr, int color) +{ + int newlen; + + newlen=strlen(f->textbuff)+strlen(text); + f->textbuff=owl_realloc(f->textbuff, newlen+2); + f->fmbuff=owl_realloc(f->fmbuff, newlen+2); + f->colorbuff=owl_realloc(f->colorbuff, newlen+2); + + strcat(f->textbuff, text); + _owl_fmtext_set_attr(f, attr, f->textlen, newlen); + _owl_fmtext_set_color(f, color, f->textlen, newlen); + f->textlen=newlen; +} + +/* Append normal, uncolored text 'text' to 'f' */ +void owl_fmtext_append_normal(owl_fmtext *f, char *text) +{ + owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_NONE, OWL_COLOR_DEFAULT); +} + +/* Append normal text 'text' to 'f' with color 'color' */ +void owl_fmtext_append_normal_color(owl_fmtext *f, char *text, int color) +{ + owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_NONE, color); +} + +/* Append bold text 'text' to 'f' */ +void owl_fmtext_append_bold(owl_fmtext *f, char *text) +{ + owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_BOLD, OWL_COLOR_DEFAULT); +} + +/* Append reverse video text 'text' to 'f' */ +void owl_fmtext_append_reverse(owl_fmtext *f, char *text) +{ + owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_REVERSE, OWL_COLOR_DEFAULT); +} + +/* Append reversed and bold, uncolored text 'text' to 'f' */ +void owl_fmtext_append_reversebold(owl_fmtext *f, char *text) +{ + owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_REVERSE | OWL_FMTEXT_ATTR_BOLD, OWL_COLOR_DEFAULT); +} + +/* Add the attribute 'attr' to all text in 'f' */ +void owl_fmtext_addattr(owl_fmtext *f, int attr) +{ + /* add the attribute to all text */ + int i, j; + + j=f->textlen; + for (i=0; i<j; i++) { + f->fmbuff[i] |= attr; + } +} + +/* Anywhere the color is NOT ALREDY SET, set the color to 'color'. + * Other colors are left unchanged + */ +void owl_fmtext_colorize(owl_fmtext *f, int color) +{ + /* everywhere the color is OWL_COLOR_DEFAULT, change it to be 'color' */ + int i, j; + + j=f->textlen; + for(i=0; i<j; i++) { + if (f->colorbuff[i]==OWL_COLOR_DEFAULT) f->colorbuff[i] = color; + } +} + +/* Internal function. Append text from 'in' between index 'start' and + * 'stop' to the end of 'f' + */ +void _owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in, int start, int stop) +{ + int newlen, i; + + newlen=strlen(f->textbuff)+(stop-start+1); + f->textbuff=owl_realloc(f->textbuff, newlen+1); + f->fmbuff=owl_realloc(f->fmbuff, newlen+1); + f->colorbuff=owl_realloc(f->colorbuff, newlen+1); + + strncat(f->textbuff, in->textbuff+start, stop-start+1); + f->textbuff[newlen]='\0'; + for (i=start; i<=stop; i++) { + f->fmbuff[f->textlen+(i-start)]=in->fmbuff[i]; + f->colorbuff[f->textlen+(i-start)]=in->colorbuff[i]; + } + f->textlen=newlen; +} + +/* append fmtext 'in' to 'f' */ +void owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in) +{ + _owl_fmtext_append_fmtext(f, in, 0, in->textlen); + +} + +/* Append 'nspaces' number of spaces to the end of 'f' */ +void owl_fmtext_append_spaces(owl_fmtext *f, int nspaces) +{ + int i; + for (i=0; i<nspaces; i++) { + owl_fmtext_append_normal(f, " "); + } +} + +/* Return a plain version of the fmtext. Caller is responsible for + * freeing the return + */ +char *owl_fmtext_print_plain(owl_fmtext *f) +{ + return(owl_strdup(f->textbuff)); +} + +/* add the formatted text to the curses window 'w'. The window 'w' + * must already be initiatlized with curses + */ +void owl_fmtext_curs_waddstr(owl_fmtext *f, WINDOW *w) +{ + char *tmpbuff; + int position, trans1, trans2, len, lastsame; + + if (w==NULL) { + owl_function_debugmsg("Hit a null window in owl_fmtext_curs_waddstr."); + return; + } + + tmpbuff=owl_malloc(f->textlen+10); + + position=0; + len=f->textlen; + while (position<=len) { + /* find the last char with the current format and color */ + trans1=owl_util_find_trans(f->fmbuff+position, len-position); + trans2=owl_util_find_trans(f->colorbuff+position, len-position); + + if (trans1<trans2) { + lastsame=position+trans1; + } else { + lastsame=position+trans2; + } + + /* set the format */ + wattrset(w, A_NORMAL); + if (f->fmbuff[position] & OWL_FMTEXT_ATTR_BOLD) { + wattron(w, A_BOLD); + } + if (f->fmbuff[position] & OWL_FMTEXT_ATTR_REVERSE) { + wattron(w, A_REVERSE); + } + if (f->fmbuff[position] & OWL_FMTEXT_ATTR_UNDERLINE) { + wattron(w, A_UNDERLINE); + } + + /* set the color */ + /* warning, this is sort of a hack */ + if (owl_global_get_hascolors(&g)) { + if (f->colorbuff[position]!=OWL_COLOR_DEFAULT) { + wattron(w, COLOR_PAIR(f->colorbuff[position])); + } + } + + /* add the text */ + strncpy(tmpbuff, f->textbuff + position, lastsame-position+1); + tmpbuff[lastsame-position+1]='\0'; + waddstr(w, tmpbuff); + + position=lastsame+1; + } + owl_free(tmpbuff); +} + + +/* start with line 'aline' (where the first line is 0) and print + * 'lines' number of lines into 'out' + */ +int owl_fmtext_truncate_lines(owl_fmtext *in, int aline, int lines, owl_fmtext *out) +{ + char *ptr1, *ptr2; + int i, offset; + + /* find the starting line */ + ptr1=in->textbuff; + if (aline!=0) { + for (i=0; i<aline; i++) { + ptr1=strchr(ptr1, '\n'); + if (!ptr1) return(-1); + ptr1++; + } + } + /* ptr1 now holds the starting point */ + + /* copy in the next 'lines' lines */ + if (lines<1) return(-1); + + for (i=0; i<lines; i++) { + offset=ptr1-in->textbuff; + ptr2=strchr(ptr1, '\n'); + if (!ptr2) { + _owl_fmtext_append_fmtext(out, in, offset, (in->textlen)-1); + return(-1); + } + _owl_fmtext_append_fmtext(out, in, offset, (ptr2-ptr1)+offset); + ptr1=ptr2+1; + } + return(0); +} + +/* Truncate the message so that each line begins at column 'acol' and + * ends at 'bcol' or sooner. The first column is number 0. The new + * message is placed in 'out'. The message is * expected to end in a + * new line for now + */ +void owl_fmtext_truncate_cols(owl_fmtext *in, int acol, int bcol, owl_fmtext *out) +{ + char *ptr1, *ptr2, *last; + int len, offset; + + last=in->textbuff+in->textlen-1; + ptr1=in->textbuff; + while (ptr1<=last) { + ptr2=strchr(ptr1, '\n'); + if (!ptr2) { + /* but this shouldn't happen if we end in a \n */ + break; + } + + if (ptr2==ptr1) { + owl_fmtext_append_normal(out, "\n"); + ptr1++; + continue; + } + + /* we need to check that we won't run over here */ + len=bcol-acol; + if (len > (ptr2-(ptr1+acol))) { + /* the whole line fits with room to spare, don't take a full 'len' */ + len=ptr2-(ptr1+acol); + } + if (len>last-ptr1) { + /* the whole rest of the text fits with room to spare, adjust for it */ + len-=(last-ptr1); + } + if (len<=0) { + /* saftey check */ + owl_fmtext_append_normal(out, "\n"); + ptr1=ptr2+1; + continue; + } + + offset=ptr1-in->textbuff; + _owl_fmtext_append_fmtext(out, in, offset+acol, offset+acol+len); + + ptr1=ptr2+1; + } +} + +/* Return the number of lines in 'f' */ +int owl_fmtext_num_lines(owl_fmtext *f) +{ + int lines, i; + + if (f->textlen==0) return(0); + + lines=0; + for (i=0; i<f->textlen; i++) { + if (f->textbuff[i]=='\n') lines++; + } + + /* if the last char wasn't a \n there's one more line */ + if (f->textbuff[i-1]!='\n') lines++; + + return(lines); +} + +char *owl_fmtext_get_text(owl_fmtext *f) +{ + return(f->textbuff); +} + +/* set the charater at 'index' to be 'char'. If index is out of + * bounds don't do anything */ +void owl_fmtext_set_char(owl_fmtext *f, int index, int ch) +{ + if ((index < 0) || (index > f->textlen-1)) return; + f->textbuff[index]=ch; +} + +/* Make a copy of the fmtext 'src' into 'dst' */ +void owl_fmtext_copy(owl_fmtext *dst, owl_fmtext *src) +{ + int mallocsize; + + if (src->textlen==0) { + mallocsize=5; + } else { + mallocsize=src->textlen+2; + } + dst->textlen=src->textlen; + dst->textbuff=owl_malloc(mallocsize); + dst->fmbuff=owl_malloc(mallocsize); + dst->colorbuff=owl_malloc(mallocsize); + memcpy(dst->textbuff, src->textbuff, src->textlen+1); + memcpy(dst->fmbuff, src->fmbuff, src->textlen); + memcpy(dst->colorbuff, src->colorbuff, src->textlen); +} + +/* highlight all instances of "string". Return the number of + * instances found. This is a case insensitive search. + */ +int owl_fmtext_search_and_highlight(owl_fmtext *f, char *string) +{ + + int found, len; + char *ptr1, *ptr2; + + len=strlen(string); + found=0; + ptr1=f->textbuff; + while (ptr1-f->textbuff <= f->textlen) { + ptr2=stristr(ptr1, string); + if (!ptr2) return(found); + + found++; + _owl_fmtext_add_attr(f, OWL_FMTEXT_ATTR_REVERSE, + ptr2 - f->textbuff, + ptr2 - f->textbuff + len - 1); + + ptr1=ptr2+len; + } + return(found); +} + +/* return 1 if the string is found, 0 if not. This is a case + * insensitive search. + */ +int owl_fmtext_search(owl_fmtext *f, char *string) +{ + + if (stristr(f->textbuff, string)) return(1); + return(0); +} + + +/* Append the text 'text' to 'f' and interpret the zephyr style + * formatting syntax to set appropriate attributes. + */ +void owl_fmtext_append_ztext(owl_fmtext *f, char *text) +{ + int stacksize, curattrs, curcolor; + char *ptr, *txtptr, *buff, *tmpptr; + int attrstack[32], chrstack[32]; + + curattrs=OWL_FMTEXT_ATTR_NONE; + curcolor=OWL_COLOR_DEFAULT; + stacksize=0; + txtptr=text; + while (1) { + ptr=strpbrk(txtptr, "@{[<()>]}"); + if (!ptr) { + /* add all the rest of the text and exit */ + owl_fmtext_append_attr(f, txtptr, curattrs, curcolor); + return; + } else if (ptr[0]=='@') { + /* add the text up to this point then deal with the stack */ + buff=owl_malloc(ptr-txtptr+20); + strncpy(buff, txtptr, ptr-txtptr); + buff[ptr-txtptr]='\0'; + owl_fmtext_append_attr(f, buff, curattrs, curcolor); + owl_free(buff); + + /* update pointer to point at the @ */ + txtptr=ptr; + + /* now the stack */ + + /* if we've hit our max stack depth, print the @ and move on */ + if (stacksize==32) { + owl_fmtext_append_attr(f, "@", curattrs, curcolor); + txtptr++; + continue; + } + + /* if it's an @@, print an @ and continue */ + if (txtptr[1]=='@') { + owl_fmtext_append_attr(f, "@", curattrs, curcolor); + txtptr+=2; + continue; + } + + /* if there's no opener, print the @ and continue */ + tmpptr=strpbrk(txtptr, "(<[{ "); + if (!tmpptr || tmpptr[0]==' ') { + owl_fmtext_append_attr(f, "@", curattrs, curcolor); + txtptr++; + continue; + } + + /* check what command we've got, push it on the stack, start + using it, and continue ... unless it's a color command */ + buff=owl_malloc(tmpptr-ptr+20); + strncpy(buff, ptr, tmpptr-ptr); + buff[tmpptr-ptr]='\0'; + if (!strcasecmp(buff, "@bold")) { + attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD; + chrstack[stacksize]=tmpptr[0]; + stacksize++; + curattrs|=OWL_FMTEXT_ATTR_BOLD; + txtptr+=6; + owl_free(buff); + continue; + } else if (!strcasecmp(buff, "@b")) { + attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD; + chrstack[stacksize]=tmpptr[0]; + stacksize++; + curattrs|=OWL_FMTEXT_ATTR_BOLD; + txtptr+=3; + owl_free(buff); + continue; + } else if (!strcasecmp(buff, "@i")) { + attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE; + chrstack[stacksize]=tmpptr[0]; + stacksize++; + curattrs|=OWL_FMTEXT_ATTR_UNDERLINE; + txtptr+=3; + owl_free(buff); + continue; + } else if (!strcasecmp(buff, "@italic")) { + attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE; + chrstack[stacksize]=tmpptr[0]; + stacksize++; + curattrs|=OWL_FMTEXT_ATTR_UNDERLINE; + txtptr+=8; + owl_free(buff); + continue; + + /* if it's a color read the color, set the current color and + continue */ + } else if (!strcasecmp(buff, "@color") + && owl_global_get_hascolors(&g) + && owl_global_is_colorztext(&g)) { + owl_free(buff); + txtptr+=7; + tmpptr=strpbrk(txtptr, "@{[<()>]}"); + if (tmpptr && + ((txtptr[-1]=='(' && tmpptr[0]==')') || + (txtptr[-1]=='<' && tmpptr[0]=='>') || + (txtptr[-1]=='[' && tmpptr[0]==']') || + (txtptr[-1]=='{' && tmpptr[0]=='}'))) { + + /* grab the color name */ + buff=owl_malloc(tmpptr-txtptr+20); + strncpy(buff, txtptr, tmpptr-txtptr); + buff[tmpptr-txtptr]='\0'; + + /* set it as the current color */ + curcolor=owl_util_string_to_color(buff); + if (curcolor==-1) curcolor=OWL_COLOR_DEFAULT; + owl_free(buff); + txtptr=tmpptr+1; + continue; + + } else { + + } + + } else { + /* if we didn't understand it, we'll print it. This is different from zwgc + * but zwgc seems to be smarter about some screw cases than I am + */ + owl_fmtext_append_attr(f, "@", curattrs, curcolor); + txtptr++; + continue; + } + + } else if (ptr[0]=='}' || ptr[0]==']' || ptr[0]==')' || ptr[0]=='>') { + /* add the text up to this point first */ + buff=owl_malloc(ptr-txtptr+20); + strncpy(buff, txtptr, ptr-txtptr); + buff[ptr-txtptr]='\0'; + owl_fmtext_append_attr(f, buff, curattrs, curcolor); + owl_free(buff); + + /* now deal with the closer */ + txtptr=ptr; + + /* first, if the stack is empty we must bail (just print and go) */ + if (stacksize==0) { + buff=owl_malloc(5); + buff[0]=ptr[0]; + buff[1]='\0'; + owl_fmtext_append_attr(f, buff, curattrs, curcolor); + owl_free(buff); + txtptr++; + continue; + } + + /* if the closing char is what's on the stack, turn off the + attribue and pop the stack */ + if ((ptr[0]==')' && chrstack[stacksize-1]=='(') || + (ptr[0]=='>' && chrstack[stacksize-1]=='<') || + (ptr[0]==']' && chrstack[stacksize-1]=='[') || + (ptr[0]=='}' && chrstack[stacksize-1]=='{')) { + int i; + stacksize--; + curattrs=OWL_FMTEXT_ATTR_NONE; + for (i=0; i<stacksize; i++) { + curattrs|=attrstack[i]; + } + txtptr+=1; + continue; + } else { + /* otherwise print and continue */ + buff=owl_malloc(5); + buff[0]=ptr[0]; + buff[1]='\0'; + owl_fmtext_append_attr(f, buff, curattrs, curcolor); + owl_free(buff); + txtptr++; + continue; + } + } else { + /* we've found an unattached opener, print everything and move on */ + buff=owl_malloc(ptr-txtptr+20); + strncpy(buff, txtptr, ptr-txtptr+1); + buff[ptr-txtptr+1]='\0'; + owl_fmtext_append_attr(f, buff, curattrs, curcolor); + owl_free(buff); + txtptr=ptr+1; + continue; + } + } +} + +/* requires that the list values are strings or NULL. + * joins the elements together with join_with. + * If format_fn is specified, passes it the list element value + * and it will return a string which this needs to free. */ +void owl_fmtext_append_list(owl_fmtext *f, owl_list *l, char *join_with, char *(format_fn)(void*)) +{ + int i, size; + void *elem; + char *text; + + size = owl_list_get_size(l); + for (i=0; i<size; i++) { + elem = (char*)owl_list_get_element(l,i); + if (elem && format_fn) { + text = format_fn(elem); + if (text) { + owl_fmtext_append_normal(f, text); + owl_free(text); + } + } else if (elem) { + owl_fmtext_append_normal(f, elem); + } + if ((i < size-1) && join_with) { + owl_fmtext_append_normal(f, join_with); + } + } +} + +/* Free all memory allocated by the object */ +void owl_fmtext_free(owl_fmtext *f) +{ + if (f->textbuff) owl_free(f->textbuff); + if (f->fmbuff) owl_free(f->fmbuff); + if (f->colorbuff) owl_free(f->colorbuff); +} + diff --git a/functions.c b/functions.c new file mode 100644 index 0000000..e7e4da1 --- /dev/null +++ b/functions.c @@ -0,0 +1,3590 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <netinet/in.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <errno.h> +#include <signal.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: functions.c,v 1.141 2009/04/05 23:36:53 kretch Exp $"; + +void owl_function_noop(void) +{ + return; +} + +char *owl_function_command(char *cmdbuff) +{ + owl_function_debugmsg("executing command: %s", cmdbuff); + return owl_cmddict_execute(owl_global_get_cmddict(&g), + owl_global_get_context(&g), cmdbuff); +} + +void owl_function_command_norv(char *cmdbuff) +{ + char *rv; + rv=owl_function_command(cmdbuff); + if (rv) owl_free(rv); +} + +void owl_function_command_alias(char *alias_from, char *alias_to) +{ + owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to); +} + +owl_cmd *owl_function_get_cmd(char *name) +{ + return owl_cmddict_find(owl_global_get_cmddict(&g), name); +} + +void owl_function_show_commands() +{ + owl_list l; + owl_fmtext fm; + + owl_fmtext_init_null(&fm); + owl_fmtext_append_bold(&fm, "Commands: "); + owl_fmtext_append_normal(&fm, "(use 'show command <name>' for details)\n"); + owl_cmddict_get_names(owl_global_get_cmddict(&g), &l); + owl_fmtext_append_list(&fm, &l, "\n", owl_function_cmd_describe); + owl_fmtext_append_normal(&fm, "\n"); + owl_function_popless_fmtext(&fm); + owl_cmddict_namelist_free(&l); + owl_fmtext_free(&fm); +} + +void owl_function_show_view(char *viewname) +{ + owl_view *v; + owl_fmtext fm; + + /* we only have the one view right now */ + v=owl_global_get_current_view(&g); + if (viewname && strcmp(viewname, owl_view_get_name(v))) { + owl_function_error("No view named '%s'", viewname); + return; + } + + owl_fmtext_init_null(&fm); + owl_view_to_fmtext(v, &fm); + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +void owl_function_show_styles() { + owl_list l; + owl_fmtext fm; + + owl_fmtext_init_null(&fm); + owl_fmtext_append_bold(&fm, "Styles:\n"); + owl_global_get_style_names(&g, &l); + owl_fmtext_append_list(&fm, &l, "\n", owl_function_style_describe); + owl_fmtext_append_normal(&fm, "\n"); + owl_function_popless_fmtext(&fm); + owl_list_free_all(&l, owl_free); + owl_fmtext_free(&fm); +} + +char *owl_function_style_describe(void *name) { + char *desc, *s; + owl_style *style; + style = owl_global_get_style_by_name(&g, name); + if (style) { + desc = owl_style_get_description(style); + } else { + desc = "???"; + } + s = owl_sprintf("%-20s - %s%s", name, + 0==owl_style_validate(style)?"":"[INVALID] ", + desc); + return s; +} + +char *owl_function_cmd_describe(void *name) +{ + owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name); + if (cmd) return owl_cmd_describe(cmd); + else return(NULL); +} + +void owl_function_show_command(char *name) +{ + owl_function_help_for_command(name); +} + +void owl_function_show_license() +{ + char *text; + + text="" + "Owl version " OWL_VERSION_STRING "\n" + "\n" + "Copyright (c) 2002,2003,2004,2009 James M. Kretchmar\n" + "\n" + "Owl is free software: you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation, either version 3 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "Owl is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with Owl. If not, see <http://www.gnu.org/licenses/>.\n" + "\n" + "---------------------------------------------------------------\n" + "\n" + "As of Owl version 2.1.12 there are patches contributed by\n" + "developers of the branched BarnOwl project, Copyright (c)\n" + "2006-2009 The BarnOwl Developers. All rights reserved.\n"; + owl_function_popless_text(text); +} + + +/* Add the given message to Owl's internal queue. If displayoutgoing + * is disabled, the message is NOT added to any internal queue, -1 is + * returned and THE CALLER IS EXPECTED TO FREE THE GIVEN MESSAGE. + * Otherwise 0 is returned and the caller need do nothing more + */ +int owl_function_add_message(owl_message *m) +{ + /* if displayoutgoing is disabled, nuke the message and move on */ + if (! owl_global_is_displayoutgoing(&g)) { + return(-1); + } + + /* add it to the global list and current view */ + owl_messagelist_append_element(owl_global_get_msglist(&g), m); + owl_view_consider_message(owl_global_get_current_view(&g), m); + + /* do followlast if necessary */ + if (owl_global_should_followlast(&g)) owl_function_lastmsg_noredisplay(); + + /* redisplay etc. */ + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + if (owl_popwin_is_active(owl_global_get_popwin(&g))) { + owl_popwin_refresh(owl_global_get_popwin(&g)); + } + wnoutrefresh(owl_global_get_curs_recwin(&g)); + owl_global_set_needrefresh(&g); + return(0); +} + +/* Create an admin message, append it to the global list of messages + * and redisplay if necessary. + */ +void owl_function_adminmsg(char *header, char *body) +{ + owl_message *m; + + m=owl_malloc(sizeof(owl_message)); + owl_message_create_admin(m, header, body); + + /* add it to the global list and current view */ + owl_messagelist_append_element(owl_global_get_msglist(&g), m); + owl_view_consider_message(owl_global_get_current_view(&g), m); + + /* do followlast if necessary */ + if (owl_global_should_followlast(&g)) owl_function_lastmsg_noredisplay(); + + /* redisplay etc. */ + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + if (owl_popwin_is_active(owl_global_get_popwin(&g))) { + owl_popwin_refresh(owl_global_get_popwin(&g)); + } + wnoutrefresh(owl_global_get_curs_recwin(&g)); + owl_global_set_needrefresh(&g); +} + +/* Create an outgoing zephyr message and return a pointer to it. Does + * not put it on the global queue, use owl_function_add_message() for + * that. + */ +owl_message *owl_function_make_outgoing_zephyr(char *body, char *zwriteline, char *zsig) +{ + owl_message *m; + owl_zwrite z; + + /* create a zwrite for the purpose of filling in other message fields */ + owl_zwrite_create_from_line(&z, zwriteline); + + /* create the message */ + m=owl_malloc(sizeof(owl_message)); + owl_message_create_from_zwriteline(m, zwriteline, body, zsig); + owl_zwrite_free(&z); + + return(m); +} + +/* Create an outgoing AIM message, returns a pointer to the created + * message or NULL if we're not logged into AIM (and thus unable to + * create the message). Does not put it on the global queue. Use + * owl_function_add_message() for that . + */ +owl_message *owl_function_make_outgoing_aim(char *body, char *to) +{ + owl_message *m; + + /* error if we're not logged into aim */ + if (!owl_global_is_aimloggedin(&g)) return(NULL); + + m=owl_malloc(sizeof(owl_message)); + owl_message_create_aim(m, + owl_global_get_aim_screenname(&g), + to, + body, + OWL_MESSAGE_DIRECTION_OUT, + 0); + return(m); +} + +/* Create an outgoing loopback message and return a pointer to it. + * Does not append it to the global queue, use + * owl_function_add_message() for that. + */ +owl_message *owl_function_make_outgoing_loopback(char *body) +{ + owl_message *m; + + /* create the message */ + m=owl_malloc(sizeof(owl_message)); + owl_message_create_loopback(m, body); + owl_message_set_direction_out(m); + + return(m); +} + +void owl_function_zwrite_setup(char *line) +{ + owl_editwin *e; + char *buff; + owl_zwrite z; + int ret; + + /* check the arguments */ + ret=owl_zwrite_create_from_line(&z, line); + if (ret) { + owl_function_error("Error in zwrite arugments"); + owl_zwrite_free(&z); + return; + } + + /* send a ping if necessary */ + if (owl_global_is_txping(&g)) { + owl_zwrite_send_ping(&z); + } + owl_zwrite_free(&z); + + /* create and setup the editwin */ + e=owl_global_get_typwin(&g); + owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE, owl_global_get_msg_history(&g)); + + if (!owl_global_get_lockout_ctrld(&g)) { + owl_function_makemsg("Type your zephyr below. End with ^D or a dot on a line by itself. ^C will quit."); + } else { + owl_function_makemsg("Type your zephyr below. End with a dot on a line by itself. ^C will quit."); + } + + owl_editwin_clear(e); + owl_editwin_set_dotsend(e); + buff=owl_sprintf("----> %s\n", line); + owl_editwin_set_locktext(e, buff); + + owl_global_set_typwin_active(&g); + + owl_global_set_buffercommand(&g, line); + owl_free(buff); +} + +void owl_function_aimwrite_setup(char *line) +{ + owl_editwin *e; + char *buff; + + /* check the arguments */ + + /* create and setup the editwin */ + e=owl_global_get_typwin(&g); + owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE, owl_global_get_msg_history(&g)); + + if (!owl_global_get_lockout_ctrld(&g)) { + owl_function_makemsg("Type your message below. End with ^D or a dot on a line by itself. ^C will quit."); + } else { + owl_function_makemsg("Type your message below. End with a dot on a line by itself. ^C will quit."); + } + + owl_editwin_clear(e); + owl_editwin_set_dotsend(e); + buff=owl_sprintf("----> %s\n", line); + owl_editwin_set_locktext(e, buff); + + owl_global_set_typwin_active(&g); + + owl_global_set_buffercommand(&g, line); + owl_free(buff); +} + +void owl_function_loopwrite_setup() +{ + owl_editwin *e; + + /* create and setup the editwin */ + e=owl_global_get_typwin(&g); + owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE, owl_global_get_msg_history(&g)); + + if (!owl_global_get_lockout_ctrld(&g)) { + owl_function_makemsg("Type your message below. End with ^D or a dot on a line by itself. ^C will quit."); + } else { + owl_function_makemsg("Type your message below. End with a dot on a line by itself. ^C will quit."); + } + + owl_editwin_clear(e); + owl_editwin_set_dotsend(e); + owl_editwin_set_locktext(e, "----> loopwrite\n"); + + /* make it active */ + owl_global_set_typwin_active(&g); + + owl_global_set_buffercommand(&g, "loopwrite"); +} + +/* send, log and display an outgoing zephyr. If 'msg' is NULL + * the message is expected to be set from the zwrite line itself + */ +void owl_function_zwrite(char *line, char *msg) +{ + owl_zwrite z; + char *mymsg; + owl_message *m; + + /* create the zwrite and send the message */ + owl_zwrite_create_from_line(&z, line); + if (msg) { + owl_zwrite_set_message(&z, msg); + } + owl_zwrite_send_message(&z); + owl_function_makemsg("Waiting for ack..."); + + /* If it's personal */ + if (owl_zwrite_is_personal(&z)) { + /* create the outgoing message */ + mymsg=owl_zwrite_get_message(&z); + m=owl_function_make_outgoing_zephyr(mymsg, line, owl_zwrite_get_zsig(&z)); + + /* log it */ + owl_log_message(m); + + /* add it or nuke it */ + if (owl_global_is_displayoutgoing(&g)) { + owl_function_add_message(m); + } else { + owl_message_free(m); + } + } + + /* free the zwrite */ + owl_zwrite_free(&z); +} + +/* send, log and display an outgoing zcrypt zephyr. If 'msg' is NULL + * the message is expected to be set from the zwrite line itself + */ +void owl_function_zcrypt(char *line, char *msg) +{ + owl_zwrite z; + char *mymsg; + char *cryptmsg; + owl_message *m; +#ifdef OWL_ENABLE_ZCRYPT + int ret; +#endif + + /* create the zwrite and send the message */ + owl_zwrite_create_from_line(&z, line); + if (msg) { + owl_zwrite_set_message(&z, msg); + } + + mymsg=owl_zwrite_get_message(&z); +#ifdef OWL_ENABLE_ZCRYPT + /* Allocate enough space for the crypted message. For each byte of + * the message, the encoded cyphertext will have two bytes. Block + * size is 8 bytes of input, or 16 bytes of output, so make sure we + * have at least one block worth of space allocated. If the message + * is empty, no blocks are sent, but we still allocate one + * block. The additional 16 bytes also provide space for the null + * terminator, as we will never use all of it for cyphertext. + */ + cryptmsg=owl_malloc((strlen(mymsg)*2)+16); + ret=owl_zcrypt_encrypt(cryptmsg, mymsg, owl_zwrite_get_class(&z), owl_zwrite_get_instance(&z)); + if (ret) { + owl_function_error("Error in zcrypt, possibly no key found. Message not sent."); + owl_function_beep(); + owl_free(cryptmsg); + owl_zwrite_free(&z); + return; + } +#else + cryptmsg=owl_strdup(mymsg); +#endif + + owl_zwrite_set_message(&z, cryptmsg); + owl_zwrite_set_opcode(&z, "crypt"); + mymsg=cryptmsg; + + owl_zwrite_send_message(&z); + owl_function_makemsg("Waiting for ack..."); + + /* If it's personal */ + if (owl_zwrite_is_personal(&z)) { + /* create the outgoing message */ + mymsg=owl_zwrite_get_message(&z); + m=owl_function_make_outgoing_zephyr(mymsg, line, owl_zwrite_get_zsig(&z)); + + /* log it */ + owl_log_message(m); + + /* add it or nuke it */ + if (owl_global_is_displayoutgoing(&g)) { + owl_function_add_message(m); + } else { + owl_message_free(m); + } + } + + /* free the zwrite */ + owl_free(cryptmsg); + owl_zwrite_free(&z); +} + +void owl_function_aimwrite(char *to) +{ + int ret; + char *msg, *format_msg; + owl_message *m; + + /* make a formatted copy of the message */ + msg=owl_editwin_get_text(owl_global_get_typwin(&g)); + format_msg=owl_strdup(msg); + owl_text_wordunwrap(format_msg); + + /* send the message */ + ret=owl_aim_send_im(to, format_msg); + if (!ret) { + owl_function_makemsg("AIM message sent."); + } else { + owl_function_error("Could not send AIM message."); + } + + /* create the outgoing message */ + m=owl_function_make_outgoing_aim(msg, to); + + /* log it */ + owl_log_message(m); + + /* display it or nuke it */ + if (owl_global_is_displayoutgoing(&g)) { + owl_function_add_message(m); + } else { + owl_message_free(m); + } + + owl_free(format_msg); +} + +void owl_function_send_aimawymsg(char *to, char *msg) +{ + int ret; + char *format_msg; + owl_message *m; + + /* make a formatted copy of the message */ + format_msg=owl_strdup(msg); + owl_text_wordunwrap(format_msg); + + /* send the message */ + ret=owl_aim_send_awaymsg(to, format_msg); + if (!ret) { + /* owl_function_makemsg("AIM message sent."); */ + } else { + owl_function_error("Could not send AIM message."); + } + + /* create the message */ + m=owl_function_make_outgoing_aim(msg, to); + if (m) { + /* log it */ + owl_log_message(m); + + /* display it or nuke it */ + if (owl_global_is_displayoutgoing(&g)) { + owl_function_add_message(m); + } else { + owl_message_free(m); + } + } else { + owl_function_error("Could not create AIM message"); + } + owl_free(format_msg); +} + +void owl_function_loopwrite() +{ + owl_message *min, *mout; + + /* create a message and put it on the message queue. This simulates + * an incoming message */ + min=owl_malloc(sizeof(owl_message)); + owl_message_create_loopback(min, owl_editwin_get_text(owl_global_get_typwin(&g))); + owl_message_set_direction_in(min); + owl_global_messagequeue_addmsg(&g, min); + + mout=owl_function_make_outgoing_loopback(owl_editwin_get_text(owl_global_get_typwin(&g))); + owl_log_message(mout); + if (owl_global_is_displayoutgoing(&g)) { + owl_function_add_message(mout); + } else { + owl_message_free(mout); + } + + /* fake a makemsg */ + owl_function_makemsg("loopback message sent"); +} + +/* If filter is non-null, looks for the next message matching + * that filter. If skip_deleted, skips any deleted messages. + * If last_if_none, will stop at the last message in the view + * if no matching messages are found. */ +void owl_function_nextmsg_full(char *filter, int skip_deleted, int last_if_none) +{ + int curmsg, i, viewsize, found; + owl_view *v; + owl_filter *f = NULL; + owl_message *m; + + v=owl_global_get_current_view(&g); + + if (filter) { + f=owl_global_get_filter(&g, filter); + if (!f) { + owl_function_error("No %s filter defined", filter); + return; + } + } + + curmsg=owl_global_get_curmsg(&g); + viewsize=owl_view_get_size(v); + found=0; + + /* just check to make sure we're in bounds... */ + if (curmsg>viewsize-1) curmsg=viewsize-1; + if (curmsg<0) curmsg=0; + + for (i=curmsg+1; i<viewsize; i++) { + m=owl_view_get_element(v, i); + if (skip_deleted && owl_message_is_delete(m)) continue; + if (f && !owl_filter_message_match(f, m)) continue; + found = 1; + break; + } + + if (i>owl_view_get_size(v)-1) i=owl_view_get_size(v)-1; + + if (!found) { + owl_function_makemsg("already at last%s message%s%s", + skip_deleted?" non-deleted":"", + filter?" in ":"", filter?filter:""); + /* if (!skip_deleted) owl_function_beep(); */ + } + + if (last_if_none || found) { + owl_global_set_curmsg(&g, i); + owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_global_set_direction_downwards(&g); + } +} + +void owl_function_prevmsg_full(char *filter, int skip_deleted, int first_if_none) +{ + int curmsg, i, viewsize, found; + owl_view *v; + owl_filter *f = NULL; + owl_message *m; + + v=owl_global_get_current_view(&g); + + if (filter) { + f=owl_global_get_filter(&g, filter); + if (!f) { + owl_function_error("No %s filter defined", filter); + return; + } + } + + curmsg=owl_global_get_curmsg(&g); + viewsize=owl_view_get_size(v); + found=0; + + /* just check to make sure we're in bounds... */ + if (curmsg<0) curmsg=0; + + for (i=curmsg-1; i>=0; i--) { + m=owl_view_get_element(v, i); + if (skip_deleted && owl_message_is_delete(m)) continue; + if (f && !owl_filter_message_match(f, m)) continue; + found = 1; + break; + } + + if (i<0) i=0; + + if (!found) { + owl_function_makemsg("already at first%s message%s%s", + skip_deleted?" non-deleted":"", + filter?" in ":"", filter?filter:""); + /* if (!skip_deleted) owl_function_beep(); */ + } + + if (first_if_none || found) { + owl_global_set_curmsg(&g, i); + owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_global_set_direction_upwards(&g); + } +} + +void owl_function_nextmsg() +{ + owl_function_nextmsg_full(NULL, 0, 1); +} + +void owl_function_prevmsg() +{ + owl_function_prevmsg_full(NULL, 0, 1); +} + +void owl_function_nextmsg_notdeleted() +{ + owl_function_nextmsg_full(NULL, 1, 1); +} + +void owl_function_prevmsg_notdeleted() +{ + owl_function_prevmsg_full(NULL, 1, 1); +} + +void owl_function_nextmsg_personal() +{ + owl_function_nextmsg_full("personal", 0, 0); +} + +void owl_function_prevmsg_personal() +{ + owl_function_prevmsg_full("personal", 0, 0); +} + + +/* if move_after is 1, moves after the delete */ +void owl_function_deletecur(int move_after) +{ + int curmsg; + owl_view *v; + + v=owl_global_get_current_view(&g); + + /* bail if there's no current message */ + if (owl_view_get_size(v) < 1) { + owl_function_error("No current message to delete"); + return; + } + + /* mark the message for deletion */ + curmsg=owl_global_get_curmsg(&g); + owl_view_delete_element(v, curmsg); + + if (move_after) { + /* move the poiner in the appropriate direction + * to the next undeleted msg */ + if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) { + owl_function_prevmsg_notdeleted(); + } else { + owl_function_nextmsg_notdeleted(); + } + } +} + +void owl_function_undeletecur(int move_after) +{ + int curmsg; + owl_view *v; + + v=owl_global_get_current_view(&g); + + if (owl_view_get_size(v) < 1) { + owl_function_error("No current message to undelete"); + return; + } + curmsg=owl_global_get_curmsg(&g); + + owl_view_undelete_element(v, curmsg); + + if (move_after) { + if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) { + if (curmsg>0) { + owl_function_prevmsg(); + } else { + owl_function_nextmsg(); + } + } else { + owl_function_nextmsg(); + } + } + + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); +} + +void owl_function_expunge() +{ + int curmsg; + owl_message *m; + owl_messagelist *ml; + owl_view *v; + int lastmsgid=0; + + curmsg=owl_global_get_curmsg(&g); + v=owl_global_get_current_view(&g); + ml=owl_global_get_msglist(&g); + + m=owl_view_get_element(v, curmsg); + if (m) lastmsgid = owl_message_get_id(m); + + /* expunge the message list */ + owl_messagelist_expunge(ml); + + /* update all views (we only have one right now) */ + owl_view_recalculate(v); + + /* find where the new position should be + (as close as possible to where we last where) */ + curmsg = owl_view_get_nearest_to_msgid(v, lastmsgid); + if (curmsg>owl_view_get_size(v)-1) curmsg = owl_view_get_size(v)-1; + if (curmsg<0) curmsg = 0; + owl_global_set_curmsg(&g, curmsg); + owl_function_calculate_topmsg(OWL_DIRECTION_NONE); + /* if there are no messages set the direction to down in case we + delete everything upwards */ + owl_global_set_direction_downwards(&g); + + owl_function_makemsg("Messages expunged"); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); +} + +void owl_function_firstmsg() +{ + owl_global_set_curmsg(&g, 0); + owl_global_set_topmsg(&g, 0); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_global_set_direction_downwards(&g); +} + +void owl_function_lastmsg_noredisplay() +{ + int oldcurmsg, curmsg; + owl_view *v; + + v=owl_global_get_current_view(&g); + oldcurmsg=owl_global_get_curmsg(&g); + curmsg=owl_view_get_size(v)-1; + if (curmsg<0) curmsg=0; + owl_global_set_curmsg(&g, curmsg); + if (oldcurmsg < curmsg) { + owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS); + } else if (curmsg<owl_view_get_size(v)) { + /* If already at the end, blank the screen and move curmsg + * past the end of the messages. */ + owl_global_set_topmsg(&g, curmsg+1); + owl_global_set_curmsg(&g, curmsg+1); + } + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_global_set_direction_downwards(&g); +} + +void owl_function_lastmsg() +{ + owl_function_lastmsg_noredisplay(); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); +} + +void owl_function_shift_right() +{ + owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_global_set_needrefresh(&g); +} + +void owl_function_shift_left() +{ + int shift; + + shift=owl_global_get_rightshift(&g); + if (shift>=10) { + owl_global_set_rightshift(&g, shift-10); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_global_set_needrefresh(&g); + } else { + owl_function_beep(); + owl_function_makemsg("Already full left"); + } +} + +void owl_function_unsuball() +{ + unsuball(); + owl_function_makemsg("Unsubscribed from all messages."); +} + + +/* Load zephyr subscriptions from the named 'file' and load zephyr's + * default subscriptions as well. An error message is printed if + * 'file' can't be opened or if zephyr reports an error in + * subscribing. + * + * If 'file' is NULL, this look for the default filename + * $HOME/.zephyr.subs. If the file can not be opened in this case + * only, no error message is printed. + */ +void owl_function_loadsubs(char *file) +{ + int ret, ret2; + char *foo, *path; + + if (file==NULL) { + ret=owl_zephyr_loadsubs(NULL, 0); + } else { + path = owl_util_makepath(file); + ret=owl_zephyr_loadsubs(path, 1); + free(path); + } + + /* for backwards compatibility for now */ + ret2=owl_zephyr_loaddefaultsubs(); + + if (!owl_context_is_interactive(owl_global_get_context(&g))) return; + + foo=file?file:"file"; + if (ret==0 && ret2==0) { + if (!file) { + owl_function_makemsg("Subscribed to messages."); + } else { + owl_function_makemsg("Subscribed to messages from %s", file); + } + } else if (ret==-1) { + owl_function_error("Could not read %s", foo); + } else { + owl_function_error("Error subscribing to messages"); + } +} + +void owl_function_loadloginsubs(char *file) +{ + int ret; + + ret=owl_zephyr_loadloginsubs(file); + + if (!owl_context_is_interactive(owl_global_get_context(&g))) return; + if (ret==0) { + owl_function_makemsg("Subscribed to login messages from file."); + } else if (ret==-1) { + owl_function_error("Could not open file for login subscriptions."); + } else { + owl_function_error("Error subscribing to login messages from file."); + } +} + +void owl_function_suspend() +{ + endwin(); + printf("\n"); + kill(getpid(), SIGSTOP); + + /* resize to reinitialize all the windows when we come back */ + owl_command_resize(); +} + +void owl_function_zaway_toggle() +{ + if (!owl_global_is_zaway(&g)) { + owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g)); + owl_function_zaway_on(); + } else { + owl_function_zaway_off(); + } +} + +void owl_function_zaway_on() +{ + owl_global_set_zaway_on(&g); + owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g)); +} + +void owl_function_zaway_off() +{ + owl_global_set_zaway_off(&g); + owl_function_makemsg("zaway off"); +} + +void owl_function_aaway_toggle() +{ + if (!owl_global_is_aaway(&g)) { + owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g)); + owl_function_aaway_on(); + } else { + owl_function_aaway_off(); + } +} + +void owl_function_aaway_on() +{ + owl_global_set_aaway_on(&g); + /* owl_aim_set_awaymsg(owl_global_get_zaway_msg(&g)); */ + owl_function_makemsg("AIM away set (%s)", owl_global_get_aaway_msg(&g)); +} + +void owl_function_aaway_off() +{ + owl_global_set_aaway_off(&g); + /* owl_aim_set_awaymsg(""); */ + owl_function_makemsg("AIM away off"); +} + +void owl_function_quit() +{ + char *ret; + + /* zlog out if we need to */ + if (owl_global_is_shutdownlogout(&g)) { + owl_zephyr_zlog_out(); + } + + /* execute the commands in shutdown */ + ret = owl_perlconfig_execute("owl::shutdown();"); + if (ret) owl_free(ret); + + /* signal our child process, if any */ + if (owl_global_get_newmsgproc_pid(&g)) { + kill(owl_global_get_newmsgproc_pid(&g), SIGHUP); + } + + /* Quit zephyr */ + owl_zephyr_shutdown(); + + /* Quit AIM */ + if (owl_global_is_aimloggedin(&g)) { + owl_aim_logout(); + } + + /* done with curses */ + endwin(); + + /* restore terminal settings */ + tcsetattr(0, TCSAFLUSH, owl_global_get_startup_tio(&g)); + + owl_function_debugmsg("Quitting Owl"); + exit(0); +} + +void owl_function_calculate_topmsg(int direction) +{ + int recwinlines, topmsg, curmsg; + owl_view *v; + + v=owl_global_get_current_view(&g); + curmsg=owl_global_get_curmsg(&g); + topmsg=owl_global_get_topmsg(&g); + recwinlines=owl_global_get_recwin_lines(&g); + + /* + if (owl_view_get_size(v) < 1) { + return; + } + */ + + switch (owl_global_get_scrollmode(&g)) { + case OWL_SCROLLMODE_TOP: + topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines); + break; + case OWL_SCROLLMODE_NEARTOP: + topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines); + break; + case OWL_SCROLLMODE_CENTER: + topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines); + break; + case OWL_SCROLLMODE_PAGED: + topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0); + break; + case OWL_SCROLLMODE_PAGEDCENTER: + topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1); + break; + case OWL_SCROLLMODE_NORMAL: + default: + topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines); + } + owl_function_debugmsg("Calculated a topmsg of %i", topmsg); + owl_global_set_topmsg(&g, topmsg); +} + +/* Returns what the new topmsg should be. + * Passed the last direction of movement, + * the current view, + * the current message number in the view, + * the top message currently being displayed, + * and the number of lines in the recwin. + */ +int owl_function_calculate_topmsg_top(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines) +{ + return(curmsg); +} + +int owl_function_calculate_topmsg_neartop(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines) +{ + if (curmsg>0 + && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1)) + < recwinlines/2)) { + return(curmsg-1); + } else { + return(curmsg); + } +} + +int owl_function_calculate_topmsg_center(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines) +{ + int i, last, lines; + + last = curmsg; + lines = 0; + for (i=curmsg-1; i>=0; i--) { + lines += owl_message_get_numlines(owl_view_get_element(v, i)); + if (lines > recwinlines/2) break; + last = i; + } + return(last); +} + +int owl_function_calculate_topmsg_paged(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page) +{ + int i, last, lines, savey; + + /* If we're off the top of the screen, scroll up such that the + * curmsg is near the botton of the screen. */ + if (curmsg < topmsg) { + last = curmsg; + lines = 0; + for (i=curmsg; i>=0; i--) { + lines += owl_message_get_numlines(owl_view_get_element(v, i)); + if (lines > recwinlines) break; + last = i; + } + if (center_on_page) { + return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines)); + } else { + return(last); + } + } + + /* Find number of lines from top to bottom of curmsg (store in savey) */ + savey=0; + for (i=topmsg; i<=curmsg; i++) { + savey+=owl_message_get_numlines(owl_view_get_element(v, i)); + } + + /* if we're off the bottom of the screen, scroll down */ + if (savey > recwinlines) { + if (center_on_page) { + return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines)); + } else { + return(curmsg); + } + } + + /* else just stay as we are... */ + return(topmsg); +} + +int owl_function_calculate_topmsg_normal(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines) +{ + int savey, i, foo, y; + + if (curmsg<0) return(topmsg); + + /* If we're off the top of the screen then center */ + if (curmsg<topmsg) { + topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines); + } + + /* If curmsg is so far past topmsg that there are more messages than + lines, skip the line counting that follows because we're + certainly off screen. */ + savey=curmsg-topmsg; + if (savey <= recwinlines) { + /* Find number of lines from top to bottom of curmsg (store in savey) */ + savey = 0; + for (i=topmsg; i<=curmsg; i++) { + savey+=owl_message_get_numlines(owl_view_get_element(v, i)); + } + } + + /* If we're off the bottom of the screen, set the topmsg to curmsg + * and scroll upwards */ + if (savey > recwinlines) { + topmsg=curmsg; + savey=owl_message_get_numlines(owl_view_get_element(v, curmsg)); + direction=OWL_DIRECTION_UPWARDS; + } + + /* If our bottom line is less than 1/4 down the screen then scroll up */ + if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) { + if (savey < (recwinlines / 4)) { + y=0; + for (i=curmsg; i>=0; i--) { + foo=owl_message_get_numlines(owl_view_get_element(v, i)); + /* will we run the curmsg off the screen? */ + if ((foo+y) >= recwinlines) { + i++; + if (i>curmsg) i=curmsg; + break; + } + /* have saved 1/2 the screen space? */ + y+=foo; + if (y > (recwinlines / 2)) break; + } + if (i<0) i=0; + return(i); + } + } + + if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) { + /* If curmsg bottom line is more than 3/4 down the screen then scroll down */ + if (savey > ((recwinlines * 3)/4)) { + y=0; + /* count lines from the top until we can save 1/2 the screen size */ + for (i=topmsg; i<curmsg; i++) { + y+=owl_message_get_numlines(owl_view_get_element(v, i)); + if (y > (recwinlines / 2)) break; + } + if (i==curmsg) { + i--; + } + return(i+1); + } + } + + return(topmsg); +} + +void owl_function_resize() +{ + owl_global_set_resize_pending(&g); +} + +void owl_function_run_buffercommand() +{ + char *buff, *ptr; + + buff=owl_global_get_buffercommand(&g); + if (!strncmp(buff, "zwrite ", 7)) { + owl_function_zwrite(buff, owl_editwin_get_text(owl_global_get_typwin(&g))); + } else if (!strncmp(buff, "zcrypt ", 7)) { + owl_function_zcrypt(buff, owl_editwin_get_text(owl_global_get_typwin(&g))); + } else if (!strncmp(buff, "aimwrite ", 9)) { + owl_function_aimwrite(buff+9); + } else if (!strncmp(buff, "loopwrite", 9) || !strncmp(buff, "loopwrite ", 10)) { + owl_function_loopwrite(); + } else if (!strncmp(buff, "aimlogin ", 9)) { + ptr=owl_sprintf("%s %s", buff, owl_global_get_response(&g)); + owl_function_command(ptr); + owl_free(ptr); + } else { + owl_function_error("Internal error: invalid buffercommand %s", buff); + } +} + +void owl_function_debugmsg(char *fmt, ...) +{ + FILE *file; + time_t now; + char buff1[LINE]; + char *buff2; + va_list ap; + va_start(ap, fmt); + + if (!owl_global_is_debug_fast(&g)) return; + + file=fopen(owl_global_get_debug_file(&g), "a"); + if (!file) return; + + now=time(NULL); + strcpy(buff1, ctime(&now)); + buff1[strlen(buff1)-1]='\0'; + + buff2=owl_global_get_runtime_string(&g); + + fprintf(file, "[%i - %s - %s]: ", (int) getpid(), buff1, buff2); + vfprintf(file, fmt, ap); + fprintf(file, "\n"); + fclose(file); + owl_free(buff2); + + va_end(ap); +} + +void owl_function_refresh() +{ + owl_function_resize(); +} + +void owl_function_beep() +{ + if (owl_global_is_bell(&g)) { + beep(); + owl_global_set_needrefresh(&g); /* do we really need this? */ + } +} + +void owl_function_subscribe(char *class, char *inst, char *recip) +{ + int ret; + + ret=owl_zephyr_sub(class, inst, recip); + if (ret) { + owl_function_error("Error subscribing."); + } else { + owl_function_makemsg("Subscribed."); + } +} + +void owl_function_unsubscribe(char *class, char *inst, char *recip) +{ + int ret; + + ret=owl_zephyr_unsub(class, inst, recip); + if (ret) { + owl_function_error("Error subscribing."); + } else { + owl_function_makemsg("Unsubscribed."); + } +} + +void owl_function_set_cursor(WINDOW *win) +{ + wnoutrefresh(win); +} + +void owl_function_full_redisplay() +{ + redrawwin(owl_global_get_curs_recwin(&g)); + redrawwin(owl_global_get_curs_sepwin(&g)); + redrawwin(owl_global_get_curs_typwin(&g)); + redrawwin(owl_global_get_curs_msgwin(&g)); + + wnoutrefresh(owl_global_get_curs_recwin(&g)); + wnoutrefresh(owl_global_get_curs_sepwin(&g)); + wnoutrefresh(owl_global_get_curs_typwin(&g)); + wnoutrefresh(owl_global_get_curs_msgwin(&g)); + + sepbar(""); + owl_function_makemsg(""); + + owl_global_set_needrefresh(&g); +} + +void owl_function_popless_text(char *text) +{ + owl_popwin *pw; + owl_viewwin *v; + + pw=owl_global_get_popwin(&g); + v=owl_global_get_viewwin(&g); + + owl_popwin_up(pw); + owl_viewwin_init_text(v, owl_popwin_get_curswin(pw), + owl_popwin_get_lines(pw), owl_popwin_get_cols(pw), + text); + owl_popwin_refresh(pw); + owl_viewwin_redisplay(v, 0); + owl_global_set_needrefresh(&g); +} + +void owl_function_popless_fmtext(owl_fmtext *fm) +{ + owl_popwin *pw; + owl_viewwin *v; + + pw=owl_global_get_popwin(&g); + v=owl_global_get_viewwin(&g); + + owl_popwin_up(pw); + owl_viewwin_init_fmtext(v, owl_popwin_get_curswin(pw), + owl_popwin_get_lines(pw), owl_popwin_get_cols(pw), + fm); + owl_popwin_refresh(pw); + owl_viewwin_redisplay(v, 0); + owl_global_set_needrefresh(&g); +} + +void owl_function_popless_file(char *filename) +{ + owl_fmtext fm; + FILE *file; + char buff[1024]; + + file=fopen(filename, "r"); + if (!file) { + owl_function_error("Could not open file: %s", filename); + return; + } + + owl_fmtext_init_null(&fm); + while (fgets(buff, 1024, file)) { + owl_fmtext_append_normal(&fm, buff); + /* owl_fmtext_append_normal(&fm, "\n"); */ + } + + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); + fclose(file); +} + +void owl_function_about() +{ + owl_function_show_license(); +} + +void owl_function_info() +{ + owl_message *m; + owl_fmtext fm, attrfm; + char *buff; + owl_view *v; +#ifdef HAVE_LIBZEPHYR + ZNotice_t *n; +#endif + + owl_fmtext_init_null(&fm); + + v=owl_global_get_current_view(&g); + m=owl_view_get_element(v, owl_global_get_curmsg(&g)); + if (!m || owl_view_get_size(v)==0) { + owl_function_error("No message selected\n"); + return; + } + + owl_fmtext_append_bold(&fm, "General Information:\n"); + owl_fmtext_append_normal(&fm, " Msg Id : "); + buff=owl_sprintf("%i", owl_message_get_id(m)); + owl_fmtext_append_normal(&fm, buff); + owl_free(buff); + owl_fmtext_append_normal(&fm, "\n"); + + owl_fmtext_append_normal(&fm, " Type : "); + owl_fmtext_append_bold(&fm, owl_message_get_type(m)); + owl_fmtext_append_normal(&fm, "\n"); + + if (owl_message_is_direction_in(m)) { + owl_fmtext_append_normal(&fm, " Direction : in\n"); + } else if (owl_message_is_direction_out(m)) { + owl_fmtext_append_normal(&fm, " Direction : out\n"); + } else if (owl_message_is_direction_none(m)) { + owl_fmtext_append_normal(&fm, " Direction : none\n"); + } else { + owl_fmtext_append_normal(&fm, " Direction : unknown\n"); + } + + owl_fmtext_append_normal(&fm, " Time : "); + owl_fmtext_append_normal(&fm, owl_message_get_timestr(m)); + owl_fmtext_append_normal(&fm, "\n"); + + if (!owl_message_is_type_admin(m)) { + owl_fmtext_append_normal(&fm, " Sender : "); + owl_fmtext_append_normal(&fm, owl_message_get_sender(m)); + owl_fmtext_append_normal(&fm, "\n"); + + owl_fmtext_append_normal(&fm, " Recipient : "); + owl_fmtext_append_normal(&fm, owl_message_get_recipient(m)); + owl_fmtext_append_normal(&fm, "\n"); + } + + if (owl_message_is_type_zephyr(m)) { + owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n"); + + owl_fmtext_append_normal(&fm, " Class : "); + owl_fmtext_append_normal(&fm, owl_message_get_class(m)); + owl_fmtext_append_normal(&fm, "\n"); + owl_fmtext_append_normal(&fm, " Instance : "); + owl_fmtext_append_normal(&fm, owl_message_get_instance(m)); + owl_fmtext_append_normal(&fm, "\n"); + owl_fmtext_append_normal(&fm, " Opcode : "); + owl_fmtext_append_normal(&fm, owl_message_get_opcode(m)); + owl_fmtext_append_normal(&fm, "\n"); + + owl_fmtext_append_normal(&fm, " Time : "); + owl_fmtext_append_normal(&fm, owl_message_get_timestr(m)); + owl_fmtext_append_normal(&fm, "\n"); +#ifdef HAVE_LIBZEPHYR + if (owl_message_is_direction_in(m)) { + char *ptr, tmpbuff[1024]; + int i, j, fields, len; + + n=owl_message_get_notice(m); + + if (!owl_message_is_pseudo(m)) { + owl_fmtext_append_normal(&fm, " Kind : "); + if (n->z_kind==UNSAFE) { + owl_fmtext_append_normal(&fm, "UNSAFE\n"); + } else if (n->z_kind==UNACKED) { + owl_fmtext_append_normal(&fm, "UNACKED\n"); + } else if (n->z_kind==ACKED) { + owl_fmtext_append_normal(&fm, "ACKED\n"); + } else if (n->z_kind==HMACK) { + owl_fmtext_append_normal(&fm, "HMACK\n"); + } else if (n->z_kind==HMCTL) { + owl_fmtext_append_normal(&fm, "HMCTL\n"); + } else if (n->z_kind==SERVACK) { + owl_fmtext_append_normal(&fm, "SERVACK\n"); + } else if (n->z_kind==SERVNAK) { + owl_fmtext_append_normal(&fm, "SERVNACK\n"); + } else if (n->z_kind==CLIENTACK) { + owl_fmtext_append_normal(&fm, "CLIENTACK\n"); + } else if (n->z_kind==STAT) { + owl_fmtext_append_normal(&fm, "STAT\n"); + } else { + owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n"); + } + } + owl_fmtext_append_normal(&fm, " Host : "); + owl_fmtext_append_normal(&fm, owl_message_get_hostname(m)); + + if (!owl_message_is_pseudo(m)) { + owl_fmtext_append_normal(&fm, "\n"); + buff=owl_sprintf(" Port : %i\n", ntohs(n->z_port)); + owl_fmtext_append_normal(&fm, buff); + owl_free(buff); + + owl_fmtext_append_normal(&fm, " Auth : "); + owl_fmtext_append_normal(&fm, owl_zephyr_get_authstr(n)); + owl_fmtext_append_normal(&fm, "\n"); + + buff=owl_sprintf(" Checkd Ath: %i\n Multi notc: %s\n Num other : %i\n Msg Len : %i\n", + n->z_checked_auth, + n->z_multinotice, + n->z_num_other_fields, + n->z_message_len); + owl_fmtext_append_normal(&fm, buff); + owl_free(buff); + + buff=owl_sprintf(" Fields : %i\n", owl_zephyr_get_num_fields(n)); + owl_fmtext_append_normal(&fm, buff); + owl_free(buff); + + fields=owl_zephyr_get_num_fields(n); + for (i=0; i<fields; i++) { + buff=owl_sprintf(" Field %i : ", i+1); + owl_fmtext_append_normal(&fm, buff); + owl_free(buff); + + ptr=owl_zephyr_get_field(n, i+1); + len=strlen(ptr); + if (len<30) { + strncpy(tmpbuff, ptr, len); + tmpbuff[len]='\0'; + } else { + strncpy(tmpbuff, ptr, 30); + tmpbuff[30]='\0'; + strcat(tmpbuff, "..."); + } + owl_free(ptr); + + for (j=0; j<strlen(tmpbuff); j++) { + if (tmpbuff[j]=='\n') tmpbuff[j]='~'; + if (tmpbuff[j]=='\r') tmpbuff[j]='!'; + } + + owl_fmtext_append_normal(&fm, tmpbuff); + owl_fmtext_append_normal(&fm, "\n"); + } + owl_fmtext_append_normal(&fm, " Default Fm:"); + owl_fmtext_append_normal(&fm, n->z_default_format); + } + + } +#endif + } + + if (owl_message_is_type_aim(m)) { + owl_fmtext_append_bold(&fm, "\nAIM Specific Information:\n"); + } + + owl_fmtext_append_bold(&fm, "\nOwl Message Attributes:\n"); + owl_message_attributes_tofmtext(m, &attrfm); + owl_fmtext_append_fmtext(&fm, &attrfm); + + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); + owl_fmtext_free(&attrfm); +} + +/* print the current message in a popup window. + * Use the 'default' style regardless of whatever + * style the user may be using + */ +void owl_function_curmsg_to_popwin() +{ + owl_popwin *pw; + owl_view *v; + owl_message *m; + owl_style *s; + owl_fmtext fm; + + v=owl_global_get_current_view(&g); + s=owl_global_get_style_by_name(&g, "default"); + pw=owl_global_get_popwin(&g); + + m=owl_view_get_element(v, owl_global_get_curmsg(&g)); + + if (!m || owl_view_get_size(v)==0) { + owl_function_error("No current message"); + return; + } + + owl_fmtext_init_null(&fm); + owl_style_get_formattext(s, &fm, m); + + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +void owl_function_page_curmsg(int step) +{ + /* scroll down or up within the current message IF the message is truncated */ + + int offset, curmsg, lines; + owl_view *v; + owl_message *m; + + offset=owl_global_get_curmsg_vert_offset(&g); + v=owl_global_get_current_view(&g); + curmsg=owl_global_get_curmsg(&g); + m=owl_view_get_element(v, curmsg); + if (!m || owl_view_get_size(v)==0) return; + lines=owl_message_get_numlines(m); + + if (offset==0) { + /* Bail if the curmsg isn't the last one displayed */ + if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) { + owl_function_makemsg("The entire message is already displayed"); + return; + } + + /* Bail if we're not truncated */ + if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) { + owl_function_makemsg("The entire message is already displayed"); + return; + } + } + + + /* don't scroll past the last line */ + if (step>0) { + if (offset+step > lines-1) { + owl_global_set_curmsg_vert_offset(&g, lines-1); + } else { + owl_global_set_curmsg_vert_offset(&g, offset+step); + } + } + + /* would we be before the beginning of the message? */ + if (step<0) { + if (offset+step<0) { + owl_global_set_curmsg_vert_offset(&g, 0); + } else { + owl_global_set_curmsg_vert_offset(&g, offset+step); + } + } + + /* redisplay */ + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_global_set_needrefresh(&g); +} + +void owl_function_resize_typwin(int newsize) +{ + owl_global_set_typwin_lines(&g, newsize); + owl_function_resize(); +} + +void owl_function_typwin_grow() +{ + int i; + + i=owl_global_get_typwin_lines(&g); + owl_function_resize_typwin(i+1); +} + +void owl_function_typwin_shrink() +{ + int i; + + i=owl_global_get_typwin_lines(&g); + if (i>2) { + owl_function_resize_typwin(i-1); + } +} + +void owl_function_mainwin_pagedown() +{ + int i; + + i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g)); + if (i<0) return; + if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g)) + && (owl_global_get_curmsg(&g) < i) + && (i>0)) { + i--; + } + owl_global_set_curmsg(&g, i); + owl_function_nextmsg(); +} + +void owl_function_mainwin_pageup() +{ + owl_global_set_curmsg(&g, owl_global_get_topmsg(&g)); + owl_function_prevmsg(); +} + +void owl_function_getsubs() +{ + char *buff; + + buff=owl_zephyr_getsubs(); + + if (buff) { + owl_function_popless_text(buff); + } else { + owl_function_popless_text("Error getting subscriptions"); + } + + owl_free(buff); +} + +void owl_function_printallvars() +{ + char *name; + char var[LINE]; + owl_list varnames; + int i, numvarnames; + GString *str = g_string_new(""); + + g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE"); + g_string_append_printf(str, "%-20s %s\n", "--------", "-----"); + owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames); + numvarnames = owl_list_get_size(&varnames); + for (i=0; i<numvarnames; i++) { + name = owl_list_get_element(&varnames, i); + if (name && name[0]!='_') { + g_string_append_printf(str, "\n%-20s = ", name); + owl_variable_get_tostring(owl_global_get_vardict(&g), name, var, LINE); + g_string_append(str, var); + } + } + g_string_append(str, "\n"); + owl_variable_dict_namelist_free(&varnames); + + owl_function_popless_text(str->str); + g_string_free(str, TRUE); +} + +void owl_function_show_variables() +{ + owl_list varnames; + owl_fmtext fm; + int i, numvarnames; + char *varname; + + owl_fmtext_init_null(&fm); + owl_fmtext_append_bold(&fm, + "Variables: (use 'show variable <name>' for details)\n"); + owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames); + numvarnames = owl_list_get_size(&varnames); + for (i=0; i<numvarnames; i++) { + varname = owl_list_get_element(&varnames, i); + if (varname && varname[0]!='_') { + owl_variable_describe(owl_global_get_vardict(&g), varname, &fm); + } + } + owl_variable_dict_namelist_free(&varnames); + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +void owl_function_show_variable(char *name) +{ + owl_fmtext fm; + + owl_fmtext_init_null(&fm); + owl_variable_get_help(owl_global_get_vardict(&g), name, &fm); + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +/* note: this applies to global message list, not to view. + * If flag is 1, deletes. If flag is 0, undeletes. */ +void owl_function_delete_by_id(int id, int flag) +{ + owl_messagelist *ml; + owl_message *m; + ml = owl_global_get_msglist(&g); + m = owl_messagelist_get_by_id(ml, id); + if (m) { + if (flag == 1) { + owl_message_mark_delete(m); + } else if (flag == 0) { + owl_message_unmark_delete(m); + } + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_global_set_needrefresh(&g); + } else { + owl_function_error("No message with id %d: unable to mark for (un)delete",id); + } +} + +void owl_function_delete_automsgs() +{ + /* mark for deletion all messages in the current view that match the + * 'trash' filter */ + + int i, j, count; + owl_message *m; + owl_view *v; + owl_filter *f; + + /* get the trash filter */ + f=owl_global_get_filter(&g, "trash"); + if (!f) { + owl_function_error("No trash filter defined"); + return; + } + + v=owl_global_get_current_view(&g); + + count=0; + j=owl_view_get_size(v); + for (i=0; i<j; i++) { + m=owl_view_get_element(v, i); + if (owl_filter_message_match(f, m)) { + count++; + owl_message_mark_delete(m); + } + } + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_function_makemsg("%i messages marked for deletion", count); + owl_global_set_needrefresh(&g); +} + +void owl_function_status() +{ + char cwdbuff[MAXPATHLEN+1]; + char *buff; + time_t start; + int up, days, hours, minutes; + owl_fmtext fm; + + owl_fmtext_init_null(&fm); + + start=owl_global_get_starttime(&g); + + owl_fmtext_append_normal(&fm, "General Information:\n"); + + owl_fmtext_append_normal(&fm, " Version: "); + owl_fmtext_append_normal(&fm, OWL_VERSION_STRING); + owl_fmtext_append_normal(&fm, "\n"); + + owl_fmtext_append_normal(&fm, " Startup Arugments: "); + owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g)); + owl_fmtext_append_normal(&fm, "\n"); + + owl_fmtext_append_normal(&fm, " Current Directory: "); + if(getcwd(cwdbuff, MAXPATHLEN) == NULL) { + owl_fmtext_append_normal(&fm, "<Error in getcwd>"); + } else { + owl_fmtext_append_normal(&fm, cwdbuff); + } + owl_fmtext_append_normal(&fm, "\n"); + + buff=owl_sprintf(" Startup Time: %s", ctime(&start)); + owl_fmtext_append_normal(&fm, buff); + owl_free(buff); + + up=owl_global_get_runtime(&g); + days=up/86400; + up-=days*86400; + hours=up/3600; + up-=hours*3600; + minutes=up/60; + up-=minutes*60; + + buff=owl_sprintf(" Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up); + owl_fmtext_append_normal(&fm, buff); + owl_free(buff); + + owl_fmtext_append_normal(&fm, "\nProtocol Options:\n"); + owl_fmtext_append_normal(&fm, " Zephyr included : "); + if (owl_global_is_havezephyr(&g)) { + owl_fmtext_append_normal(&fm, "yes\n"); + } else { + owl_fmtext_append_normal(&fm, "no\n"); + } + owl_fmtext_append_normal(&fm, " AIM included : yes\n"); + owl_fmtext_append_normal(&fm, " Loopback included : yes\n"); + + + owl_fmtext_append_normal(&fm, "\nBuild Options:\n"); + owl_fmtext_append_normal(&fm, " Stderr redirection : "); +#if OWL_STDERR_REDIR + owl_fmtext_append_normal(&fm, "yes\n"); +#else + owl_fmtext_append_normal(&fm, "no\n"); +#endif + + + owl_fmtext_append_normal(&fm, "\nMemory Usage:\n"); + owl_fmtext_append_normal(&fm, " Not currently available.\n"); + + owl_fmtext_append_normal(&fm, "\nAIM Status:\n"); + owl_fmtext_append_normal(&fm, " Logged in: "); + if (owl_global_is_aimloggedin(&g)) { + owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g)); + owl_fmtext_append_normal(&fm, "\n"); + } else { + owl_fmtext_append_normal(&fm, "(not logged in)\n"); + } + + owl_fmtext_append_normal(&fm, " Processing events: "); + if (owl_global_is_doaimevents(&g)) { + owl_fmtext_append_normal(&fm, "yes\n"); + } else { + owl_fmtext_append_normal(&fm, "no\n"); + } + + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +void owl_function_show_term() +{ + owl_fmtext fm; + char *buff; + + owl_fmtext_init_null(&fm); + buff=owl_sprintf("Terminal Lines: %i\nTerminal Columns: %i\n", + owl_global_get_lines(&g), + owl_global_get_cols(&g)); + owl_fmtext_append_normal(&fm, buff); + owl_free(buff); + + if (owl_global_get_hascolors(&g)) { + owl_fmtext_append_normal(&fm, "Color: Yes\n"); + buff=owl_sprintf("Number of color pairs: %i\n", owl_global_get_colorpairs(&g)); + owl_fmtext_append_normal(&fm, buff); + owl_free(buff); + buff=owl_sprintf("Can change colors: %s\n", can_change_color() ? "yes" : "no"); + owl_fmtext_append_normal(&fm, buff); + owl_free(buff); + } else { + owl_fmtext_append_normal(&fm, "Color: No\n"); + } + + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +/* if type = 0 then normal reply. + * if type = 1 then it's a reply to sender + * if enter = 0 then allow the command to be edited + * if enter = 1 then don't wait for editing + */ +void owl_function_reply(int type, int enter) +{ + char *buff=NULL, *tmpbuff; + owl_message *m; + owl_filter *f; + + if (owl_view_get_size(owl_global_get_current_view(&g))==0) { + owl_function_error("No message selected"); + } else { + char *class, *inst, *to, *cc=NULL; + + m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g)); + if (!m) { + owl_function_error("No message selected"); + return; + } + + /* first check if we catch the reply-lockout filter */ + f=owl_global_get_filter(&g, "reply-lockout"); + if (f) { + if (owl_filter_message_match(f, m)) { + owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter"); + return; + } + } + + /* admin */ + if (owl_message_is_type_admin(m)) { + owl_function_error("You cannot reply to an admin message"); + return; + } + + /* loopback */ + if (owl_message_is_type_loopback(m)) { + owl_function_loopwrite_setup(); + return; + } + + /* zephyr */ + if (owl_message_is_type_zephyr(m)) { + /* if it's a zephyr we sent, send it out the same way again */ + if (owl_message_is_direction_out(m)) { + owl_function_zwrite_setup(owl_message_get_zwriteline(m)); + owl_global_set_buffercommand(&g, owl_message_get_zwriteline(m)); + return; + } + + /* Special case a personal reply to a webzephyr user on a class */ + if ((type==1) && !strcasecmp(owl_message_get_opcode(m), OWL_WEBZEPHYR_OPCODE)) { + class=OWL_WEBZEPHYR_CLASS; + inst=owl_message_get_sender(m); + to=OWL_WEBZEPHYR_PRINCIPAL; + } else if (!strcasecmp(owl_message_get_class(m), OWL_WEBZEPHYR_CLASS) && owl_message_is_loginout(m)) { + /* Special case LOGIN/LOGOUT notifications on class "webzephyr" */ + class=OWL_WEBZEPHYR_CLASS; + inst=owl_message_get_instance(m); + to=OWL_WEBZEPHYR_PRINCIPAL; + } else if (owl_message_is_loginout(m)) { + /* Normal LOGIN/LOGOUT messages */ + class="MESSAGE"; + inst="PERSONAL"; + to=owl_message_get_sender(m); + } else if (type==1) { + /* Personal reply */ + class="MESSAGE"; + inst="PERSONAL"; + to=owl_message_get_sender(m); + } else { + /* General reply */ + class=owl_message_get_class(m); + inst=owl_message_get_instance(m); + to=owl_message_get_recipient(m); + cc=owl_message_get_cc(m); + if (!strcmp(to, "") || !strcmp(to, "*")) { + to=""; + } else if (to[0]=='@') { + /* leave it, to get the realm */ + } else { + to=owl_message_get_sender(m); + } + } + + /* create the command line */ + if (!strcasecmp(owl_message_get_opcode(m), "CRYPT")) { + buff=owl_strdup("zcrypt"); + } else { + buff=owl_strdup("zwrite"); + } + if (strcasecmp(class, "message")) { + tmpbuff=owl_sprintf("%s -c %s%s%s", buff, owl_getquoting(class), class, owl_getquoting(class)); + owl_free(buff); + buff=tmpbuff; + } + if (strcasecmp(inst, "personal")) { + tmpbuff=owl_sprintf("%s -i %s%s%s", buff, owl_getquoting(inst), inst, owl_getquoting(inst)); + owl_free(buff); + buff=tmpbuff; + } + if (*to != '\0') { + char *tmp, *oldtmp, *tmp2; + tmp=short_zuser(to); + if (cc) { + tmp = owl_util_uniq(oldtmp=tmp, cc, "-"); + owl_free(oldtmp); + tmpbuff=owl_sprintf("%s -C %s", buff, tmp); + owl_free(buff); + buff=tmpbuff; + } else { + if (owl_global_is_smartstrip(&g)) { + tmp2=tmp; + tmp=owl_zephyr_smartstripped_user(tmp2); + owl_free(tmp2); + } + tmpbuff=owl_sprintf("%s %s", buff, tmp); + owl_free(buff); + buff=tmpbuff; + } + owl_free(tmp); + } + if (cc) owl_free(cc); + } + + /* aim */ + if (owl_message_is_type_aim(m)) { + if (owl_message_is_direction_out(m)) { + buff=owl_sprintf("aimwrite %s", owl_message_get_recipient(m)); + } else { + buff=owl_sprintf("aimwrite %s", owl_message_get_sender(m)); + } + } + + if (enter) { + owl_history *hist = owl_global_get_cmd_history(&g); + owl_history_store(hist, buff); + owl_history_reset(hist); + owl_function_command_norv(buff); + } else { + owl_function_start_command(buff); + } + owl_free(buff); + } +} + +void owl_function_zlocate(int argc, char **argv, int auth) +{ + owl_fmtext fm; + char *ptr, *buff; + int i; + + owl_fmtext_init_null(&fm); + + for (i=0; i<argc; i++) { + ptr=long_zuser(argv[i]); + buff=owl_zephyr_zlocate(ptr, auth); + owl_fmtext_append_normal(&fm, buff); + owl_free(ptr); + owl_free(buff); + } + + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +void owl_function_start_command(char *line) +{ + int i, j; + owl_editwin *tw; + + tw=owl_global_get_typwin(&g); + owl_global_set_typwin_active(&g); + owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, + owl_global_get_cmd_history(&g)); + + owl_editwin_set_locktext(tw, "command: "); + owl_global_set_needrefresh(&g); + + j=strlen(line); + for (i=0; i<j; i++) { + owl_editwin_process_char(tw, line[i]); + } + owl_editwin_redisplay(tw, 0); + + owl_context_set_editline(owl_global_get_context(&g), tw); + owl_function_activate_keymap("editline"); +} + +void owl_function_start_question(char *line) +{ + owl_editwin *tw; + + tw=owl_global_get_typwin(&g); + owl_global_set_typwin_active(&g); + owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g)); + + owl_editwin_set_locktext(tw, line); + owl_global_set_needrefresh(&g); + + owl_editwin_redisplay(tw, 0); + + owl_context_set_editresponse(owl_global_get_context(&g), tw); + owl_function_activate_keymap("editresponse"); +} + +void owl_function_start_password(char *line) +{ + owl_editwin *tw; + + tw=owl_global_get_typwin(&g); + owl_global_set_typwin_active(&g); + owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g)); + owl_editwin_set_echochar(tw, '*'); + + owl_editwin_set_locktext(tw, line); + owl_global_set_needrefresh(&g); + + owl_editwin_redisplay(tw, 0); + + owl_context_set_editresponse(owl_global_get_context(&g), tw); + owl_function_activate_keymap("editresponse"); +} + +char *owl_function_exec(int argc, char **argv, char *buff, int type) +{ + /* if type == 1 display in a popup + * if type == 2 display an admin messages + * if type == 0 return output + * else display in a popup + */ + char *newbuff, *redirect = " 2>&1 < /dev/null"; + char *out, buff2[1024]; + int size; + FILE *p; + +#if OWL_STDERR_REDIR + redirect = " < /dev/null"; +#endif + + if (argc<2) { + owl_function_error("Wrong number of arguments to the exec command"); + return NULL; + } + + buff = skiptokens(buff, 1); + newbuff = owl_malloc(strlen(buff)+strlen(redirect)+1); + strcpy(newbuff, buff); + strcat(newbuff, redirect); + + if (type == 1) { + owl_popexec_new(newbuff); + } else { + p=popen(newbuff, "r"); + out=owl_malloc(1024); + size=1024; + strcpy(out, ""); + while (fgets(buff2, 1024, p)!=NULL) { + size+=1024; + out=owl_realloc(out, size); + strcat(out, buff2); + } + pclose(p); + + if (type==1) { + owl_function_popless_text(out); + } else if (type==0) { + return out; + } else if (type==2) { + owl_function_adminmsg(buff, out); + } else { + owl_function_popless_text(out); + } + owl_free(out); + } + return NULL; +} + +char *owl_function_perl(int argc, char **argv, char *buff, int type) +{ + /* if type == 1 display in a popup + * if type == 2 display an admin messages + * if type == 0 return output + * else display in a popup + */ + char *perlout; + + if (argc<2) { + owl_function_error("Wrong number of arguments to perl command"); + return NULL; + } + + /* consume first token (argv[0]) */ + buff = skiptokens(buff, 1); + + perlout = owl_perlconfig_execute(buff); + if (perlout) { + if (type==1) { + owl_function_popless_text(perlout); + } else if (type==2) { + owl_function_adminmsg(buff, perlout); + } else if (type==0) { + return perlout; + } else { + owl_function_popless_text(perlout); + } + owl_free(perlout); + } + return NULL; +} + +/* Change the filter associated with the current view. + * This also figures out which message in the new filter + * should have the pointer. + */ +void owl_function_change_currentview_filter(char *filtname) +{ + owl_view *v; + owl_filter *f; + int curid=-1, newpos, curmsg; + owl_message *curm=NULL; + + v=owl_global_get_current_view(&g); + + curmsg=owl_global_get_curmsg(&g); + if (curmsg==-1) { + owl_function_debugmsg("Hit the curmsg==-1 case in change_view"); + } else { + curm=owl_view_get_element(v, curmsg); + if (curm) { + curid=owl_message_get_id(curm); + owl_view_save_curmsgid(v, curid); + } + } + + f=owl_global_get_filter(&g, filtname); + if (!f) { + owl_function_error("Unknown filter %s", filtname); + return; + } + + owl_view_new_filter(v, f); + + /* Figure out what to set the current message to. + * - If the view we're leaving has messages in it, go to the closest message + * to the last message pointed to in that view. + * - If the view we're leaving is empty, try to restore the position + * from the last time we were in the new view. */ + if (curm) { + newpos = owl_view_get_nearest_to_msgid(v, curid); + } else { + newpos = owl_view_get_nearest_to_saved(v); + } + + owl_global_set_curmsg(&g, newpos); + owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_global_set_direction_downwards(&g); +} + +/* Create a new filter, or replace an existing one + * with a new definition. + */ +void owl_function_create_filter(int argc, char **argv) +{ + owl_filter *f; + owl_view *v; + int ret, inuse=0; + + if (argc < 2) { + owl_function_error("Wrong number of arguments to filter command"); + return; + } + + owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]); + + v=owl_global_get_current_view(&g); + + /* don't touch the all filter */ + if (!strcmp(argv[1], "all")) { + owl_function_error("You may not change the 'all' filter."); + return; + } + + /* deal with the case of trying change the filter color */ + if (argc==4 && !strcmp(argv[2], "-c")) { + f=owl_global_get_filter(&g, argv[1]); + if (!f) { + owl_function_error("The filter '%s' does not exist.", argv[1]); + return; + } + if (owl_util_string_to_color(argv[3])==-1) { + owl_function_error("The color '%s' is not available.", argv[3]); + return; + } + owl_filter_set_color(f, owl_util_string_to_color(argv[3])); + owl_global_set_needrefresh(&g); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + return; + } + + /* create the filter and check for errors */ + f=owl_malloc(sizeof(owl_filter)); + ret=owl_filter_init(f, argv[1], argc-2, argv+2); + if (ret==-1) { + owl_free(f); + owl_function_error("Invalid filter"); + return; + } + + /* if the named filter is in use by the current view, remember it */ + if (!strcmp(owl_view_get_filtname(v), argv[1])) { + inuse=1; + } + + /* if the named filter already exists, nuke it */ + if (owl_global_get_filter(&g, argv[1])) { + owl_global_remove_filter(&g, argv[1]); + } + + /* add the filter */ + owl_global_add_filter(&g, f); + + /* if it was in use by the current view then update */ + if (inuse) { + owl_function_change_currentview_filter(argv[1]); + } + owl_global_set_needrefresh(&g); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); +} + +/* If 'filtername' does not start with 'not-' create a filter named + * 'not-<filtername>' defined as "not filter <filtername>". If the + * filter 'not-<filtername>' already exists, do not overwrite it. If + * 'filtername' begins with 'not-' and a filter 'filtername' already + * exists, then do nothing. If the filter 'filtername' does not + * exist, create it and define it as 'not filter <filtername>' + * + * Returns the name of the negated filter, which the caller must free. + */ +char *owl_function_create_negative_filter(char *filtername) +{ + char *newname; + owl_filter *tmpfilt; + char *argv[5]; + + owl_function_debugmsg("owl_function_create_negative_filter"); + + if (!strncmp(filtername, "not-", 4)) { + newname=owl_strdup(filtername+4); + } else { + newname=owl_sprintf("not-%s", filtername); + } + + tmpfilt=owl_global_get_filter(&g, newname); + if (!tmpfilt) { + argv[0]="filter"; /* anything is fine here */ + argv[1]=newname; + argv[2]="not"; + argv[3]="filter"; + argv[4]=filtername; + owl_function_create_filter(5, argv); + } + + owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname); + return(newname); +} + +void owl_function_show_filters() +{ + owl_list *l; + owl_filter *f; + int i, j; + owl_fmtext fm; + + owl_fmtext_init_null(&fm); + + l=owl_global_get_filterlist(&g); + j=owl_list_get_size(l); + + owl_fmtext_append_bold(&fm, "Filters:\n"); + + for (i=0; i<j; i++) { + f=owl_list_get_element(l, i); + owl_fmtext_append_normal(&fm, " "); + if (owl_global_get_hascolors(&g)) { + owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_color(f)); + } else { + owl_fmtext_append_normal(&fm, owl_filter_get_name(f)); + } + owl_fmtext_append_normal(&fm, "\n"); + } + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +void owl_function_show_filter(char *name) +{ + owl_filter *f; + char *buff, *tmp; + + f=owl_global_get_filter(&g, name); + if (!f) { + owl_function_error("There is no filter named %s", name); + return; + } + tmp = owl_filter_print(f); + buff = owl_sprintf("%s: %s", owl_filter_get_name(f), tmp); + owl_function_popless_text(buff); + owl_free(buff); + owl_free(tmp); +} + +void owl_function_show_zpunts() +{ + owl_filter *f; + owl_list *fl; + char buff[5000]; + char *tmp; + owl_fmtext fm; + int i, j; + + owl_fmtext_init_null(&fm); + + fl=owl_global_get_puntlist(&g); + j=owl_list_get_size(fl); + owl_fmtext_append_bold(&fm, "Active zpunt filters:\n"); + + for (i=0; i<j; i++) { + f=owl_list_get_element(fl, i); + snprintf(buff, sizeof(buff), "[% 2d] ", i+1); + owl_fmtext_append_normal(&fm, buff); + tmp = owl_filter_print(f); + owl_fmtext_append_normal(&fm, tmp); + owl_free(tmp); + owl_fmtext_append_normal(&fm, "\n"); + } + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +/* Create a filter for a class, instance if one doesn't exist. If + * instance is NULL then catch all messgaes in the class. Returns the + * name of the filter, which the caller must free. + */ +char *owl_function_classinstfilt(char *c, char *i) +{ + owl_list *fl; + owl_filter *f; + char *argbuff, *filtname; + char *tmpclass, *tmpinstance = NULL; + char *class, *instance = NULL; + + class = owl_util_baseclass(c); + if(i) { + instance = owl_util_baseclass(i); + } + + fl=owl_global_get_filterlist(&g); + + /* name for the filter */ + if (!instance) { + filtname = owl_sprintf("class-%s", class); + } else { + filtname = owl_sprintf("class-%s-instance-%s", class, instance); + } + /* downcase it */ + { + char *temp = g_utf8_strdown(filtname, -1); + if (temp) { + owl_free(filtname); + filtname = temp; + } + } + /* turn spaces, single quotes, and double quotes into dots */ + owl_text_tr(filtname, ' ', '.'); + owl_text_tr(filtname, '\'', '.'); + owl_text_tr(filtname, '"', '.'); + + /* if it already exists then go with it. This lets users override */ + if (owl_global_get_filter(&g, filtname)) { + return(filtname); + } + + /* create the new filter */ + tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH); + owl_text_tr(tmpclass, ' ', '.'); + owl_text_tr(tmpclass, '\'', '.'); + owl_text_tr(tmpclass, '"', '.'); + if (instance) { + tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH); + owl_text_tr(tmpinstance, ' ', '.'); + owl_text_tr(tmpinstance, '\'', '.'); + owl_text_tr(tmpinstance, '"', '.'); + } + + argbuff = owl_sprintf("class ^(un)*%s(\\.d)*$", tmpclass); + if (tmpinstance) { + char *tmp = argbuff; + argbuff = owl_sprintf("%s and ( instance ^(un)*%s(\\.d)*$ )", tmp, tmpinstance); + owl_free(tmp); + } + owl_free(tmpclass); + if (tmpinstance) owl_free(tmpinstance); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, filtname, argbuff); + + /* add it to the global list */ + owl_global_add_filter(&g, f); + + owl_free(argbuff); + owl_free(class); + if (instance) { + owl_free(instance); + } + return(filtname); +} + +/* Create a filter for personal zephyrs to or from the specified + * zephyr user. Includes login/logout notifications for the user. + * The name of the filter will be 'user-<user>'. If a filter already + * exists with this name, no new filter will be created. This allows + * the configuration to override this function. Returns the name of + * the filter, which the caller must free. + */ +char *owl_function_zuserfilt(char *user) +{ + owl_filter *f; + char *argbuff, *longuser, *esclonguser, *shortuser, *filtname; + + /* stick the local realm on if it's not there */ + longuser=long_zuser(user); + shortuser=short_zuser(user); + + /* name for the filter */ + filtname=owl_sprintf("user-%s", shortuser); + + /* if it already exists then go with it. This lets users override */ + if (owl_global_get_filter(&g, filtname)) { + return(owl_strdup(filtname)); + } + + /* create the new-internal filter */ + f=owl_malloc(sizeof(owl_filter)); + + esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH); + + argbuff=owl_sprintf("( type ^zephyr$ and filter personal and " + "( ( direction ^in$ and sender ^%1$s$ ) or ( direction ^out$ and " + "recipient ^%1$s$ ) ) ) or ( ( class ^login$ ) and ( sender ^%1$s$ ) )", + esclonguser); + + owl_filter_init_fromstring(f, filtname, argbuff); + + /* add it to the global list */ + owl_global_add_filter(&g, f); + + /* free stuff */ + owl_free(argbuff); + owl_free(longuser); + owl_free(esclonguser); + owl_free(shortuser); + + return(filtname); +} + +/* Create a filter for AIM IM messages to or from the specified + * screenname. The name of the filter will be 'aimuser-<user>'. If a + * filter already exists with this name, no new filter will be + * created. This allows the configuration to override this function. + * Returns the name of the filter, which the caller must free. + */ +char *owl_function_aimuserfilt(char *user) +{ + owl_filter *f; + char *argbuff, *filtname; + char *escuser; + + /* name for the filter */ + filtname=owl_sprintf("aimuser-%s", user); + + /* if it already exists then go with it. This lets users override */ + if (owl_global_get_filter(&g, filtname)) { + return(owl_strdup(filtname)); + } + + /* create the new-internal filter */ + f=owl_malloc(sizeof(owl_filter)); + + escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH); + + argbuff = owl_sprintf( + "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or " + "( sender ^%2$s$ and recipient ^%1$s$ ) ) )", + escuser, owl_global_get_aim_screenname(&g)); + + owl_filter_init_fromstring(f, filtname, argbuff); + + /* add it to the global list */ + owl_global_add_filter(&g, f); + + /* free stuff */ + owl_free(argbuff); + owl_free(escuser); + + return(filtname); +} + +char *owl_function_typefilt(char *type) +{ + owl_filter *f; + char *argbuff, *filtname; + + /* name for the filter */ + filtname=owl_sprintf("type-%s", type); + + /* if it already exists then go with it. This lets users override */ + if (owl_global_get_filter(&g, filtname)) { + return filtname; + } + + /* create the new-internal filter */ + f=owl_malloc(sizeof(owl_filter)); + + argbuff = owl_sprintf("type ^%s$", type); + + owl_filter_init_fromstring(f, filtname, argbuff); + + /* add it to the global list */ + owl_global_add_filter(&g, f); + + /* free stuff */ + owl_free(argbuff); + + return filtname; +} + +/* If flag is 1, marks for deletion. If flag is 0, + * unmarks for deletion. */ +void owl_function_delete_curview_msgs(int flag) +{ + owl_view *v; + int i, j; + + v=owl_global_get_current_view(&g); + j=owl_view_get_size(v); + for (i=0; i<j; i++) { + if (flag == 1) { + owl_message_mark_delete(owl_view_get_element(v, i)); + } else if (flag == 0) { + owl_message_unmark_delete(owl_view_get_element(v, i)); + } + } + + owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un"); + + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); +} + +/* Create a filter based on the current message. Returns the name of + * a filter or null. The caller must free this name. + * + * if the curmsg is a personal zephyr return a filter name + * to the zephyr converstaion with that user. + * If the curmsg is a zephyr class message, instance foo, recip *, + * return a filter name to the class, inst. + * If the curmsg is a zephyr class message and type==0 then + * return a filter name for just the class. + * If the curmsg is a zephyr class message and type==1 then + * return a filter name for the class and instance. + * If the curmsg is a personal AIM message returna filter + * name to the AIM conversation with that user + */ +char *owl_function_smartfilter(int type) +{ + owl_view *v; + owl_message *m; + char *zperson, *filtname=NULL; + + v=owl_global_get_current_view(&g); + m=owl_view_get_element(v, owl_global_get_curmsg(&g)); + + if (!m || owl_view_get_size(v)==0) { + owl_function_error("No message selected\n"); + return(NULL); + } + + /* very simple handling of admin messages for now */ + if (owl_message_is_type_admin(m)) { + return(owl_function_typefilt("admin")); + } + + /* very simple handling of loopback messages for now */ + if (owl_message_is_type_loopback(m)) { + return(owl_function_typefilt("loopback")); + } + + /* aim messages */ + if (owl_message_is_type_aim(m)) { + if (owl_message_is_direction_in(m)) { + filtname=owl_function_aimuserfilt(owl_message_get_sender(m)); + } else if (owl_message_is_direction_out(m)) { + filtname=owl_function_aimuserfilt(owl_message_get_recipient(m)); + } + return(filtname); + } + + /* narrow personal and login messages to the sender or recip as appropriate */ + if (owl_message_is_personal(m) || owl_message_is_loginout(m)) { + if (owl_message_is_type_zephyr(m)) { + if (owl_message_is_direction_in(m)) { + zperson=short_zuser(owl_message_get_sender(m)); + } else { + zperson=short_zuser(owl_message_get_recipient(m)); + } + filtname=owl_function_zuserfilt(zperson); + owl_free(zperson); + return(filtname); + } + return(NULL); + } + + /* narrow class MESSAGE, instance foo, recip * messages to class, inst */ + if (!strcasecmp(owl_message_get_class(m), "message") && !owl_message_is_personal(m)) { + filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m)); + return(filtname); + } + + /* otherwise narrow to the class */ + if (type==0) { + filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL); + } else if (type==1) { + filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m)); + } + return(filtname); +} + +void owl_function_smartzpunt(int type) +{ + /* Starts a zpunt command based on the current class,instance pair. + * If type=0, uses just class. If type=1, uses instance as well. */ + owl_view *v; + owl_message *m; + char *cmd, *cmdprefix, *mclass, *minst; + + v=owl_global_get_current_view(&g); + m=owl_view_get_element(v, owl_global_get_curmsg(&g)); + + if (!m || owl_view_get_size(v)==0) { + owl_function_error("No message selected\n"); + return; + } + + /* for now we skip admin messages. */ + if (owl_message_is_type_admin(m) + || owl_message_is_loginout(m) + || !owl_message_is_type_zephyr(m)) { + owl_function_error("smartzpunt doesn't support this message type."); + return; + } + + mclass = owl_message_get_class(m); + minst = owl_message_get_instance(m); + if (!mclass || !*mclass || *mclass==' ' + || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal")) + || (type && (!minst || !*minst|| *minst==' '))) { + owl_function_error("smartzpunt can't safely do this for <%s,%s>", + mclass, minst); + } else { + cmdprefix = "start-command zpunt "; + cmd = owl_malloc(strlen(cmdprefix)+strlen(mclass)+strlen(minst)+10); + strcpy(cmd, cmdprefix); + strcat(cmd, owl_getquoting(mclass)); + strcat(cmd, mclass); + strcat(cmd, owl_getquoting(mclass)); + if (type) { + strcat(cmd, " "); + strcat(cmd, owl_getquoting(minst)); + strcat(cmd, minst); + strcat(cmd, owl_getquoting(minst)); + } else { + strcat(cmd, " *"); + } + owl_function_command(cmd); + owl_free(cmd); + } +} + +/* Set the color of the current view's filter to + * be 'color' + */ +void owl_function_color_current_filter(char *color) +{ + char *name; + + name=owl_view_get_filtname(owl_global_get_current_view(&g)); + owl_function_color_filter(name, color); +} + +/* Set the color of the filter 'filter' to be 'color'. If the color + * name does not exist, return -1, if the filter does not exist or is + * the "all" filter, return -2. Return 0 on success + */ +int owl_function_color_filter(char *filtname, char *color) +{ + owl_filter *f; + + f=owl_global_get_filter(&g, filtname); + if (!f) { + owl_function_error("Unknown filter"); + return(-2); + } + + /* don't touch the all filter */ + if (!strcmp(filtname, "all")) { + owl_function_error("You may not change the 'all' filter."); + return(-2); + } + + if (owl_util_string_to_color(color)==-1) { + owl_function_error("No color named '%s' avilable."); + return(-1); + } + owl_filter_set_color(f, owl_util_string_to_color(color)); + owl_global_set_needrefresh(&g); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + return(0); +} + +void owl_function_show_colors() +{ + owl_fmtext fm; + + owl_fmtext_init_null(&fm); + owl_fmtext_append_normal(&fm, "default: "); + owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT); + + owl_fmtext_append_normal(&fm,"red: "); + owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED); + + owl_fmtext_append_normal(&fm,"green: "); + owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN); + + owl_fmtext_append_normal(&fm,"yellow: "); + owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW); + + owl_fmtext_append_normal(&fm,"blue: "); + owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE); + + owl_fmtext_append_normal(&fm,"magenta: "); + owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA); + + owl_fmtext_append_normal(&fm,"cyan: "); + owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN); + + owl_fmtext_append_normal(&fm,"white: "); + owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE); + + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +/* add the given class, inst, recip to the punt list for filtering. + * if direction==0 then punt + * if direction==1 then unpunt + */ +void owl_function_zpunt(char *class, char *inst, char *recip, int direction) +{ + char *puntexpr, *classexpr, *instexpr, *recipexpr; + char *quoted; + + if (!strcmp(class, "*")) { + classexpr = owl_sprintf("class .*"); + } else { + quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH); + owl_text_tr(quoted, ' ', '.'); + owl_text_tr(quoted, '\'', '.'); + owl_text_tr(quoted, '"', '.'); + classexpr = owl_sprintf("class ^(un)*%s(\\.d)*$", quoted); + owl_free(quoted); + } + if (!strcmp(inst, "*")) { + instexpr = owl_sprintf(" and instance .*"); + } else { + quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH); + owl_text_tr(quoted, ' ', '.'); + owl_text_tr(quoted, '\'', '.'); + owl_text_tr(quoted, '"', '.'); + instexpr = owl_sprintf(" and instance ^(un)*%s(\\.d)*$", quoted); + owl_free(quoted); + } + if (!strcmp(recip, "*")) { + recipexpr = owl_sprintf(""); + } else { + quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH); + owl_text_tr(quoted, ' ', '.'); + owl_text_tr(quoted, '\'', '.'); + owl_text_tr(quoted, '"', '.'); + recipexpr = owl_sprintf(" and recipient ^%s$", quoted); + owl_free(quoted); + } + + puntexpr = owl_sprintf("%s %s %s", classexpr, instexpr, recipexpr); + owl_function_punt(puntexpr, direction); + owl_free(puntexpr); + owl_free(classexpr); + owl_free(instexpr); + owl_free(recipexpr); +} + +void owl_function_punt(char *filter, int direction) +{ + owl_filter *f; + owl_list *fl; + int ret, i, j; + fl=owl_global_get_puntlist(&g); + + /* first, create the filter */ + f=malloc(sizeof(owl_filter)); + + owl_function_debugmsg("About to filter %s", filter); + ret=owl_filter_init_fromstring(f, "punt-filter", filter); + if (ret) { + owl_function_error("Error creating filter for zpunt"); + owl_filter_free(f); + return; + } + + /* Check for an identical filter */ + j=owl_list_get_size(fl); + for (i=0; i<j; i++) { + if (owl_filter_equiv(f, owl_list_get_element(fl, i))) { + owl_function_debugmsg("found an equivalent punt filter"); + /* if we're punting, then just silently bow out on this duplicate */ + if (direction==0) { + owl_filter_free(f); + return; + } + + /* if we're unpunting, then remove this filter from the puntlist */ + if (direction==1) { + owl_filter_free(owl_list_get_element(fl, i)); + owl_list_remove_element(fl, i); + owl_filter_free(f); + return; + } + } + } + + owl_function_debugmsg("punting"); + /* If we're punting, add the filter to the global punt list */ + if (direction==0) { + owl_list_append_element(fl, f); + } +} + +void owl_function_activate_keymap(char *keymap) +{ + if (!owl_keyhandler_activate(owl_global_get_keyhandler(&g), keymap)) { + owl_function_error("Unable to activate keymap '%s'", keymap); + } +} + +void owl_function_show_keymaps() +{ + owl_list l; + owl_fmtext fm; + owl_keymap *km; + owl_keyhandler *kh; + int i, numkm; + char *kmname; + + kh = owl_global_get_keyhandler(&g); + owl_fmtext_init_null(&fm); + owl_fmtext_append_bold(&fm, "Keymaps: "); + owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n"); + owl_keyhandler_get_keymap_names(kh, &l); + owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary); + owl_fmtext_append_normal(&fm, "\n"); + + numkm = owl_list_get_size(&l); + for (i=0; i<numkm; i++) { + kmname = owl_list_get_element(&l, i); + km = owl_keyhandler_get_keymap(kh, kmname); + owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n"); + owl_keymap_get_details(km, &fm); + } + owl_fmtext_append_normal(&fm, "\n"); + + owl_function_popless_fmtext(&fm); + owl_keyhandler_keymap_namelist_free(&l); + owl_fmtext_free(&fm); +} + +char *owl_function_keymap_summary(void *name) +{ + owl_keymap *km + = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name); + if (km) return owl_keymap_summary(km); + else return(NULL); +} + +/* TODO: implement for real */ +void owl_function_show_keymap(char *name) +{ + owl_fmtext fm; + owl_keymap *km; + + owl_fmtext_init_null(&fm); + km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name); + if (km) { + owl_keymap_get_details(km, &fm); + } else { + owl_fmtext_append_normal(&fm, "No such keymap...\n"); + } + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +void owl_function_help_for_command(char *cmdname) +{ + owl_fmtext fm; + + owl_fmtext_init_null(&fm); + owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm); + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +void owl_function_search_start(char *string, int direction) +{ + /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */ + owl_global_set_search_active(&g, string); + owl_function_search_helper(0, direction); +} + +void owl_function_search_continue(int direction) +{ + /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */ + owl_function_search_helper(1, direction); +} + +void owl_function_search_helper(int mode, int direction) +{ + /* move to a message that contains the string. If direction is + * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is + * OWL_DIRECTION_UPWARDS then search backwards. + * + * If mode==0 then it will stay on the current message if it + * contains the string. + */ + + owl_view *v; + int viewsize, i, curmsg, start; + owl_message *m; + + v=owl_global_get_current_view(&g); + viewsize=owl_view_get_size(v); + curmsg=owl_global_get_curmsg(&g); + + if (viewsize==0) { + owl_function_error("No messages present"); + return; + } + + if (mode==0) { + start=curmsg; + } else if (direction==OWL_DIRECTION_DOWNWARDS) { + start=curmsg+1; + } else { + start=curmsg-1; + } + + /* bounds check */ + if (start>=viewsize || start<0) { + owl_function_error("No further matches found"); + return; + } + + for (i=start; i<viewsize && i>=0;) { + m=owl_view_get_element(v, i); + if (owl_message_search(m, owl_global_get_search_string(&g))) { + owl_global_set_curmsg(&g, i); + owl_function_calculate_topmsg(direction); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + if (direction==OWL_DIRECTION_DOWNWARDS) { + owl_global_set_direction_downwards(&g); + } else { + owl_global_set_direction_upwards(&g); + } + return; + } + if (direction==OWL_DIRECTION_DOWNWARDS) { + i++; + } else { + i--; + } + } + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_function_error("No matches found"); +} + +/* strips formatting from ztext and returns the unformatted text. + * caller is responsible for freeing. */ +char *owl_function_ztext_stylestrip(char *zt) +{ + owl_fmtext fm; + char *plaintext; + + owl_fmtext_init_null(&fm); + owl_fmtext_append_ztext(&fm, zt); + plaintext = owl_fmtext_print_plain(&fm); + owl_fmtext_free(&fm); + return(plaintext); +} + +/* Popup a buddylisting. If filename is NULL use the default .anyone */ +void owl_function_buddylist(int aim, int zephyr, char *filename) +{ + int i, j, x, idle; + owl_fmtext fm; + owl_buddylist *bl; + owl_buddy *b; + owl_list anyone; + char *foo, *timestr; +#ifdef HAVE_LIBZEPHYR + char *tmp, *user, *line; + ZLocations_t location[200]; + int numlocs, ret; +#endif + + owl_fmtext_init_null(&fm); + + /* AIM first */ + if (aim && owl_global_is_aimloggedin(&g)) { + bl=owl_global_get_buddylist(&g); + + owl_fmtext_append_bold(&fm, "AIM users logged in:\n"); + /* we're assuming AIM for now */ + j=owl_buddylist_get_size(bl); + for (i=0; i<j; i++) { + b=owl_buddylist_get_buddy_n(bl, i); + idle=owl_buddy_get_idle_time(b); + if (idle!=0) { + timestr=owl_util_minutes_to_timestr(idle); + } else { + timestr=owl_strdup(""); + } + foo=owl_sprintf(" %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr); + owl_fmtext_append_normal(&fm, foo); + owl_free(timestr); + owl_free(foo); + } + } + +#ifdef HAVE_LIBZEPHYR + if (zephyr) { + owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n"); + owl_list_create(&anyone); + ret=owl_zephyr_get_anyone_list(&anyone, filename); + if (ret) { + owl_fmtext_append_normal(&fm, " Error opening file for zephyr buddies.\n"); + } else { + j=owl_list_get_size(&anyone); + for (i=0; i<j; i++) { + user=owl_list_get_element(&anyone, i); + ret=ZLocateUser(user, &numlocs, ZAUTH); + if (ret!=ZERR_NONE) { + owl_function_error("Error getting location for %s", user); + continue; + } + + numlocs=200; + ret=ZGetLocations(location, &numlocs); + if (ret==0) { + for (x=0; x<numlocs; x++) { + tmp=short_zuser(user); + line=owl_sprintf(" %-10.10s %-24.24s %-12.12s %20.20s\n", + tmp, + location[x].host, + location[x].tty, + location[x].time); + owl_fmtext_append_normal(&fm, line); + owl_free(tmp); + owl_free(line); + } + if (numlocs>=200) { + owl_fmtext_append_normal(&fm, " Too many locations found for this user, truncating.\n"); + } + } + } + } + owl_list_free_all(&anyone, owl_free); + } +#endif + + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} + +/* Dump messages in the current view to the file 'filename'. */ +void owl_function_dump(char *filename) +{ + int i, j, count; + owl_message *m; + owl_view *v; + FILE *file; + + v=owl_global_get_current_view(&g); + + /* in the future make it ask yes/no */ + /* + ret=stat(filename, &sbuf); + if (!ret) { + ret=owl_function_askyesno("File exists, continue? [Y/n]"); + if (!ret) return; + } + */ + + file=fopen(filename, "w"); + if (!file) { + owl_function_error("Error opening file"); + return; + } + + count=0; + j=owl_view_get_size(v); + for (i=0; i<j; i++) { + m=owl_view_get_element(v, i); + fputs(owl_message_get_text(m), file); + } + fclose(file); + owl_function_makemsg("Messages dumped to %s", filename); +} + +void owl_function_do_newmsgproc(void) +{ + if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) { + /* if there's a process out there, we need to check on it */ + if (owl_global_get_newmsgproc_pid(&g)) { + owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g)); + owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)); + waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG); + if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) { + /* it exited */ + owl_global_set_newmsgproc_pid(&g, 0); + owl_function_debugmsg("newmsgproc exited"); + } else { + owl_function_debugmsg("newmsgproc did not exit"); + } + } + + /* if it exited, fork & exec a new one */ + if (owl_global_get_newmsgproc_pid(&g)==0) { + int i, myargc; + i=fork(); + if (i) { + /* parent set the child's pid */ + owl_global_set_newmsgproc_pid(&g, i); + owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", i); + } else { + /* child exec's the program */ + char **parsed; + parsed=owl_parseline(owl_global_get_newmsgproc(&g), &myargc); + if (myargc < 0) { + owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?", owl_global_get_newmsgproc(&g)); + } + if (myargc <= 0) { + _exit(127); + } + parsed=realloc(parsed, sizeof(*parsed) * (myargc+1)); + parsed[myargc] = NULL; + + owl_function_debugmsg("About to exec \"%s\" with %d arguments", parsed[0], myargc); + + execvp(parsed[0], parsed); + + + /* was there an error exec'ing? */ + owl_function_debugmsg("Cannot run newmsgproc '%s': cannot exec '%s': %s", + owl_global_get_newmsgproc(&g), parsed[0], strerror(errno)); + _exit(127); + } + } + } +} + +/* print the xterm escape sequence to raise the window */ +void owl_function_xterm_raise(void) +{ + printf("\033[5t"); +} + +/* print the xterm escape sequence to deiconify the window */ +void owl_function_xterm_deiconify(void) +{ + printf("\033[1t"); +} + +/* Add the specified command to the startup file. Eventually this + * should be clever, and rewriting settings that will obviosly + * override earlier settings with 'set' 'bindkey' and 'alias' + * commands. For now though we just remove any line that would + * duplicate this one and then append this line to the end of + * startupfile. + */ +void owl_function_addstartup(char *buff) +{ + FILE *file; + char *filename; + + filename=owl_sprintf("%s/%s", owl_global_get_homedir(&g), OWL_STARTUP_FILE); + file=fopen(filename, "a"); + if (!file) { + owl_function_error("Error opening startupfile for new command"); + owl_free(filename); + return; + } + + /* delete earlier copies */ + owl_util_file_deleteline(filename, buff, 1); + owl_free(filename); + + /* add this line */ + fprintf(file, "%s\n", buff); + + fclose(file); +} + +/* Remove the specified command from the startup file. */ +void owl_function_delstartup(char *buff) +{ + char *filename; + filename=owl_sprintf("%s/%s", owl_global_get_homedir(&g), OWL_STARTUP_FILE); + owl_util_file_deleteline(filename, buff, 1); + owl_free(filename); +} + +/* Execute owl commands from the given filename. If the filename + * is NULL, use the default owl startup commands file. + */ +void owl_function_source(char *filename) +{ + char *path; + FILE *file; + char buff[LINE]; + int fail_silent = 0; + + if (!filename) { + fail_silent = 1; + path = owl_sprintf("%s/%s", owl_global_get_homedir(&g), OWL_STARTUP_FILE); + } else { + path = owl_util_makepath(filename); + } + file=fopen(path, "r"); + owl_free(path); + if (!file) { + if (!fail_silent) { + owl_function_error("Error opening file: %s", filename); + } + return; + } + while (fgets(buff, LINE, file)!=NULL) { + if (buff[0] == '#') continue; + buff[strlen(buff)-1]='\0'; + owl_function_command(buff); + } + fclose(file); +} + +void owl_function_change_style(owl_view *v, char *stylename) +{ + owl_style *s; + + s=owl_global_get_style_by_name(&g, stylename); + if (!s) { + owl_function_error("No style named %s", stylename); + return; + } + owl_view_set_style(v, s); + owl_messagelist_invalidate_formats(owl_global_get_msglist(&g)); + owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); +} + +void owl_function_toggleoneline() +{ + owl_view *v; + owl_style *s; + + v=owl_global_get_current_view(&g); + s=owl_view_get_style(v); + + if (!owl_style_matches_name(s, "oneline")) { + owl_function_change_style(v, "oneline"); + } else { + owl_function_change_style(v, owl_global_get_default_style(&g)); + } + + owl_messagelist_invalidate_formats(owl_global_get_msglist(&g)); + owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); +} + +void owl_function_error(char *fmt, ...) +{ + va_list ap; + char *buff, *buff2; + char *nl; + char *date; + time_t now; + + now=time(NULL); + date=owl_strdup(ctime(&now)); + date[strlen(date)-1]='\0'; + + va_start(ap, fmt); + + buff = g_strdup_vprintf(fmt, ap); + buff2 = owl_sprintf("%s %s", date, buff); + owl_function_debugmsg("ERROR: %s", buff); + nl = strchr(buff, '\n'); + if(nl && *(nl + 1)) { + /* Multiline error */ + owl_function_adminmsg("ERROR", buff); + } else { + owl_function_makemsg("[Error] %s", buff); + } + owl_errqueue_append_err(owl_global_get_errqueue(&g), buff2); + va_end(ap); + owl_free(date); + owl_free(buff); + owl_free(buff2); +} + +void owl_function_showerrs() +{ + owl_fmtext fm; + + owl_fmtext_init_null(&fm); + owl_fmtext_append_normal(&fm, "Errors:\n\n"); + owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm); + owl_function_popless_fmtext(&fm); +} + +void owl_function_makemsg(char *fmt, ...) +{ + va_list ap; + char buff[2048]; + + if (!owl_global_get_curs_msgwin(&g)) return; + + va_start(ap, fmt); + werase(owl_global_get_curs_msgwin(&g)); + + vsnprintf(buff, 2048, fmt, ap); + owl_function_debugmsg("makemsg: %s", buff); + waddstr(owl_global_get_curs_msgwin(&g), buff); + wnoutrefresh(owl_global_get_curs_msgwin(&g)); + owl_global_set_needrefresh(&g); + va_end(ap); +} + +/* get locations for everyone in .anyone. If 'notify' is '1' then + * send a pseudo login or logout message for everyone not in sync with + * the global zephyr buddy list. The list is updated regardless of + * the status of 'notify'. + */ +void owl_function_zephyr_buddy_check(int notify) +{ +#ifdef HAVE_LIBZEPHYR + int i, j; + owl_list anyone; + owl_message *m; + owl_zbuddylist *zbl; + char *user; + ZLocations_t location[200]; + int numlocs, ret; + + zbl=owl_global_get_zephyr_buddylist(&g); + + owl_list_create(&anyone); + ret=owl_zephyr_get_anyone_list(&anyone, NULL); + + j=owl_list_get_size(&anyone); + for (i=0; i<j; i++) { + user=owl_list_get_element(&anyone, i); + ret=ZLocateUser(user, &numlocs, ZAUTH); + if (ret!=ZERR_NONE) { + owl_function_error("Error getting location for %s", user); + continue; + } + numlocs=200; + ret=ZGetLocations(location, &numlocs); + if (ret==0) { + if ((numlocs>0) && !owl_zbuddylist_contains_user(zbl, user)) { + /* Send a PSEUDO LOGIN! */ + if (notify) { + m=owl_malloc(sizeof(owl_message)); + owl_message_create_pseudo_zlogin(m, 0, user, location[0].host, location[0].time, location[0].tty); + owl_global_messagequeue_addmsg(&g, m); + } + owl_zbuddylist_adduser(zbl, user); + owl_function_debugmsg("owl_function_zephyr_buddy_check: login for %s ", user); + } else if ((numlocs==0) && owl_zbuddylist_contains_user(zbl, user)) { + /* I don't think this ever happens (if there are 0 locations we should get an error from + * ZGetLocations) + */ + owl_function_error("owl_function_zephyr_buddy_check: exceptional case logout for %s ",user); + } + } else if ((ret==ZERR_NOLOCATIONS) && owl_zbuddylist_contains_user(zbl, user)) { + /* Send a PSEUDO LOGOUT! */ + if (notify) { + m=owl_malloc(sizeof(owl_message)); + owl_message_create_pseudo_zlogin(m, 1, user, "", "", ""); + owl_global_messagequeue_addmsg(&g, m); + } + owl_zbuddylist_deluser(zbl, user); + owl_function_debugmsg("owl_function_zephyr_buddy_check: logout for %s ",user); + } + } + + owl_list_free_all(&anyone, owl_free); +#endif +} + +void owl_function_aimsearch_results(char *email, owl_list *namelist) +{ + owl_fmtext fm; + int i, j; + + owl_fmtext_init_null(&fm); + owl_fmtext_append_normal(&fm, "AIM screennames associated with "); + owl_fmtext_append_normal(&fm, email); + owl_fmtext_append_normal(&fm, ":\n"); + + j=owl_list_get_size(namelist); + for (i=0; i<j; i++) { + owl_fmtext_append_normal(&fm, " "); + owl_fmtext_append_normal(&fm, owl_list_get_element(namelist, i)); + owl_fmtext_append_normal(&fm, "\n"); + } + + owl_function_popless_fmtext(&fm); + owl_fmtext_free(&fm); +} diff --git a/global.c b/global.c new file mode 100644 index 0000000..f5c21cc --- /dev/null +++ b/global.c @@ -0,0 +1,914 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <time.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: global.c,v 1.42 2009/04/07 15:41:22 kretch Exp $"; + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +void owl_global_init(owl_global *g) { + struct hostent *hent; + char hostname[MAXHOSTNAMELEN]; + + g->malloced=0; + g->freed=0; + + gethostname(hostname, MAXHOSTNAMELEN); + hent=gethostbyname(hostname); + if (!hent) { + g->thishost=owl_strdup("localhost"); + } else { + g->thishost=owl_strdup(hent->h_name); + } + + owl_context_init(&g->ctx); + owl_context_set_startup(&g->ctx); + g->curmsg=0; + g->topmsg=0; + g->needrefresh=1; + g->startupargs=NULL; + + owl_variable_dict_setup(&(g->vars)); + owl_cmddict_setup(&(g->cmds)); + + g->lines=LINES; + g->cols=COLS; + + g->rightshift=0; + + owl_editwin_init(&(g->tw), NULL, owl_global_get_typwin_lines(g), g->cols, OWL_EDITWIN_STYLE_ONELINE, NULL); + + owl_keyhandler_init(&g->kh); + owl_keys_setup_keymaps(&g->kh); + + owl_list_create(&(g->filterlist)); + owl_list_create(&(g->puntlist)); + owl_list_create(&(g->messagequeue)); + owl_dict_create(&(g->styledict)); + g->curmsg_vert_offset=0; + g->resizepending=0; + g->typwinactive=0; + g->direction=OWL_DIRECTION_DOWNWARDS; + g->zaway=0; + if (has_colors()) { + g->hascolors=1; + } + g->colorpairs=COLOR_PAIRS; + g->debug=OWL_DEBUG; + g->searchactive=0; + g->searchstring=NULL; + g->starttime=time(NULL); /* assumes we call init only a start time */ + g->buffercommand=NULL; + g->newmsgproc_pid=0; + + owl_global_set_config_format(g, 0); + owl_global_set_userclue(g, OWL_USERCLUE_NONE); + owl_global_set_no_have_config(g); + owl_history_init(&(g->msghist)); + owl_history_init(&(g->cmdhist)); + owl_history_set_norepeats(&(g->cmdhist)); + g->nextmsgid=0; + + owl_filterelement_create_true(&(g->fe_true)); + owl_filterelement_create_false(&(g->fe_false)); + owl_filterelement_create_null(&(g->fe_null)); + + _owl_global_setup_windows(g); + + /* Fill in some variables which don't have constant defaults */ + /* TODO: come back later and check passwd file first */ + g->homedir=owl_strdup(getenv("HOME")); + + owl_messagelist_create(&(g->msglist)); + owl_mainwin_init(&(g->mw)); + owl_popwin_init(&(g->pw)); + + g->aim_screenname=NULL; + g->aim_loggedin=0; + owl_timer_create_countdown(&(g->aim_noop_timer), 30); + owl_timer_create_countdown(&(g->aim_ignorelogin_timer), 0); + owl_timer_create_countdown(&(g->aim_buddyinfo_timer), 60); + owl_buddylist_init(&(g->buddylist)); + + g->response=NULL; + g->havezephyr=0; + g->haveaim=0; + owl_global_set_no_doaimevents(g); + + owl_errqueue_init(&(g->errqueue)); + g->got_err_signal=0; + + owl_zbuddylist_create(&(g->zbuddies)); + owl_timer_create_countdown(&(g->zephyr_buddycheck_timer), 60*3); + + owl_list_create(&(g->dispatchlist)); +} + +void _owl_global_setup_windows(owl_global *g) { + int cols, typwin_lines; + + cols=g->cols; + typwin_lines=owl_global_get_typwin_lines(g); + + /* set the new window sizes */ + g->recwinlines=g->lines-(typwin_lines+2); + if (g->recwinlines<0) { + /* gotta deal with this */ + g->recwinlines=0; + } + + owl_function_debugmsg("_owl_global_setup_windows: about to call newwin(%i, %i, 0, 0)\n", g->recwinlines, cols); + + /* create the new windows */ + g->recwin=newwin(g->recwinlines, cols, 0, 0); + if (g->recwin==NULL) { + owl_function_debugmsg("_owl_global_setup_windows: newwin returned NULL\n", g->recwinlines, cols); + endwin(); + exit(50); + } + + g->sepwin=newwin(1, cols, g->recwinlines, 0); + g->msgwin=newwin(1, cols, g->recwinlines+1, 0); + g->typwin=newwin(typwin_lines, cols, g->recwinlines+2, 0); + + owl_editwin_set_curswin(&(g->tw), g->typwin, typwin_lines, g->cols); + + idlok(g->typwin, FALSE); + idlok(g->recwin, FALSE); + idlok(g->sepwin, FALSE); + idlok(g->msgwin, FALSE); + + nodelay(g->typwin, 1); + keypad(g->typwin, TRUE); + wmove(g->typwin, 0, 0); + + meta(g->typwin, TRUE); +} + +owl_context *owl_global_get_context(owl_global *g) { + return(&g->ctx); +} + +int owl_global_get_lines(owl_global *g) { + return(g->lines); +} + +int owl_global_get_cols(owl_global *g) { + return(g->cols); +} + +int owl_global_get_recwin_lines(owl_global *g) { + return(g->recwinlines); +} + +/* curmsg */ + +int owl_global_get_curmsg(owl_global *g) { + return(g->curmsg); +} + +void owl_global_set_curmsg(owl_global *g, int i) { + g->curmsg=i; + /* we will reset the vertical offset from here */ + /* we might want to move this out to the functions later */ + owl_global_set_curmsg_vert_offset(g, 0); +} + +/* topmsg */ + +int owl_global_get_topmsg(owl_global *g) { + return(g->topmsg); +} + +void owl_global_set_topmsg(owl_global *g, int i) { + g->topmsg=i; +} + +/* windows */ + +owl_mainwin *owl_global_get_mainwin(owl_global *g) { + return(&(g->mw)); +} + +owl_popwin *owl_global_get_popwin(owl_global *g) { + return(&(g->pw)); +} + +/* msglist */ + +owl_messagelist *owl_global_get_msglist(owl_global *g) { + return(&(g->msglist)); +} + +/* keyhandler */ + +owl_keyhandler *owl_global_get_keyhandler(owl_global *g) { + return(&(g->kh)); +} + +/* curses windows */ + +WINDOW *owl_global_get_curs_recwin(owl_global *g) { + return(g->recwin); +} + +WINDOW *owl_global_get_curs_sepwin(owl_global *g) { + return(g->sepwin); +} + +WINDOW *owl_global_get_curs_msgwin(owl_global *g) { + return(g->msgwin); +} + +WINDOW *owl_global_get_curs_typwin(owl_global *g) { + return(g->typwin); +} + +/* typwin */ + +owl_editwin *owl_global_get_typwin(owl_global *g) { + return(&(g->tw)); +} + +/* buffercommand */ + +void owl_global_set_buffercommand(owl_global *g, char *command) { + if (g->buffercommand) owl_free(g->buffercommand); + g->buffercommand=owl_strdup(command); +} + +char *owl_global_get_buffercommand(owl_global *g) { + if (g->buffercommand) return(g->buffercommand); + return(""); +} + +/* refresh */ + +int owl_global_is_needrefresh(owl_global *g) { + if (g->needrefresh==1) return(1); + return(0); +} + +void owl_global_set_needrefresh(owl_global *g) { + g->needrefresh=1; +} + +void owl_global_set_noneedrefresh(owl_global *g) { + g->needrefresh=0; +} + +/* variable dictionary */ + +owl_vardict *owl_global_get_vardict(owl_global *g) { + return &(g->vars); +} + +/* command dictionary */ + +owl_cmddict *owl_global_get_cmddict(owl_global *g) { + return &(g->cmds); +} + +/* rightshift */ + +void owl_global_set_rightshift(owl_global *g, int i) { + g->rightshift=i; +} + +int owl_global_get_rightshift(owl_global *g) { + return(g->rightshift); +} + +/* typwin */ + +int owl_global_is_typwin_active(owl_global *g) { + if (g->typwinactive==1) return(1); + return(0); +} + +void owl_global_set_typwin_active(owl_global *g) { + g->typwinactive=1; +} + +void owl_global_set_typwin_inactive(owl_global *g) { + g->typwinactive=0; +} + +/* resize */ + +void owl_global_set_resize_pending(owl_global *g) { + g->resizepending=1; +} + +char *owl_global_get_homedir(owl_global *g) { + if (g->homedir) return(g->homedir); + return("/"); +} + +int owl_global_get_direction(owl_global *g) { + return(g->direction); +} + +void owl_global_set_direction_downwards(owl_global *g) { + g->direction=OWL_DIRECTION_DOWNWARDS; +} + +void owl_global_set_direction_upwards(owl_global *g) { + g->direction=OWL_DIRECTION_UPWARDS; +} + +/* perl stuff */ + +void owl_global_set_perlinterp(owl_global *g, void *p) { + g->perl=p; +} + +void *owl_global_get_perlinterp(owl_global *g) { + return(g->perl); +} + +int owl_global_is_config_format(owl_global *g) { + if (g->config_format) return(1); + return(0); +} + +void owl_global_set_config_format(owl_global *g, int state) { + if (state==1) { + g->config_format=1; + } else { + g->config_format=0; + } +} + +void owl_global_set_have_config(owl_global *g) { + g->haveconfig=1; +} + +void owl_global_set_no_have_config(owl_global *g) { + g->haveconfig=0; +} + +int owl_global_have_config(owl_global *g) { + if (g->haveconfig) return(1); + return(0); +} + +void owl_global_resize(owl_global *g, int x, int y) { + /* resize the screen. If x or y is 0 use the terminal size */ + struct winsize size; + + if (!g->resizepending) return; + + /* delete the current windows */ + delwin(g->recwin); + delwin(g->sepwin); + delwin(g->msgwin); + delwin(g->typwin); + if (!isendwin()) { + endwin(); + } + + refresh(); + + /* get the new size */ + ioctl(STDIN_FILENO, TIOCGWINSZ, &size); + if (x==0) { + g->lines=size.ws_row; + } else { + g->lines=x; + } + + if (y==0) { + g->cols=size.ws_col; + } else { + g->cols=y; + } + + resizeterm(size.ws_row, size.ws_col); + + /* re-initialize the windows */ + _owl_global_setup_windows(g); + + /* in case any styles rely on the current width */ + owl_messagelist_invalidate_formats(owl_global_get_msglist(g)); + + /* refresh stuff */ + g->needrefresh=1; + owl_mainwin_redisplay(&(g->mw)); + sepbar(NULL); + + if (owl_global_is_typwin_active(g)) { + owl_editwin_redisplay(&(g->tw), 0); + } + /* TODO: this should handle other forms of popwins */ + if (owl_popwin_is_active(owl_global_get_popwin(g)) + && owl_global_get_viewwin(g)) { + owl_popwin_refresh(owl_global_get_popwin(g)); + owl_viewwin_redisplay(owl_global_get_viewwin(g), 0); + } + + owl_function_debugmsg("New size is %i lines, %i cols.", size.ws_row, size.ws_col); + owl_function_makemsg(""); + g->resizepending=0; +} + +/* debug */ + +int owl_global_is_debug_fast(owl_global *g) { + if (g->debug) return(1); + return(0); +} + +/* starttime */ + +time_t owl_global_get_starttime(owl_global *g) { + return(g->starttime); +} + +time_t owl_global_get_runtime(owl_global *g) { + return(time(NULL)-g->starttime); +} + +char *owl_global_get_runtime_string(owl_global *g) { + time_t diff; + + diff=time(NULL)-owl_global_get_starttime(g); + + /* print something nicer later */ + return(owl_sprintf("%i seconds", (int) diff)); +} + +char *owl_global_get_hostname(owl_global *g) { + if (g->thishost) return(g->thishost); + return(""); +} + +/* userclue */ + +void owl_global_set_userclue(owl_global *g, int clue) { + g->userclue=clue; +} + +void owl_global_add_userclue(owl_global *g, int clue) { + g->userclue|=clue; +} + +int owl_global_get_userclue(owl_global *g) { + return(g->userclue); +} + +int owl_global_is_userclue(owl_global *g, int clue) { + if (g->userclue & clue) return(1); + return(0); +} + +/* viewwin */ + +owl_viewwin *owl_global_get_viewwin(owl_global *g) { + return(&(g->vw)); +} + + +/* vert offset */ + +int owl_global_get_curmsg_vert_offset(owl_global *g) { + return(g->curmsg_vert_offset); +} + +void owl_global_set_curmsg_vert_offset(owl_global *g, int i) { + g->curmsg_vert_offset=i; +} + +/* startup args */ + +void owl_global_set_startupargs(owl_global *g, int argc, char **argv) { + int i, len; + + if (g->startupargs) owl_free(g->startupargs); + + len=0; + for (i=0; i<argc; i++) { + len+=strlen(argv[i])+5; + } + g->startupargs=owl_malloc(len+5); + + strcpy(g->startupargs, ""); + for (i=0; i<argc; i++) { + sprintf(g->startupargs + strlen(g->startupargs), "%s ", argv[i]); + } + g->startupargs[strlen(g->startupargs)-1]='\0'; +} + +char *owl_global_get_startupargs(owl_global *g) { + if (g->startupargs) return(g->startupargs); + return(""); +} + +/* history */ + +owl_history *owl_global_get_msg_history(owl_global *g) { + return(&(g->msghist)); +} + +owl_history *owl_global_get_cmd_history(owl_global *g) { + return(&(g->cmdhist)); +} + +/* filterlist */ + +owl_list *owl_global_get_filterlist(owl_global *g) { + return(&(g->filterlist)); +} + +owl_filter *owl_global_get_filter(owl_global *g, char *name) { + int i, j; + owl_filter *f; + + j=owl_list_get_size(&(g->filterlist)); + for (i=0; i<j; i++) { + f=owl_list_get_element(&(g->filterlist), i); + if (!strcmp(name, owl_filter_get_name(f))) { + return(f); + } + } + return(NULL); +} + +void owl_global_add_filter(owl_global *g, owl_filter *f) { + owl_list_append_element(&(g->filterlist), f); +} + +void owl_global_remove_filter(owl_global *g, char *name) { + int i, j; + owl_filter *f; + + j=owl_list_get_size(&(g->filterlist)); + for (i=0; i<j; i++) { + f=owl_list_get_element(&(g->filterlist), i); + if (!strcmp(name, owl_filter_get_name(f))) { + owl_filter_free(f); + owl_list_remove_element(&(g->filterlist), i); + break; + } + } +} + +/* nextmsgid */ + +int owl_global_get_nextmsgid(owl_global *g) { + return(g->nextmsgid++); +} + +/* current view */ + +owl_view *owl_global_get_current_view(owl_global *g) { + return(&(g->current_view)); +} + +owl_filterelement *owl_global_get_filterelement_true(owl_global *g) { + return(&(g->fe_true)); +} + +owl_filterelement *owl_global_get_filterelement_false(owl_global *g) { + return(&(g->fe_false)); +} + +owl_filterelement *owl_global_get_filterelement_null(owl_global *g) { + return(&(g->fe_null)); +} + +/* has colors */ + +int owl_global_get_hascolors(owl_global *g) { + if (g->hascolors) return(1); + return(0); +} + +/* color pairs */ + +int owl_global_get_colorpairs(owl_global *g) { + return(g->colorpairs); +} + +/* puntlist */ + +owl_list *owl_global_get_puntlist(owl_global *g) { + return(&(g->puntlist)); +} + +int owl_global_message_is_puntable(owl_global *g, owl_message *m) { + owl_list *pl; + int i, j; + + pl=owl_global_get_puntlist(g); + j=owl_list_get_size(pl); + for (i=0; i<j; i++) { + if (owl_filter_message_match(owl_list_get_element(pl, i), m)) return(1); + } + return(0); +} + +int owl_global_should_followlast(owl_global *g) { + owl_view *v; + + if (!owl_global_is__followlast(g)) return(0); + + v=owl_global_get_current_view(g); + + if (owl_global_get_curmsg(g)==owl_view_get_size(v)-1) return(1); + return(0); +} + +int owl_global_is_search_active(owl_global *g) { + if (g->searchactive) return(1); + return(0); +} + +void owl_global_set_search_active(owl_global *g, char *string) { + g->searchactive=1; + if (g->searchstring != NULL) owl_free(g->searchstring); + g->searchstring=owl_strdup(string); +} + +void owl_global_set_search_inactive(owl_global *g) { + g->searchactive=0; +} + +char *owl_global_get_search_string(owl_global *g) { + if (g->searchstring==NULL) return(""); + return(g->searchstring); +} + +void owl_global_set_newmsgproc_pid(owl_global *g, int i) { + g->newmsgproc_pid=i; +} + +int owl_global_get_newmsgproc_pid(owl_global *g) { + return(g->newmsgproc_pid); +} + +void owl_global_add_to_malloced(owl_global *g, int i) { + g->malloced+=i; +} + +void owl_global_add_to_freed(owl_global *g, int i) { + g->freed+=1; +} + +int owl_global_get_malloced(owl_global *g) { + return(g->malloced); +} + +int owl_global_get_freed(owl_global *g) { + return(g->freed); +} + +int owl_global_get_meminuse(owl_global *g) { + return(g->malloced-g->freed); +} + +/* AIM stuff */ + +int owl_global_is_aimloggedin(owl_global *g) +{ + if (g->aim_loggedin) return(1); + return(0); +} + +char *owl_global_get_aim_screenname(owl_global *g) +{ + if (owl_global_is_aimloggedin(g)) { + return (g->aim_screenname); + } + return(""); +} + +void owl_global_set_aimloggedin(owl_global *g, char *screenname) +{ + g->aim_loggedin=1; + if (g->aim_screenname) owl_free(g->aim_screenname); + g->aim_screenname=owl_strdup(screenname); +} + +void owl_global_set_aimnologgedin(owl_global *g) +{ + g->aim_loggedin=0; +} + +int owl_global_is_doaimevents(owl_global *g) +{ + if (g->aim_doprocessing) return(1); + return(0); +} + +void owl_global_set_doaimevents(owl_global *g) +{ + g->aim_doprocessing=1; +} + +void owl_global_set_no_doaimevents(owl_global *g) +{ + g->aim_doprocessing=0; +} + +aim_session_t *owl_global_get_aimsess(owl_global *g) +{ + return(&(g->aimsess)); +} + +aim_conn_t *owl_global_get_bosconn(owl_global *g) +{ + return(&(g->bosconn)); +} + +void owl_global_set_bossconn(owl_global *g, aim_conn_t *conn) +{ + g->bosconn=*conn; +} + +int owl_global_is_aimnop_time(owl_global *g) +{ + if (owl_timer_is_expired(&(g->aim_noop_timer))) return(1); + return(0); +} + +void owl_global_aimnop_sent(owl_global *g) +{ + owl_timer_reset(&(g->aim_noop_timer)); +} + +owl_timer *owl_global_get_aim_login_timer(owl_global *g) +{ + return(&(g->aim_ignorelogin_timer)); +} + +/* message queue */ + +void owl_global_messagequeue_addmsg(owl_global *g, owl_message *m) +{ + owl_list_append_element(&(g->messagequeue), m); +} + +/* pop off the first message and return it. Return NULL if the queue + * is empty. The caller should free the message after using it, if + * necessary. + */ +owl_message *owl_global_messageuque_popmsg(owl_global *g) +{ + owl_message *out; + + if (owl_list_get_size(&(g->messagequeue))==0) return(NULL); + out=owl_list_get_element(&(g->messagequeue), 0); + owl_list_remove_element(&(g->messagequeue), 0); + return(out); +} + +int owl_global_messagequeue_pending(owl_global *g) +{ + if (owl_list_get_size(&(g->messagequeue))==0) return(0); + return(1); +} + +owl_buddylist *owl_global_get_buddylist(owl_global *g) +{ + return(&(g->buddylist)); +} + +/* style */ + +/* Return the style with name 'name'. If it does not exist return + * NULL */ +owl_style *owl_global_get_style_by_name(owl_global *g, char *name) +{ + return owl_dict_find_element(&(g->styledict), name); +} + +/* creates a list and fills it in with keys. duplicates the keys, + * so they will need to be freed by the caller. */ +int owl_global_get_style_names(owl_global *g, owl_list *l) { + return owl_dict_get_keys(&(g->styledict), l); +} + +void owl_global_add_style(owl_global *g, owl_style *s) +{ + owl_dict_insert_element(&(g->styledict), owl_style_get_name(s), + s, (void(*)(void*))owl_style_free); +} + +char *owl_global_get_response(owl_global *g) +{ + if (g->response==NULL) return(""); + return(g->response); +} + +void owl_global_set_response(owl_global *g, char *resp) +{ + if (g->response) owl_free(g->response); + g->response=owl_strdup(resp); +} + + +void owl_global_set_haveaim(owl_global *g) +{ + g->haveaim=1; +} + +int owl_global_is_haveaim(owl_global *g) +{ + if (g->haveaim) return(1); + return(0); +} + +void owl_global_set_havezephyr(owl_global *g) +{ + g->havezephyr=1; +} + +int owl_global_is_havezephyr(owl_global *g) +{ + if (g->havezephyr) return(1); + return(0); +} + +owl_timer *owl_global_get_aim_buddyinfo_timer(owl_global *g) +{ + return(&(g->aim_buddyinfo_timer)); +} + +owl_errqueue *owl_global_get_errqueue(owl_global *g) +{ + return(&(g->errqueue)); +} + +void owl_global_set_errsignal(owl_global *g, int signum, siginfo_t *siginfo) +{ + g->got_err_signal = signum; + if (siginfo) { + g->err_signal_info = *siginfo; + } else { + memset(&(g->err_signal_info), 0, sizeof(siginfo_t)); + } +} + +int owl_global_get_errsignal_and_clear(owl_global *g, siginfo_t *siginfo) +{ + int signum; + if (siginfo && g->got_err_signal) { + *siginfo = g->err_signal_info; + } + signum = g->got_err_signal; + g->got_err_signal = 0; + return signum; +} + +owl_timer *owl_global_get_zephyr_buddycheck_timer(owl_global *g) +{ + return(&(g->zephyr_buddycheck_timer)); +} + +owl_zbuddylist *owl_global_get_zephyr_buddylist(owl_global *g) +{ + return(&(g->zbuddies)); +} + +struct termios *owl_global_get_startup_tio(owl_global *g) +{ + return(&(g->startup_tio)); +} + +owl_list *owl_global_get_dispatchlist(owl_global *g) +{ + return &(g->dispatchlist); +} @@ -0,0 +1,167 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" +#include <string.h> + +static const char fileIdent[] = "$Id: help.c,v 1.12 2009/03/29 12:38:21 kretch Exp $"; + +void owl_help() +{ + owl_fmtext fm; + char *varname; + owl_list varnames; + int i, numvarnames; + + owl_fmtext_init_null(&fm); + owl_fmtext_append_bold + (&fm, + "OWL HELP\n\n"); + + owl_fmtext_append_normal + (&fm, + " For help on a specific command use 'help <command>'\n" + " For information on advanced keys, use 'show keymaps'.\n" + " For information on advanced commands, use 'show commands'.\n" + " For information on variables, use 'show variables'.\n\n"); + + owl_fmtext_append_bold + (&fm, + " Basic Keys:\n" + ); + owl_fmtext_append_normal + (&fm, + " n Move to next non-deleted message\n" + " p Move to previous non-deleted message\n" + " C-n , down Move to next message\n" + " C-p , up Move to previous message\n" + " < , > Move to first, last message\n" + " right , left Scroll screen left or right\n" + " C-v Page down\n" + " M-v Page up\n" + " i Print more information about a message\n" + " P Move to the next personal message\n" + " M-P Move to the preivous personal message\n" + "\n" + " d Mark message for deletion\n" + " u Undelete a message marked for deletion\n" + " x Expunge deleted messages\n" + " X Expunge deleted messages and switch to home view\n" + " T Mark all 'trash' messages for deletion\n" + " M-D Mark all messages in current view for deletion\n" + " M-u Unmark all messages in the current view for deletion\n" + "\n" + " z Start a zwrite command\n" + " a Start an aimwrite command\n" + " r Reply to the current message\n" + " R Reply to sender\n" + " C-r Reply but allow editing of reply line\n" + "\n" + " M-n View zephyrs in selected conversation\n" + " M-N View zephyrs in selected converstaion of instance\n" + " V Change to back to home view ('all' by default)\n" + " v Start a view command\n" + " ! Invert the current view\n" + "\n" + " l Print a zephyr/AIM buddy listing\n" + " A Toggle zaway\n" + " o Toggle one-line display mode\n" + " w Open a URL in the current message\n" + " C-l Refresh the screen\n" + " C-z Suspend Owl\n" + " h Print this help message\n" + " : , M-x Enter command mode\n" + "\n" + " / Foward search\n" + " ? Reverse search\n" + "\n\n" + ); + owl_fmtext_append_bold + (&fm, + " Basic Commands:\n" + ); + owl_fmtext_append_normal + (&fm, + " quit, exit Exit owl\n" + " help Get help about commands\n" + " show Show information about owl (see detailed help)\n" + "\n" + " zwrite Send a zephyr\n" + " aimlogin Login to AIM\n" + " aimwrite Send an AIM message\n" + "\n" + " addbuddy Add a zephyr or AIM buddy\n" + " zaway Turn zaway on or off, or set the message\n" + " zlocate Locate a user\n" + " subscribe Subscribe to a zephyr class or instance\n" + " unsubscribe Unsubscribe to a zephyr class or instance\n" + " blist Print a list of zephyr and AIM buddies logged in\n" + " search Search for a text string\n" + "\n" + " set Set a variable (see list below)\n" + " print Print a variable's value (variables listed below)\n" + " startup Set a command to be run at every Owl startup\n" + " unstartup Remove a command to be run at every Owl startup\n" + "\n" + " getsubs Print a list of current subscriptions\n" + " unsuball Unsubscribe from all zephyr classes\n" + " load-subs Load zephyr subscriptions from a file\n" + " zpunt Supress messages from a zephyr triplet\n" + " zlog Send a login or logout notification\n" + " zlist Print a list of zephyr buddies logged in\n" + " alist Print a list of AIM buddies logged in\n" + " info Print detailed information about the current message\n" + " filter Create a message filter\n" + " view View messages matching a filter\n" + " viewuser View messages to or from a particular user\n" + " viewclass View messages to a particular class\n" + " expunge Expunge messages marked for deletion\n" + " bindkey Create a new key binding\n" + " alias Create a command alias\n" + " dump\n" + "\n" + " about Print information about owl\n" + " status Print status information about the running owl\n" + " version Print the version number of owl\n" + "\n"); + + /* help for variables */ + owl_fmtext_append_bold(&fm, + "Variables:\n"); + owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames); + numvarnames = owl_list_get_size(&varnames); + for (i=0; i<numvarnames; i++) { + varname = owl_list_get_element(&varnames, i); + if (varname && varname[0]!='_') { + owl_variable_describe(owl_global_get_vardict(&g), varname, &fm); + } + } + owl_variable_dict_namelist_free(&varnames); + + owl_fmtext_append_normal(&fm, "\n"); + + owl_function_popless_fmtext(&fm); + + owl_fmtext_free(&fm); +} diff --git a/history.c b/history.c new file mode 100644 index 0000000..fcb47de --- /dev/null +++ b/history.c @@ -0,0 +1,118 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id: history.c,v 1.10 2009/03/29 12:38:21 kretch Exp $"; + +void owl_history_init(owl_history *h) +{ + owl_list_create(&(h->hist)); + h->cur=0; /* current position in history */ + h->touched=0; /* whether we've gone into history */ + h->partial=0; /* is the 0th element is partially composed? */ + h->repeats=1; /* by default we'll allow repeat entries */ +} + +void owl_history_set_norepeats(owl_history *h) +{ + h->repeats=0; +} + +char *owl_history_get_prev(owl_history *h) +{ + + if (!h) return NULL; + h->touched=1; + + if (owl_list_get_size(&(h->hist))==0) return(NULL); + + if (h->cur == owl_list_get_size(&(h->hist))-1) { + return(NULL); + } + + h->cur++; + + return(owl_list_get_element(&(h->hist), h->cur)); +} + +char *owl_history_get_next(owl_history *h) +{ + if (!h) return NULL; + if (owl_list_get_size(&(h->hist))==0) return(NULL); + if (h->cur==0) { + return(NULL); + } + + h->cur--; + return(owl_list_get_element(&(h->hist), h->cur)); +} + +void owl_history_store(owl_history *h, char *line) +{ + int size; + + if (!h) return; + size=owl_list_get_size(&(h->hist)); + + /* if partial is set, remove the first entry first */ + if (h->partial) { + owl_list_remove_element(&(h->hist), 0); + } + + /* if repeats are disallowed, check if the line is the same as the last */ + if (owl_list_get_size(&(h->hist))>0) { + if (!strcmp(line, owl_list_get_element(&(h->hist), 0))) return; + } + + /* if we've reached the max history size, pop off the last element */ + if (size>OWL_HISTORYSIZE) { + owl_free(owl_list_get_element(&(h->hist), size-1)); + owl_list_remove_element(&(h->hist), size-1); + } + + /* add the new line */ + owl_list_prepend_element(&(h->hist), owl_strdup(line)); +} + +void owl_history_set_partial(owl_history *h) +{ + if (!h) return; + h->partial=1; +} + +void owl_history_reset(owl_history *h) +{ + if (!h) return; + h->cur=0; + h->touched=0; + h->partial=0; +} + +int owl_history_is_touched(owl_history *h) +{ + if (!h) return(0); + if (h->touched) return(1); + return(0); +} diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/install.sh diff --git a/keybinding.c b/keybinding.c new file mode 100644 index 0000000..ea6b73d --- /dev/null +++ b/keybinding.c @@ -0,0 +1,163 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <ctype.h> +#include <string.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: keybinding.c,v 1.6 2009/03/29 12:38:21 kretch Exp $"; + +/* + * TODO: Idea for allowing functions to be user-specified --- + * Have function have a context bitmask that says where it + * can be used, and have keymaps also have one, and compare + * the two when setting. + * + */ + +/* sets up a new keybinding for a command */ +int owl_keybinding_init(owl_keybinding *kb, char *keyseq, char *command, void (*function_fn)(void), char *desc) +{ + char **ktokens; + int nktokens, i; + + owl_function_debugmsg("owl_keybinding_init: creating binding for <%s> with desc: <%s>", keyseq, desc); + if (command && !function_fn) { + kb->type = OWL_KEYBINDING_COMMAND; + } else if (!command && function_fn) { + kb->type = OWL_KEYBINDING_FUNCTION; + } else { + return(-1); + } + + ktokens = atokenize(keyseq, " ", &nktokens); + if (!ktokens) return(-1); + if (nktokens > OWL_KEYMAP_MAXSTACK) { + atokenize_free(ktokens, nktokens); + return(-1); + } + kb->j = owl_malloc((nktokens+1)*sizeof(int)); + for (i=0; i<nktokens; i++) { + kb->j[i] = owl_keypress_fromstring(ktokens[i]); + if (kb->j[i] == ERR) { + atokenize_free(ktokens, nktokens); + owl_free(kb->j); + return(-1); + } + } + kb->j[i] = 0; + + atokenize_free(ktokens, nktokens); + + if (command) kb->command = owl_strdup(command); + kb->function_fn = function_fn; + if (desc) kb->desc = owl_strdup(desc); + else kb->desc = NULL; + return(0); +} + +/* Releases data associated with a keybinding */ +void owl_keybinding_free(owl_keybinding *kb) +{ + if (kb->j) owl_free(kb->j); + if (kb->desc) owl_free(kb->desc); + if (kb->command) owl_free(kb->command); +} + +/* Releases data associated with a keybinding, and the kb itself */ +void owl_keybinding_free_all(owl_keybinding *kb) +{ + owl_keybinding_free(kb); + owl_free(kb); +} + +/* executes a keybinding */ +void owl_keybinding_execute(owl_keybinding *kb, int j) +{ + if (kb->type == OWL_KEYBINDING_COMMAND && kb->command) { + owl_function_command_norv(kb->command); + } else if (kb->type == OWL_KEYBINDING_FUNCTION && kb->function_fn) { + kb->function_fn(); + } +} + +/* returns 0 on success */ +int owl_keybinding_stack_tostring(int *j, char *buff, int bufflen) +{ + char *pos = buff; + int rem = bufflen; + int i, n; + + for (i=0; j[i]; i++) { + owl_keypress_tostring(j[i], 0, pos, rem-1); + if (j[i+1]) strcat(pos, " "); + n = strlen(pos); + pos += n; + rem -= n; + } + return 0; +} + +/* returns 0 on success */ +int owl_keybinding_tostring(owl_keybinding *kb, char *buff, int bufflen) +{ + return owl_keybinding_stack_tostring(kb->j, buff, bufflen); +} + +char *owl_keybinding_get_desc(owl_keybinding *kb) +{ + return kb->desc; +} + +/* returns 0 on no match, 1 on subset match, and 2 on complete match */ +int owl_keybinding_match(owl_keybinding *kb, int *kpstack) +{ + int *kbstack = kb->j; + + while (*kbstack && *kpstack) { + if (*kbstack != *kpstack) { + return 0; + } + kbstack++; kpstack++; + } + if (!*kpstack && !*kbstack) { + return 2; + } else if (!*kpstack) { + return 1; + } else { + return 0; + } +} + +/* returns 1 if keypress sequence is the same */ +int owl_keybinding_equal(owl_keybinding *kb1, owl_keybinding *kb2) +{ + int *j1 = kb1->j; + int *j2 = kb2->j; + while (*j1 && *j2) { + if (*(j1++) != *(j2++)) return(0); + } + if (*j1 != *j2) return(0); + return(1); +} diff --git a/keymap.c b/keymap.c new file mode 100644 index 0000000..9e71ab8 --- /dev/null +++ b/keymap.c @@ -0,0 +1,319 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <string.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: keymap.c,v 1.8 2009/03/29 12:38:21 kretch Exp $"; + +/* returns 0 on success */ +int owl_keymap_init(owl_keymap *km, char *name, char *desc, void (*default_fn)(int), void (*prealways_fn)(int), void (*postalways_fn)(int)) +{ + if (!name || !desc) return(-1); + if ((km->name = owl_strdup(name)) == NULL) return(-1); + if ((km->desc = owl_strdup(desc)) == NULL) return(-1); + if (0 != owl_list_create(&km->bindings)) return(-1); + km->submap = NULL; + km->default_fn = default_fn; + km->prealways_fn = prealways_fn; + km->postalways_fn = postalways_fn; + return(0); +} + +/* note that this will free the memory for the bindings! */ +void owl_keymap_free(owl_keymap *km) +{ + owl_free(km->name); + owl_free(km->desc); + owl_list_free_all(&km->bindings, (void(*)(void*))owl_keybinding_free_all); +} + +void owl_keymap_set_submap(owl_keymap *km, owl_keymap *submap) +{ + km->submap = submap; +} + +/* creates and adds a key binding */ +int owl_keymap_create_binding(owl_keymap *km, char *keyseq, char *command, void (*function_fn)(void), char *desc) +{ + owl_keybinding *kb, *curkb; + int i; + + if ((kb = owl_malloc(sizeof(owl_keybinding))) == NULL) return(-1); + if (0 != owl_keybinding_init(kb, keyseq, command, function_fn, desc)) { + owl_free(kb); + return(-1); + } + /* see if another matching binding, and if so remove it. + * otherwise just add this one. + */ + for (i = owl_list_get_size(&km->bindings)-1; i>=0; i--) { + curkb = (owl_keybinding*)owl_list_get_element(&km->bindings, i); + if (owl_keybinding_equal(curkb, kb)) { + owl_list_remove_element(&km->bindings, i); + owl_keybinding_free_all(curkb); + } + } + return owl_list_append_element(&km->bindings, kb); +} + +/* returns a summary line describing this keymap. the caller must free. */ +char *owl_keymap_summary(owl_keymap *km) +{ + char *s; + int slen; + if (!km || !km->name || !km->desc) return NULL; + slen = strlen(km->name)+strlen(km->desc)+20; + s = owl_malloc(slen); + snprintf(s, slen-1, "%-15s - %s", km->name, km->desc); + return s; +} + +/* Appends details about the keymap to fm */ +void owl_keymap_get_details(owl_keymap *km, owl_fmtext *fm) +{ + int i, nbindings; + owl_keybinding *kb; + + owl_fmtext_append_bold(fm, "KEYMAP - "); + owl_fmtext_append_bold(fm, km->name); + owl_fmtext_append_normal(fm, "\n"); + if (km->desc) { + owl_fmtext_append_normal(fm, OWL_TABSTR "Purpose: "); + owl_fmtext_append_normal(fm, km->desc); + owl_fmtext_append_normal(fm, "\n"); + } + if (km->submap) { + owl_fmtext_append_normal(fm, OWL_TABSTR "Has submap: "); + owl_fmtext_append_normal(fm, km->submap->name); + owl_fmtext_append_normal(fm, "\n"); + } + owl_fmtext_append_normal(fm, "\n"); + if (km->default_fn) { + owl_fmtext_append_normal(fm, OWL_TABSTR + "Has a default keypress handler (default_fn).\n"); + } + if (km->prealways_fn) { + owl_fmtext_append_normal(fm, OWL_TABSTR + "Executes a function (prealways_fn) on every keypress.\n"); + } + if (km->postalways_fn) { + owl_fmtext_append_normal(fm, OWL_TABSTR + "Executes a function (postalways_fn) after handling every keypress.\n"); + } + + owl_fmtext_append_bold(fm, "\nKey bindings:\n\n"); + nbindings = owl_list_get_size(&km->bindings); + for (i=0; i<nbindings; i++) { + char buff[100]; + owl_cmd *cmd; + char *tmpdesc, *desc = ""; + + kb = owl_list_get_element(&km->bindings, i); + owl_keybinding_tostring(kb, buff, 100); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, buff); + owl_fmtext_append_spaces(fm, 11-strlen(buff)); + owl_fmtext_append_normal(fm, " - "); + if (kb->desc && *kb->desc) { + desc = kb->desc; + } else if ((cmd=owl_function_get_cmd(kb->command)) + && (tmpdesc = owl_cmd_get_summary(cmd))) { + desc = tmpdesc; + } + owl_fmtext_append_normal(fm, desc); + if (kb->command && *(kb->command)) { + owl_fmtext_append_normal(fm, " ["); + owl_fmtext_append_normal(fm, kb->command); + owl_fmtext_append_normal(fm, "]"); + } + owl_fmtext_append_normal(fm, "\n"); + } +} + + + +/***********************************************************************/ +/***************************** KEYHANDLER ******************************/ +/***********************************************************************/ + +/* NOTE: keyhandler has private access to the internals of keymap */ + +int owl_keyhandler_init(owl_keyhandler *kh) +{ + if (0 != owl_dict_create(&kh->keymaps)) return(-1); + kh->active = NULL; + owl_keyhandler_reset(kh); + return(0); +} + +/* adds a new keymap */ +void owl_keyhandler_add_keymap(owl_keyhandler *kh, owl_keymap *km) +{ + owl_dict_insert_element(&kh->keymaps, km->name, km, NULL); +} + +owl_keymap *owl_keyhandler_create_and_add_keymap(owl_keyhandler *kh, char *name, char *desc, void (*default_fn)(int), void (*prealways_fn)(int), void (*postalways_fn)(int)) +{ + owl_keymap *km; + km = (owl_keymap*)owl_malloc(sizeof(owl_keymap)); + if (!km) return NULL; + owl_keymap_init(km, name, desc, default_fn, prealways_fn, postalways_fn); + owl_keyhandler_add_keymap(kh, km); + return km; +} + +/* resets state and clears out key stack */ +void owl_keyhandler_reset(owl_keyhandler *kh) +{ + kh->in_esc = 0; + memset(kh->kpstack, 0, (OWL_KEYMAP_MAXSTACK+1)*sizeof(int)); + kh->kpstackpos = -1; +} + +owl_keymap *owl_keyhandler_get_keymap(owl_keyhandler *kh, char *mapname) +{ + return (owl_keymap*)owl_dict_find_element(&kh->keymaps, mapname); +} + +/* free the list with owl_cmddict_namelist_free */ +void owl_keyhandler_get_keymap_names(owl_keyhandler *kh, owl_list *l) +{ + owl_dict_get_keys(&kh->keymaps, l); +} + +void owl_keyhandler_keymap_namelist_free(owl_list *l) +{ + owl_list_free_all(l, owl_free); +} + + + +/* sets the active keymap, which will also reset any key state. + * returns the new keymap, or NULL on failure. */ +owl_keymap *owl_keyhandler_activate(owl_keyhandler *kh, char *mapname) +{ + owl_keymap *km; + if (kh->active && !strcmp(mapname, kh->active->name)) return(kh->active); + km = (owl_keymap*)owl_dict_find_element(&kh->keymaps, mapname); + if (!km) return(NULL); + owl_keyhandler_reset(kh); + kh->active = km; + return(km); +} + +/* processes a keypress. returns 0 if the keypress was handled, + * 1 if not handled, -1 on error, and -2 if j==ERR. */ +int owl_keyhandler_process(owl_keyhandler *kh, int j) +{ + owl_keymap *km; + owl_keybinding *kb; + int i, match; + + if (!kh->active) { + owl_function_makemsg("No active keymap!!!"); + return(-1); + } + + /* temporarily disallow C-`/C-SPACE until we fix associated bugs */ + if (j==ERR || j==0) { + return(-1); + } + + /* + owl_function_debugmsg("processkey: got key %d, active keymap %s, stack at %d", + j, kh->active->name, kh->kpstackpos); + */ + + /* deal with ESC prefixing */ + if (!kh->in_esc && j==27) { + kh->in_esc = 1; + return(0); + } + if (kh->in_esc) { + j = OWL_META(j); + kh->in_esc = 0; + } + + kh->kpstack[++(kh->kpstackpos)] = j; + if (kh->kpstackpos >= OWL_KEYMAP_MAXSTACK) { + owl_keyhandler_reset(kh); + owl_function_makemsg("Too many prefix keys pressed..."); + return(-1); + } + + /* deal with the always_fn for the map and submaps */ + for (km=kh->active; km; km=km->submap) { + if (km->prealways_fn) { + km->prealways_fn(j); + } + } + + /* search for a match. goes through active keymap and then + * through submaps... TODO: clean this up so we can pull + * keyhandler and keymap apart. */ + for (km=kh->active; km; km=km->submap) { + for (i=owl_list_get_size(&km->bindings)-1; i>=0; i--) { + kb = (owl_keybinding*)owl_list_get_element(&km->bindings, i); + match = owl_keybinding_match(kb, kh->kpstack); + if (match == 1) { /* subset match */ + + /* owl_function_debugmsg("processkey: found subset match in %s", km->name); */ + return(0); + } else if (match == 2) { /* exact match */ + /* owl_function_debugmsg("processkey: found exact match in %s", km->name); */ + owl_keybinding_execute(kb, j); + owl_keyhandler_reset(kh); + if (km->postalways_fn) { + km->postalways_fn(j); + } + return(0); + } + } + } + + /* see if a default action exists for the active keymap */ + if (kh->active->default_fn && kh->kpstackpos<1) { + /*owl_function_debugmsg("processkey: executing default_fn");*/ + + kh->active->default_fn(j); + owl_keyhandler_reset(kh); + if (kh->active->postalways_fn) { + kh->active->postalways_fn(j); + } + return(0); + } + + owl_keyhandler_invalidkey(kh); + /* unable to handle */ + return(1); +} + +void owl_keyhandler_invalidkey(owl_keyhandler *kh) +{ + char kbbuff[500]; + owl_keybinding_stack_tostring(kh->kpstack, kbbuff, 500); + owl_function_makemsg("'%s' is not a valid key in this context.", kbbuff); + owl_keyhandler_reset(kh); +} diff --git a/keypress.c b/keypress.c new file mode 100644 index 0000000..00fd3ce --- /dev/null +++ b/keypress.c @@ -0,0 +1,235 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <ctype.h> +#include <string.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: keypress.c,v 1.8 2009/03/29 12:38:21 kretch Exp $"; + +static struct _owl_keypress_specialmap { + int kj; + char *ks; +} specialmap[] = { +#ifdef KEY_CODE_YES + { KEY_CODE_YES, "CODE_YES" }, +#endif + { KEY_MIN, "MIN" }, + { KEY_BREAK, "BREAK" }, + { KEY_DOWN, "DOWN" }, + { KEY_UP, "UP" }, + { KEY_LEFT, "LEFT" }, + { KEY_RIGHT, "RIGHT" }, + { KEY_HOME, "HOME" }, + { KEY_BACKSPACE, "BACKSPACE" }, + { KEY_F0, "F0" }, + { KEY_F(1), "F1" }, + { KEY_F(2), "F2" }, + { KEY_F(3), "F3" }, + { KEY_F(4), "F4" }, + { KEY_F(5), "F5" }, + { KEY_F(6), "F6" }, + { KEY_F(7), "F7" }, + { KEY_F(8), "F8" }, + { KEY_F(9), "F9" }, + { KEY_F(10), "F10" }, + { KEY_F(11), "F11" }, + { KEY_F(12), "F12" }, + { KEY_DL, "DL" }, + { KEY_IL, "IL" }, + { KEY_DC, "DC" }, + { KEY_IC, "IC" }, + { KEY_EIC, "EIC" }, + { KEY_CLEAR, "CLEAR" }, + { KEY_EOS, "EOS" }, + { KEY_EOL, "EOL" }, + { KEY_SF, "SF" }, + { KEY_SR, "SR" }, + { KEY_NPAGE, "NPAGE" }, + { KEY_PPAGE, "PPAGE" }, + { KEY_STAB, "STAB" }, + { KEY_CTAB, "CTAB" }, + { KEY_CATAB, "CATAB" }, + { KEY_ENTER, "ENTER" }, + { KEY_SRESET, "SRESET" }, + { KEY_RESET, "RESET" }, + { KEY_PRINT, "PRINT" }, + { KEY_LL, "LL" }, + { KEY_A1, "A1" }, + { KEY_A3, "A3" }, + { KEY_B2, "B2" }, + { KEY_C1, "C1" }, + { KEY_C3, "C3" }, + { KEY_BTAB, "BTAB" }, + { KEY_BEG, "BEG" }, + { KEY_CANCEL, "CANCEL" }, + { KEY_CLOSE, "CLOSE" }, + { KEY_COMMAND, "COMMAND" }, + { KEY_COPY, "COPY" }, + { KEY_CREATE, "CREATE" }, + { KEY_END, "END" }, + { KEY_EXIT, "EXIT" }, + { KEY_FIND, "FIND" }, + { KEY_HELP, "HELP" }, + { KEY_MARK, "MARK" }, + { KEY_MESSAGE, "MESSAGE" }, + { KEY_MOVE, "MOVE" }, + { KEY_NEXT, "NEXT" }, + { KEY_OPEN, "OPEN" }, + { KEY_OPTIONS, "OPTIONS" }, + { KEY_PREVIOUS, "PREVIOUS" }, + { KEY_REDO, "REDO" }, + { KEY_REFERENCE, "REFERENCE" }, + { KEY_REFRESH, "REFRESH" }, + { KEY_REPLACE, "REPLACE" }, + { KEY_RESTART, "RESTART" }, + { KEY_RESUME, "RESUME" }, + { KEY_SAVE, "SAVE" }, + { KEY_SBEG, "SBEG" }, + { KEY_SCANCEL, "SCANCEL" }, + { KEY_SCOMMAND, "SCOMMAND" }, + { KEY_SCOPY, "SCOPY" }, + { KEY_SCREATE, "SCREATE" }, + { KEY_SDC, "SDC" }, + { KEY_SDL, "SDL" }, + { KEY_SELECT, "SELECT" }, + { KEY_SEND, "SEND" }, + { KEY_SEOL, "SEOL" }, + { KEY_SEXIT, "SEXIT" }, + { KEY_SFIND, "SFIND" }, + { KEY_SHELP, "SHELP" }, + { KEY_SHOME, "SHOME" }, + { KEY_SIC, "SIC" }, + { KEY_SLEFT, "SLEFT" }, + { KEY_SMESSAGE, "SMESSAGE" }, + { KEY_SMOVE, "SMOVE" }, + { KEY_SNEXT, "SNEXT" }, + { KEY_SOPTIONS, "SOPTIONS" }, + { KEY_SPREVIOUS, "SPREVIOUS" }, + { KEY_SPRINT, "SPRINT" }, + { KEY_SREDO, "SREDO" }, + { KEY_SREPLACE, "SREPLACE" }, + { KEY_SRIGHT, "SRIGHT" }, + { KEY_SRSUME, "SRSUME" }, + { KEY_SSAVE, "SSAVE" }, + { KEY_SSUSPEND, "SSUSPEND" }, + { KEY_SUNDO, "SUNDO" }, + { KEY_SUSPEND, "SUSPEND" }, + { KEY_UNDO, "UNDO" }, + { KEY_MOUSE, "MOUSE" }, +#ifdef KEY_RESIZE + { KEY_RESIZE, "RESIZE" }, +#endif + { KEY_MAX, "MAX" }, + { ' ', "SPACE" }, + { 27, "ESCAPE" }, + { 127, "DELETE" }, + { '\r', "CR" }, + { '\n', "LF" }, + { 0, NULL } +}; + +#define OWL_CTRL(key) ((key)&037) +/* OWL_META is definied in owl.h */ + +/* returns 0 on success */ +int owl_keypress_tostring(int j, int esc, char *buff, int bufflen) +{ + char kb[64], kb2[2]; + struct _owl_keypress_specialmap *sm; + + *kb = '\0'; + for (sm = specialmap; sm->kj!=0; sm++) { + if (j == OWL_META(sm->kj) || (esc && j == sm->kj)) { + strcat(kb, "M-"); + strcat(kb, sm->ks); + break; + } else if (j == sm->kj) { + strcat(kb, sm->ks); + break; + } + } + if (!*kb) { + if (j&OWL_META(0)) { + strcat(kb, "M-"); + j &= ~OWL_META(0); + } + if ((OWL_CTRL(j) == j)) { + strcat(kb, "C-"); + j |= 0x40; + if (isupper(j)) j = tolower(j); + + } + if (isascii(j)) { + kb2[0] = j; + kb2[1] = 0; + strcat(kb, kb2); + } + } + if (!*kb) { + /* not a valid key */ + strncpy(buff, "INVALID", bufflen); + return(-1); + } + strncpy(buff, kb, bufflen); + return(0); +} + + +/* returns ERR on failure, else a keycode */ +int owl_keypress_fromstring(char *kb) +{ + struct _owl_keypress_specialmap *sm; + int ismeta=0, isctrl=0; + int j = ERR; + + while (*kb && kb[1] == '-' && (kb[0] == 'C' || kb[0] == 'M')) { + if ((kb[0] == 'C') && (kb[1] == '-')) { + isctrl = 1; + kb+=2; + } + if ((kb[0] == 'M') && (kb[1] == '-')) { + ismeta = 1; + kb+=2; + } + } + if (isascii(kb[0]) && !kb[1]) { + j = kb[0]; + } else { + for (sm = specialmap; sm->kj!=0; sm++) { + if (!strcmp(sm->ks, kb)) { + j = sm->kj; + } + } + } + if (j==ERR) return(ERR); + if (isctrl) { + j = OWL_CTRL(j); + } + if (ismeta) { + j = OWL_META(j); + } + return(j); +} + @@ -0,0 +1,351 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id: keys.c,v 1.26 2009/03/29 12:38:21 kretch Exp $"; + +#define BIND_CMD(kpress, command, desc) \ + owl_keymap_create_binding(km, kpress, command, NULL, desc); + +#define BIND_FNV(kpress, fn, desc) \ + owl_keymap_create_binding(km, kpress, NULL, fn, desc); + + +/* sets up the default keymaps */ +void owl_keys_setup_keymaps(owl_keyhandler *kh) { + owl_keymap *km, *km_global, *km_editwin, *km_mainwin, + *km_ew_multi, *km_ew_onel, *km_viewwin; + + + /****************************************************************/ + /*************************** GLOBAL *****************************/ + /****************************************************************/ + + km_global = km = owl_keyhandler_create_and_add_keymap(kh, "global", + "System-wide default key bindings", + owl_keys_default_invalid, NULL, NULL); + BIND_CMD("C-z", "suspend", "Suspend owl"); + + /****************************************************************/ + /***************************** EDIT *****************************/ + /****************************************************************/ + + km_editwin = km = owl_keyhandler_create_and_add_keymap(kh, "edit", + "Text editing and command window", + owl_keys_editwin_default, NULL, owl_keys_editwin_postalways); + owl_keymap_set_submap(km_editwin, km_global); + /* + BIND_CMD("F1", "help", ""); + BIND_CMD("HELP", "help", ""); + BIND_CMD("M-[ 2 8 ~", "help", ""); + */ + + BIND_CMD("C-c", "edit:cancel", ""); + BIND_CMD("C-g", "edit:cancel", ""); + + BIND_CMD("M-f", "edit:move-next-word", ""); + BIND_CMD("M-O 3 C", "edit:move-next-word", ""); + BIND_CMD("M-b", "edit:move-prev-word", ""); + BIND_CMD("M-O 3 D", "edit:move-prev-word", ""); + + BIND_CMD("LEFT", "edit:move-left", ""); + BIND_CMD("C-b", "edit:move-left", ""); + BIND_CMD("RIGHT", "edit:move-right", ""); + BIND_CMD("C-f", "edit:move-right", ""); + + BIND_CMD("M-<", "edit:move-to-buffer-start", ""); + BIND_CMD("HOME", "edit:move-to-buffer-start", ""); + BIND_CMD("M->", "edit:move-to-buffer-end", ""); + BIND_CMD("END", "edit:move-to-buffer-end", ""); + + BIND_CMD("C-a", "edit:move-to-line-start", ""); + BIND_CMD("C-e", "edit:move-to-line-end", ""); + + BIND_CMD("M-BACKSPACE", "edit:delete-prev-word", ""); + BIND_CMD("M-DELETE", "edit:delete-prev-word", ""); + BIND_CMD("M-d", "edit:delete-next-word", ""); + BIND_CMD("M-[ 3 ; 3 ~", "edit:delete-next-word", ""); + + BIND_CMD("C-h", "edit:delete-prev-char", ""); + BIND_CMD("BACKSPACE", "edit:delete-prev-char", ""); + BIND_CMD("DC", "edit:delete-prev-char", ""); + BIND_CMD("DELETE", "edit:delete-prev-char", ""); + + BIND_CMD("C-k", "edit:delete-to-line-end", ""); + + BIND_CMD("C-t", "edit:transpose-chars", ""); + + BIND_CMD("M-q", "edit:fill-paragraph", ""); + + BIND_CMD("C-l", "( edit:recenter ; redisplay )", ""); + + BIND_CMD("C-d", "edit:delete-next-char", ""); + + + /****************************************************************/ + /**************************** EDITMULTI *************************/ + /****************************************************************/ + + km_ew_multi = km = owl_keyhandler_create_and_add_keymap(kh, "editmulti", + "Multi-line text editing", + owl_keys_editwin_default, NULL, owl_keys_editwin_postalways); + owl_keymap_set_submap(km_ew_multi, km_editwin); + + BIND_CMD("UP", "editmulti:move-up-line", ""); + BIND_CMD("C-p", "editmulti:move-up-line", ""); + BIND_CMD("DOWN", "editmulti:move-down-line", ""); + BIND_CMD("C-n", "editmulti:move-down-line", ""); + + /* This would be nice, but interferes with C-c to cancel */ + /*BIND_CMD("C-c C-c", "editmulti:done", "sends the zephyr");*/ + + BIND_CMD("M-p", "edit:history-prev", ""); + BIND_CMD("M-n", "edit:history-next", ""); + + /* note that changing "disable-ctrl-d" to "on" will change this to + * edit:delete-next-char */ + BIND_CMD("C-d", "editmulti:done-or-delete", "sends the zephyr if at the end of the message"); + + + /****************************************************************/ + /**************************** EDITLINE **************************/ + /****************************************************************/ + + km_ew_onel = km = owl_keyhandler_create_and_add_keymap(kh, "editline", + "Single-line text editing", + owl_keys_editwin_default, NULL, owl_keys_editwin_postalways); + owl_keymap_set_submap(km_ew_onel, km_editwin); + + BIND_CMD("C-u", "edit:delete-all", "Clears the entire line"); + + BIND_CMD("UP", "edit:history-prev", ""); + BIND_CMD("C-p", "edit:history-prev", ""); + BIND_CMD("M-p", "edit:history-prev", ""); + + BIND_CMD("DOWN", "edit:history-next", ""); + BIND_CMD("C-n", "edit:history-next", ""); + BIND_CMD("M-n", "edit:history-next", ""); + + BIND_CMD("LF", "editline:done", "executes the command"); + BIND_CMD("CR", "editline:done", "executes the command"); + + + /****************************************************************/ + /**************************** EDITRESPONSE **********************/ + /****************************************************************/ + + km_ew_onel = km = owl_keyhandler_create_and_add_keymap(kh, "editresponse", + "Single-line response to question", + owl_keys_editwin_default, NULL, owl_keys_editwin_postalways); + owl_keymap_set_submap(km_ew_onel, km_editwin); + + BIND_CMD("C-u", "edit:delete-all", "Clears the entire line"); + + BIND_CMD("LF", "editresponse:done", "executes the command"); + BIND_CMD("CR", "editresponse:done", "executes the command"); + + + /****************************************************************/ + /**************************** POPLESS ***************************/ + /****************************************************************/ + + km_viewwin = km = owl_keyhandler_create_and_add_keymap(kh, "popless", + "Pop-up window (eg, help)", + owl_keys_default_invalid, NULL, owl_keys_popless_postalways); + owl_keymap_set_submap(km_viewwin, km_global); + + BIND_CMD("SPACE", "popless:scroll-down-page", ""); + BIND_CMD("NPAGE", "popless:scroll-down-page", ""); + BIND_CMD("M-n", "popless:scroll-down-page", ""); + + BIND_CMD("b", "popless:scroll-up-page", ""); + BIND_CMD("PPAGE", "popless:scroll-up-page", "") + BIND_CMD("M-p", "popless:scroll-up-page", ""); + + BIND_CMD("CR", "popless:scroll-down-line", ""); + BIND_CMD("LF", "popless:scroll-down-line", ""); + BIND_CMD("DOWN", "popless:scroll-down-line", ""); + BIND_CMD("C-n", "popless:scroll-down-line", ""); + + BIND_CMD("UP", "popless:scroll-up-line", ""); + BIND_CMD("C-h", "popless:scroll-up-line", ""); + BIND_CMD("C-p", "popless:scroll-up-line", ""); + BIND_CMD("DELETE", "popless:scroll-up-line", ""); + BIND_CMD("BACKSPACE", "popless:scroll-up-line", ""); + BIND_CMD("DC", "popless:scroll-up-line", ""); + + BIND_CMD("RIGHT", "popless:scroll-right 10", "scrolls right"); + BIND_CMD("LEFT", "popless:scroll-left 10", "scrolls left"); + + BIND_CMD("HOME", "popless:scroll-to-top", ""); + BIND_CMD("<", "popless:scroll-to-top", ""); + BIND_CMD("M-<", "popless:scroll-to-top", ""); + + BIND_CMD(">", "popless:scroll-to-bottom", ""); + BIND_CMD("M->", "popless:scroll-to-bottom", ""); + + BIND_CMD("q", "popless:quit", ""); + BIND_CMD("C-c", "popless:quit", ""); + BIND_CMD("C-g", "popless:quit", ""); + + BIND_CMD("C-l", "redisplay", ""); + + + /****************************************************************/ + /***************************** RECV *****************************/ + /****************************************************************/ + + km_mainwin = km = owl_keyhandler_create_and_add_keymap(kh, "recv", + "Main window / message list", + owl_keys_default_invalid, owl_keys_recwin_prealways, NULL); + owl_keymap_set_submap(km_mainwin, km_global); + BIND_CMD("C-x C-c", "start-command quit", ""); + BIND_CMD("F1", "help", ""); + BIND_CMD("h", "help", ""); + BIND_CMD("HELP", "help", ""); + BIND_CMD("M-[ 2 8 ~", "help", ""); + + BIND_CMD(":", "start-command", "start a new command"); + BIND_CMD("M-x", "start-command", "start a new command"); + + BIND_CMD("x", "expunge", ""); + BIND_CMD("u", "undelete", ""); + BIND_CMD("M-u", "undelete view", "undelete all messages in view"); + BIND_CMD("d", "delete", "mark message for deletion"); + BIND_CMD("M-D", "delete view", "mark all messages in view for deletion"); + BIND_CMD("C-x k", "smartzpunt -i", "zpunt current <class,instance>"); + + BIND_CMD("X", "( expunge ; view --home )", "expunge deletions and switch to home view"); + + BIND_CMD("v", "start-command view ", "start a view command"); + BIND_CMD("V", "view --home", "change to the home view ('all' by default)"); + BIND_CMD("!", "view -r", "invert the current view filter"); + BIND_CMD("M-n", "smartnarrow", "narrow to a view based on the current message"); + BIND_CMD("M-N", "smartnarrow -i", "narrow to a view based on the current message, and consider instance pair"); + BIND_CMD("M-p", "view personal", ""); + + BIND_CMD("/", "start-command search ", "start a search command"); + BIND_CMD("?", "start-command search -r ", "start a revrerse search command"); + + BIND_CMD("LEFT", "recv:shiftleft", ""); + BIND_CMD("RIGHT", "recv:shiftright",""); + BIND_CMD("DOWN", "recv:next", ""); + BIND_CMD("C-n", "recv:next", ""); + BIND_CMD("M-C-n", "recv:next --smart-filter", "move to next message matching the current one"); + BIND_CMD("UP", "recv:prev", ""); + BIND_CMD("n", "recv:next-notdel", ""); + BIND_CMD("p", "recv:prev-notdel", ""); + BIND_CMD("C-p", "recv:prev", ""); + BIND_CMD("M-C-p", "recv:prev --smart-filter", "move to previous message matching the current one"); + BIND_CMD("P", "recv:next-personal", ""); + BIND_CMD("M-P", "recv:prev-personal", ""); + BIND_CMD("M-<", "recv:first", ""); + BIND_CMD("<", "recv:first", ""); + BIND_CMD("M->", "recv:last", ""); + BIND_CMD(">", "recv:last", ""); + BIND_CMD("C-v", "recv:pagedown", ""); + BIND_CMD("NPAGE", "recv:pagedown", ""); + BIND_CMD("M-v", "recv:pageup", ""); + BIND_CMD("PPAGE", "recv:pageup", ""); + + BIND_CMD("SPACE", "recv:scroll 10", "scroll message down a page"); + BIND_CMD("CR", "recv:scroll 1", "scroll message down a line"); + BIND_CMD("LF", "recv:scroll 1", "scroll message down a line"); + BIND_CMD("C-h" , "recv:scroll -1", "scroll message up a line"); + BIND_CMD("DELETE", "recv:scroll -1", "scroll message up a line"); + BIND_CMD("BACKSPACE", "recv:scroll -1", "scroll message up a line"); + BIND_CMD("DC", "recv:scroll -1", "scroll message up a line"); + BIND_CMD("b", "recv:scroll -10", "scroll message up a page"); + + BIND_CMD("C-l", "redisplay", ""); + + BIND_CMD("i", "info", ""); + BIND_CMD("l", "blist", ""); + BIND_CMD("B", "alist", ""); + BIND_CMD("M", "pop-message", ""); + BIND_CMD("T", "delete trash", "mark all 'trash' messages for deletion"); + + BIND_CMD("o", "toggle-oneline", ""); + + BIND_CMD("A", "away toggle", "toggles away message on and off"); + + BIND_CMD("z", "start-command zwrite ", "start a zwrite command"); + BIND_CMD("a", "start-command aimwrite ", "start an aimwrite command"); + BIND_CMD("r", "reply", "reply to the current message"); + BIND_CMD("R", "reply sender", "reply to sender of the current message"); + BIND_CMD("C-r", "reply -e", "reply to the current message, but allow editing of recipient"); + BIND_CMD("M-r", "reply -e", "reply to the current message, but allow editing of recipient"); + BIND_CMD("M-R", "reply -e sender", "reply to sender of the current message, but allow editing of recipient"); + + BIND_CMD("C-c", "", "no effect in this mode"); + BIND_CMD("C-g", "", "no effect in this mode"); + + + /**********************/ + + owl_function_activate_keymap("recv"); + +} + + +/****************************************************************/ +/********************* Support Functions ************************/ +/****************************************************************/ + +void owl_keys_recwin_prealways(int j) { + /* Clear the message line on subsequent key presses */ + owl_function_makemsg(""); +} + +void owl_keys_editwin_default(int j) { + owl_editwin *e; + if (NULL != (e=owl_global_get_typwin(&g))) { + owl_editwin_process_char(e, j); + } +} + +void owl_keys_editwin_postalways(int j) { + owl_editwin *e; + if (NULL != (e=owl_global_get_typwin(&g))) { + owl_editwin_post_process_char(e, j); + } + owl_global_set_needrefresh(&g); +} + +void owl_keys_popless_postalways(int j) { + owl_viewwin *v = owl_global_get_viewwin(&g); + owl_popwin *pw = owl_global_get_popwin(&g); + + if (pw && owl_popwin_is_active(pw) && v) { + owl_viewwin_redisplay(v, 1); + } +} + +void owl_keys_default_invalid(int j) { + if (j==ERR) return; + if (j==410) return; + owl_keyhandler_invalidkey(owl_global_get_keyhandler(&g)); +} + diff --git a/libfaim/Makefile.in b/libfaim/Makefile.in new file mode 100644 index 0000000..0d07e10 --- /dev/null +++ b/libfaim/Makefile.in @@ -0,0 +1,21 @@ +CC=@CC@ +LIBS=@LIBS@ +LDFLAGS=@LDFLAGS@ +RANLIB=@RANLIB@ +OBJS=admin.o adverts.o auth.o bos.o buddylist.o bstream.o \ + chat.o chatnav.o conn.o email.o ft.o icq.o im.o \ + invite.o md5.o misc.o msgcookie.o locate.o \ + popups.o rxhandlers.o rxqueue.o search.o service.o \ + snac.o ssi.o stats.o tlv.o translate.o txqueue.o \ + util.o odir.o bart.o +CFLAGS=@CFLAGS@ -I. -DAIM_BUILDDATE=\"x\" -DAIM_BUILDTIME=\"x\" + +libfaim: $(OBJS) + $(AR) -r libfaim.a $(OBJS) + $(RANLIB) libfaim.a + +clean: + $(RM) *.o libfaim.a + +distclean: clean + $(RM) config.cache config.log config.status Makefile config.h TAGS *~ core diff --git a/libfaim/admin.c b/libfaim/admin.c new file mode 100644 index 0000000..26ffdff --- /dev/null +++ b/libfaim/admin.c @@ -0,0 +1,238 @@ +/* + * Family 0x0007 - Account Administration. + * + * Used for stuff like changing the formating of your screen name, changing your + * email address, requesting an account confirmation email, getting account info, + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * Subtype 0x0002 - Request a bit of account info. + * + * Info should be one of the following: + * 0x0001 - Screen name formatting + * 0x0011 - Email address + * 0x0013 - Unknown + * + */ +faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0007, 0x0002, 0x0000, snacid); + + aimbs_put16(&fr->data, info); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtypes 0x0003 and 0x0005 - Parse account info. + * + * Called in reply to both an information request (subtype 0x0002) and + * an information change (subtype 0x0004). + * + */ +static int infochange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + char *url=NULL, *sn=NULL, *email=NULL; + fu16_t perms, tlvcount, err=0; + + perms = aimbs_get16(bs); + tlvcount = aimbs_get16(bs); + + while (tlvcount && aim_bstream_empty(bs)) { + fu16_t type, length; + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + + switch (type) { + case 0x0001: { + sn = aimbs_getstr(bs, length); + } break; + + case 0x0004: { + url = aimbs_getstr(bs, length); + } break; + + case 0x0008: { + err = aimbs_get16(bs); + } break; + + case 0x0011: { + if (length == 0) { + email = (char*)malloc(13*sizeof(char)); + strcpy(email, "*suppressed*"); + } else + email = aimbs_getstr(bs, length); + } break; + } + + tlvcount--; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, err, url, sn, email); + + if (sn) free(sn); + if (url) free(url); + if (email) free(email); + + return 1; +} + +/* + * Subtype 0x0004 - Set screenname formatting. + * + */ +faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newnick)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid); + + aim_tlvlist_add_raw(&tl, 0x0001, strlen(newnick), newnick); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + + return 0; +} + +/* + * Subtype 0x0004 - Change password. + * + */ +faim_export int aim_admin_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + aim_snacid_t snacid; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid); + + /* new password TLV t(0002) */ + aim_tlvlist_add_raw(&tl, 0x0002, strlen(newpw), newpw); + + /* current password TLV t(0012) */ + aim_tlvlist_add_raw(&tl, 0x0012, strlen(curpw), curpw); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0004 - Change email address. + * + */ +faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid); + + aim_tlvlist_add_raw(&tl, 0x0011, strlen(newemail), newemail); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0006 - Request account confirmation. + * + * This will cause an email to be sent to the address associated with + * the account. By following the instructions in the mail, you can + * get the TRIAL flag removed from your account. + * + */ +faim_export int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0007, 0x0006); +} + +/* + * Subtype 0x0007 - Account confirmation request acknowledgement. + * + */ +static int accountconfirm(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t status; + aim_tlvlist_t *tl; + + status = aimbs_get16(bs); + /* This is 0x0013 if unable to confirm at this time */ + + tl = aim_tlvlist_read(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, status); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if ((snac->subtype == 0x0003) || (snac->subtype == 0x0005)) + return infochange(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return accountconfirm(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0007; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "admin", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/adverts.c b/libfaim/adverts.c new file mode 100644 index 0000000..5e80bd2 --- /dev/null +++ b/libfaim/adverts.c @@ -0,0 +1,31 @@ +/* + * Family 0x0005 - Advertisements. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0005, 0x0002); +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + return 0; +} + +faim_internal int adverts_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0005; + mod->version = 0x0001; + mod->toolid = 0x0001; + mod->toolversion = 0x0001; + mod->flags = 0; + strncpy(mod->name, "adverts", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/aim.h b/libfaim/aim.h new file mode 100644 index 0000000..fb9627f --- /dev/null +++ b/libfaim/aim.h @@ -0,0 +1,1484 @@ +/* + * Main libfaim header. Must be included in client for prototypes/macros. + * + * "come on, i turned a chick lesbian; i think this is the hackish equivalent" + * -- Josh Myer + * + */ + +#ifndef __AIM_H__ +#define __AIM_H__ + +#define FAIM_VERSION_MAJOR 0 +#define FAIM_VERSION_MINOR 99 +#define FAIM_VERSION_MINORMINOR 1 + +#include <faimconfig.h> +#include <aim_cbtypes.h> + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <time.h> + +#ifndef _WIN32 +#include <sys/time.h> +#include <unistd.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> +#else +#include <winsock.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* XXX adjust these based on autoconf-detected platform */ +typedef unsigned char fu8_t; +typedef unsigned short fu16_t; +typedef unsigned int fu32_t; +typedef fu32_t aim_snacid_t; +typedef fu16_t flap_seqnum_t; + +#if defined(mach) && defined(__APPLE__) +#define gethostbyname(x) gethostbyname2(x, AF_INET) +#endif + +#if defined(_WIN32) && !defined(WIN32_STATIC) +/* + * For a win32 DLL, we define WIN32_INDLL if this file + * is included while compiling the DLL. If its not + * defined (its included in a client app), the symbols + * will be imported instead of exported. + */ +#ifdef WIN32_INDLL +#define faim_export __declspec(dllexport) +#else +#define faim_export __declspec(dllimport) +#endif /* WIN32_INDLL */ +#define faim_internal +#else +/* + * Nothing normally needed for unix... + */ +#define faim_export +#define faim_internal +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#ifndef bool +#define bool fu8_t +#endif + +/* + * Current Maximum Length for Screen Names (not including NULL) + * + * Currently only names up to 16 characters can be registered + * however it is aparently legal for them to be larger. + */ +#define MAXSNLEN 97 + +/* + * Current Maximum Length for Instant Messages + * + * This was found basically by experiment, but not wholly + * accurate experiment. It should not be regarded + * as completely correct. But its a decent approximation. + * + * Note that although we can send this much, its impossible + * for WinAIM clients (up through the latest (4.0.1957)) to + * send any more than 1kb. Amaze all your windows friends + * with utterly oversized instant messages! + * + * XXX: the real limit is the total SNAC size at 8192. Fix this. + * + */ +#define MAXMSGLEN 7987 + +/* + * Maximum size of a Buddy Icon. + */ +#define MAXICONLEN 7168 +#define AIM_ICONIDENT "AVT1picture.id" + +/* + * Current Maximum Length for Chat Room Messages + * + * This is actually defined by the protocol to be + * dynamic, but I have yet to see due cause to + * define it dynamically here. Maybe later. + * + */ +#define MAXCHATMSGLEN 512 + +/** + * Maximum length for the password of an ICQ account + */ +#define MAXICQPASSLEN 8 + +#define AIM_MD5_STRING "AOL Instant Messenger (SM)" + +/* + * Client info. Filled in by the client and passed in to + * aim_send_login(). The information ends up getting passed to OSCAR + * through the initial login command. + * + */ +struct client_info_s { + const char *clientstring; + fu16_t clientid; + fu16_t major; + fu16_t minor; + fu16_t point; + fu16_t build; + fu32_t distrib; + const char *country; /* two-letter abbrev */ + const char *lang; /* two-letter abbrev */ +}; + +/* Needs to be checked */ +#define CLIENTINFO_AIM_3_5_1670 { \ + "AOL Instant Messenger (SM), version 3.5.1670/WIN32", \ + 0x0004, \ + 0x0003, 0x0005, \ + 0x0000, 0x0686, \ + 0x0000002a, \ + "us", "en", \ +} + +/* Needs to be checked */ +/* Latest winaim without ssi */ +#define CLIENTINFO_AIM_4_1_2010 { \ + "AOL Instant Messenger (SM), version 4.1.2010/WIN32", \ + 0x0004, \ + 0x0004, 0x0001, \ + 0x0000, 0x07da, \ + 0x0000004b, \ + "us", "en", \ +} + +/* Needs to be checked */ +#define CLIENTINFO_AIM_4_3_2188 { \ + "AOL Instant Messenger (SM), version 4.3.2188/WIN32", \ + 0x0109, \ + 0x0400, 0x0003, \ + 0x0000, 0x088c, \ + 0x00000086, \ + "us", "en", \ +} + +/* Needs to be checked */ +#define CLIENTINFO_AIM_4_8_2540 { \ + "AOL Instant Messenger (SM), version 4.8.2540/WIN32", \ + 0x0109, \ + 0x0004, 0x0008, \ + 0x0000, 0x09ec, \ + 0x000000af, \ + "us", "en", \ +} + +/* Needs to be checked */ +#define CLIENTINFO_AIM_5_0_2938 { \ + "AOL Instant Messenger, version 5.0.2938/WIN32", \ + 0x0109, \ + 0x0005, 0x0000, \ + 0x0000, 0x0b7a, \ + 0x00000000, \ + "us", "en", \ +} + +#define CLIENTINFO_AIM_5_1_3036 { \ + "AOL Instant Messenger, version 5.1.3036/WIN32", \ + 0x0109, \ + 0x0005, 0x0001, \ + 0x0000, 0x0bdc, \ + 0x000000d2, \ + "us", "en", \ +} + +#define CLIENTINFO_AIM_5_5_3415 { \ + "AOL Instant Messenger, version 5.5.3415/WIN32", \ + 0x0109, \ + 0x0005, 0x0005, \ + 0x0000, 0x0057, \ + 0x000000ef, \ + "us", "en", \ +} + +#define CLIENTINFO_ICHAT_1_0 { \ + "Apple iChat", \ + 0x311a, \ + 0x0001, 0x0000, \ + 0x0000, 0x003c, \ + 0x000000c6, \ + "us", "en", \ +} + +/* Needs to be checked */ +#define CLIENTINFO_ICQ_4_65_3281 { \ + "ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85", \ + 0x010a, \ + 0x0004, 0x0041, \ + 0x0001, 0x0cd1, \ + 0x00000055, \ + "us", "en", \ +} + +/* Needs to be checked */ +#define CLIENTINFO_ICQ_5_34_3728 { \ + "ICQ Inc. - Product of ICQ (TM).2002a.5.34.1.3728.85", \ + 0x010a, \ + 0x0005, 0x0022, \ + 0x0001, 0x0e8f, \ + 0x00000055, \ + "us", "en", \ +} + +#define CLIENTINFO_ICQ_5_45_3777 { \ + "ICQ Inc. - Product of ICQ (TM).2003a.5.45.1.3777.85", \ + 0x010a, \ + 0x0005, 0x002d, \ + 0x0001, 0x0ec1, \ + 0x00000055, \ + "us", "en", \ +} + +#define CLIENTINFO_ICQBASIC_14_3_1068 { \ + "ICQBasic", \ + 0x010a, \ + 0x0014, 0x0003, \ + 0x0000, 0x042c, \ + 0x0000043d, \ + "us", "en", \ +} + +#define CLIENTINFO_NETSCAPE_7_0_1 { \ + "Netscape 2000 an approved user of AOL Instant Messenger (SM)", \ + 0x1d0d, \ + 0x0007, 0x0000, \ + 0x0001, 0x0000, \ + 0x00000058, \ + "us", "en", \ +} + +#define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036 +#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQ_5_45_3777 + +/* + * These could be arbitrary, but its easier to use the actual AIM values + */ +#define AIM_CONN_TYPE_BOS 0x0002 +#define AIM_CONN_TYPE_ADS 0x0005 +#define AIM_CONN_TYPE_AUTH 0x0007 +#define AIM_CONN_TYPE_CHATNAV 0x000d +#define AIM_CONN_TYPE_CHAT 0x000e +#define AIM_CONN_TYPE_SEARCH 0x000f +#define AIM_CONN_TYPE_ICON 0x0010 +#define AIM_CONN_TYPE_EMAIL 0x0018 + +/* they start getting arbitrary for rendezvous stuff =) */ +#define AIM_CONN_TYPE_RENDEZVOUS 0xfffe /* these do not speak FLAP! */ +#define AIM_CONN_TYPE_LISTENER 0xffff /* socket waiting for accept() */ + +/* + * Subtypes, we need these for OFT stuff. + */ +#define AIM_CONN_SUBTYPE_OFT_DIRECTIM 0x0001 +#define AIM_CONN_SUBTYPE_OFT_GETFILE 0x0002 +#define AIM_CONN_SUBTYPE_OFT_SENDFILE 0x0003 +#define AIM_CONN_SUBTYPE_OFT_BUDDYICON 0x0004 +#define AIM_CONN_SUBTYPE_OFT_VOICE 0x0005 + +/* + * Status values returned from aim_conn_new(). ORed together. + */ +#define AIM_CONN_STATUS_READY 0x0001 +#define AIM_CONN_STATUS_INTERNALERR 0x0002 +#define AIM_CONN_STATUS_RESOLVERR 0x0040 +#define AIM_CONN_STATUS_CONNERR 0x0080 +#define AIM_CONN_STATUS_INPROGRESS 0x0100 + +#define AIM_FRAMETYPE_FLAP 0x0000 +#define AIM_FRAMETYPE_OFT 0x0001 + +typedef struct aim_conn_s { + int fd; + fu16_t type; + fu16_t subtype; + flap_seqnum_t seqnum; + fu32_t status; + void *priv; /* misc data the client may want to store */ + void *internal; /* internal conn-specific libfaim data */ + time_t lastactivity; /* time of last transmit */ + int forcedlatency; + void *handlerlist; + void *sessv; /* pointer to parent session */ + void *inside; /* only accessible from inside libfaim */ + struct aim_conn_s *next; +} aim_conn_t; + +/* + * Byte Stream type. Sort of. + * + * Use of this type serves a couple purposes: + * - Buffer/buflen pairs are passed all around everywhere. This turns + * that into one value, as well as abstracting it slightly. + * - Through the abstraction, it is possible to enable bounds checking + * for robustness at the cost of performance. But a clean failure on + * weird packets is much better than a segfault. + * - I like having variables named "bs". + * + * Don't touch the insides of this struct. Or I'll have to kill you. + * + */ +typedef struct aim_bstream_s { + fu8_t *data; + fu32_t len; + fu32_t offset; +} aim_bstream_t; + +typedef struct aim_frame_s { + fu8_t hdrtype; /* defines which piece of the union to use */ + union { + struct { + fu8_t type; + flap_seqnum_t seqnum; + } flap; + struct { + fu8_t magic[4]; /* ODC2 or OFT2 */ + fu16_t hdrlen; + fu16_t type; + } rend; + } hdr; + aim_bstream_t data; /* payload stream */ + fu8_t handled; /* 0 = new, !0 = been handled */ + fu8_t nofree; /* 0 = free data on purge, 1 = only unlink */ + aim_conn_t *conn; /* the connection it came in on... */ + struct aim_frame_s *next; +} aim_frame_t; + +typedef struct aim_msgcookie_s { + fu8_t cookie[8]; + int type; + void *data; + time_t addtime; + struct aim_msgcookie_s *next; +} aim_msgcookie_t; + +/* + * AIM Session: The main client-data interface. + * + */ +typedef struct aim_session_s { + + /* ---- Client Accessible ------------------------ */ + + /* Our screen name. */ + char sn[MAXSNLEN+1]; + + /* + * Pointer to anything the client wants to + * explicitly associate with this session. + * + * This is for use in the callbacks mainly. In any + * callback, you can access this with sess->aux_data. + * + */ + void *aux_data; + + /* ---- Internal Use Only ------------------------ */ + + /* Connection information */ + aim_conn_t *connlist; + + /* + * Transmit/receive queues. + * + * These are only used when you don't use your own lowlevel + * I/O. I don't suggest that you use libfaim's internal I/O. + * Its really bad and the API/event model is quirky at best. + * + */ + aim_frame_t *queue_outgoing; + aim_frame_t *queue_incoming; + + /* + * Tx Enqueuing function. + * + * This is how you override the transmit direction of libfaim's + * internal I/O. This function will be called whenever it needs + * to send something. + * + */ + int (*tx_enqueue)(struct aim_session_s *, aim_frame_t *); + + void *modlistv; + + struct { + char server[128]; + char username[128]; + char password[128]; + } socksproxy; + + bool nonblocking; + + int debug; + void (*debugcb)(struct aim_session_s *sess, int level, const char *format, va_list va); /* same as faim_debugging_callback_t */ + + /* + * Outstanding snac handling + * + * XXX: Should these be per-connection? -mid + */ + void *snac_hash[FAIM_SNAC_HASH_SIZE]; + aim_snacid_t snacid_next; + + aim_msgcookie_t *msgcookies; + struct aim_icq_info *icq_info; + struct aim_oft_info *oft_info; + struct aim_authresp_info *authinfo; + struct aim_emailinfo *emailinfo; + + struct { + struct aim_userinfo_s *userinfo; + struct userinfo_node *torequest; + struct userinfo_node *requested; + int waiting_for_response; + } locate; + + /* Server-stored information (ssi) */ + struct { + int received_data; + fu16_t numitems; + struct aim_ssi_item *official; + struct aim_ssi_item *local; + struct aim_ssi_tmp *pending; + time_t timestamp; + int waiting_for_ack; + } ssi; +} aim_session_t; + +/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */ +#define AIM_ICQ_STATE_NORMAL 0x00000000 +#define AIM_ICQ_STATE_AWAY 0x00000001 +#define AIM_ICQ_STATE_DND 0x00000002 +#define AIM_ICQ_STATE_OUT 0x00000004 +#define AIM_ICQ_STATE_BUSY 0x00000010 +#define AIM_ICQ_STATE_CHAT 0x00000020 +#define AIM_ICQ_STATE_INVISIBLE 0x00000100 +#define AIM_ICQ_STATE_WEBAWARE 0x00010000 +#define AIM_ICQ_STATE_HIDEIP 0x00020000 +#define AIM_ICQ_STATE_BIRTHDAY 0x00080000 +#define AIM_ICQ_STATE_DIRECTDISABLED 0x00100000 +#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000 +#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000 +#define AIM_ICQ_STATE_DIRECTCONTACTLIST 0x20000000 + + + +/* + * Get command from connections + * + * aim_get_commmand() is the libfaim lowlevel I/O in the receive direction. + * XXX Make this easily overridable. + * + */ +faim_export int aim_get_command(aim_session_t *, aim_conn_t *); + +/* + * Dispatch commands that are in the rx queue. + */ +faim_export void aim_rxdispatch(aim_session_t *); + +faim_export int aim_debugconn_sendconnect(aim_session_t *sess, aim_conn_t *conn); + +faim_export int aim_logoff(aim_session_t *); + +#if !defined(FAIM_INTERNAL) || defined(FAIM_INTERNAL_INSANE) +/* the library should never call aim_conn_kill */ +faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn); +#endif + +typedef int (*aim_rxcallback_t)(aim_session_t *, aim_frame_t *, ...); + + +/* auth.c */ +struct aim_clientrelease { + char *name; + fu32_t build; + char *url; + char *info; +}; + +struct aim_authresp_info { + char *sn; + fu16_t errorcode; + char *errorurl; + fu16_t regstatus; + char *email; + char *bosip; + fu16_t cookielen; + fu8_t *cookie; + char *chpassurl; + struct aim_clientrelease latestrelease; + struct aim_clientrelease latestbeta; +}; + +/* Callback data for redirect. */ +struct aim_redirect_data { + fu16_t group; + const char *ip; + fu16_t cookielen; + const fu8_t *cookie; + struct { /* group == AIM_CONN_TYPE_CHAT */ + fu16_t exchange; + const char *room; + fu16_t instance; + } chat; +}; + +faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn); +faim_export int aim_send_login(aim_session_t *, aim_conn_t *, const char *, const char *, struct client_info_s *, const char *key); +faim_export void aim_purge_rxqueue(aim_session_t *); +faim_export void aim_cleansnacs(aim_session_t *, int maxage); + +#define AIM_TX_QUEUED 0 /* default */ +#define AIM_TX_IMMEDIATE 1 +#define AIM_TX_USER 2 +faim_export int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *)); + +faim_export int aim_tx_flushqueue(aim_session_t *); +faim_export void aim_tx_purgequeue(aim_session_t *); + +faim_export int aim_conn_setlatency(aim_conn_t *conn, int newval); + +faim_export int aim_conn_addhandler(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t type, aim_rxcallback_t newhandler, fu16_t flags); +faim_export int aim_clearhandlers(aim_conn_t *conn); + +faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, fu16_t group); +faim_export aim_session_t *aim_conn_getsess(aim_conn_t *conn); +faim_export void aim_conn_close(aim_conn_t *deadconn); +faim_export aim_conn_t *aim_newconn(aim_session_t *, int type, const char *dest); +faim_export int aim_conngetmaxfd(aim_session_t *); +faim_export aim_conn_t *aim_select(aim_session_t *, struct timeval *, int *); +faim_export int aim_conn_in_sess(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_conn_isready(aim_conn_t *); +faim_export int aim_conn_setstatus(aim_conn_t *, int); +faim_export int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_conn_isconnecting(aim_conn_t *conn); + +typedef void (*faim_debugging_callback_t)(aim_session_t *sess, int level, const char *format, va_list va); +faim_export int aim_setdebuggingcb(aim_session_t *sess, faim_debugging_callback_t); +faim_export void aim_session_init(aim_session_t *, bool nonblocking, int debuglevel); +faim_export void aim_session_kill(aim_session_t *); +faim_export void aim_setupproxy(aim_session_t *sess, const char *server, const char *username, const char *password); +faim_export aim_conn_t *aim_getconn_type(aim_session_t *, int type); +faim_export aim_conn_t *aim_getconn_type_all(aim_session_t *, int type); +faim_export aim_conn_t *aim_getconn_fd(aim_session_t *, int fd); + + + +/* 0x0001 - service.c */ +faim_export int aim_srv_setavailmsg(aim_session_t *sess, char *msg); +faim_export int aim_srv_setidle(aim_session_t *sess, fu32_t idletime); + + + +/* misc.c */ + +#define AIM_VISIBILITYCHANGE_PERMITADD 0x05 +#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06 +#define AIM_VISIBILITYCHANGE_DENYADD 0x07 +#define AIM_VISIBILITYCHANGE_DENYREMOVE 0x08 + +#define AIM_PRIVFLAGS_ALLOWIDLE 0x01 +#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE 0x02 + +#define AIM_WARN_ANON 0x01 + +faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_nop(aim_session_t *, aim_conn_t *); +faim_export int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_bos_changevisibility(aim_session_t *, aim_conn_t *, int, const char *); +faim_export int aim_bos_setgroupperm(aim_session_t *, aim_conn_t *, fu32_t mask); +faim_export int aim_bos_setprivacyflags(aim_session_t *, aim_conn_t *, fu32_t); +faim_export int aim_reqpersonalinfo(aim_session_t *, aim_conn_t *); +faim_export int aim_reqservice(aim_session_t *, aim_conn_t *, fu16_t); +faim_export int aim_bos_reqrights(aim_session_t *, aim_conn_t *); +faim_export int aim_setextstatus(aim_session_t *sess, fu32_t status); + +#define AIM_CLIENTTYPE_UNKNOWN 0x0000 +#define AIM_CLIENTTYPE_MC 0x0001 +#define AIM_CLIENTTYPE_WINAIM 0x0002 +#define AIM_CLIENTTYPE_WINAIM41 0x0003 +#define AIM_CLIENTTYPE_AOL_TOC 0x0004 +faim_export fu16_t aim_im_fingerprint(const fu8_t *msghdr, int len); + +#define AIM_RATE_CODE_CHANGE 0x0001 +#define AIM_RATE_CODE_WARNING 0x0002 +#define AIM_RATE_CODE_LIMIT 0x0003 +#define AIM_RATE_CODE_CLEARLIMIT 0x0004 +faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn); + + + +/* im.c */ +#define AIM_OFT_SUBTYPE_SEND_FILE 0x0001 +#define AIM_OFT_SUBTYPE_SEND_DIR 0x0002 +#define AIM_OFT_SUBTYPE_GET_FILE 0x0011 +#define AIM_OPT_SUBTYPE_GET_LIST 0x0012 + +#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000 +#define AIM_TRANSFER_DENY_DECLINE 0x0001 +#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002 + +#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED 0x00000001 +#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED 0x00000002 + +/* This is what the server will give you if you don't set them yourself. */ +#define AIM_IMPARAM_DEFAULTS { \ + 0, \ + AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \ + 512, /* !! Note how small this is. */ \ + (99.9)*10, (99.9)*10, \ + 1000 /* !! And how large this is. */ \ +} + +/* This is what most AIM versions use. */ +#define AIM_IMPARAM_REASONABLE { \ + 0, \ + AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \ + 8000, \ + (99.9)*10, (99.9)*10, \ + 0 \ +} + +struct aim_icbmparameters { + fu16_t maxchan; + fu32_t flags; /* AIM_IMPARAM_FLAG_ */ + fu16_t maxmsglen; /* message size that you will accept */ + fu16_t maxsenderwarn; /* this and below are *10 (999=99.9%) */ + fu16_t maxrecverwarn; + fu32_t minmsginterval; /* in milliseconds? */ +}; + +struct aim_chat_roominfo { + fu16_t exchange; + char *name; + fu16_t instance; +}; + +#define AIM_IMFLAGS_AWAY 0x0001 /* mark as an autoreply */ +#define AIM_IMFLAGS_ACK 0x0002 /* request a receipt notice */ +#define AIM_IMFLAGS_UNICODE 0x0004 +#define AIM_IMFLAGS_ISO_8859_1 0x0008 +#define AIM_IMFLAGS_BUDDYREQ 0x0010 /* buddy icon requested */ +#define AIM_IMFLAGS_HASICON 0x0020 /* already has icon */ +#define AIM_IMFLAGS_SUBENC_MACINTOSH 0x0040 /* damn that Steve Jobs! */ +#define AIM_IMFLAGS_CUSTOMFEATURES 0x0080 /* features field present */ +#define AIM_IMFLAGS_EXTDATA 0x0100 +#define AIM_IMFLAGS_X 0x0200 +#define AIM_IMFLAGS_MULTIPART 0x0400 /* ->mpmsg section valid */ +#define AIM_IMFLAGS_OFFLINE 0x0800 /* send to offline user */ +#define AIM_IMFLAGS_TYPINGNOT 0x1000 /* typing notification */ + +/* + * Multipart message structures. + */ +typedef struct aim_mpmsg_section_s { + fu16_t charset; + fu16_t charsubset; + fu8_t *data; + fu16_t datalen; + struct aim_mpmsg_section_s *next; +} aim_mpmsg_section_t; + +typedef struct aim_mpmsg_s { + int numparts; + aim_mpmsg_section_t *parts; +} aim_mpmsg_t; + +faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm); +faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, const fu8_t *data, fu16_t datalen); +faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii); +faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const fu16_t *unicode, fu16_t unicodelen); +faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm); + +/* + * Arguments to aim_send_im_ext(). + * + * This is really complicated. But immensely versatile. + * + */ +struct aim_sendimext_args { + + /* These are _required_ */ + const char *destsn; + fu32_t flags; /* often 0 */ + + /* Only required if not using multipart messages */ + const char *msg; + int msglen; + + /* Required if ->msg is not provided */ + aim_mpmsg_t *mpmsg; + + /* Only used if AIM_IMFLAGS_HASICON is set */ + fu32_t iconlen; + time_t iconstamp; + fu32_t iconsum; + + /* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */ + fu8_t *features; + fu8_t featureslen; + + /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */ + fu16_t charset; + fu16_t charsubset; +}; + +/* + * Arguments to aim_send_rtfmsg(). + */ +struct aim_sendrtfmsg_args { + const char *destsn; + fu32_t fgcolor; + fu32_t bgcolor; + const char *rtfmsg; /* must be in RTF */ +}; + +/* + * This information is provided in the Incoming ICBM callback for + * Channel 1 ICBM's. + * + * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they + * are optional, both are always set by the current libfaim code. + * That may or may not change in the future. It is mainly for + * consistency with aim_sendimext_args. + * + * Multipart messages require some explanation. If you want to use them, + * I suggest you read all the comments in im.c. + * + */ +struct aim_incomingim_ch1_args { + + /* Always provided */ + aim_mpmsg_t mpmsg; + fu32_t icbmflags; /* some flags apply only to ->msg, not all mpmsg */ + + /* Only provided if message has a human-readable section */ + char *msg; + int msglen; + + /* Only provided if AIM_IMFLAGS_HASICON is set */ + time_t iconstamp; + fu32_t iconlen; + fu16_t iconsum; + + /* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */ + fu8_t *features; + fu8_t featureslen; + + /* Only provided if AIM_IMFLAGS_EXTDATA is set */ + fu8_t extdatalen; + fu8_t *extdata; + + /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */ + fu16_t charset; + fu16_t charsubset; +}; + +/* Valid values for channel 2 args->status */ +#define AIM_RENDEZVOUS_PROPOSE 0x0000 +#define AIM_RENDEZVOUS_CANCEL 0x0001 +#define AIM_RENDEZVOUS_ACCEPT 0x0002 + +struct aim_incomingim_ch2_args { + fu16_t status; + fu8_t cookie[8]; + int reqclass; + const char *proxyip; + const char *clientip; + const char *verifiedip; + fu16_t port; + fu16_t errorcode; + const char *msg; /* invite message or file description */ + const char *encoding; + const char *language; + union { + struct { + fu32_t checksum; + fu32_t length; + time_t timestamp; + fu8_t *icon; + } icon; + struct { + struct aim_chat_roominfo roominfo; + } chat; + struct { + fu32_t fgcolor; + fu32_t bgcolor; + const char *rtfmsg; + } rtfmsg; + struct { + fu16_t subtype; + fu16_t totfiles; + fu32_t totsize; + char *filename; + } sendfile; + } info; + void *destructor; /* used internally only */ +}; + +/* Valid values for channel 4 args->type */ +#define AIM_ICQMSG_AUTHREQUEST 0x0006 +#define AIM_ICQMSG_AUTHDENIED 0x0007 +#define AIM_ICQMSG_AUTHGRANTED 0x0008 + +struct aim_incomingim_ch4_args { + fu32_t uin; /* Of the sender of the ICBM */ + fu8_t type; + fu8_t flags; + char *msg; /* Reason for auth request, deny, or accept */ + int msglen; +}; + +/* SNAC sending functions */ +/* 0x0002 */ faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params); +/* 0x0004 */ faim_export int aim_im_reqparams(aim_session_t *sess); +/* 0x0006 */ faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args); +/* 0x0006 */ faim_export int aim_im_sendch1(aim_session_t *, const char *destsn, fu16_t flags, const char *msg); +/* 0x0006 */ faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum); +/* 0x0006 */ faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args); +/* 0x0006 */ faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, fu8_t *cookie, const char *sn, const fu8_t *ip, fu16_t port); +/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info); +/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *info); +/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info); +/* 0x0006 */ faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type); +/* 0x0006 */ faim_export int aim_im_sendch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message); +/* 0x0008 */ faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags); +/* 0x000b */ faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, fu16_t code); +/* 0x0014 */ faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, const char *sn, fu16_t type2); + + + +/* ft.c */ +struct aim_fileheader_t { +#if 0 + char magic[4]; /* 0 */ + fu16_t hdrlen; /* 4 */ + fu16_t hdrtype; /* 6 */ +#endif + char bcookie[8]; /* 8 */ + fu16_t encrypt; /* 16 */ + fu16_t compress; /* 18 */ + fu16_t totfiles; /* 20 */ + fu16_t filesleft; /* 22 */ + fu16_t totparts; /* 24 */ + fu16_t partsleft; /* 26 */ + fu32_t totsize; /* 28 */ + fu32_t size; /* 32 */ + fu32_t modtime; /* 36 */ + fu32_t checksum; /* 40 */ + fu32_t rfrcsum; /* 44 */ + fu32_t rfsize; /* 48 */ + fu32_t cretime; /* 52 */ + fu32_t rfcsum; /* 56 */ + fu32_t nrecvd; /* 60 */ + fu32_t recvcsum; /* 64 */ + fu8_t idstring[32]; /* 68 */ + fu8_t flags; /* 100 */ + fu8_t lnameoffset; /* 101 */ + fu8_t lsizeoffset; /* 102 */ + char dummy[69]; /* 103 */ + char macfileinfo[16]; /* 172 */ + fu16_t nencode; /* 188 */ + fu16_t nlanguage; /* 190 */ + char name[64]; /* 192 */ + /* 256 */ +}; + +struct aim_oft_info { + char cookie[8]; + char *sn; + char *proxyip; + char *clientip; + char *verifiedip; + fu16_t port; + aim_conn_t *conn; + aim_session_t *sess; + struct aim_fileheader_t fh; + struct aim_oft_info *next; +}; + +faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck); +faim_export fu32_t aim_oft_checksum_file(char *filename); +faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur); +faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing); +faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg); +faim_export const char *aim_odc_getsn(aim_conn_t *conn); +faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn); +faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn); +faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie); + +faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn, const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename); +faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info); +faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info); +faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info); + + + +/* 0x0002 - locate.c */ +/* + * AIM User Info, Standard Form. + */ +#define AIM_FLAG_UNCONFIRMED 0x0001 /* "damned transients" */ +#define AIM_FLAG_ADMINISTRATOR 0x0002 +#define AIM_FLAG_AOL 0x0004 +#define AIM_FLAG_OSCAR_PAY 0x0008 +#define AIM_FLAG_FREE 0x0010 +#define AIM_FLAG_AWAY 0x0020 +#define AIM_FLAG_ICQ 0x0040 +#define AIM_FLAG_WIRELESS 0x0080 +#define AIM_FLAG_UNKNOWN100 0x0100 +#define AIM_FLAG_UNKNOWN200 0x0200 +#define AIM_FLAG_ACTIVEBUDDY 0x0400 +#define AIM_FLAG_UNKNOWN800 0x0800 +#define AIM_FLAG_ABINTERNAL 0x1000 +#define AIM_FLAG_ALLUSERS 0x001f + +#define AIM_USERINFO_PRESENT_FLAGS 0x00000001 +#define AIM_USERINFO_PRESENT_MEMBERSINCE 0x00000002 +#define AIM_USERINFO_PRESENT_ONLINESINCE 0x00000004 +#define AIM_USERINFO_PRESENT_IDLE 0x00000008 +#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010 +#define AIM_USERINFO_PRESENT_ICQIPADDR 0x00000020 +#define AIM_USERINFO_PRESENT_ICQDATA 0x00000040 +#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080 +#define AIM_USERINFO_PRESENT_SESSIONLEN 0x00000100 +#define AIM_USERINFO_PRESENT_CREATETIME 0x00000200 + +struct userinfo_node { + char *sn; + struct userinfo_node *next; +}; + +typedef struct aim_userinfo_s { + char *sn; + fu16_t warnlevel; /* evil percent * 10 (999 = 99.9%) */ + fu16_t idletime; /* in seconds */ + fu16_t flags; + fu32_t createtime; /* time_t */ + fu32_t membersince; /* time_t */ + fu32_t onlinesince; /* time_t */ + fu32_t sessionlen; /* in seconds */ + fu32_t capabilities; + struct { + fu32_t status; + fu32_t ipaddr; + fu8_t crap[0x25]; /* until we figure it out... */ + } icqinfo; + fu32_t present; + + fu16_t iconcsumlen; + fu8_t *iconcsum; + + char *info; + char *info_encoding; + fu16_t info_len; + + char *avail; + char *avail_encoding; + fu16_t avail_len; + + char *away; + char *away_encoding; + fu16_t away_len; + + struct aim_userinfo_s *next; +} aim_userinfo_t; + +#define AIM_CAPS_BUDDYICON 0x00000001 +#define AIM_CAPS_VOICE 0x00000002 +#define AIM_CAPS_DIRECTIM 0x00000004 +#define AIM_CAPS_CHAT 0x00000008 +#define AIM_CAPS_GETFILE 0x00000010 +#define AIM_CAPS_SENDFILE 0x00000020 +#define AIM_CAPS_GAMES 0x00000040 +#define AIM_CAPS_SAVESTOCKS 0x00000080 +#define AIM_CAPS_SENDBUDDYLIST 0x00000100 +#define AIM_CAPS_GAMES2 0x00000200 +#define AIM_CAPS_ICQ_DIRECT 0x00000400 +#define AIM_CAPS_APINFO 0x00000800 +#define AIM_CAPS_ICQRTF 0x00001000 +#define AIM_CAPS_EMPTY 0x00002000 +#define AIM_CAPS_ICQSERVERRELAY 0x00004000 +#define AIM_CAPS_ICQUTF8OLD 0x00008000 +#define AIM_CAPS_TRILLIANCRYPT 0x00010000 +#define AIM_CAPS_ICQUTF8 0x00020000 +#define AIM_CAPS_INTEROPERATE 0x00040000 +#define AIM_CAPS_ICHAT 0x00080000 +#define AIM_CAPS_HIPTOP 0x00100000 +#define AIM_CAPS_SECUREIM 0x00200000 +#define AIM_CAPS_SMS 0x00400000 +#define AIM_CAPS_GENERICUNKNOWN 0x00800000 +#define AIM_CAPS_VIDEO 0x01000000 +#define AIM_CAPS_LAST 0x02000000 + +#define AIM_SENDMEMBLOCK_FLAG_ISREQUEST 0 +#define AIM_SENDMEMBLOCK_FLAG_ISHASH 1 + +faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, fu32_t offset, fu32_t len, const fu8_t *buf, fu8_t flag); + +struct aim_invite_priv { + char *sn; + char *roomname; + fu16_t exchange; + fu16_t instance; +}; + +#define AIM_COOKIETYPE_UNKNOWN 0x00 +#define AIM_COOKIETYPE_ICBM 0x01 +#define AIM_COOKIETYPE_ADS 0x02 +#define AIM_COOKIETYPE_BOS 0x03 +#define AIM_COOKIETYPE_IM 0x04 +#define AIM_COOKIETYPE_CHAT 0x05 +#define AIM_COOKIETYPE_CHATNAV 0x06 +#define AIM_COOKIETYPE_INVITE 0x07 +/* we'll move OFT up a bit to give breathing room. not like it really + * matters. */ +#define AIM_COOKIETYPE_OFTIM 0x10 +#define AIM_COOKIETYPE_OFTGET 0x11 +#define AIM_COOKIETYPE_OFTSEND 0x12 +#define AIM_COOKIETYPE_OFTVOICE 0x13 +#define AIM_COOKIETYPE_OFTIMAGE 0x14 +#define AIM_COOKIETYPE_OFTICON 0x15 + +faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn); + +/* 0x0002 */ faim_export int aim_locate_reqrights(aim_session_t *sess); +/* 0x0004 */ faim_export int aim_locate_setprofile(aim_session_t *sess, const char *profile_encoding, const char *profile, const int profile_len, const char *awaymsg_encoding, const char *awaymsg, const int awaymsg_len); +/* 0x0004 */ faim_export int aim_locate_setcaps(aim_session_t *sess, fu32_t caps); +/* 0x0005 */ faim_export int aim_locate_getinfo(aim_session_t *sess, const char *, fu16_t); +/* 0x0009 */ faim_export int aim_locate_setdirinfo(aim_session_t *sess, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, fu16_t privacy); +/* 0x000b */ faim_export int aim_locate_000b(aim_session_t *sess, const char *sn); +/* 0x000f */ faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy); +/* 0x0015 */ faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, fu32_t flags); + + + +/* 0x0003 - buddylist.c */ +/* 0x0002 */ faim_export int aim_buddylist_reqrights(aim_session_t *, aim_conn_t *); +/* 0x0004 */ faim_export int aim_buddylist_set(aim_session_t *, aim_conn_t *, const char *); +/* 0x0004 */ faim_export int aim_buddylist_addbuddy(aim_session_t *, aim_conn_t *, const char *); +/* 0x0005 */ faim_export int aim_buddylist_removebuddy(aim_session_t *, aim_conn_t *, const char *); +/* 0x000b */ faim_export int aim_buddylist_oncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info); +/* 0x000c */ faim_export int aim_buddylist_offgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn); + + + +/* 0x000a - search.c */ +faim_export int aim_search_address(aim_session_t *, aim_conn_t *, const char *); + + + +/* 0x000d - chatnav.c */ +/* 0x000e - chat.c */ +/* These apply to exchanges as well. */ +#define AIM_CHATROOM_FLAG_EVILABLE 0x0001 +#define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002 +#define AIM_CHATROOM_FLAG_INSTANCING_ALLOWED 0x0004 +#define AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED 0x0008 + +struct aim_chat_exchangeinfo { + fu16_t number; + fu16_t flags; + char *name; + char *charset1; + char *lang1; + char *charset2; + char *lang2; +}; + +#define AIM_CHATFLAGS_NOREFLECT 0x0001 +#define AIM_CHATFLAGS_AWAY 0x0002 +faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const char *msg, int msglen); +faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance); +faim_export int aim_chat_attachname(aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance); +faim_export char *aim_chat_getname(aim_conn_t *conn); +faim_export aim_conn_t *aim_chat_getconn(aim_session_t *, const char *name); + +faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn); + +faim_export int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance); + +faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, fu16_t exchange); +faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name); + + + +/* 0x000f - odir.c */ +struct aim_odir { + char *first; + char *last; + char *middle; + char *maiden; + char *email; + char *country; + char *state; + char *city; + char *sn; + char *interest; + char *nick; + char *zip; + char *region; + char *address; + struct aim_odir *next; +}; + +faim_export int aim_odir_email(aim_session_t *, const char *, const char *); +faim_export int aim_odir_name(aim_session_t *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *); +faim_export int aim_odir_interest(aim_session_t *, const char *, const char *); + + + +/* 0x0010 - icon.c */ +faim_export int aim_bart_upload(aim_session_t *sess, const fu8_t *icon, fu16_t iconlen); +faim_export int aim_bart_request(aim_session_t *sess, const char *sn, const fu8_t *iconstr, fu16_t iconstrlen); + + + +/* 0x0013 - ssi.c */ +#define AIM_SSI_TYPE_BUDDY 0x0000 +#define AIM_SSI_TYPE_GROUP 0x0001 +#define AIM_SSI_TYPE_PERMIT 0x0002 +#define AIM_SSI_TYPE_DENY 0x0003 +#define AIM_SSI_TYPE_PDINFO 0x0004 +#define AIM_SSI_TYPE_PRESENCEPREFS 0x0005 +#define AIM_SSI_TYPE_ICONINFO 0x0014 + +#define AIM_SSI_ACK_SUCCESS 0x0000 +#define AIM_SSI_ACK_ITEMNOTFOUND 0x0002 +#define AIM_SSI_ACK_IDNUMINUSE 0x000a +#define AIM_SSI_ACK_ATMAX 0x000c +#define AIM_SSI_ACK_INVALIDNAME 0x000d +#define AIM_SSI_ACK_AUTHREQUIRED 0x000e + +struct aim_ssi_item { + char *name; + fu16_t gid; + fu16_t bid; + fu16_t type; + struct aim_tlvlist_s *data; + struct aim_ssi_item *next; +}; + +struct aim_ssi_tmp { + fu16_t action; + fu16_t ack; + char *name; + struct aim_ssi_item *item; + struct aim_ssi_tmp *next; +}; + +/* These build the actual SNACs and queue them to be sent */ +/* 0x0002 */ faim_export int aim_ssi_reqrights(aim_session_t *sess); +/* 0x0004 */ faim_export int aim_ssi_reqdata(aim_session_t *sess); +/* 0x0005 */ faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t localstamp, fu16_t localrev); +/* 0x0007 */ faim_export int aim_ssi_enable(aim_session_t *sess); +/* 0x0008 */ faim_export int aim_ssi_addmoddel(aim_session_t *sess); +/* 0x0011 */ faim_export int aim_ssi_modbegin(aim_session_t *sess); +/* 0x0012 */ faim_export int aim_ssi_modend(aim_session_t *sess); +/* 0x0014 */ faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg); +/* 0x0018 */ faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, char *msg); +/* 0x001a */ faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, fu8_t reply, char *msg); + +/* Client functions for retrieving SSI data */ +faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid); +faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, fu16_t type); +faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn); +faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn); +faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list); +faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list); +faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn); +faim_export char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn); +faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn); + +/* Client functions for changing SSI data */ +faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth); +faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name); +faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name); +faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group); +faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name); +faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name); +faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn); +faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias); +faim_export int aim_ssi_editcomment(aim_session_t *sess, const char *gn, const char *sn, const char *alias); +faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn); +faim_export int aim_ssi_cleanlist(aim_session_t *sess); +faim_export int aim_ssi_deletelist(aim_session_t *sess); +faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask); +faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence); +faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen); +faim_export int aim_ssi_delicon(aim_session_t *sess); + + + +/* 0x0015 - icq.c */ +#define AIM_ICQ_INFO_SIMPLE 0x001 +#define AIM_ICQ_INFO_SUMMARY 0x002 +#define AIM_ICQ_INFO_EMAIL 0x004 +#define AIM_ICQ_INFO_PERSONAL 0x008 +#define AIM_ICQ_INFO_ADDITIONAL 0x010 +#define AIM_ICQ_INFO_WORK 0x020 +#define AIM_ICQ_INFO_INTERESTS 0x040 +#define AIM_ICQ_INFO_ORGS 0x080 +#define AIM_ICQ_INFO_UNKNOWN 0x100 +#define AIM_ICQ_INFO_HAVEALL 0x1ff + +struct aim_icq_offlinemsg { + fu32_t sender; + fu16_t year; + fu8_t month, day, hour, minute; + fu8_t type; + fu8_t flags; + char *msg; + int msglen; +}; + +struct aim_icq_info { + fu16_t reqid; + + /* simple */ + fu32_t uin; + + /* general and "home" information (0x00c8) */ + char *nick; + char *first; + char *last; + char *email; + char *homecity; + char *homestate; + char *homephone; + char *homefax; + char *homeaddr; + char *mobile; + char *homezip; + fu16_t homecountry; +/* fu8_t timezone; + fu8_t hideemail; */ + + /* personal (0x00dc) */ + fu8_t age; + fu8_t unknown; + fu8_t gender; + char *personalwebpage; + fu16_t birthyear; + fu8_t birthmonth; + fu8_t birthday; + fu8_t language1; + fu8_t language2; + fu8_t language3; + + /* work (0x00d2) */ + char *workcity; + char *workstate; + char *workphone; + char *workfax; + char *workaddr; + char *workzip; + fu16_t workcountry; + char *workcompany; + char *workdivision; + char *workposition; + char *workwebpage; + + /* additional personal information (0x00e6) */ + char *info; + + /* email (0x00eb) */ + fu16_t numaddresses; + char **email2; + + /* we keep track of these in a linked list because we're 1337 */ + struct aim_icq_info *next; +}; + +faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess); +faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess); +faim_export int aim_icq_hideip(aim_session_t *sess); +faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd); +faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin); +faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin); +faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin); + + + +/* 0x0017 - auth.c */ +faim_export int aim_sendcookie(aim_session_t *, aim_conn_t *, const fu16_t length, const fu8_t *); +faim_export int aim_admin_changepasswd(aim_session_t *, aim_conn_t *, const char *newpw, const char *curpw); +faim_export int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info); +faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail); +faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick); + + + +/* 0x0018 - email.c */ +struct aim_emailinfo { + fu8_t *cookie16; + fu8_t *cookie8; + char *url; + fu16_t nummsgs; + fu8_t unread; + char *domain; + fu16_t flag; + struct aim_emailinfo *next; +}; + +faim_export int aim_email_sendcookies(aim_session_t *sess); +faim_export int aim_email_activate(aim_session_t *sess); + + + +#if defined(FAIM_INTERNAL) || defined(FAIM_NEED_TLV) +/* tlv.c - TLV handling */ + +/* TLV structure */ +typedef struct aim_tlv_s { + fu16_t type; + fu16_t length; + fu8_t *value; +} aim_tlv_t; + +/* TLV List structure */ +typedef struct aim_tlvlist_s { + aim_tlv_t *tlv; + struct aim_tlvlist_s *next; +} aim_tlvlist_t; + +/* TLV handling functions */ +faim_internal aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, fu16_t type, const int nth); +faim_internal char *aim_tlv_getstr(aim_tlvlist_t *list, const fu16_t type, const int nth); +faim_internal fu8_t aim_tlv_get8(aim_tlvlist_t *list, const fu16_t type, const int nth); +faim_internal fu16_t aim_tlv_get16(aim_tlvlist_t *list, const fu16_t type, const int nth); +faim_internal fu32_t aim_tlv_get32(aim_tlvlist_t *list, const fu16_t type, const int nth); + +/* TLV list handling functions */ +faim_internal aim_tlvlist_t *aim_tlvlist_read(aim_bstream_t *bs); +faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, fu16_t num); +faim_internal aim_tlvlist_t *aim_tlvlist_readlen(aim_bstream_t *bs, fu16_t len); +faim_internal aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig); + +faim_internal int aim_tlvlist_count(aim_tlvlist_t **list); +faim_internal int aim_tlvlist_size(aim_tlvlist_t **list); +faim_internal int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two); +faim_internal int aim_tlvlist_write(aim_bstream_t *bs, aim_tlvlist_t **list); +faim_internal void aim_tlvlist_free(aim_tlvlist_t **list); + +faim_internal int aim_tlvlist_add_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value); +faim_internal int aim_tlvlist_add_noval(aim_tlvlist_t **list, const fu16_t type); +faim_internal int aim_tlvlist_add_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value); +faim_internal int aim_tlvlist_add_16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value); +faim_internal int aim_tlvlist_add_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value); +faim_internal int aim_tlvlist_add_caps(aim_tlvlist_t **list, const fu16_t type, const fu32_t caps); +faim_internal int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, fu16_t type, aim_userinfo_t *userinfo); +faim_internal int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, fu16_t type, aim_tlvlist_t **tl); + +faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t lenth, const fu8_t *value); +faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const fu16_t type); +faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value); +faim_internal int aim_tlvlist_replace_16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value); +faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value); + +faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const fu16_t type); +#endif /* FAIM_INTERNAL */ + + + +/* util.c */ +/* + * These are really ugly. You'd think this was LISP. I wish it was. + * + * XXX With the advent of bstream's, these should be removed to enforce + * their use. + * + */ +#define aimutil_put8(buf, data) ((*(buf) = (fu8_t)(data)&0xff),1) +#define aimutil_get8(buf) ((*(buf))&0xff) +#define aimutil_put16(buf, data) ( \ + (*(buf) = (fu8_t)((data)>>8)&0xff), \ + (*((buf)+1) = (fu8_t)(data)&0xff), \ + 2) +#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff)) +#define aimutil_put32(buf, data) ( \ + (*((buf)) = (fu8_t)((data)>>24)&0xff), \ + (*((buf)+1) = (fu8_t)((data)>>16)&0xff), \ + (*((buf)+2) = (fu8_t)((data)>>8)&0xff), \ + (*((buf)+3) = (fu8_t)(data)&0xff), \ + 4) +#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \ + (((*((buf)+1))<<16)&0x00ff0000) + \ + (((*((buf)+2))<< 8)&0x0000ff00) + \ + (((*((buf)+3) )&0x000000ff))) + +/* Little-endian versions (damn ICQ) */ +#define aimutil_putle8(buf, data) ( \ + (*(buf) = (fu8_t)(data) & 0xff), \ + 1) +#define aimutil_getle8(buf) ( \ + (*(buf)) & 0xff \ + ) +#define aimutil_putle16(buf, data) ( \ + (*((buf)+0) = (fu8_t)((data) >> 0) & 0xff), \ + (*((buf)+1) = (fu8_t)((data) >> 8) & 0xff), \ + 2) +#define aimutil_getle16(buf) ( \ + (((*((buf)+0)) << 0) & 0x00ff) + \ + (((*((buf)+1)) << 8) & 0xff00) \ + ) +#define aimutil_putle32(buf, data) ( \ + (*((buf)+0) = (fu8_t)((data) >> 0) & 0xff), \ + (*((buf)+1) = (fu8_t)((data) >> 8) & 0xff), \ + (*((buf)+2) = (fu8_t)((data) >> 16) & 0xff), \ + (*((buf)+3) = (fu8_t)((data) >> 24) & 0xff), \ + 4) +#define aimutil_getle32(buf) ( \ + (((*((buf)+0)) << 0) & 0x000000ff) + \ + (((*((buf)+1)) << 8) & 0x0000ff00) + \ + (((*((buf)+2)) << 16) & 0x00ff0000) + \ + (((*((buf)+3)) << 24) & 0xff000000)) + + +faim_export int aimutil_putstr(char *, const char *, int); +faim_export fu16_t aimutil_iconsum(const fu8_t *buf, int buflen); +faim_export int aim_util_getlocalip(fu8_t *ip); +faim_export int aimutil_tokslen(char *toSearch, int theindex, char dl); +faim_export int aimutil_itemcnt(char *toSearch, char dl); +faim_export char *aimutil_itemindex(char *toSearch, int theindex, char dl); + +faim_export int aim_snlen(const char *sn); +faim_export int aim_sncmp(const char *sn1, const char *sn2); + +#include <aim_internal.h> + +#ifdef __cplusplus +} +#endif + +#endif /* __AIM_H__ */ diff --git a/libfaim/aim_cbtypes.h b/libfaim/aim_cbtypes.h new file mode 100644 index 0000000..200f0f5 --- /dev/null +++ b/libfaim/aim_cbtypes.h @@ -0,0 +1,310 @@ +/* + * AIM Callback Types + * + */ +#ifndef __AIM_CBTYPES_H__ +#define __AIM_CBTYPES_H__ + +/* + * SNAC Families. + */ +#define AIM_CB_FAM_ACK 0x0000 +#define AIM_CB_FAM_GEN 0x0001 +#define AIM_CB_FAM_LOC 0x0002 +#define AIM_CB_FAM_BUD 0x0003 +#define AIM_CB_FAM_MSG 0x0004 +#define AIM_CB_FAM_ADS 0x0005 +#define AIM_CB_FAM_INV 0x0006 +#define AIM_CB_FAM_ADM 0x0007 +#define AIM_CB_FAM_POP 0x0008 +#define AIM_CB_FAM_BOS 0x0009 +#define AIM_CB_FAM_LOK 0x000a +#define AIM_CB_FAM_STS 0x000b +#define AIM_CB_FAM_TRN 0x000c +#define AIM_CB_FAM_CTN 0x000d /* ChatNav */ +#define AIM_CB_FAM_CHT 0x000e /* Chat */ +#define AIM_CB_FAM_SCH 0x000f /* "New" search */ +#define AIM_CB_FAM_ICO 0x0010 /* Used for uploading buddy icons */ +#define AIM_CB_FAM_SSI 0x0013 /* Server stored information */ +#define AIM_CB_FAM_ICQ 0x0015 +#define AIM_CB_FAM_ATH 0x0017 +#define AIM_CB_FAM_EML 0x0018 +#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */ +#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */ + +/* + * SNAC Family: Ack. + * + * Not really a family, but treating it as one really + * helps it fit into the libfaim callback structure better. + * + */ +#define AIM_CB_ACK_ACK 0x0001 + +/* + * SNAC Family: General. + */ +#define AIM_CB_GEN_ERROR 0x0001 +#define AIM_CB_GEN_CLIENTREADY 0x0002 +#define AIM_CB_GEN_SERVERREADY 0x0003 +#define AIM_CB_GEN_SERVICEREQ 0x0004 +#define AIM_CB_GEN_REDIRECT 0x0005 +#define AIM_CB_GEN_RATEINFOREQ 0x0006 +#define AIM_CB_GEN_RATEINFO 0x0007 +#define AIM_CB_GEN_RATEINFOACK 0x0008 +#define AIM_CB_GEN_RATECHANGE 0x000a +#define AIM_CB_GEN_SERVERPAUSE 0x000b +#define AIM_CB_GEN_SERVERRESUME 0x000d +#define AIM_CB_GEN_REQSELFINFO 0x000e +#define AIM_CB_GEN_SELFINFO 0x000f +#define AIM_CB_GEN_EVIL 0x0010 +#define AIM_CB_GEN_SETIDLE 0x0011 +#define AIM_CB_GEN_MIGRATIONREQ 0x0012 +#define AIM_CB_GEN_MOTD 0x0013 +#define AIM_CB_GEN_SETPRIVFLAGS 0x0014 +#define AIM_CB_GEN_WELLKNOWNURL 0x0015 +#define AIM_CB_GEN_NOP 0x0016 +#define AIM_CB_GEN_DEFAULT 0xffff + +/* + * SNAC Family: Location Services. + */ +#define AIM_CB_LOC_ERROR 0x0001 +#define AIM_CB_LOC_REQRIGHTS 0x0002 +#define AIM_CB_LOC_RIGHTSINFO 0x0003 +#define AIM_CB_LOC_SETUSERINFO 0x0004 +#define AIM_CB_LOC_REQUSERINFO 0x0005 +#define AIM_CB_LOC_USERINFO 0x0006 +#define AIM_CB_LOC_WATCHERSUBREQ 0x0007 +#define AIM_CB_LOC_WATCHERNOT 0x0008 +#define AIM_CB_LOC_DEFAULT 0xffff + +/* + * SNAC Family: Buddy List Management Services. + */ +#define AIM_CB_BUD_ERROR 0x0001 +#define AIM_CB_BUD_REQRIGHTS 0x0002 +#define AIM_CB_BUD_RIGHTSINFO 0x0003 +#define AIM_CB_BUD_ADDBUDDY 0x0004 +#define AIM_CB_BUD_REMBUDDY 0x0005 +#define AIM_CB_BUD_REJECT 0x000a +#define AIM_CB_BUD_ONCOMING 0x000b +#define AIM_CB_BUD_OFFGOING 0x000c +#define AIM_CB_BUD_DEFAULT 0xffff + +/* + * SNAC Family: Messeging Services. + */ +#define AIM_CB_MSG_ERROR 0x0001 +#define AIM_CB_MSG_PARAMINFO 0x0005 +#define AIM_CB_MSG_INCOMING 0x0007 +#define AIM_CB_MSG_EVIL 0x0009 +#define AIM_CB_MSG_MISSEDCALL 0x000a +#define AIM_CB_MSG_CLIENTAUTORESP 0x000b +#define AIM_CB_MSG_ACK 0x000c +#define AIM_CB_MSG_MTN 0x0014 +#define AIM_CB_MSG_DEFAULT 0xffff + +/* + * SNAC Family: Advertisement Services + */ +#define AIM_CB_ADS_ERROR 0x0001 +#define AIM_CB_ADS_DEFAULT 0xffff + +/* + * SNAC Family: Invitation Services. + */ +#define AIM_CB_INV_ERROR 0x0001 +#define AIM_CB_INV_DEFAULT 0xffff + +/* + * SNAC Family: Administrative Services. + */ +#define AIM_CB_ADM_ERROR 0x0001 +#define AIM_CB_ADM_INFOCHANGE_REPLY 0x0005 +#define AIM_CB_ADM_DEFAULT 0xffff + +/* + * SNAC Family: Popup Messages + */ +#define AIM_CB_POP_ERROR 0x0001 +#define AIM_CB_POP_DEFAULT 0xffff + +/* + * SNAC Family: Misc BOS Services. + */ +#define AIM_CB_BOS_ERROR 0x0001 +#define AIM_CB_BOS_RIGHTSQUERY 0x0002 +#define AIM_CB_BOS_RIGHTS 0x0003 +#define AIM_CB_BOS_DEFAULT 0xffff + +/* + * SNAC Family: User Lookup Services + */ +#define AIM_CB_LOK_ERROR 0x0001 +#define AIM_CB_LOK_DEFAULT 0xffff + +/* + * SNAC Family: User Status Services + */ +#define AIM_CB_STS_ERROR 0x0001 +#define AIM_CB_STS_SETREPORTINTERVAL 0x0002 +#define AIM_CB_STS_REPORTACK 0x0004 +#define AIM_CB_STS_DEFAULT 0xffff + +/* + * SNAC Family: Translation Services + */ +#define AIM_CB_TRN_ERROR 0x0001 +#define AIM_CB_TRN_DEFAULT 0xffff + +/* + * SNAC Family: Chat Navigation Services + */ +#define AIM_CB_CTN_ERROR 0x0001 +#define AIM_CB_CTN_CREATE 0x0008 +#define AIM_CB_CTN_INFO 0x0009 +#define AIM_CB_CTN_DEFAULT 0xffff + +/* + * SNAC Family: Chat Services + */ +#define AIM_CB_CHT_ERROR 0x0001 +#define AIM_CB_CHT_ROOMINFOUPDATE 0x0002 +#define AIM_CB_CHT_USERJOIN 0x0003 +#define AIM_CB_CHT_USERLEAVE 0x0004 +#define AIM_CB_CHT_OUTGOINGMSG 0x0005 +#define AIM_CB_CHT_INCOMINGMSG 0x0006 +#define AIM_CB_CHT_DEFAULT 0xffff + +/* + * SNAC Family: "New" Search + */ +#define AIM_CB_SCH_ERROR 0x0001 +#define AIM_CB_SCH_SEARCH 0x0002 +#define AIM_CB_SCH_RESULTS 0x0003 + +/* + * SNAC Family: Buddy icons + */ +#define AIM_CB_ICO_ERROR 0x0001 +#define AIM_CB_ICO_REQUEST 0x0004 +#define AIM_CB_ICO_RESPONSE 0x0005 + +/* + * SNAC Family: ICQ + * + * Most of these are actually special. + */ +#define AIM_CB_ICQ_ERROR 0x0001 +#define AIM_CB_ICQ_OFFLINEMSG 0x00f0 +#define AIM_CB_ICQ_OFFLINEMSGCOMPLETE 0x00f1 +#define AIM_CB_ICQ_INFO 0x00f2 +#define AIM_CB_ICQ_ALIAS 0x00f3 +#define AIM_CB_ICQ_DEFAULT 0xffff + +/* + * SNAC Family: Server-Stored Buddy Lists + */ +#define AIM_CB_SSI_ERROR 0x0001 +#define AIM_CB_SSI_REQRIGHTS 0x0002 +#define AIM_CB_SSI_RIGHTSINFO 0x0003 +#define AIM_CB_SSI_REQDATA 0x0004 +#define AIM_CB_SSI_REQIFCHANGED 0x0005 +#define AIM_CB_SSI_LIST 0x0006 +#define AIM_CB_SSI_ACTIVATE 0x0007 +#define AIM_CB_SSI_ADD 0x0008 +#define AIM_CB_SSI_MOD 0x0009 +#define AIM_CB_SSI_DEL 0x000A +#define AIM_CB_SSI_SRVACK 0x000E +#define AIM_CB_SSI_NOLIST 0x000F +#define AIM_CB_SSI_EDITSTART 0x0011 +#define AIM_CB_SSI_EDITSTOP 0x0012 +#define AIM_CB_SSI_SENDAUTH 0x0014 +#define AIM_CB_SSI_RECVAUTH 0x0015 +#define AIM_CB_SSI_SENDAUTHREQ 0x0018 +#define AIM_CB_SSI_RECVAUTHREQ 0x0019 +#define AIM_CB_SSI_SENDAUTHREP 0x001a +#define AIM_CB_SSI_RECVAUTHREP 0x001b +#define AIM_CB_SSI_ADDED 0x001c + +/* + * SNAC Family: Authorizer + * + * Used only in protocol versions three and above. + * + */ +#define AIM_CB_ATH_ERROR 0x0001 +#define AIM_CB_ATH_LOGINREQEST 0x0002 +#define AIM_CB_ATH_LOGINRESPONSE 0x0003 +#define AIM_CB_ATH_AUTHREQ 0x0006 +#define AIM_CB_ATH_AUTHRESPONSE 0x0007 + +/* + * SNAC Family: Email + * + * Used for getting information on the email address + * associated with your screen name. + * + */ +#define AIM_CB_EML_ERROR 0x0001 +#define AIM_CB_EML_SENDCOOKIES 0x0006 +#define AIM_CB_EML_MAILSTATUS 0x0007 +#define AIM_CB_EML_INIT 0x0016 + +/* + * OFT Services + * + * For all of the above #defines, the number is the subtype + * of the SNAC. For OFT #defines, the number is the + * "hdrtype" which comes after the magic string and OFT + * packet length. + * + * I'm pretty sure the ODC ones are arbitrary right now, + * that should be changed. + */ +#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001 /* connect request -- actually an OSCAR CAP */ +#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002 +#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003 +#define AIM_CB_OFT_DIRECTIMTYPING 0x0004 +#define AIM_CB_OFT_DIRECTIM_ESTABLISHED 0x0005 + +#define AIM_CB_OFT_PROMPT 0x0101 /* "I am going to send you this file, is that ok?" */ +#define AIM_CB_OFT_RESUMESOMETHING 0x0106 /* I really don't know */ +#define AIM_CB_OFT_ACK 0x0202 /* "Yes, it is ok for you to send me that file" */ +#define AIM_CB_OFT_DONE 0x0204 /* "I received that file with no problems, thanks a bunch" */ +#define AIM_CB_OFT_RESUME 0x0205 /* Resume transferring, sent by whoever paused? */ +#define AIM_CB_OFT_RESUMEACK 0x0207 /* Not really sure */ + +#define AIM_CB_OFT_GETFILE_REQUESTLISTING 0x1108 /* "I have a listing.txt file, do you want it?" */ +#define AIM_CB_OFT_GETFILE_RECEIVELISTING 0x1209 /* "Yes, please send me your listing.txt file" */ +#define AIM_CB_OFT_GETFILE_RECEIVEDLISTING 0x120a /* received corrupt listing.txt file? */ /* I'm just guessing about this one... */ +#define AIM_CB_OFT_GETFILE_ACKLISTING 0x120b /* "I received the listing.txt file successfully" */ +#define AIM_CB_OFT_GETFILE_REQUESTFILE 0x120c /* "Please send me this file" */ + +#define AIM_CB_OFT_ESTABLISHED 0xFFFF /* connection to buddy initiated */ + +/* + * SNAC Family: Internal Messages + * + * This isn't truely a SNAC family either, but using + * these, we can integrated non-SNAC services into + * the SNAC-centered libfaim callback structure. + * + */ +#define AIM_CB_SPECIAL_AUTHSUCCESS 0x0001 +#define AIM_CB_SPECIAL_AUTHOTHER 0x0002 +#define AIM_CB_SPECIAL_CONNERR 0x0003 +#define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004 +#define AIM_CB_SPECIAL_FLAPVER 0x0005 +#define AIM_CB_SPECIAL_CONNINITDONE 0x0006 +#define AIM_CB_SPECIAL_IMAGETRANSFER 0x0007 +#define AIM_CB_SPECIAL_MSGTIMEOUT 0x0008 +#define AIM_CB_SPECIAL_CONNDEAD 0x0009 +#define AIM_CB_SPECIAL_UNKNOWN 0xffff +#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN + +/* SNAC flags */ +#define AIM_SNACFLAGS_DESTRUCTOR 0x0001 + +#endif/*__AIM_CBTYPES_H__ */ diff --git a/libfaim/aim_internal.h b/libfaim/aim_internal.h new file mode 100644 index 0000000..aed99a4 --- /dev/null +++ b/libfaim/aim_internal.h @@ -0,0 +1,226 @@ +/* + * aim_internal.h -- prototypes/structs for the guts of libfaim + * + */ + +#ifdef FAIM_INTERNAL +#ifndef __AIM_INTERNAL_H__ +#define __AIM_INTERNAL_H__ 1 + +typedef struct { + fu16_t family; + fu16_t subtype; + fu16_t flags; + fu32_t id; +} aim_modsnac_t; + +#define AIM_MODULENAME_MAXLEN 16 +#define AIM_MODFLAG_MULTIFAMILY 0x0001 +typedef struct aim_module_s { + fu16_t family; + fu16_t version; + fu16_t toolid; + fu16_t toolversion; + fu16_t flags; + char name[AIM_MODULENAME_MAXLEN+1]; + int (*snachandler)(aim_session_t *sess, struct aim_module_s *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs); + + void (*shutdown)(aim_session_t *sess, struct aim_module_s *mod); + void *priv; + struct aim_module_s *next; +} aim_module_t; + +faim_internal int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *)); +faim_internal void aim__shutdownmodules(aim_session_t *sess); +faim_internal aim_module_t *aim__findmodulebygroup(aim_session_t *sess, fu16_t group); +faim_internal aim_module_t *aim__findmodule(aim_session_t *sess, const char *name); + +faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int auth_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int misc_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int invite_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int translate_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int popups_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int adverts_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int odir_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int bart_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int email_modfirst(aim_session_t *sess, aim_module_t *mod); + +faim_internal int aim_genericreq_n(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype); +faim_internal int aim_genericreq_n_snacid(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype); +faim_internal int aim_genericreq_l(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu32_t *); +faim_internal int aim_genericreq_s(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu16_t *); + +#define AIMBS_CURPOSPAIR(x) ((x)->data + (x)->offset), ((x)->len - (x)->offset) + +/* bstream.c */ +faim_internal int aim_bstream_init(aim_bstream_t *bs, fu8_t *data, int len); +faim_internal int aim_bstream_empty(aim_bstream_t *bs); +faim_internal int aim_bstream_curpos(aim_bstream_t *bs); +faim_internal int aim_bstream_setpos(aim_bstream_t *bs, int off); +faim_internal void aim_bstream_rewind(aim_bstream_t *bs); +faim_internal int aim_bstream_advance(aim_bstream_t *bs, int n); +faim_internal fu8_t aimbs_get8(aim_bstream_t *bs); +faim_internal fu16_t aimbs_get16(aim_bstream_t *bs); +faim_internal fu32_t aimbs_get32(aim_bstream_t *bs); +faim_internal fu8_t aimbs_getle8(aim_bstream_t *bs); +faim_internal fu16_t aimbs_getle16(aim_bstream_t *bs); +faim_internal fu32_t aimbs_getle32(aim_bstream_t *bs); +faim_internal int aimbs_put8(aim_bstream_t *bs, fu8_t v); +faim_internal int aimbs_put16(aim_bstream_t *bs, fu16_t v); +faim_internal int aimbs_put32(aim_bstream_t *bs, fu32_t v); +faim_internal int aimbs_putle8(aim_bstream_t *bs, fu8_t v); +faim_internal int aimbs_putle16(aim_bstream_t *bs, fu16_t v); +faim_internal int aimbs_putle32(aim_bstream_t *bs, fu32_t v); +faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, fu8_t *buf, int len); +faim_internal fu8_t *aimbs_getraw(aim_bstream_t *bs, int len); +faim_internal char *aimbs_getstr(aim_bstream_t *bs, int len); +faim_internal int aimbs_putraw(aim_bstream_t *bs, const fu8_t *v, int len); +faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len); + +/* conn.c */ +faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src); + +/* ft.c */ +faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr); + +/* rxhandlers.c */ +faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type); +faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type, aim_frame_t *ptr); +faim_internal int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...); +faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src); + +/* rxqueue.c */ +faim_internal int aim_recv(int fd, void *buf, size_t count); +faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count); +faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn); +faim_internal void aim_frame_destroy(aim_frame_t *); + +/* txqueue.c */ +faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, fu8_t framing, fu16_t chan, int datalen); +faim_internal int aim_tx_enqueue(aim_session_t *, aim_frame_t *); +faim_internal flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *); +faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur); +faim_internal void aim_tx_cleanqueue(aim_session_t *, aim_conn_t *); + +/* XXX - What is this? faim_internal int aim_tx_printqueue(aim_session_t *); */ + +/* + * Generic SNAC structure. Rarely if ever used. + */ +typedef struct aim_snac_s { + aim_snacid_t id; + fu16_t family; + fu16_t type; + fu16_t flags; + void *data; + time_t issuetime; + struct aim_snac_s *next; +} aim_snac_t; + +/* snac.c */ +faim_internal void aim_initsnachash(aim_session_t *sess); +faim_internal aim_snacid_t aim_newsnac(aim_session_t *, aim_snac_t *newsnac); +faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const fu16_t family, const fu16_t type, const fu16_t flags, const void *data, const int datalen); +faim_internal aim_snac_t *aim_remsnac(aim_session_t *, aim_snacid_t id); +faim_internal int aim_putsnac(aim_bstream_t *, fu16_t family, fu16_t type, fu16_t flags, aim_snacid_t id); + +/* Stored in ->priv of the service request SNAC for chats. */ +struct chatsnacinfo { + fu16_t exchange; + char name[128]; + fu16_t instance; +}; + +/* + * In SNACland, the terms 'family' and 'group' are synonymous -- the former + * is my term, the latter is AOL's. + */ +struct snacgroup { + fu16_t group; + struct snacgroup *next; +}; + +#ifdef FAIM_NEED_CONN_INTERNAL +struct snacpair { + fu16_t group; + fu16_t subtype; + struct snacpair *next; +}; + +struct rateclass { + fu16_t classid; + fu32_t windowsize; + fu32_t clear; + fu32_t alert; + fu32_t limit; + fu32_t disconnect; + fu32_t current; + fu32_t max; + fu8_t unknown[5]; /* only present in versions >= 3 */ + struct snacpair *members; + struct rateclass *next; +}; +#endif /* FAIM_NEED_CONN_INTERNAL */ + +/* + * This is inside every connection. But it is a void * to anything + * outside of libfaim. It should remain that way. It's called data + * abstraction. Maybe you've heard of it. (Probably not if you're a + * libfaim user.) + * + */ +typedef struct aim_conn_inside_s { + struct snacgroup *groups; + struct rateclass *rates; +} aim_conn_inside_t; + +faim_internal void aim_conn_addgroup(aim_conn_t *conn, fu16_t group); + +faim_internal int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie); +faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, fu8_t *cookie, int type); +faim_internal aim_msgcookie_t *aim_mkcookie(fu8_t *, int, void *); +faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *, const unsigned char *, const int); +faim_internal int aim_freecookie(aim_session_t *sess, aim_msgcookie_t *cookie); +faim_internal int aim_msgcookie_gettype(int reqclass); +faim_internal int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie); + +/* 0x0002 - locate.c */ +faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn); +faim_internal fu32_t aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len); +faim_internal fu32_t aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len); +faim_internal int aim_putcap(aim_bstream_t *bs, fu32_t caps); +faim_internal void aim_info_free(aim_userinfo_t *); +faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *); +faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info); + +faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo); + +faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn); + +/* These are all handled internally now. */ +faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn); +faim_internal int aim_reqrates(aim_session_t *, aim_conn_t *); +faim_internal int aim_rates_addparam(aim_session_t *, aim_conn_t *); +faim_internal int aim_rates_delparam(aim_session_t *, aim_conn_t *); + +faim_internal void faimdprintf(aim_session_t *sess, int dlevel, const char *format, ...); + +#ifndef FAIM_INTERNAL_INSANE +#define printf() printf called inside libfaim +#define sprintf() unbounded sprintf used inside libfaim +#endif + +#endif /* __AIM_INTERNAL_H__ */ +#endif /* FAIM_INTERNAL */ diff --git a/libfaim/auth.c b/libfaim/auth.c new file mode 100644 index 0000000..0aa7d62 --- /dev/null +++ b/libfaim/auth.c @@ -0,0 +1,558 @@ +/* + * Family 0x0017 - Authentication. + * + * Deals with the authorizer for SNAC-based login, and also old-style + * non-SNAC login. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#include "md5.h" + +#include <ctype.h> + +/** + * Encode a password using old XOR method + * + * This takes a const pointer to a (null terminated) string + * containing the unencoded password. It also gets passed + * an already allocated buffer to store the encoded password. + * This buffer should be the exact length of the password without + * the null. The encoded password buffer /is not %NULL terminated/. + * + * The encoding_table seems to be a fixed set of values. We'll + * hope it doesn't change over time! + * + * This is only used for the XOR method, not the better MD5 method. + * + * @param password Incoming password. + * @param encoded Buffer to put encoded password. + */ +static int aim_encode_password(const char *password, fu8_t *encoded) +{ + fu8_t encoding_table[] = { +#if 0 /* old v1 table */ + 0xf3, 0xb3, 0x6c, 0x99, + 0x95, 0x3f, 0xac, 0xb6, + 0xc5, 0xfa, 0x6b, 0x63, + 0x69, 0x6c, 0xc3, 0x9f +#else /* v2.1 table, also works for ICQ */ + 0xf3, 0x26, 0x81, 0xc4, + 0x39, 0x86, 0xdb, 0x92, + 0x71, 0xa3, 0xb9, 0xe6, + 0x53, 0x7a, 0x95, 0x7c +#endif + }; + int i; + + for (i = 0; i < strlen(password); i++) + encoded[i] = (password[i] ^ encoding_table[i]); + + return 0; +} + +#ifdef USE_OLD_MD5 +static int aim_encode_password_md5(const char *password, const char *key, fu8_t *digest) +{ + md5_state_t state; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)key, strlen(key)); + md5_append(&state, (const md5_byte_t *)password, strlen(password)); + md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); + md5_finish(&state, (md5_byte_t *)digest); + + return 0; +} +#else +static int aim_encode_password_md5(const char *password, const char *key, fu8_t *digest) +{ + md5_state_t state; + fu8_t passdigest[16]; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)password, strlen(password)); + md5_finish(&state, (md5_byte_t *)&passdigest); + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)key, strlen(key)); + md5_append(&state, (const md5_byte_t *)&passdigest, 16); + md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); + md5_finish(&state, (md5_byte_t *)digest); + + return 0; +} +#endif + +/* + * The FLAP version is sent by itself at the beginning of authorization + * connections. The FLAP version is also sent before the cookie when connecting + * for other services (BOS, chatnav, chat, etc.). + */ +faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *fr; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4))) + return -ENOMEM; + + aimbs_put32(&fr->data, 0x00000001); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * This just pushes the passed cookie onto the passed connection, without + * the SNAC header or any of that. + * + * Very commonly used, as every connection except auth will require this to + * be the first thing you send. + * + */ +faim_export int aim_sendcookie(aim_session_t *sess, aim_conn_t *conn, const fu16_t length, const fu8_t *chipsahoy) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0001, 4+2+2+length))) + return -ENOMEM; + + aimbs_put32(&fr->data, 0x00000001); + aim_tlvlist_add_raw(&tl, 0x0006, length, chipsahoy); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Part two of the ICQ hack. Note the ignoring of the key. + */ +static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + int passwdlen; + fu8_t *password_encoded; + + passwdlen = strlen(password); + if (!(password_encoded = (char *)malloc(passwdlen+1))) + return -ENOMEM; + if (passwdlen > MAXICQPASSLEN) + passwdlen = MAXICQPASSLEN; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 1152))) { + free(password_encoded); + return -ENOMEM; + } + + aim_encode_password(password, password_encoded); + + aimbs_put32(&fr->data, 0x00000001); /* FLAP Version */ + aim_tlvlist_add_raw(&tl, 0x0001, strlen(sn), sn); + aim_tlvlist_add_raw(&tl, 0x0002, passwdlen, password_encoded); + + if (ci->clientstring) + aim_tlvlist_add_raw(&tl, 0x0003, strlen(ci->clientstring), ci->clientstring); + aim_tlvlist_add_16(&tl, 0x0016, (fu16_t)ci->clientid); + aim_tlvlist_add_16(&tl, 0x0017, (fu16_t)ci->major); + aim_tlvlist_add_16(&tl, 0x0018, (fu16_t)ci->minor); + aim_tlvlist_add_16(&tl, 0x0019, (fu16_t)ci->point); + aim_tlvlist_add_16(&tl, 0x001a, (fu16_t)ci->build); + aim_tlvlist_add_32(&tl, 0x0014, (fu32_t)ci->distrib); /* distribution chan */ + aim_tlvlist_add_raw(&tl, 0x000f, strlen(ci->lang), ci->lang); + aim_tlvlist_add_raw(&tl, 0x000e, strlen(ci->country), ci->country); + + aim_tlvlist_write(&fr->data, &tl); + + free(password_encoded); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0002 + * + * This is the initial login request packet. + * + * NOTE!! If you want/need to make use of the aim_sendmemblock() function, + * then the client information you send here must exactly match the + * executable that you're pulling the data from. + * + * Java AIM 1.1.19: + * clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001" + * clientid = 0x0001 + * major = 0x0001 + * minor = 0x0001 + * point = (not sent) + * build = 0x0013 + * unknown= (not sent) + * + * AIM for Linux 1.1.112: + * clientstring = "AOL Instant Messenger (SM)" + * clientid = 0x1d09 + * major = 0x0001 + * minor = 0x0001 + * point = 0x0001 + * build = 0x0070 + * unknown= 0x0000008b + * serverstore = 0x01 + * + */ +faim_export int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci, const char *key) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + fu8_t digest[16]; + aim_snacid_t snacid; + + if (!ci || !sn || !password) + return -EINVAL; + + /* If we're signing on an ICQ account then use the older, XOR login method */ + if (isdigit(sn[0])) + return goddamnicq2(sess, conn, sn, password, ci); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid); + + aim_tlvlist_add_raw(&tl, 0x0001, strlen(sn), sn); + + aim_encode_password_md5(password, key, digest); + aim_tlvlist_add_raw(&tl, 0x0025, 16, digest); + +#ifndef USE_OLD_MD5 + aim_tlvlist_add_noval(&tl, 0x004c); +#endif + + if (ci->clientstring) + aim_tlvlist_add_raw(&tl, 0x0003, strlen(ci->clientstring), ci->clientstring); + aim_tlvlist_add_16(&tl, 0x0016, (fu16_t)ci->clientid); + aim_tlvlist_add_16(&tl, 0x0017, (fu16_t)ci->major); + aim_tlvlist_add_16(&tl, 0x0018, (fu16_t)ci->minor); + aim_tlvlist_add_16(&tl, 0x0019, (fu16_t)ci->point); + aim_tlvlist_add_16(&tl, 0x001a, (fu16_t)ci->build); + aim_tlvlist_add_32(&tl, 0x0014, (fu32_t)ci->distrib); + aim_tlvlist_add_raw(&tl, 0x000f, strlen(ci->lang), ci->lang); + aim_tlvlist_add_raw(&tl, 0x000e, strlen(ci->country), ci->country); + +#ifndef NOSSI + /* + * If set, old-fashioned buddy lists will not work. You will need + * to use SSI. + */ + aim_tlvlist_add_8(&tl, 0x004a, 0x01); +#endif + + aim_tlvlist_write(&fr->data, &tl); + + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * This is sent back as a general response to the login command. + * It can be either an error or a success, depending on the + * precense of certain TLVs. + * + * The client should check the value passed as errorcode. If + * its nonzero, there was an error. + */ +static int parse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_tlvlist_t *tlvlist; + aim_rxcallback_t userfunc; + struct aim_authresp_info *info; + int ret = 0; + + info = (struct aim_authresp_info *)malloc(sizeof(struct aim_authresp_info)); + memset(info, 0, sizeof(struct aim_authresp_info)); + + /* + * Read block of TLVs. All further data is derived + * from what is parsed here. + */ + tlvlist = aim_tlvlist_read(bs); + + /* + * No matter what, we should have a screen name. + */ + memset(sess->sn, 0, sizeof(sess->sn)); + if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) { + info->sn = aim_tlv_getstr(tlvlist, 0x0001, 1); + strncpy(sess->sn, info->sn, sizeof(sess->sn)); + } + + /* + * Check for an error code. If so, we should also + * have an error url. + */ + if (aim_tlv_gettlv(tlvlist, 0x0008, 1)) + info->errorcode = aim_tlv_get16(tlvlist, 0x0008, 1); + if (aim_tlv_gettlv(tlvlist, 0x0004, 1)) + info->errorurl = aim_tlv_getstr(tlvlist, 0x0004, 1); + + /* + * BOS server address. + */ + if (aim_tlv_gettlv(tlvlist, 0x0005, 1)) + info->bosip = aim_tlv_getstr(tlvlist, 0x0005, 1); + + /* + * Authorization cookie. + */ + if (aim_tlv_gettlv(tlvlist, 0x0006, 1)) { + aim_tlv_t *tmptlv; + + tmptlv = aim_tlv_gettlv(tlvlist, 0x0006, 1); + + info->cookielen = tmptlv->length; + info->cookie = tmptlv->value; + } + + /* + * The email address attached to this account + * Not available for ICQ or @mac.com logins. + * If you receive this TLV, then you are allowed to use + * family 0x0018 to check the status of your email. + * XXX - Not really true! + */ + if (aim_tlv_gettlv(tlvlist, 0x0011, 1)) + info->email = aim_tlv_getstr(tlvlist, 0x0011, 1); + + /* + * The registration status. (Not real sure what it means.) + * Not available for ICQ or @mac.com logins. + * + * 1 = No disclosure + * 2 = Limited disclosure + * 3 = Full disclosure + * + * This has to do with whether your email address is available + * to other users or not. AFAIK, this feature is no longer used. + * + * Means you can use the admin family? (0x0007) + * + */ + if (aim_tlv_gettlv(tlvlist, 0x0013, 1)) + info->regstatus = aim_tlv_get16(tlvlist, 0x0013, 1); + + if (aim_tlv_gettlv(tlvlist, 0x0040, 1)) + info->latestbeta.build = aim_tlv_get32(tlvlist, 0x0040, 1); + if (aim_tlv_gettlv(tlvlist, 0x0041, 1)) + info->latestbeta.url = aim_tlv_getstr(tlvlist, 0x0041, 1); + if (aim_tlv_gettlv(tlvlist, 0x0042, 1)) + info->latestbeta.info = aim_tlv_getstr(tlvlist, 0x0042, 1); + if (aim_tlv_gettlv(tlvlist, 0x0043, 1)) + info->latestbeta.name = aim_tlv_getstr(tlvlist, 0x0043, 1); + if (aim_tlv_gettlv(tlvlist, 0x0048, 1)) + ; /* beta serial */ + + if (aim_tlv_gettlv(tlvlist, 0x0044, 1)) + info->latestrelease.build = aim_tlv_get32(tlvlist, 0x0044, 1); + if (aim_tlv_gettlv(tlvlist, 0x0045, 1)) + info->latestrelease.url = aim_tlv_getstr(tlvlist, 0x0045, 1); + if (aim_tlv_gettlv(tlvlist, 0x0046, 1)) + info->latestrelease.info = aim_tlv_getstr(tlvlist, 0x0046, 1); + if (aim_tlv_gettlv(tlvlist, 0x0047, 1)) + info->latestrelease.name = aim_tlv_getstr(tlvlist, 0x0047, 1); + if (aim_tlv_gettlv(tlvlist, 0x0049, 1)) + ; /* lastest release serial */ + + /* + * URL to change password. + */ + if (aim_tlv_gettlv(tlvlist, 0x0054, 1)) + info->chpassurl = aim_tlv_getstr(tlvlist, 0x0054, 1); + + /* + * Unknown. Seen on an @mac.com screen name with value of 0x003f + */ + if (aim_tlv_gettlv(tlvlist, 0x0055, 1)) + ; + + sess->authinfo = info; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003))) + ret = userfunc(sess, rx, info); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0007 (kind of) - Send a fake type 0x0007 SNAC to the client + * + * This is a bit confusing. + * + * Normal SNAC login goes like this: + * - connect + * - server sends flap version + * - client sends flap version + * - client sends screen name (17/6) + * - server sends hash key (17/7) + * - client sends auth request (17/2 -- aim_send_login) + * - server yells + * + * XOR login (for ICQ) goes like this: + * - connect + * - server sends flap version + * - client sends auth request which contains flap version (aim_send_login) + * - server yells + * + * For the client API, we make them implement the most complicated version, + * and for the simpler version, we fake it and make it look like the more + * complicated process. + * + * This is done by giving the client a faked key, just so we can convince + * them to call aim_send_login right away, which will detect the session + * flag that says this is XOR login and ignore the key, sending an ICQ + * login request instead of the normal SNAC one. + * + * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/. + * + * XXX This may cause problems if the client relies on callbacks only + * being called from the context of aim_rxdispatch()... + * + */ +static int goddamnicq(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t fr; + aim_rxcallback_t userfunc; + + fr.conn = conn; + + if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007))) + userfunc(sess, &fr, ""); + + return 0; +} + +/* + * Subtype 0x0006 + * + * In AIM 3.5 protocol, the first stage of login is to request login from the + * Authorizer, passing it the screen name for verification. If the name is + * invalid, a 0017/0003 is spit back, with the standard error contents. If + * valid, a 0017/0007 comes back, which is the signal to send it the main + * login command (0017/0002). + * + */ +faim_export int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !conn || !sn) + return -EINVAL; + + if (isdigit(sn[0])) + return goddamnicq(sess, conn, sn); + + aim_sendflapver(sess, conn); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn) /*+8*/ ))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid); + + aim_tlvlist_add_raw(&tl, 0x0001, strlen(sn), sn); +/* aim_tlvlist_add_noval(&tl, 0x004b); + aim_tlvlist_add_noval(&tl, 0x005a); */ + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0007 + * + * Middle handler for 0017/0007 SNACs. Contains the auth key prefixed + * by only its length in a two byte word. + * + * Calls the client, which should then use the value to call aim_send_login. + * + */ +static int keyparse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int keylen, ret = 1; + aim_rxcallback_t userfunc; + char *keystr; + + keylen = aimbs_get16(bs); + keystr = aimbs_getstr(bs, keylen); + + /* XXX - When GiantGrayPanda signed on AIM I got a thing asking me to register + * for the netscape network. This SNAC had a type 0x0058 TLV with length 10. + * Data is 0x0007 0004 3e19 ae1e 0006 0004 0000 0005 */ + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, keystr); + + free(keystr); + + return ret; +} + +static void auth_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + if (sess->authinfo) { + free(sess->authinfo->sn); + free(sess->authinfo->bosip); + free(sess->authinfo->errorurl); + free(sess->authinfo->email); + free(sess->authinfo->chpassurl); + free(sess->authinfo->latestrelease.name); + free(sess->authinfo->latestrelease.url); + free(sess->authinfo->latestrelease.info); + free(sess->authinfo->latestbeta.name); + free(sess->authinfo->latestbeta.url); + free(sess->authinfo->latestbeta.info); + free(sess->authinfo); + } +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return parse(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return keyparse(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int auth_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0017; + mod->version = 0x0000; + mod->flags = 0; + strncpy(mod->name, "auth", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = auth_shutdown; + + return 0; +} diff --git a/libfaim/bart.c b/libfaim/bart.c new file mode 100644 index 0000000..e46667b --- /dev/null +++ b/libfaim/bart.c @@ -0,0 +1,165 @@ +/* + * Family 0x0010 - Server stored buddy art + * + * Used for storing and retrieving your cute little buddy icon + * from the AIM servers. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/** + * Subtype 0x0002 - Upload your icon. + * + * @param sess The oscar session. + * @param conn The icon connection for this session. + * @param icon The raw data of the icon image file. + * @param iconlen Length of the raw data of the icon image file. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_bart_upload(aim_session_t *sess, const fu8_t *icon, fu16_t iconlen) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !icon || !iconlen) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 2 + 2+iconlen))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x0010, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0010, 0x0002, 0x0000, snacid); + + /* The reference number for the icon */ + aimbs_put16(&fr->data, 1); + + /* The icon */ + aimbs_put16(&fr->data, iconlen); + aimbs_putraw(&fr->data, icon, iconlen); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0003 - Acknowledgement for uploading a buddy icon. + * + * You get this honky after you upload a buddy icon. + */ +static int uploadack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t something, somethingelse; + fu8_t onemorething; + + something = aimbs_get16(bs); + somethingelse = aimbs_get16(bs); + onemorething = aimbs_get8(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + return ret; +} + +/** + * Subtype 0x0004 - Request someone's icon. + * + * @param sess The oscar session. + * @param conn The icon connection for this session. + * @param sn The screen name of the person who's icon you are requesting. + * @param iconcsum The MD5 checksum of the icon you are requesting. + * @param iconcsumlen Length of the MD5 checksum given above. Should be 10 bytes. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_bart_request(aim_session_t *sess, const char *sn, const fu8_t *iconcsum, fu16_t iconcsumlen) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 4 + 1+iconcsumlen))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x0010, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0010, 0x0004, 0x0000, snacid); + + /* Screen name */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + /* Some numbers. You like numbers, right? */ + aimbs_put8(&fr->data, 0x01); + aimbs_put16(&fr->data, 0x0001); + aimbs_put8(&fr->data, 0x01); + + /* Icon string */ + aimbs_put8(&fr->data, iconcsumlen); + aimbs_putraw(&fr->data, iconcsum, iconcsumlen); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0005 - Receive a buddy icon. + * + * This is sent in response to a buddy icon request. + */ +static int parseicon(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + char *sn; + fu16_t flags, iconlen; + fu8_t number, iconcsumlen, *iconcsum, *icon; + + sn = aimbs_getstr(bs, aimbs_get8(bs)); + flags = aimbs_get16(bs); + number = aimbs_get8(bs); + iconcsumlen = aimbs_get8(bs); + iconcsum = aimbs_getraw(bs, iconcsumlen); + iconlen = aimbs_get16(bs); + icon = aimbs_getraw(bs, iconlen); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sn, iconcsum, iconcsumlen, icon, iconlen); + + free(sn); + free(iconcsum); + free(icon); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return uploadack(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0005) + return parseicon(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int bart_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0010; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "bart", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/bos.c b/libfaim/bos.c new file mode 100644 index 0000000..a3a2421 --- /dev/null +++ b/libfaim/bos.c @@ -0,0 +1,167 @@ +/* + * Family 0x0009 - Basic Oscar Service. + * + * The functionality of this family has been replaced by SSI. + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#include <string.h> + +/* Subtype 0x0002 - Request BOS rights. */ +faim_export int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x0009, 0x0002); +} + +/* Subtype 0x0003 - BOS Rights. */ +static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + fu16_t maxpermits = 0, maxdenies = 0; + int ret = 0; + + /* + * TLVs follow + */ + tlvlist = aim_tlvlist_read(bs); + + /* + * TLV type 0x0001: Maximum number of buddies on permit list. + */ + if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) + maxpermits = aim_tlv_get16(tlvlist, 0x0001, 1); + + /* + * TLV type 0x0002: Maximum number of buddies on deny list. + */ + if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) + maxdenies = aim_tlv_get16(tlvlist, 0x0002, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, maxpermits, maxdenies); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0004 - Set group permisson mask. + * + * Normally 0x1f (all classes). + * + * The group permission mask allows you to keep users of a certain + * class or classes from talking to you. The mask should be + * a bitwise OR of all the user classes you want to see you. + * + */ +faim_export int aim_bos_setgroupperm(aim_session_t *sess, aim_conn_t *conn, fu32_t mask) +{ + return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask); +} + +/* + * Stubtypes 0x0005, 0x0006, 0x0007, and 0x0008 - Modify permit/deny lists. + * + * Changes your visibility depending on changetype: + * + * AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you + * AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list + * AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names + * AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again + * + * list should be a list of + * screen names in the form "Screen Name One&ScreenNameTwo&" etc. + * + * Equivelents to options in WinAIM: + * - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD + * with only your name on it. + * - Allow only users on my Buddy List: Send an + * AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your + * buddy list + * - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD + * with everyone listed that you want to see you. + * - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only + * yourself in the list + * - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with + * the list of users to be blocked + * + * XXX ye gods. + */ +faim_export int aim_bos_changevisibility(aim_session_t *sess, aim_conn_t *conn, int changetype, const char *denylist) +{ + aim_frame_t *fr; + int packlen = 0; + fu16_t subtype; + char *localcpy = NULL, *tmpptr = NULL; + int i; + int listcount; + aim_snacid_t snacid; + + if (!denylist) + return -EINVAL; + + if (changetype == AIM_VISIBILITYCHANGE_PERMITADD) + subtype = 0x05; + else if (changetype == AIM_VISIBILITYCHANGE_PERMITREMOVE) + subtype = 0x06; + else if (changetype == AIM_VISIBILITYCHANGE_DENYADD) + subtype = 0x07; + else if (changetype == AIM_VISIBILITYCHANGE_DENYREMOVE) + subtype = 0x08; + else + return -EINVAL; + + localcpy = strdup(denylist); + + listcount = aimutil_itemcnt(localcpy, '&'); + packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, packlen))) { + free(localcpy); + return -ENOMEM; + } + + snacid = aim_cachesnac(sess, 0x0009, subtype, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0009, subtype, 0x00, snacid); + + for (i = 0; (i < (listcount - 1)) && (i < 99); i++) { + tmpptr = aimutil_itemindex(localcpy, i, '&'); + + aimbs_put8(&fr->data, strlen(tmpptr)); + aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr)); + + free(tmpptr); + } + free(localcpy); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return rights(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0009; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "bos", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/bstream.c b/libfaim/bstream.c new file mode 100644 index 0000000..c3f5394 --- /dev/null +++ b/libfaim/bstream.c @@ -0,0 +1,265 @@ +/* + * bstream.c + * + * This file contains all functions needed to use bstreams. + */ + +#define FAIM_INTERNAL +#include <aim.h> + +faim_internal int aim_bstream_init(aim_bstream_t *bs, fu8_t *data, int len) +{ + + if (!bs) + return -1; + + bs->data = data; + bs->len = len; + bs->offset = 0; + + return 0; +} + +faim_internal int aim_bstream_empty(aim_bstream_t *bs) +{ + return bs->len - bs->offset; +} + +faim_internal int aim_bstream_curpos(aim_bstream_t *bs) +{ + return bs->offset; +} + +faim_internal int aim_bstream_setpos(aim_bstream_t *bs, int off) +{ + + if (off > bs->len) + return -1; + + bs->offset = off; + + return off; +} + +faim_internal void aim_bstream_rewind(aim_bstream_t *bs) +{ + + aim_bstream_setpos(bs, 0); + + return; +} + +faim_internal int aim_bstream_advance(aim_bstream_t *bs, int n) +{ + + if (aim_bstream_empty(bs) < n) + return 0; /* XXX throw an exception */ + + bs->offset += n; + + return n; +} + +faim_internal fu8_t aimbs_get8(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 1) + return 0; /* XXX throw an exception */ + + bs->offset++; + + return aimutil_get8(bs->data + bs->offset - 1); +} + +faim_internal fu16_t aimbs_get16(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 2) + return 0; /* XXX throw an exception */ + + bs->offset += 2; + + return aimutil_get16(bs->data + bs->offset - 2); +} + +faim_internal fu32_t aimbs_get32(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 4) + return 0; /* XXX throw an exception */ + + bs->offset += 4; + + return aimutil_get32(bs->data + bs->offset - 4); +} + +faim_internal fu8_t aimbs_getle8(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 1) + return 0; /* XXX throw an exception */ + + bs->offset++; + + return aimutil_getle8(bs->data + bs->offset - 1); +} + +faim_internal fu16_t aimbs_getle16(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 2) + return 0; /* XXX throw an exception */ + + bs->offset += 2; + + return aimutil_getle16(bs->data + bs->offset - 2); +} + +faim_internal fu32_t aimbs_getle32(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 4) + return 0; /* XXX throw an exception */ + + bs->offset += 4; + + return aimutil_getle32(bs->data + bs->offset - 4); +} + +faim_internal int aimbs_put8(aim_bstream_t *bs, fu8_t v) +{ + + if (aim_bstream_empty(bs) < 1) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_put8(bs->data + bs->offset, v); + + return 1; +} + +faim_internal int aimbs_put16(aim_bstream_t *bs, fu16_t v) +{ + + if (aim_bstream_empty(bs) < 2) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_put16(bs->data + bs->offset, v); + + return 2; +} + +faim_internal int aimbs_put32(aim_bstream_t *bs, fu32_t v) +{ + + if (aim_bstream_empty(bs) < 4) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_put32(bs->data + bs->offset, v); + + return 1; +} + +faim_internal int aimbs_putle8(aim_bstream_t *bs, fu8_t v) +{ + + if (aim_bstream_empty(bs) < 1) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_putle8(bs->data + bs->offset, v); + + return 1; +} + +faim_internal int aimbs_putle16(aim_bstream_t *bs, fu16_t v) +{ + + if (aim_bstream_empty(bs) < 2) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_putle16(bs->data + bs->offset, v); + + return 2; +} + +faim_internal int aimbs_putle32(aim_bstream_t *bs, fu32_t v) +{ + + if (aim_bstream_empty(bs) < 4) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_putle32(bs->data + bs->offset, v); + + return 1; +} + +faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, fu8_t *buf, int len) +{ + + if (aim_bstream_empty(bs) < len) + return 0; + + memcpy(buf, bs->data + bs->offset, len); + bs->offset += len; + + return len; +} + +faim_internal fu8_t *aimbs_getraw(aim_bstream_t *bs, int len) +{ + fu8_t *ob; + + if (!(ob = malloc(len))) + return NULL; + + if (aimbs_getrawbuf(bs, ob, len) < len) { + free(ob); + return NULL; + } + + return ob; +} + +faim_internal char *aimbs_getstr(aim_bstream_t *bs, int len) +{ + char *ob; + + if (!(ob = malloc(len+1))) + return NULL; + + if (aimbs_getrawbuf(bs, ob, len) < len) { + free(ob); + return NULL; + } + + ob[len] = '\0'; + + return ob; +} + +faim_internal int aimbs_putraw(aim_bstream_t *bs, const fu8_t *v, int len) +{ + + if (aim_bstream_empty(bs) < len) + return 0; /* XXX throw an exception */ + + memcpy(bs->data + bs->offset, v, len); + bs->offset += len; + + return len; +} + +faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len) +{ + + if (aim_bstream_empty(srcbs) < len) + return 0; /* XXX throw exception (underrun) */ + + if (aim_bstream_empty(bs) < len) + return 0; /* XXX throw exception (overflow) */ + + memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len); + bs->offset += len; + srcbs->offset += len; + + return len; +} diff --git a/libfaim/buddylist.c b/libfaim/buddylist.c new file mode 100644 index 0000000..2e1c78c --- /dev/null +++ b/libfaim/buddylist.c @@ -0,0 +1,285 @@ +/* + * Family 0x0003 - Old-style Buddylist Management (non-SSI). + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#include <string.h> + +/* + * Subtype 0x0002 - Request rights. + * + * Request Buddy List rights. + * + */ +faim_export int aim_buddylist_reqrights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x0003, 0x0002); +} + +/* + * Subtype 0x0003 - Rights. + * + */ +static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + fu16_t maxbuddies = 0, maxwatchers = 0; + int ret = 0; + + /* + * TLVs follow + */ + tlvlist = aim_tlvlist_read(bs); + + /* + * TLV type 0x0001: Maximum number of buddies. + */ + if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) + maxbuddies = aim_tlv_get16(tlvlist, 0x0001, 1); + + /* + * TLV type 0x0002: Maximum number of watchers. + * + * Watchers are other users who have you on their buddy + * list. (This is called the "reverse list" by a certain + * other IM protocol.) + * + */ + if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) + maxwatchers = aim_tlv_get16(tlvlist, 0x0002, 1); + + /* + * TLV type 0x0003: Unknown. + * + * ICQ only? + */ + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, maxbuddies, maxwatchers); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0004 - Add buddy to list. + * + * Adds a single buddy to your buddy list after login. + * XXX This should just be an extension of setbuddylist() + * + */ +faim_export int aim_buddylist_addbuddy(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sn || !strlen(sn)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid); + + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0004 - Add multiple buddies to your buddy list. + * + * This just builds the "set buddy list" command then queues it. + * + * buddy_list = "Screen Name One&ScreenNameTwo&"; + * + * XXX Clean this up. + * + */ +faim_export int aim_buddylist_set(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + int len = 0; + char *localcpy = NULL; + char *tmpptr = NULL; + + if (!buddy_list || !(localcpy = strdup(buddy_list))) + return -EINVAL; + + for (tmpptr = strtok(localcpy, "&"); tmpptr; ) { + faimdprintf(sess, 2, "---adding: %s (%d)\n", tmpptr, strlen(tmpptr)); + len += 1 + strlen(tmpptr); + tmpptr = strtok(NULL, "&"); + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid); + + strncpy(localcpy, buddy_list, strlen(buddy_list) + 1); + + for (tmpptr = strtok(localcpy, "&"); tmpptr; ) { + + faimdprintf(sess, 2, "---adding: %s (%d)\n", tmpptr, strlen(tmpptr)); + + aimbs_put8(&fr->data, strlen(tmpptr)); + aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr)); + tmpptr = strtok(NULL, "&"); + } + + aim_tx_enqueue(sess, fr); + + free(localcpy); + + return 0; +} + +/* + * Subtype 0x0005 - Remove buddy from list. + * + * XXX generalise to support removing multiple buddies (basically, its + * the same as setbuddylist() but with a different snac subtype). + * + */ +faim_export int aim_buddylist_removebuddy(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sn || !strlen(sn)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0003, 0x0005, 0x0000, snacid); + + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x000b + * + * XXX Why would we send this? + * + */ +faim_export int aim_buddylist_oncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !info) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid); + aim_putuserinfo(&fr->data, info); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x000c + * + * XXX Why would we send this? + * + */ +faim_export int aim_buddylist_offgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtypes 0x000b and 0x000c - Change in buddy status + * + * Oncoming Buddy notifications contain a subset of the + * user information structure. It's close enough to run + * through aim_info_extract() however. + * + * Although the offgoing notification contains no information, + * it is still in a format parsable by aim_info_extract(). + * + */ +static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_userinfo_t userinfo; + aim_rxcallback_t userfunc; + + aim_info_extract(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &userinfo); + + if (snac->subtype == 0x000b) + aim_locate_requestuserinfo(sess, userinfo.sn); + aim_info_free(&userinfo); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return rights(sess, mod, rx, snac, bs); + else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c)) + return buddychange(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0003; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "buddylist", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/chat.c b/libfaim/chat.c new file mode 100644 index 0000000..a0f009a --- /dev/null +++ b/libfaim/chat.c @@ -0,0 +1,702 @@ +/* + * Family 0x000e - Routines for the Chat service. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#include <string.h> + +/* Stored in the ->priv of chat connections */ +struct chatconnpriv { + fu16_t exchange; + char *name; + fu16_t instance; +}; + +faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn) +{ + struct chatconnpriv *ccp = (struct chatconnpriv *)conn->priv; + + if (ccp) + free(ccp->name); + free(ccp); + + return; +} + +faim_export char *aim_chat_getname(aim_conn_t *conn) +{ + struct chatconnpriv *ccp; + + if (!conn) + return NULL; + + if (conn->type != AIM_CONN_TYPE_CHAT) + return NULL; + + ccp = (struct chatconnpriv *)conn->priv; + + return ccp->name; +} + +/* XXX get this into conn.c -- evil!! */ +faim_export aim_conn_t *aim_chat_getconn(aim_session_t *sess, const char *name) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + struct chatconnpriv *ccp = (struct chatconnpriv *)cur->priv; + + if (cur->type != AIM_CONN_TYPE_CHAT) + continue; + if (!cur->priv) { + faimdprintf(sess, 0, "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd); + continue; + } + + if (strcmp(ccp->name, name) == 0) + break; + } + + return cur; +} + +faim_export int aim_chat_attachname(aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance) +{ + struct chatconnpriv *ccp; + + if (!conn || !roomname) + return -EINVAL; + + if (conn->priv) + free(conn->priv); + + if (!(ccp = malloc(sizeof(struct chatconnpriv)))) + return -ENOMEM; + + ccp->exchange = exchange; + ccp->name = strdup(roomname); + ccp->instance = instance; + + conn->priv = (void *)ccp; + + return 0; +} + +static int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, fu16_t type, fu16_t exchange, const char *roomname, fu16_t instance) +{ + fu8_t *buf; + int buflen; + aim_bstream_t bs; + + buflen = 2 + 1 + strlen(roomname) + 2; + + if (!(buf = malloc(buflen))) + return 0; + + aim_bstream_init(&bs, buf, buflen); + + aimbs_put16(&bs, exchange); + aimbs_put8(&bs, strlen(roomname)); + aimbs_putraw(&bs, roomname, strlen(roomname)); + aimbs_put16(&bs, instance); + + aim_tlvlist_add_raw(list, type, aim_bstream_curpos(&bs), buf); + + free(buf); + + return 0; +} + +/* + * Join a room of name roomname. This is the first step to joining an + * already created room. It's basically a Service Request for + * family 0x000e, with a little added on to specify the exchange and room + * name. + */ +faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + struct chatsnacinfo csi; + + if (!sess || !conn || !roomname || !strlen(roomname)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) + return -ENOMEM; + + memset(&csi, 0, sizeof(csi)); + csi.exchange = exchange; + strncpy(csi.name, roomname, sizeof(csi.name)); + csi.instance = instance; + + snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi)); + aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid); + + /* + * Requesting service chat (0x000e) + */ + aimbs_put16(&fr->data, 0x000e); + + aim_addtlvtochain_chatroom(&tl, 0x0001, exchange, roomname, instance); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo) +{ + int namelen; + + if (!bs || !outinfo) + return 0; + + outinfo->exchange = aimbs_get16(bs); + namelen = aimbs_get8(bs); + outinfo->name = aimbs_getstr(bs, namelen); + outinfo->instance = aimbs_get16(bs); + + return 0; +} + +faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name) +{ + aim_conn_t *conn; + + if (!(conn = aim_chat_getconn(sess, name))) + return -ENOENT; + + aim_conn_close(conn); + + return 0; +} + +/* + * conn must be a BOS connection! + */ +faim_export int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance) +{ + int i; + aim_frame_t *fr; + aim_msgcookie_t *cookie; + struct aim_invite_priv *priv; + fu8_t ckstr[8]; + aim_snacid_t snacid; + aim_tlvlist_t *otl = NULL, *itl = NULL; + fu8_t *hdr; + int hdrlen; + aim_bstream_t hdrbs; + + if (!sess || !conn || !sn || !msg || !roomname) + return -EINVAL; + + if (conn->type != AIM_CONN_TYPE_BOS) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* + * Cookie + */ + for (i = 0; i < 8; i++) + ckstr[i] = (fu8_t)rand(); + + /* XXX should be uncached by an unwritten 'invite accept' handler */ + if ((priv = malloc(sizeof(struct aim_invite_priv)))) { + priv->sn = strdup(sn); + priv->roomname = strdup(roomname); + priv->exchange = exchange; + priv->instance = instance; + } + + if ((cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_INVITE, priv))) + aim_cachecookie(sess, cookie); + else + free(priv); + + /* ICBM Header */ + aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */ + aimbs_put16(&fr->data, 0x0002); /* Channel */ + aimbs_put8(&fr->data, strlen(sn)); /* Screename length */ + aimbs_putraw(&fr->data, sn, strlen(sn)); /* Screenname */ + + /* + * TLV t(0005) + * + * Everything else is inside this TLV. + * + * Sigh. AOL was rather inconsistent right here. So we have + * to play some minor tricks. Right inside the type 5 is some + * raw data, followed by a series of TLVs. + * + */ + hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2; + hdr = malloc(hdrlen); + aim_bstream_init(&hdrbs, hdr, hdrlen); + + aimbs_put16(&hdrbs, 0x0000); /* Unknown! */ + aimbs_putraw(&hdrbs, ckstr, sizeof(ckstr)); /* I think... */ + aim_putcap(&hdrbs, AIM_CAPS_CHAT); + + aim_tlvlist_add_16(&itl, 0x000a, 0x0001); + aim_tlvlist_add_noval(&itl, 0x000f); + aim_tlvlist_add_raw(&itl, 0x000c, strlen(msg), msg); + aim_addtlvtochain_chatroom(&itl, 0x2711, exchange, roomname, instance); + aim_tlvlist_write(&hdrbs, &itl); + + aim_tlvlist_add_raw(&otl, 0x0005, aim_bstream_curpos(&hdrbs), hdr); + + aim_tlvlist_write(&fr->data, &otl); + + free(hdr); + aim_tlvlist_free(&itl); + aim_tlvlist_free(&otl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0002 - General room information. Lots of stuff. + * + * Values I know are in here but I havent attached + * them to any of the 'Unknown's: + * - Language (English) + * + */ +static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_userinfo_t *userinfo = NULL; + aim_rxcallback_t userfunc; + int ret = 0; + int usercount = 0; + fu8_t detaillevel = 0; + char *roomname = NULL; + struct aim_chat_roominfo roominfo; + fu16_t tlvcount = 0; + aim_tlvlist_t *tlvlist; + char *roomdesc = NULL; + fu16_t flags = 0; + fu32_t creationtime = 0; + fu16_t maxmsglen = 0, maxvisiblemsglen = 0; + fu16_t unknown_d2 = 0, unknown_d5 = 0; + + aim_chat_readroominfo(bs, &roominfo); + + detaillevel = aimbs_get8(bs); + + if (detaillevel != 0x02) { + faimdprintf(sess, 0, "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel); + return 1; + } + + tlvcount = aimbs_get16(bs); + + /* + * Everything else are TLVs. + */ + tlvlist = aim_tlvlist_read(bs); + + /* + * TLV type 0x006a is the room name in Human Readable Form. + */ + if (aim_tlv_gettlv(tlvlist, 0x006a, 1)) + roomname = aim_tlv_getstr(tlvlist, 0x006a, 1); + + /* + * Type 0x006f: Number of occupants. + */ + if (aim_tlv_gettlv(tlvlist, 0x006f, 1)) + usercount = aim_tlv_get16(tlvlist, 0x006f, 1); + + /* + * Type 0x0073: Occupant list. + */ + if (aim_tlv_gettlv(tlvlist, 0x0073, 1)) { + int curoccupant = 0; + aim_tlv_t *tmptlv; + aim_bstream_t occbs; + + tmptlv = aim_tlv_gettlv(tlvlist, 0x0073, 1); + + /* Allocate enough userinfo structs for all occupants */ + userinfo = calloc(usercount, sizeof(aim_userinfo_t)); + + aim_bstream_init(&occbs, tmptlv->value, tmptlv->length); + + while (curoccupant < usercount) + aim_info_extract(sess, &occbs, &userinfo[curoccupant++]); + } + + /* + * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG) + */ + if (aim_tlv_gettlv(tlvlist, 0x00c9, 1)) + flags = aim_tlv_get16(tlvlist, 0x00c9, 1); + + /* + * Type 0x00ca: Creation time (4 bytes) + */ + if (aim_tlv_gettlv(tlvlist, 0x00ca, 1)) + creationtime = aim_tlv_get32(tlvlist, 0x00ca, 1); + + /* + * Type 0x00d1: Maximum Message Length + */ + if (aim_tlv_gettlv(tlvlist, 0x00d1, 1)) + maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1); + + /* + * Type 0x00d2: Unknown. (2 bytes) + */ + if (aim_tlv_gettlv(tlvlist, 0x00d2, 1)) + unknown_d2 = aim_tlv_get16(tlvlist, 0x00d2, 1); + + /* + * Type 0x00d3: Room Description + */ + if (aim_tlv_gettlv(tlvlist, 0x00d3, 1)) + roomdesc = aim_tlv_getstr(tlvlist, 0x00d3, 1); + + /* + * Type 0x000d4: Unknown (flag only) + */ + if (aim_tlv_gettlv(tlvlist, 0x000d4, 1)) + ; + + /* + * Type 0x00d5: Unknown. (1 byte) + */ + if (aim_tlv_gettlv(tlvlist, 0x00d5, 1)) + unknown_d5 = aim_tlv_get8(tlvlist, 0x00d5, 1); + + + /* + * Type 0x00d6: Encoding 1 ("us-ascii") + */ + if (aim_tlv_gettlv(tlvlist, 0x000d6, 1)) + ; + + /* + * Type 0x00d7: Language 1 ("en") + */ + if (aim_tlv_gettlv(tlvlist, 0x000d7, 1)) + ; + + /* + * Type 0x00d8: Encoding 2 ("us-ascii") + */ + if (aim_tlv_gettlv(tlvlist, 0x000d8, 1)) + ; + + /* + * Type 0x00d9: Language 2 ("en") + */ + if (aim_tlv_gettlv(tlvlist, 0x000d9, 1)) + ; + + /* + * Type 0x00da: Maximum visible message length + */ + if (aim_tlv_gettlv(tlvlist, 0x000da, 1)) + maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { + ret = userfunc(sess, + rx, + &roominfo, + roomname, + usercount, + userinfo, + roomdesc, + flags, + creationtime, + maxmsglen, + unknown_d2, + unknown_d5, + maxvisiblemsglen); + } + + free(roominfo.name); + + while (usercount > 0) + aim_info_free(&userinfo[--usercount]); + + free(userinfo); + free(roomname); + free(roomdesc); + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* Subtypes 0x0003 and 0x0004 */ +static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_userinfo_t *userinfo = NULL; + aim_rxcallback_t userfunc; + int curcount = 0, ret = 0; + + while (aim_bstream_empty(bs)) { + curcount++; + userinfo = realloc(userinfo, curcount * sizeof(aim_userinfo_t)); + aim_info_extract(sess, bs, &userinfo[curcount-1]); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, curcount, userinfo); + + aim_info_free(userinfo); + free(userinfo); + + return ret; +} + +/* + * Subtype 0x0005 - Send a Chat Message. + * + * Possible flags: + * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages + * should be sent to their sender. + * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse + * (Note that WinAIM does not honor this, + * and displays the message as normal.) + * + * XXX convert this to use tlvchains + */ +faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const char *msg, int msglen) +{ + int i; + aim_frame_t *fr; + aim_msgcookie_t *cookie; + aim_snacid_t snacid; + fu8_t ckstr[8]; + aim_tlvlist_t *otl = NULL, *itl = NULL; + + if (!sess || !conn || !msg || (msglen <= 0)) + return 0; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid); + + /* + * Cookie + * + * XXX mkcookie should generate the cookie and cache it in one + * operation to preserve uniqueness. + */ + for (i = 0; i < 8; i++) + ckstr[i] = (fu8_t)rand(); + + cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL); + cookie->data = NULL; /* XXX store something useful here */ + + aim_cachecookie(sess, cookie); + + /* ICBM Header */ + aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */ + aimbs_put16(&fr->data, 0x0003); /* Channel */ + + /* + * Type 1: Flag meaning this message is destined to the room. + */ + aim_tlvlist_add_noval(&otl, 0x0001); + + /* + * Type 6: Reflect + */ + if (!(flags & AIM_CHATFLAGS_NOREFLECT)) + aim_tlvlist_add_noval(&otl, 0x0006); + + /* + * Type 7: Autoresponse + */ + if (flags & AIM_CHATFLAGS_AWAY) + aim_tlvlist_add_noval(&otl, 0x0007); + + /* + * SubTLV: Type 1: Message + */ + aim_tlvlist_add_raw(&itl, 0x0001, msglen, msg); + + /* + * Type 5: Message block. Contains more TLVs. + * + * This could include other information... We just + * put in a message TLV however. + * + */ + aim_tlvlist_add_frozentlvlist(&otl, 0x0005, &itl); + + aim_tlvlist_write(&fr->data, &otl); + + aim_tlvlist_free(&itl); + aim_tlvlist_free(&otl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0006 + * + * We could probably include this in the normal ICBM parsing + * code as channel 0x0003, however, since only the start + * would be the same, we might as well do it here. + * + * General outline of this SNAC: + * snac + * cookie + * channel id + * tlvlist + * unknown + * source user info + * name + * evility + * userinfo tlvs + * online time + * etc + * message metatlv + * message tlv + * message string + * possibly others + * + */ +static int incomingmsg(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_userinfo_t userinfo; + aim_rxcallback_t userfunc; + int ret = 0; + fu8_t *cookie; + fu16_t channel; + aim_tlvlist_t *otl; + char *msg = NULL; + aim_msgcookie_t *ck; + + memset(&userinfo, 0, sizeof(aim_userinfo_t)); + + /* + * ICBM Cookie. Uncache it. + */ + cookie = aimbs_getraw(bs, 8); + + if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) { + free(ck->data); + free(ck); + } + + /* + * Channel ID + * + * Channels 1 and 2 are implemented in the normal ICBM + * parser. + * + * We only do channel 3 here. + * + */ + channel = aimbs_get16(bs); + + if (channel != 0x0003) { + faimdprintf(sess, 0, "faim: chat_incoming: unknown channel! (0x%04x)\n", channel); + return 0; + } + + /* + * Start parsing TLVs right away. + */ + otl = aim_tlvlist_read(bs); + + /* + * Type 0x0003: Source User Information + */ + if (aim_tlv_gettlv(otl, 0x0003, 1)) { + aim_tlv_t *userinfotlv; + aim_bstream_t tbs; + + userinfotlv = aim_tlv_gettlv(otl, 0x0003, 1); + + aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length); + aim_info_extract(sess, &tbs, &userinfo); + } + + /* + * Type 0x0001: If present, it means it was a message to the + * room (as opposed to a whisper). + */ + if (aim_tlv_gettlv(otl, 0x0001, 1)) + ; + + /* + * Type 0x0005: Message Block. Conains more TLVs. + */ + if (aim_tlv_gettlv(otl, 0x0005, 1)) { + aim_tlvlist_t *itl; + aim_tlv_t *msgblock; + aim_bstream_t tbs; + + msgblock = aim_tlv_gettlv(otl, 0x0005, 1); + aim_bstream_init(&tbs, msgblock->value, msgblock->length); + itl = aim_tlvlist_read(&tbs); + + /* + * Type 0x0001: Message. + */ + if (aim_tlv_gettlv(itl, 0x0001, 1)) + msg = aim_tlv_getstr(itl, 0x0001, 1); + + aim_tlvlist_free(&itl); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &userinfo, msg); + + aim_info_free(&userinfo); + free(cookie); + free(msg); + aim_tlvlist_free(&otl); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0002) + return infoupdate(sess, mod, rx, snac, bs); + else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004)) + return userlistchange(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0006) + return incomingmsg(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000e; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "chat", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/chatnav.c b/libfaim/chatnav.c new file mode 100644 index 0000000..1b97b98 --- /dev/null +++ b/libfaim/chatnav.c @@ -0,0 +1,434 @@ +/* + * Family 0x000d - Handle ChatNav. + * + * The ChatNav(igation) service does various things to keep chat + * alive. It provides room information, room searching and creating, + * as well as giving users the right ("permission") to use chat. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * Subtype 0x0002 + * + * conn must be a chatnav connection! + * + */ +faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002); +} + +/* + * Subtype 0x0008 + */ +faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, fu16_t exchange) +{ + static const char ck[] = {"create"}; + static const char lang[] = {"en"}; + static const char charset[] = {"us-ascii"}; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid); + + /* exchange */ + aimbs_put16(&fr->data, exchange); + + /* + * This looks to be a big hack. You'll note that this entire + * SNAC is just a room info structure, but the hard room name, + * here, is set to "create". + * + * Either this goes on the "list of questions concerning + * why-the-hell-did-you-do-that", or this value is completly + * ignored. Without experimental evidence, but a good knowledge of + * AOL style, I'm going to guess that it is the latter, and that + * the value of the room name in create requests is ignored. + */ + aimbs_put8(&fr->data, strlen(ck)); + aimbs_putraw(&fr->data, ck, strlen(ck)); + + /* + * instance + * + * Setting this to 0xffff apparently assigns the last instance. + * + */ + aimbs_put16(&fr->data, 0xffff); + + /* detail level */ + aimbs_put8(&fr->data, 0x01); + + aim_tlvlist_add_raw(&tl, 0x00d3, strlen(name), name); + aim_tlvlist_add_raw(&tl, 0x00d6, strlen(charset), charset); + aim_tlvlist_add_raw(&tl, 0x00d7, strlen(lang), lang); + + /* tlvcount */ + aimbs_put16(&fr->data, aim_tlvlist_count(&tl)); + aim_tlvlist_write(&fr->data, &tl); + + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int parseinfo_perms(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) +{ + aim_rxcallback_t userfunc; + int ret = 0; + struct aim_chat_exchangeinfo *exchanges = NULL; + int curexchange; + aim_tlv_t *exchangetlv; + fu8_t maxrooms = 0; + aim_tlvlist_t *tlvlist, *innerlist; + + tlvlist = aim_tlvlist_read(bs); + + /* + * Type 0x0002: Maximum concurrent rooms. + */ + if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) + maxrooms = aim_tlv_get8(tlvlist, 0x0002, 1); + + /* + * Type 0x0003: Exchange information + * + * There can be any number of these, each one + * representing another exchange. + * + */ + for (curexchange = 0; ((exchangetlv = aim_tlv_gettlv(tlvlist, 0x0003, curexchange+1))); ) { + aim_bstream_t tbs; + + aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length); + + curexchange++; + + exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo)); + + /* exchange number */ + exchanges[curexchange-1].number = aimbs_get16(&tbs); + innerlist = aim_tlvlist_read(&tbs); + + /* + * Type 0x000a: Unknown. + * + * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others). + * + */ + if (aim_tlv_gettlv(innerlist, 0x000a, 1)) + ; + + /* + * Type 0x000d: Unknown. + */ + if (aim_tlv_gettlv(innerlist, 0x000d, 1)) + ; + + /* + * Type 0x0004: Unknown + */ + if (aim_tlv_gettlv(innerlist, 0x0004, 1)) + ; + + /* + * Type 0x0002: Unknown + */ + if (aim_tlv_gettlv(innerlist, 0x0002, 1)) { + fu16_t classperms; + + classperms = aim_tlv_get16(innerlist, 0x0002, 1); + + faimdprintf(sess, 1, "faim: class permissions %x\n", classperms); + } + + /* + * Type 0x00c9: Flags + * + * 1 Evilable + * 2 Nav Only + * 4 Instancing Allowed + * 8 Occupant Peek Allowed + * + */ + if (aim_tlv_gettlv(innerlist, 0x00c9, 1)) + exchanges[curexchange-1].flags = aim_tlv_get16(innerlist, 0x00c9, 1); + + /* + * Type 0x00ca: Creation Date + */ + if (aim_tlv_gettlv(innerlist, 0x00ca, 1)) + ; + + /* + * Type 0x00d0: Mandatory Channels? + */ + if (aim_tlv_gettlv(innerlist, 0x00d0, 1)) + ; + + /* + * Type 0x00d1: Maximum Message length + */ + if (aim_tlv_gettlv(innerlist, 0x00d1, 1)) + ; + + /* + * Type 0x00d2: Maximum Occupancy? + */ + if (aim_tlv_gettlv(innerlist, 0x00d2, 1)) + ; + + /* + * Type 0x00d3: Exchange Description + */ + if (aim_tlv_gettlv(innerlist, 0x00d3, 1)) + exchanges[curexchange-1].name = aim_tlv_getstr(innerlist, 0x00d3, 1); + else + exchanges[curexchange-1].name = NULL; + + /* + * Type 0x00d4: Exchange Description URL + */ + if (aim_tlv_gettlv(innerlist, 0x00d4, 1)) + ; + + /* + * Type 0x00d5: Creation Permissions + * + * 0 Creation not allowed + * 1 Room creation allowed + * 2 Exchange creation allowed + * + */ + if (aim_tlv_gettlv(innerlist, 0x00d5, 1)) { + fu8_t createperms; + + createperms = aim_tlv_get8(innerlist, 0x00d5, 1); + } + + /* + * Type 0x00d6: Character Set (First Time) + */ + if (aim_tlv_gettlv(innerlist, 0x00d6, 1)) + exchanges[curexchange-1].charset1 = aim_tlv_getstr(innerlist, 0x00d6, 1); + else + exchanges[curexchange-1].charset1 = NULL; + + /* + * Type 0x00d7: Language (First Time) + */ + if (aim_tlv_gettlv(innerlist, 0x00d7, 1)) + exchanges[curexchange-1].lang1 = aim_tlv_getstr(innerlist, 0x00d7, 1); + else + exchanges[curexchange-1].lang1 = NULL; + + /* + * Type 0x00d8: Character Set (Second Time) + */ + if (aim_tlv_gettlv(innerlist, 0x00d8, 1)) + exchanges[curexchange-1].charset2 = aim_tlv_getstr(innerlist, 0x00d8, 1); + else + exchanges[curexchange-1].charset2 = NULL; + + /* + * Type 0x00d9: Language (Second Time) + */ + if (aim_tlv_gettlv(innerlist, 0x00d9, 1)) + exchanges[curexchange-1].lang2 = aim_tlv_getstr(innerlist, 0x00d9, 1); + else + exchanges[curexchange-1].lang2 = NULL; + + /* + * Type 0x00da: Unknown + */ + if (aim_tlv_gettlv(innerlist, 0x00da, 1)) + ; + + aim_tlvlist_free(&innerlist); + } + + /* + * Call client. + */ + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges); + + for (curexchange--; curexchange >= 0; curexchange--) { + free(exchanges[curexchange].name); + free(exchanges[curexchange].charset1); + free(exchanges[curexchange].lang1); + free(exchanges[curexchange].charset2); + free(exchanges[curexchange].lang2); + } + free(exchanges); + aim_tlvlist_free(&tlvlist); + + return ret; +} + +static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) +{ + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist, *innerlist; + char *ck = NULL, *fqcn = NULL, *name = NULL; + fu16_t exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0; + fu32_t createtime = 0; + fu8_t createperms = 0, detaillevel; + int cklen; + aim_tlv_t *bigblock; + int ret = 0; + aim_bstream_t bbbs; + + tlvlist = aim_tlvlist_read(bs); + + if (!(bigblock = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { + faimdprintf(sess, 0, "no bigblock in top tlv in create room response\n"); + aim_tlvlist_free(&tlvlist); + return 0; + } + + aim_bstream_init(&bbbs, bigblock->value, bigblock->length); + + exchange = aimbs_get16(&bbbs); + cklen = aimbs_get8(&bbbs); + ck = aimbs_getstr(&bbbs, cklen); + instance = aimbs_get16(&bbbs); + detaillevel = aimbs_get8(&bbbs); + + if (detaillevel != 0x02) { + faimdprintf(sess, 0, "unknown detaillevel in create room response (0x%02x)\n", detaillevel); + aim_tlvlist_free(&tlvlist); + free(ck); + return 0; + } + + unknown = aimbs_get16(&bbbs); + + innerlist = aim_tlvlist_read(&bbbs); + + if (aim_tlv_gettlv(innerlist, 0x006a, 1)) + fqcn = aim_tlv_getstr(innerlist, 0x006a, 1); + + if (aim_tlv_gettlv(innerlist, 0x00c9, 1)) + flags = aim_tlv_get16(innerlist, 0x00c9, 1); + + if (aim_tlv_gettlv(innerlist, 0x00ca, 1)) + createtime = aim_tlv_get32(innerlist, 0x00ca, 1); + + if (aim_tlv_gettlv(innerlist, 0x00d1, 1)) + maxmsglen = aim_tlv_get16(innerlist, 0x00d1, 1); + + if (aim_tlv_gettlv(innerlist, 0x00d2, 1)) + maxoccupancy = aim_tlv_get16(innerlist, 0x00d2, 1); + + if (aim_tlv_gettlv(innerlist, 0x00d3, 1)) + name = aim_tlv_getstr(innerlist, 0x00d3, 1); + + if (aim_tlv_gettlv(innerlist, 0x00d5, 1)) + createperms = aim_tlv_get8(innerlist, 0x00d5, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { + ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck); + } + + free(ck); + free(name); + free(fqcn); + aim_tlvlist_free(&innerlist); + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0009 + * + * Since multiple things can trigger this callback, we must lookup the + * snacid to determine the original snac subtype that was called. + * + * XXX This isn't really how this works. But this is: Every d/9 response + * has a 16bit value at the beginning. That matches to: + * Short Desc = 1 + * Full Desc = 2 + * Instance Info = 4 + * Nav Short Desc = 8 + * Nav Instance Info = 16 + * And then everything is really asynchronous. There is no specific + * attachment of a response to a create room request, for example. Creating + * the room yields no different a response than requesting the room's info. + * + */ +static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_snac_t *snac2; + int ret = 0; + + if (!(snac2 = aim_remsnac(sess, snac->id))) { + faimdprintf(sess, 0, "faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snac->id); + return 0; + } + + if (snac2->family != 0x000d) { + faimdprintf(sess, 0, "faim: chatnav_parse_info: recieved response that maps to corrupt request! (fam=%04x)\n", snac2->family); + return 0; + } + + /* + * We now know what the original SNAC subtype was. + */ + if (snac2->type == 0x0002) /* request chat rights */ + ret = parseinfo_perms(sess, mod, rx, snac, bs, snac2); + else if (snac2->type == 0x0003) /* request exchange info */ + faimdprintf(sess, 0, "chatnav_parse_info: resposne to exchange info\n"); + else if (snac2->type == 0x0004) /* request room info */ + faimdprintf(sess, 0, "chatnav_parse_info: response to room info\n"); + else if (snac2->type == 0x0005) /* request more room info */ + faimdprintf(sess, 0, "chatnav_parse_info: response to more room info\n"); + else if (snac2->type == 0x0006) /* request occupant list */ + faimdprintf(sess, 0, "chatnav_parse_info: response to occupant info\n"); + else if (snac2->type == 0x0007) /* search for a room */ + faimdprintf(sess, 0, "chatnav_parse_info: search results\n"); + else if (snac2->type == 0x0008) /* create room */ + ret = parseinfo_create(sess, mod, rx, snac, bs, snac2); + else + faimdprintf(sess, 0, "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type); + + if (snac2) + free(snac2->data); + free(snac2); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0009) + return parseinfo(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000d; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "chatnav", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/config.h.in b/libfaim/config.h.in new file mode 100644 index 0000000..abdc672 --- /dev/null +++ b/libfaim/config.h.in @@ -0,0 +1,83 @@ +/* include/config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have the strftime function. */ +#undef HAVE_STRFTIME + +/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define as __inline if that's what the C compiler calls it. */ +#undef inline + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef pid_t + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* Define to `unsigned' if <sys/types.h> doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you can safely include both <sys/time.h> and <time.h>. */ +#undef TIME_WITH_SYS_TIME + +/* Define if your <sys/time.h> declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if you have the getaddrinfo function. */ +#undef HAVE_GETADDRINFO + +/* Define if you have the gethostname function. */ +#undef HAVE_GETHOSTNAME + +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if you have the select function. */ +#undef HAVE_SELECT + +/* Define if you have the socket function. */ +#undef HAVE_SOCKET + +/* Define if you have the strdup function. */ +#undef HAVE_STRDUP + +/* Define if you have the strstr function. */ +#undef HAVE_STRSTR + +/* Define if you have the strtol function. */ +#undef HAVE_STRTOL + +/* Define if you have the uname function. */ +#undef HAVE_UNAME + +/* Define if you have the <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the faim library (-lfaim). */ +#undef HAVE_LIBFAIM + +/* Name of package */ +#undef PACKAGE + +/* Version number of package */ +#undef VERSION + diff --git a/libfaim/configure b/libfaim/configure new file mode 100755 index 0000000..35989e1 --- /dev/null +++ b/libfaim/configure @@ -0,0 +1,3659 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.61. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell autoconf@gnu.org about your system, + echo including any error possibly output before this + echo message +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 </dev/null 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="bos.c" +ac_subst_vars='SHELL +PATH_SEPARATOR +PACKAGE_NAME +PACKAGE_TARNAME +PACKAGE_VERSION +PACKAGE_STRING +PACKAGE_BUGREPORT +exec_prefix +prefix +program_transform_name +bindir +sbindir +libexecdir +datarootdir +datadir +sysconfdir +sharedstatedir +localstatedir +includedir +oldincludedir +docdir +infodir +htmldir +dvidir +pdfdir +psdir +libdir +localedir +mandir +DEFS +ECHO_C +ECHO_N +ECHO_T +LIBS +build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +CPPFLAGS +ac_ct_CC +EXEEXT +OBJEXT +RANLIB +LIBOBJS +LTLIBOBJS' +ac_subst_files='' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` + eval enable_$ac_feature=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` + eval enable_$ac_feature=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/[-.]/_/g'` + eval with_$ac_package=\$ac_optarg ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/[-.]/_/g'` + eval with_$ac_package=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute directory names. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { echo "$as_me: error: Working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$0" || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.61 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.61. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -n "$CONFIG_SITE"; then + set x "$CONFIG_SITE" +elif test "x$prefix" != xNONE; then + set x "$prefix/share/config.site" "$prefix/etc/config.site" +else + set x "$ac_default_prefix/share/config.site" \ + "$ac_default_prefix/etc/config.site" +fi +shift +for ac_site_file +do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO: checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +# +# List of possible output files, starting from the most likely. +# The algorithm is not robust to junk in `.', hence go to wildcards (a.*) +# only as a last resort. b.out is created by i960 compilers. +ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' +# +# The IRIX 6 linker writes into existing files which may not be +# executable, retaining their permissions. Remove them first so a +# subsequent execution test works. +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6; } +if test -z "$ac_file"; then + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +{ echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } +{ echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6; } + +{ echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6; } ;; + xno) + { echo "$as_me:$LINENO: result: unsupported" >&5 +echo "${ECHO_T}unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +if test "$GCC" = yes; then + CFLAGS="$CFLAGS -Wall -g"; +fi +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + + + +ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { echo "$as_me:$LINENO: updating cache $cache_file" >&5 +echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +ac_script=' +t clear +:clear +s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g +t quote +s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g +t quote +b any +:quote +s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g +s/\[/\\&/g +s/\]/\\&/g +s/\$/$$/g +H +:any +${ + g + s/^\n// + s/\n/ /g + p +} +' +DEFS=`sed -n "$ac_script" confdefs.h` + + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.61. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# Files that config.status was made for. +config_files="$ac_config_files" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to <bug-autoconf@gnu.org>." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.61, + with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2006 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + CONFIG_SHELL=$SHELL + export CONFIG_SHELL + exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# +# Set up the sed scripts for CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "$CONFIG_FILES"; then + +_ACEOF + + + +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + cat >conf$$subs.sed <<_ACEOF +SHELL!$SHELL$ac_delim +PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim +PACKAGE_NAME!$PACKAGE_NAME$ac_delim +PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim +PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim +PACKAGE_STRING!$PACKAGE_STRING$ac_delim +PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim +exec_prefix!$exec_prefix$ac_delim +prefix!$prefix$ac_delim +program_transform_name!$program_transform_name$ac_delim +bindir!$bindir$ac_delim +sbindir!$sbindir$ac_delim +libexecdir!$libexecdir$ac_delim +datarootdir!$datarootdir$ac_delim +datadir!$datadir$ac_delim +sysconfdir!$sysconfdir$ac_delim +sharedstatedir!$sharedstatedir$ac_delim +localstatedir!$localstatedir$ac_delim +includedir!$includedir$ac_delim +oldincludedir!$oldincludedir$ac_delim +docdir!$docdir$ac_delim +infodir!$infodir$ac_delim +htmldir!$htmldir$ac_delim +dvidir!$dvidir$ac_delim +pdfdir!$pdfdir$ac_delim +psdir!$psdir$ac_delim +libdir!$libdir$ac_delim +localedir!$localedir$ac_delim +mandir!$mandir$ac_delim +DEFS!$DEFS$ac_delim +ECHO_C!$ECHO_C$ac_delim +ECHO_N!$ECHO_N$ac_delim +ECHO_T!$ECHO_T$ac_delim +LIBS!$LIBS$ac_delim +build_alias!$build_alias$ac_delim +host_alias!$host_alias$ac_delim +target_alias!$target_alias$ac_delim +CC!$CC$ac_delim +CFLAGS!$CFLAGS$ac_delim +LDFLAGS!$LDFLAGS$ac_delim +CPPFLAGS!$CPPFLAGS$ac_delim +ac_ct_CC!$ac_ct_CC$ac_delim +EXEEXT!$EXEEXT$ac_delim +OBJEXT!$OBJEXT$ac_delim +RANLIB!$RANLIB$ac_delim +LIBOBJS!$LIBOBJS$ac_delim +LTLIBOBJS!$LTLIBOBJS$ac_delim +_ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 47; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` +if test -n "$ac_eof"; then + ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` + ac_eof=`expr $ac_eof + 1` +fi + +cat >>$CONFIG_STATUS <<_ACEOF +cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end +_ACEOF +sed ' +s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g +s/^/s,@/; s/!/@,|#_!!_#|/ +:n +t n +s/'"$ac_delim"'$/,g/; t +s/$/\\/; p +N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n +' >>$CONFIG_STATUS <conf$$subs.sed +rm -f conf$$subs.sed +cat >>$CONFIG_STATUS <<_ACEOF +:end +s/|#_!!_#|//g +CEOF$ac_eof +_ACEOF + + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF +fi # test -n "$CONFIG_FILES" + + +for ac_tag in :F $CONFIG_FILES +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 +echo "$as_me: error: Invalid tag $ac_tag." >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + ac_file_inputs="$ac_file_inputs $ac_f" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input="Generated from "`IFS=: + echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + fi + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin";; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +case `sed -n '/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' $ac_file_inputs` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s&@configure_input@&$configure_input&;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out"; rm -f "$tmp/out";; + *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; + esac + ;; + + + + esac + +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/libfaim/configure.in b/libfaim/configure.in new file mode 100644 index 0000000..e01408d --- /dev/null +++ b/libfaim/configure.in @@ -0,0 +1,13 @@ +AC_INIT(bos.c) + +dnl AC_CONFIG_HEADER(config.h) + +AC_PROG_CC +dnl we're using GCC, enable all warnings +if test "$GCC" = yes; then + CFLAGS="$CFLAGS -Wall -g"; +fi +AC_PROG_RANLIB + + +AC_OUTPUT(Makefile) diff --git a/libfaim/conn.c b/libfaim/conn.c new file mode 100644 index 0000000..c849ce9 --- /dev/null +++ b/libfaim/conn.c @@ -0,0 +1,1053 @@ +/* + * conn.c + * + * Does all this gloriously nifty connection handling stuff... + * + */ + +#define FAIM_INTERNAL +#define FAIM_NEED_CONN_INTERNAL +#include <aim.h> + +#ifndef _WIN32 +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +#ifdef _WIN32 +#include "win32dep.h" +#endif + +/* + * In OSCAR, every connection has a set of SNAC groups associated + * with it. These are the groups that you can send over this connection + * without being guarenteed a "Not supported" SNAC error. + * + * The grand theory of things says that these associations transcend + * what libfaim calls "connection types" (conn->type). You can probably + * see the elegance here, but since I want to revel in it for a bit, you + * get to hear it all spelled out. + * + * So let us say that you have your core BOS connection running. One + * of your modules has just given you a SNAC of the group 0x0004 to send + * you. Maybe an IM destined for some twit in Greenland. So you start + * at the top of your connection list, looking for a connection that + * claims to support group 0x0004. You find one. Why, that neat BOS + * connection of yours can do that. So you send it on its way. + * + * Now, say, that fellow from Greenland has friends and they all want to + * meet up with you in a lame chat room. This has landed you a SNAC + * in the family 0x000e and you have to admit you're a bit lost. You've + * searched your connection list for someone who wants to make your life + * easy and deliver this SNAC for you, but there isn't one there. + * + * Here comes the good bit. Without even letting anyone know, particularly + * the module that decided to send this SNAC, and definitly not that twit + * in Greenland, you send out a service request. In this request, you have + * marked the need for a connection supporting group 0x000e. A few seconds + * later, you receive a service redirect with an IP address and a cookie in + * it. Great, you say. Now I have something to do. Off you go, making + * that connection. One of the first things you get from this new server + * is a message saying that indeed it does support the group you were looking + * for. So you continue and send rate confirmation and all that. + * + * Then you remember you had that SNAC to send, and now you have a means to + * do it, and you do, and everyone is happy. Except the Greenlander, who is + * still stuck in the bitter cold. + * + * Oh, and this is useful for building the Migration SNACs, too. In the + * future, this may help convince me to implement rate limit mitigation + * for real. We'll see. + * + * Just to make me look better, I'll say that I've known about this great + * scheme for quite some time now. But I still haven't convinced myself + * to make libfaim work that way. It would take a fair amount of effort, + * and probably some client API changes as well. (Whenever I don't want + * to do something, I just say it would change the client API. Then I + * instantly have a couple of supporters of not doing it.) + * + * Generally, addgroup is only called by the internal handling of the + * server ready SNAC. So if you want to do something before that, you'll + * have to be more creative. That is done rather early, though, so I don't + * think you have to worry about it. Unless you're me. I care deeply + * about such inane things. + * + */ +faim_internal void aim_conn_addgroup(aim_conn_t *conn, fu16_t group) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + + if (!(sg = malloc(sizeof(struct snacgroup)))) + return; + + faimdprintf(aim_conn_getsess(conn), 1, "adding group 0x%04x\n", group); + sg->group = group; + + sg->next = ins->groups; + ins->groups = sg; + + return; +} + +faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, fu16_t group) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + aim_conn_inside_t *ins = (aim_conn_inside_t *)cur->inside; + struct snacgroup *sg; + + for (sg = ins->groups; sg; sg = sg->next) { + if (sg->group == group) + return cur; + } + } + + return NULL; +} + +static void connkill_snacgroups(struct snacgroup **head) +{ + struct snacgroup *sg; + + for (sg = *head; sg; ) { + struct snacgroup *tmp; + + tmp = sg->next; + free(sg); + sg = tmp; + } + + *head = NULL; + + return; +} + +static void connkill_rates(struct rateclass **head) +{ + struct rateclass *rc; + + for (rc = *head; rc; ) { + struct rateclass *tmp; + struct snacpair *sp; + + tmp = rc->next; + + for (sp = rc->members; sp; ) { + struct snacpair *tmpsp; + + tmpsp = sp->next; + free(sp); + sp = tmpsp; + } + free(rc); + + rc = tmp; + } + + *head = NULL; + + return; +} + +static void connkill_real(aim_session_t *sess, aim_conn_t **deadconn) +{ + + aim_rxqueue_cleanbyconn(sess, *deadconn); + aim_tx_cleanqueue(sess, *deadconn); + + if ((*deadconn)->fd != -1) + aim_conn_close(*deadconn); + + /* + * XXX ->priv should never be touched by the library. I know + * it used to be, but I'm getting rid of all that. Use + * ->internal instead. + */ + if ((*deadconn)->priv) + free((*deadconn)->priv); + + /* + * This will free ->internal if it necessary... + */ + if ((*deadconn)->type == AIM_CONN_TYPE_CHAT) + aim_conn_kill_chat(sess, *deadconn); + + if ((*deadconn)->inside) { + aim_conn_inside_t *inside = (aim_conn_inside_t *)(*deadconn)->inside; + + connkill_snacgroups(&inside->groups); + connkill_rates(&inside->rates); + + free(inside); + } + + free(*deadconn); + *deadconn = NULL; + + return; +} + +/** + * Clears out connection list, killing remaining connections. + * + * @param sess Session to be cleared + */ +static void aim_connrst(aim_session_t *sess) +{ + + if (sess->connlist) { + aim_conn_t *cur = sess->connlist, *tmp; + + while (cur) { + tmp = cur->next; + aim_conn_close(cur); + connkill_real(sess, &cur); + cur = tmp; + } + } + + sess->connlist = NULL; + + return; +} + +/** + * Reset a connection to default values. + * Initializes and/or resets a connection structure. + * + * @param deadconn Connection to be reset + */ +static void aim_conn_init(aim_conn_t *deadconn) +{ + + if (!deadconn) + return; + + deadconn->fd = -1; + deadconn->subtype = -1; + deadconn->type = -1; + deadconn->seqnum = 0; + deadconn->lastactivity = 0; + deadconn->forcedlatency = 0; + deadconn->handlerlist = NULL; + deadconn->priv = NULL; + memset(deadconn->inside, 0, sizeof(aim_conn_inside_t)); + + return; +} + +/** + * aim_conn_getnext - Gets a new connection structure. + * @sess: Session + * + * Allocate a new empty connection structure. + * + */ +static aim_conn_t *aim_conn_getnext(aim_session_t *sess) +{ + aim_conn_t *newconn; + + if (!(newconn = malloc(sizeof(aim_conn_t)))) + return NULL; + memset(newconn, 0, sizeof(aim_conn_t)); + + if (!(newconn->inside = malloc(sizeof(aim_conn_inside_t)))) { + free(newconn); + return NULL; + } + memset(newconn->inside, 0, sizeof(aim_conn_inside_t)); + + aim_conn_init(newconn); + + newconn->next = sess->connlist; + sess->connlist = newconn; + + return newconn; +} + +/** + * aim_conn_kill - Close and free a connection. + * @sess: Session for the connection + * @deadconn: Connection to be freed + * + * Close, clear, and free a connection structure. Should never be + * called from within libfaim. + * + */ +faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn) +{ + aim_conn_t *cur, **prev; + + if (!deadconn || !*deadconn) + return; + + for (prev = &sess->connlist; (cur = *prev); ) { + if (cur == *deadconn) { + *prev = cur->next; + break; + } + prev = &cur->next; + } + + if (!cur) + return; /* oops */ + + connkill_real(sess, &cur); + + return; +} + +/** + * aim_conn_close - Close a connection + * @deadconn: Connection to close + * + * Close (but not free) a connection. + * + * This leaves everything untouched except for clearing the + * handler list and setting the fd to -1 (used to recognize + * dead connections). It will also remove cookies if necessary. + * + */ +faim_export void aim_conn_close(aim_conn_t *deadconn) +{ + aim_rxcallback_t userfunc; + + if (deadconn->fd >= 3) + close(deadconn->fd); + + deadconn->fd = -1; + + if ((userfunc = aim_callhandler(deadconn->sessv, deadconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNDEAD))) + userfunc(deadconn->sessv, NULL, deadconn); + + if (deadconn->handlerlist) + aim_clearhandlers(deadconn); + + return; +} + +/** + * aim_getconn_type - Find a connection of a specific type + * @sess: Session to search + * @type: Type of connection to look for + * + * Searches for a connection of the specified type in the + * specified session. Returns the first connection of that + * type found. + * + * XXX except for RENDEZVOUS, all uses of this should be removed and + * use aim_conn_findbygroup() instead. + */ +faim_export aim_conn_t *aim_getconn_type(aim_session_t *sess, int type) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + if ((cur->type == type) && + !(cur->status & AIM_CONN_STATUS_INPROGRESS)) + break; + } + + return cur; +} + +faim_export aim_conn_t *aim_getconn_type_all(aim_session_t *sess, int type) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + if (cur->type == type) + break; + } + + return cur; +} + +/* If you pass -1 for the fd, you'll get what you ask for. Gibberish. */ +faim_export aim_conn_t *aim_getconn_fd(aim_session_t *sess, int fd) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + if (cur->fd == fd) + break; + } + + return cur; +} + +/** + * aim_proxyconnect - An extrememly quick and dirty SOCKS5 interface. + * @sess: Session to connect + * @host: Host to connect to + * @port: Port to connect to + * @statusret: Return value of the connection + * + * Attempts to connect to the specified host via the configured + * proxy settings, if present. If no proxy is configured for + * this session, the connection is done directly. + * + * XXX this is really awful. + * + */ +static int aim_proxyconnect(aim_session_t *sess, const char *host, fu16_t port, fu32_t *statusret) +{ + int fd = -1; + + if (strlen(sess->socksproxy.server)) { /* connecting via proxy */ + int i; + unsigned char buf[512]; + struct sockaddr_in sa; + struct hostent *hp; + char *proxy; + unsigned short proxyport = 1080; + + for(i=0;i<(int)strlen(sess->socksproxy.server);i++) { + if (sess->socksproxy.server[i] == ':') { + proxyport = atoi(&(sess->socksproxy.server[i+1])); + break; + } + } + + proxy = (char *)malloc(i+1); + strncpy(proxy, sess->socksproxy.server, i); + proxy[i] = '\0'; + + if (!(hp = gethostbyname(proxy))) { + faimdprintf(sess, 0, "proxyconnect: unable to resolve proxy name\n"); + *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR); + return -1; + } + free(proxy); + + memset(&sa.sin_zero, 0, 8); + sa.sin_port = htons(proxyport); + memcpy(&sa.sin_addr, hp->h_addr, hp->h_length); + sa.sin_family = hp->h_addrtype; + + fd = socket(hp->h_addrtype, SOCK_STREAM, 0); + if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) { + faimdprintf(sess, 0, "proxyconnect: unable to connect to proxy\n"); + close(fd); + return -1; + } + + i = 0; + buf[0] = 0x05; /* SOCKS version 5 */ + if (strlen(sess->socksproxy.username)) { + buf[1] = 0x02; /* two methods */ + buf[2] = 0x00; /* no authentication */ + buf[3] = 0x02; /* username/password authentication */ + i = 4; + } else { + buf[1] = 0x01; + buf[2] = 0x00; + i = 3; + } + if (write(fd, buf, i) < i) { + *statusret = errno; + close(fd); + return -1; + } + if (read(fd, buf, 2) < 2) { + *statusret = errno; + close(fd); + return -1; + } + + if ((buf[0] != 0x05) || (buf[1] == 0xff)) { + *statusret = EINVAL; + close(fd); + return -1; + } + + /* check if we're doing username authentication */ + if (buf[1] == 0x02) { + i = aimutil_put8(buf, 0x01); /* version 1 */ + i += aimutil_put8(buf+i, strlen(sess->socksproxy.username)); + i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username)); + i += aimutil_put8(buf+i, strlen(sess->socksproxy.password)); + i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password)); + if (write(fd, buf, i) < i) { + *statusret = errno; + close(fd); + return -1; + } + if (read(fd, buf, 2) < 2) { + *statusret = errno; + close(fd); + return -1; + } + if ((buf[0] != 0x01) || (buf[1] != 0x00)) { + *statusret = EINVAL; + close(fd); + return -1; + } + } + + i = aimutil_put8(buf, 0x05); + i += aimutil_put8(buf+i, 0x01); /* CONNECT */ + i += aimutil_put8(buf+i, 0x00); /* reserved */ + i += aimutil_put8(buf+i, 0x03); /* address type: host name */ + i += aimutil_put8(buf+i, strlen(host)); + i += aimutil_putstr(buf+i, host, strlen(host)); + i += aimutil_put16(buf+i, port); + + if (write(fd, buf, i) < i) { + *statusret = errno; + close(fd); + return -1; + } + + if (read(fd, buf, 10) < 10) { + *statusret = errno; + close(fd); + return -1; + } + if ((buf[0] != 0x05) || (buf[1] != 0x00)) { + *statusret = EINVAL; + close(fd); + return -1; + } + + } else { /* connecting directly */ + struct sockaddr_in sa; + struct hostent *hp; + + if (!(hp = gethostbyname(host))) { + *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR); + return -1; + } + + memset(&sa, 0, sizeof(struct sockaddr_in)); + sa.sin_port = htons(port); + memcpy(&sa.sin_addr, hp->h_addr, hp->h_length); + sa.sin_family = hp->h_addrtype; + + fd = socket(hp->h_addrtype, SOCK_STREAM, 0); + + if (sess->nonblocking) + fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */ + + if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) { + if (sess->nonblocking) { + if ((errno == EINPROGRESS) || (errno == EINTR)) { + if (statusret) + *statusret |= AIM_CONN_STATUS_INPROGRESS; + return fd; + } + } + close(fd); + fd = -1; + } + } + return fd; +} + +/** + * aim_cloneconn - clone an aim_conn_t + * @sess: session containing parent + * @src: connection to clone + * + * A new connection is allocated, and the values are filled in + * appropriately. Note that this function sets the new connnection's + * ->priv pointer to be equal to that of its parent: only the pointer + * is copied, not the data it points to. + * + * This function returns a pointer to the new aim_conn_t, or %NULL on + * error + */ +faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src) +{ + aim_conn_t *conn; + + if (!(conn = aim_conn_getnext(sess))) + return NULL; + + conn->fd = src->fd; + conn->type = src->type; + conn->subtype = src->subtype; + conn->seqnum = src->seqnum; + conn->priv = src->priv; + conn->internal = src->internal; + conn->lastactivity = src->lastactivity; + conn->forcedlatency = src->forcedlatency; + conn->sessv = src->sessv; + aim_clonehandlers(sess, conn, src); + + if (src->inside) { + /* + * XXX should clone this section as well, but since currently + * this function only gets called for some of that rendezvous + * crap, and not on SNAC connections, its probably okay for + * now. + * + */ + } + + return conn; +} + +/** + * aim_newconn - Open a new connection + * @sess: Session to create connection in + * @type: Type of connection to create + * @dest: Host to connect to (in "host:port" syntax) + * + * Opens a new connection to the specified dest host of specified + * type, using the proxy settings if available. If @host is %NULL, + * the connection is allocated and returned, but no connection + * is made. + * + * FIXME: Return errors in a more sane way. + * + */ +faim_export aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *dest) +{ + aim_conn_t *connstruct; + fu16_t port = FAIM_LOGIN_PORT; + char *host; + int i, ret; + + if (!(connstruct = aim_conn_getnext(sess))) + return NULL; + + connstruct->sessv = (void *)sess; + connstruct->type = type; + + if (!dest) { /* just allocate a struct */ + connstruct->fd = -1; + connstruct->status = 0; + return connstruct; + } + + /* + * As of 23 Jul 1999, AOL now sends the port number, preceded by a + * colon, in the BOS redirect. This fatally breaks all previous + * libfaims. Bad, bad AOL. + * + * We put this here to catch every case. + * + */ + + for(i = 0; i < (int)strlen(dest); i++) { + if (dest[i] == ':') { + port = atoi(&(dest[i+1])); + break; + } + } + + host = (char *)malloc(i+1); + strncpy(host, dest, i); + host[i] = '\0'; + + if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) { + connstruct->fd = -1; + connstruct->status = (errno | AIM_CONN_STATUS_CONNERR); + free(host); + return connstruct; + } else + connstruct->fd = ret; + + free(host); + + return connstruct; +} + +/** + * aim_conngetmaxfd - Return the highest valued file discriptor in session + * @sess: Session to search + * + * Returns the highest valued filed descriptor of all open + * connections in @sess. + * + */ +faim_export int aim_conngetmaxfd(aim_session_t *sess) +{ + int j; + aim_conn_t *cur; + + for (cur = sess->connlist, j = 0; cur; cur = cur->next) { + if (cur->fd > j) + j = cur->fd; + } + + return j; +} + +/** + * aim_conn_in_sess - Predicate to test the precense of a connection in a sess + * @sess: Session to look in + * @conn: Connection to look for + * + * Searches @sess for the passed connection. Returns 1 if its present, + * zero otherwise. + * + */ +faim_export int aim_conn_in_sess(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + if (cur == conn) + return 1; + } + + return 0; +} + +/** + * aim_select - Wait for a socket with data or timeout + * @sess: Session to wait on + * @timeout: How long to wait + * @status: Return status + * + * Waits for a socket with data or for timeout, whichever comes first. + * See select(2). + * + * Return codes in *status: + * -1 error in select() (%NULL returned) + * 0 no events pending (%NULL returned) + * 1 outgoing data pending (%NULL returned) + * 2 incoming data pending (connection with pending data returned) + * + */ +faim_export aim_conn_t *aim_select(aim_session_t *sess, struct timeval *timeout, int *status) +{ + aim_conn_t *cur; + fd_set fds, wfds; + int maxfd, i, haveconnecting = 0; + + if (!sess->connlist) { + *status = -1; + return NULL; + } + + FD_ZERO(&fds); + FD_ZERO(&wfds); + + for (cur = sess->connlist, maxfd = 0; cur; cur = cur->next) { + if (cur->fd == -1) { + /* don't let invalid/dead connections sit around */ + *status = 2; + return cur; + } else if (cur->status & AIM_CONN_STATUS_INPROGRESS) { + FD_SET(cur->fd, &wfds); + + haveconnecting++; + } + FD_SET(cur->fd, &fds); + if (cur->fd > maxfd) + maxfd = cur->fd; + } + + /* + * If we have data waiting to be sent, return + * + * We have to not do this if theres at least one + * connection thats still connecting, since that connection + * may have queued data and this return would prevent + * the connection from ever completing! This is a major + * inadequacy of the libfaim way of doing things. It means + * that nothing can transmit as long as there's connecting + * sockets. Evil. + * + * But its still better than having blocking connects. + * + */ + if (!haveconnecting && sess->queue_outgoing) { + *status = 1; + return NULL; + } + + if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) { + for (cur = sess->connlist; cur; cur = cur->next) { + if ((FD_ISSET(cur->fd, &fds)) || + ((cur->status & AIM_CONN_STATUS_INPROGRESS) && + FD_ISSET(cur->fd, &wfds))) { + *status = 2; + return cur; + } + } + *status = 0; /* shouldn't happen */ + } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */ + *status = 0; + else + *status = i; /* can be 0 or -1 */ + + return NULL; /* no waiting or error, return */ +} + +/** + * aim_conn_setlatency - Set a forced latency value for connection + * @conn: Conn to set latency for + * @newval: Number of seconds to force between transmits + * + * Causes @newval seconds to be spent between transmits on a connection. + * + * This is my lame attempt at overcoming not understanding the rate + * limiting. + * + * XXX: This should really be replaced with something that scales and + * backs off like the real rate limiting does. + * + */ +faim_export int aim_conn_setlatency(aim_conn_t *conn, int newval) +{ + + if (!conn) + return -1; + + conn->forcedlatency = newval; + conn->lastactivity = 0; /* reset this just to make sure */ + + return 0; +} + +/** + * aim_setupproxy - Configure a proxy for this session + * @sess: Session to set proxy for + * @server: SOCKS server + * @username: SOCKS username + * @password: SOCKS password + * + * Call this with your SOCKS5 proxy server parameters before + * the first call to aim_newconn(). If called with all %NULL + * args, it will clear out a previously set proxy. + * + * Set username and password to %NULL if not applicable. + * + */ +faim_export void aim_setupproxy(aim_session_t *sess, const char *server, const char *username, const char *password) +{ + /* clear out the proxy info */ + if (!server || !strlen(server)) { + memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server)); + memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username)); + memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password)); + return; + } + + strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server)); + if (username && strlen(username)) + strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username)); + if (password && strlen(password)) + strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password)); + + return; +} + +static void defaultdebugcb(aim_session_t *sess, int level, const char *format, va_list va) +{ + + vfprintf(stderr, format, va); + + return; +} + +/** + * Initializes a session structure by setting the initial values + * stuff in the aim_session_t struct. + * + * @param sess Session to initialize. + * @param nonblocking Set to true if you want connections to be non-blocking. + * @param debuglevel Level of debugging output (zero is least). + */ +faim_export void aim_session_init(aim_session_t *sess, bool nonblocking, int debuglevel) +{ + + if (!sess) + return; + + memset(sess, 0, sizeof(aim_session_t)); + aim_connrst(sess); + sess->queue_outgoing = NULL; + sess->queue_incoming = NULL; + aim_initsnachash(sess); + sess->msgcookies = NULL; + sess->nonblocking = nonblocking; + sess->debug = debuglevel; + sess->debugcb = defaultdebugcb; + sess->modlistv = NULL; + sess->snacid_next = 0x00000001; + + sess->locate.userinfo = NULL; + sess->locate.torequest = NULL; + sess->locate.requested = NULL; + sess->locate.waiting_for_response = FALSE; + sess->ssi.received_data = 0; + sess->ssi.numitems = 0; + sess->ssi.official = NULL; + sess->ssi.local = NULL; + sess->ssi.pending = NULL; + sess->ssi.timestamp = (time_t)0; + sess->ssi.waiting_for_ack = 0; + sess->icq_info = NULL; + sess->authinfo = NULL; + sess->emailinfo = NULL; + sess->oft_info = NULL; + + /* + * This must always be set. Default to the queue-based + * version for back-compatibility. + */ + aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL); + + /* + * Register all the modules for this session... + */ + aim__registermodule(sess, misc_modfirst); /* load the catch-all first */ + aim__registermodule(sess, service_modfirst); + aim__registermodule(sess, locate_modfirst); + aim__registermodule(sess, buddylist_modfirst); + aim__registermodule(sess, msg_modfirst); + aim__registermodule(sess, adverts_modfirst); + aim__registermodule(sess, invite_modfirst); + aim__registermodule(sess, admin_modfirst); + aim__registermodule(sess, popups_modfirst); + aim__registermodule(sess, bos_modfirst); + aim__registermodule(sess, search_modfirst); + aim__registermodule(sess, stats_modfirst); + aim__registermodule(sess, translate_modfirst); + aim__registermodule(sess, chatnav_modfirst); + aim__registermodule(sess, chat_modfirst); + aim__registermodule(sess, odir_modfirst); + aim__registermodule(sess, bart_modfirst); + /* missing 0x11 - 0x12 */ + aim__registermodule(sess, ssi_modfirst); + /* missing 0x14 */ + aim__registermodule(sess, icq_modfirst); /* XXX - Make sure this isn't sent for AIM */ + /* missing 0x16 */ + aim__registermodule(sess, auth_modfirst); + aim__registermodule(sess, email_modfirst); + + return; +} + +/** + * aim_session_kill - Deallocate a session + * @sess: Session to kill + * + */ +faim_export void aim_session_kill(aim_session_t *sess) +{ + aim_cleansnacs(sess, -1); + + aim_logoff(sess); + + aim__shutdownmodules(sess); + + return; +} + +/** + * aim_setdebuggingcb - Set the function to call when outputting debugging info + * @sess: Session to change + * @cb: Function to call + * + * The function specified is called whenever faimdprintf() is used within + * libfaim, and the session's debugging level is greater tha nor equal to + * the value faimdprintf was called with. + * + */ +faim_export int aim_setdebuggingcb(aim_session_t *sess, faim_debugging_callback_t cb) +{ + + if (!sess) + return -1; + + sess->debugcb = cb; + + return 0; +} + +/** + * aim_conn_isconnecting - Determine if a connection is connecting + * @conn: Connection to examine + * + * Returns nonzero if the connection is in the process of + * connecting (or if it just completed and aim_conn_completeconnect() + * has yet to be called on it). + * + */ +faim_export int aim_conn_isconnecting(aim_conn_t *conn) +{ + + if (!conn) + return 0; + + return !!(conn->status & AIM_CONN_STATUS_INPROGRESS); +} + +/* + * XXX this is nearly as ugly as proxyconnect(). + */ +faim_export int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn) +{ + aim_rxcallback_t userfunc; + + if (!conn || (conn->fd == -1)) + return -1; + + if (!(conn->status & AIM_CONN_STATUS_INPROGRESS)) + return -1; + + fcntl(conn->fd, F_SETFL, 0); + + conn->status &= ~AIM_CONN_STATUS_INPROGRESS; + + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE))) + userfunc(sess, NULL, conn); + + /* Flush out the queues if there was something waiting for this conn */ + aim_tx_flushqueue(sess); + + return 0; +} + +faim_export aim_session_t *aim_conn_getsess(aim_conn_t *conn) +{ + + if (!conn) + return NULL; + + return (aim_session_t *)conn->sessv; +} + +/* + * aim_logoff() + * + * Closes -ALL- open connections. + * + */ +faim_export int aim_logoff(aim_session_t *sess) +{ + + aim_connrst(sess); /* in case we want to connect again */ + + return 0; +} + +/* + * aim_flap_nop() + * + * No-op. WinAIM 4.x sends these _every minute_ to keep + * the connection alive. + */ +faim_export int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *fr; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0))) + return -ENOMEM; + + aim_tx_enqueue(sess, fr); + + /* clean out SNACs over 60sec old */ + aim_cleansnacs(sess, 60); + + return 0; +} diff --git a/libfaim/email.c b/libfaim/email.c new file mode 100644 index 0000000..ae7314b --- /dev/null +++ b/libfaim/email.c @@ -0,0 +1,216 @@ +/* + * Family 0x0018 - Email notification + * + * Used for being alerted when the email address(es) associated with + * your screen name get new electronic-m. For normal AIM accounts, you + * get the email address screenname@netscape.net. AOL accounts have + * screenname@aol.com, and can also activate a netscape.net account. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/** + * Subtype 0x0006 - Request information about your email account + * + * @param sess The oscar session. + * @param conn The email connection for this session. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_email_sendcookies(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_EML))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16+16))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x0018, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0018, 0x0006, 0x0000, snacid); + + /* Number of cookies to follow */ + aimbs_put16(&fr->data, 0x0002); + + /* Cookie */ + aimbs_put16(&fr->data, 0x5d5e); + aimbs_put16(&fr->data, 0x1708); + aimbs_put16(&fr->data, 0x55aa); + aimbs_put16(&fr->data, 0x11d3); + aimbs_put16(&fr->data, 0xb143); + aimbs_put16(&fr->data, 0x0060); + aimbs_put16(&fr->data, 0xb0fb); + aimbs_put16(&fr->data, 0x1ecb); + + /* Cookie */ + aimbs_put16(&fr->data, 0xb380); + aimbs_put16(&fr->data, 0x9ad8); + aimbs_put16(&fr->data, 0x0dba); + aimbs_put16(&fr->data, 0x11d5); + aimbs_put16(&fr->data, 0x9f8a); + aimbs_put16(&fr->data, 0x0060); + aimbs_put16(&fr->data, 0xb0ee); + aimbs_put16(&fr->data, 0x0631); + + aim_tx_enqueue(sess, fr); + + return 0; +} + + +/** + * Subtype 0x0007 - Receive information about your email account + * + * So I don't even know if you can have multiple 16 byte keys, + * but this is coded so it will handle that, and handle it well. + * This tells you if you have unread mail or not, the URL you + * should use to access that mail, and the domain name for the + * email account (screenname@domainname.com). If this is the + * first 0x0007 SNAC you've received since you signed on, or if + * this is just a periodic status update, this will also contain + * the number of unread emails that you have. + */ +static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + struct aim_emailinfo *new; + aim_tlvlist_t *tlvlist; + fu8_t *cookie8, *cookie16; + int tmp, havenewmail = 0; /* Used to tell the client we have _new_ mail */ + + char *alertitle = NULL, *alerturl = NULL; + + cookie8 = aimbs_getraw(bs, 8); /* Possibly the code used to log you in to mail? */ + cookie16 = aimbs_getraw(bs, 16); /* Mail cookie sent above */ + + /* See if we already have some info associated with this cookie */ + for (new=sess->emailinfo; (new && strncmp(cookie16, new->cookie16, 16)); new=new->next); + if (new) { + /* Free some of the old info, if existant */ + free(new->cookie8); + free(new->cookie16); + free(new->url); + free(new->domain); + } else { + /* We don't already have info, so create a new struct for it */ + if (!(new = malloc(sizeof(struct aim_emailinfo)))) + return -ENOMEM; + memset(new, 0, sizeof(struct aim_emailinfo)); + new->next = sess->emailinfo; + sess->emailinfo = new; + } + + new->cookie8 = cookie8; + new->cookie16 = cookie16; + + tlvlist = aim_tlvlist_readnum(bs, aimbs_get16(bs)); + + tmp = aim_tlv_get16(tlvlist, 0x0080, 1); + if (tmp) { + if (new->nummsgs < tmp) + havenewmail = 1; + new->nummsgs = tmp; + } else { + /* If they don't send a 0x0080 TLV, it means we definately have new mail */ + /* (ie. this is not just another status update) */ + havenewmail = 1; + new->nummsgs++; /* We know we have at least 1 new email */ + } + new->url = aim_tlv_getstr(tlvlist, 0x0007, 1); + if (!(new->unread = aim_tlv_get8(tlvlist, 0x0081, 1))) { + havenewmail = 0; + new->nummsgs = 0; + } + new->domain = aim_tlv_getstr(tlvlist, 0x0082, 1); + new->flag = aim_tlv_get16(tlvlist, 0x0084, 1); + + alertitle = aim_tlv_getstr(tlvlist, 0x0005, 1); + alerturl = aim_tlv_getstr(tlvlist, 0x000d, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, new, havenewmail, alertitle, (alerturl ? alerturl + 2 : NULL)); + + aim_tlvlist_free(&tlvlist); + + free(alertitle); + free(alerturl); + + return ret; +} + +/** + * Subtype 0x0016 - Send something or other + * + * @param sess The oscar session. + * @param conn The email connection for this session. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_email_activate(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_EML))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+16))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x0018, 0x0016, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0018, 0x0016, 0x0000, snacid); + + /* I would guess this tells AIM that you want updates for your mail accounts */ + /* ...but I really have no idea */ + aimbs_put8(&fr->data, 0x02); + aimbs_put32(&fr->data, 0x04000000); + aimbs_put32(&fr->data, 0x04000000); + aimbs_put32(&fr->data, 0x04000000); + aimbs_put32(&fr->data, 0x00000000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0007) + return parseinfo(sess, mod, rx, snac, bs); + + return 0; +} + +static void email_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + while (sess->emailinfo) { + struct aim_emailinfo *tmp = sess->emailinfo; + sess->emailinfo = sess->emailinfo->next; + free(tmp->cookie16); + free(tmp->cookie8); + free(tmp->url); + free(tmp->domain); + free(tmp); + } + + return; +} + +faim_internal int email_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0018; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "email", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = email_shutdown; + + return 0; +} diff --git a/libfaim/faimconfig.h b/libfaim/faimconfig.h new file mode 100644 index 0000000..6498703 --- /dev/null +++ b/libfaim/faimconfig.h @@ -0,0 +1,59 @@ +/* + * faimconfig.h + * + * Contains various compile-time options that apply _only_ to libfaim. + * + */ + +#ifndef __FAIMCONFIG_H__ +#define __FAIMCONFIG_H__ + +/* + * USE_SNAC_FOR_IMS is an old feature that allowed better + * tracking of error messages by caching SNAC IDs of outgoing + * ICBMs and comparing them to incoming errors. However, + * its a helluvalot of overhead for something that should + * rarely happen. + * + * Default: defined. This is now defined by default + * because it should be stable and its not too bad. + * And Josh wanted it. + * + */ +#define USE_SNAC_FOR_IMS + +/* + * Default Authorizer server name and TCP port for the OSCAR farm. + * + * You shouldn't need to change this unless you're writing + * your own server. + * + * Note that only one server is needed to start the whole + * AIM process. The later server addresses come from + * the authorizer service. + * + * This is only here for convenience. Its still up to + * the client to connect to it. + * + */ +#define FAIM_LOGIN_SERVER "login.oscar.aol.com" +#define FAIM_LOGIN_PORT 5190 + +/* + * Size of the SNAC caching hash. + * + * Default: 16 + * + */ +#define FAIM_SNAC_HASH_SIZE 16 + +/* + * If building on Win32, define WIN32_STATIC if you don't want + * to compile libfaim as a DLL (and instead link it right into + * your app). + */ +#define WIN32_STATIC + +#endif /* __FAIMCONFIG_H__ */ + + diff --git a/libfaim/ft.c b/libfaim/ft.c new file mode 100644 index 0000000..5ace227 --- /dev/null +++ b/libfaim/ft.c @@ -0,0 +1,993 @@ +/* + * Oscar File transfer (OFT) and Oscar Direct Connect (ODC). + * (ODC is also referred to as DirectIM and IM Image.) + * + * There are a few static helper functions at the top, then + * ODC stuff, then ft stuff. + * + * I feel like this is a good place to explain OFT, so I'm going to + * do just that. Each OFT packet has a header type. I guess this + * is pretty similar to the subtype of a SNAC packet. The type + * basically tells the other client the meaning of the OFT packet. + * There are two distinct types of file transfer, which I usually + * call "sendfile" and "getfile." Sendfile is when you send a file + * to another AIM user. Getfile is when you share a group of files, + * and other users request that you send them the files. + * + * A typical sendfile file transfer goes like this: + * 1) Sender sends a channel 2 ICBM telling the other user that + * we want to send them a file. At the same time, we open a + * listener socket (this should be done before sending the + * ICBM) on some port, and wait for them to connect to us. + * The ICBM we sent should contain our IP address and the port + * number that we're listening on. + * 2) The receiver connects to the sender on the given IP address + * and port. After the connection is established, the receiver + * sends an ICBM signifying that we are ready and waiting. + * 3) The sender sends an OFT PROMPT message over the OFT + * connection. + * 4) The receiver of the file sends back an exact copy of this + * OFT packet, except the cookie is filled in with the cookie + * from the ICBM. I think this might be an attempt to verify + * that the user that is connected is actually the guy that + * we sent the ICBM to. Oh, I've been calling this the ACK. + * 5) The sender starts sending raw data across the connection + * until the entire file has been sent. + * 6) The receiver knows the file is finished because the sender + * sent the file size in an earlier OFT packet. So then the + * receiver sends the DONE thingy (after filling in the + * "received" checksum and size) and closes the connection. + */ + +#define FAIM_INTERNAL +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <aim.h> + +#ifndef _WIN32 +#include <stdio.h> +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/utsname.h> /* for aim_odc_initiate */ +#include <arpa/inet.h> /* for inet_ntoa */ +#define G_DIR_SEPARATOR '/' +#endif + +#ifdef _WIN32 +#include "win32dep.h" +#endif + +#ifndef socklen_t +#define socklen_t int +#endif + +struct aim_odc_intdata { + fu8_t cookie[8]; + char sn[MAXSNLEN+1]; + char ip[22]; +}; + +/** + * Convert the directory separator from / (0x2f) to ^A (0x01) + * + * @param name The filename to convert. + */ +static void aim_oft_dirconvert_tostupid(char *name) +{ + while (name[0]) { + if (name[0] == 0x01) + name[0] = G_DIR_SEPARATOR; + name++; + } +} + +/** + * Convert the directory separator from ^A (0x01) to / (0x2f) + * + * @param name The filename to convert. + */ +static void aim_oft_dirconvert_fromstupid(char *name) +{ + while (name[0]) { + if (name[0] == G_DIR_SEPARATOR) + name[0] = 0x01; + name++; + } +} + +/** + * Calculate oft checksum of buffer + * + * Prevcheck should be 0xFFFF0000 when starting a checksum of a file. The + * checksum is kind of a rolling checksum thing, so each time you get bytes + * of a file you just call this puppy and it updates the checksum. You can + * calculate the checksum of an entire file by calling this in a while or a + * for loop, or something. + * + * Thanks to Graham Booker for providing this improved checksum routine, + * which is simpler and should be more accurate than Josh Myer's original + * code. -- wtm + * + * This algorithim works every time I have tried it. The other fails + * sometimes. So, AOL who thought this up? It has got to be the weirdest + * checksum I have ever seen. + * + * @param buffer Buffer of data to checksum. Man I'd like to buff her... + * @param bufsize Size of buffer. + * @param prevcheck Previous checksum. + */ +faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck) +{ + fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck; + int i; + unsigned short val; + + for (i=0; i<bufferlen; i++) { + oldcheck = check; + if (i&1) + val = buffer[i]; + else + val = buffer[i] << 8; + check -= val; + /* + * The following appears to be necessary.... It happens + * every once in a while and the checksum doesn't fail. + */ + if (check > oldcheck) + check--; + } + check = ((check & 0x0000ffff) + (check >> 16)); + check = ((check & 0x0000ffff) + (check >> 16)); + return check << 16; +} + +faim_export fu32_t aim_oft_checksum_file(char *filename) { + FILE *fd; + fu32_t checksum = 0xffff0000; + + if ((fd = fopen(filename, "rb"))) { + int bytes; + fu8_t buffer[1024]; + + while ((bytes = fread(buffer, 1, 1024, fd))) + checksum = aim_oft_checksum_chunk(buffer, bytes, checksum); + fclose(fd); + } + + return checksum; +} + +/** + * Create a listening socket on a given port. + * + * XXX - Give the client author the responsibility of setting up a + * listener, then we no longer have a libfaim problem with broken + * solaris *innocent smile* -- jbm + * + * @param portnum The port number to bind to. + * @return The file descriptor of the listening socket. + */ +static int listenestablish(fu16_t portnum) +{ +#if HAVE_GETADDRINFO + int listenfd; + const int on = 1; + struct addrinfo hints, *res, *ressave; + char serv[5]; + + snprintf(serv, sizeof(serv), "%d", portnum); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(NULL /* any IP */, serv, &hints, &res) != 0) { + perror("getaddrinfo"); + return -1; + } + ressave = res; + do { + listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (listenfd < 0) + continue; + setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) + break; /* success */ + close(listenfd); + } while ( (res = res->ai_next) ); + + if (!res) + return -1; + + freeaddrinfo(ressave); +#else + int listenfd; + const int on = 1; + struct sockaddr_in sockin; + + if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return -1; + } + + if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) { + perror("setsockopt"); + close(listenfd); + return -1; + } + + memset(&sockin, 0, sizeof(struct sockaddr_in)); + sockin.sin_family = AF_INET; + sockin.sin_port = htons(portnum); + + if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) { + perror("bind"); + close(listenfd); + return -1; + } +#endif + + if (listen(listenfd, 4) != 0) { + perror("listen"); + close(listenfd); + return -1; + } + fcntl(listenfd, F_SETFL, O_NONBLOCK); + + return listenfd; +} + +/** + * After establishing a listening socket, this is called to accept a connection. It + * clones the conn used by the listener, and passes both of these to a signal handler. + * The signal handler should close the listener conn and keep track of the new conn, + * since this is what is used for file transfers and what not. + * + * @param sess The session. + * @param cur The conn the incoming connection is on. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur) +{ + int acceptfd = 0; + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + int ret = 0; + aim_conn_t *newconn; + char ip[20]; + int port; + + if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1) + return 0; /* not an error */ + + if ((addr.sa_family != AF_INET) && (addr.sa_family != AF_INET6)) { /* just in case IPv6 really is happening */ + close(acceptfd); + aim_conn_close(cur); + return -1; + } + + strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip)); + port = ntohs(((struct sockaddr_in *)&addr)->sin_port); + + if (!(newconn = aim_cloneconn(sess, cur))) { + close(acceptfd); + aim_conn_close(cur); + return -ENOMEM; + } + + newconn->type = AIM_CONN_TYPE_RENDEZVOUS; + newconn->fd = acceptfd; + + if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { + aim_rxcallback_t userfunc; + struct aim_odc_intdata *priv; + + priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal); + cur->internal = NULL; + snprintf(priv->ip, sizeof(priv->ip), "%s:%u", ip, port); + + if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED))) + ret = userfunc(sess, NULL, newconn, cur); + + } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) { + } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) { + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED))) + ret = userfunc(sess, NULL, newconn, cur); + + } else { + faimdprintf(sess, 1,"Got a connection on a listener that's not rendezvous. Closing connection.\n"); + aim_conn_close(newconn); + ret = -1; + } + + return ret; +} + +/** + * Send client-to-client typing notification over an established direct connection. + * + * @param sess The session. + * @param conn The already-connected ODC connection. + * @param typing If 0x0002, sends a "typing" message, 0x0001 sends "typed," and + * 0x0000 sends "stopped." + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing) +{ + struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal; + aim_frame_t *fr; + aim_bstream_t *hdrbs; + fu8_t *hdr; + int hdrlen = 0x44; + + if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, 0))) + return -ENOMEM; + memcpy(fr->hdr.rend.magic, "ODC2", 4); + fr->hdr.rend.hdrlen = hdrlen; + + if (!(hdr = calloc(1, hdrlen))) { + aim_frame_destroy(fr); + return -ENOMEM; + } + + hdrbs = &(fr->data); + aim_bstream_init(hdrbs, hdr, hdrlen); + + aimbs_put16(hdrbs, 0x0006); + aimbs_put16(hdrbs, 0x0000); + aimbs_putraw(hdrbs, intdata->cookie, 8); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put32(hdrbs, 0x00000000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + + if (typing == 0x0002) + aimbs_put16(hdrbs, 0x0002 | 0x0008); + else if (typing == 0x0001) + aimbs_put16(hdrbs, 0x0002 | 0x0004); + else + aimbs_put16(hdrbs, 0x0002); + + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn)); + + aim_bstream_setpos(hdrbs, 52); /* bleeehh */ + + aimbs_put8(hdrbs, 0x00); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put8(hdrbs, 0x00); + + /* end of hdr */ + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Send client-to-client IM over an established direct connection. + * Call this just like you would aim_send_im, to send a directim. + * + * @param sess The session. + * @param conn The already-connected ODC connection. + * @param msg Null-terminated string to send. + * @param len The length of the message to send, including binary data. + * @param encoding 0 for ascii, 2 for Unicode, 3 for ISO 8859-1. + * @param isawaymsg 0 if this is not an auto-response, 1 if it is. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg) +{ + aim_frame_t *fr; + aim_bstream_t *hdrbs; + struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal; + int hdrlen = 0x44; + fu8_t *hdr; + + if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0))) + return -ENOMEM; + + memcpy(fr->hdr.rend.magic, "ODC2", 4); + fr->hdr.rend.hdrlen = hdrlen; + + if (!(hdr = calloc(1, hdrlen + len))) { + aim_frame_destroy(fr); + return -ENOMEM; + } + + hdrbs = &(fr->data); + aim_bstream_init(hdrbs, hdr, hdrlen + len); + + aimbs_put16(hdrbs, 0x0006); + aimbs_put16(hdrbs, 0x0000); + aimbs_putraw(hdrbs, intdata->cookie, 8); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put32(hdrbs, len); + aimbs_put16(hdrbs, encoding); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + + /* flags - used for typing notification and to mark if this is an away message */ + aimbs_put16(hdrbs, 0x0000 | isawaymsg); + + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn)); + + aim_bstream_setpos(hdrbs, 52); /* bleeehh */ + + aimbs_put8(hdrbs, 0x00); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put8(hdrbs, 0x00); + + /* end of hdr2 */ + +#if 0 /* XXX - this is how you send buddy icon info... */ + aimbs_put16(hdrbs, 0x0008); + aimbs_put16(hdrbs, 0x000c); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x1466); + aimbs_put16(hdrbs, 0x0001); + aimbs_put16(hdrbs, 0x2e0f); + aimbs_put16(hdrbs, 0x393e); + aimbs_put16(hdrbs, 0xcac8); +#endif + aimbs_putraw(hdrbs, msg, len); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Get the screen name of the peer of a direct connection. + * + * @param conn The ODC connection. + * @return The screen name of the dude, or NULL if there was an anomaly. + */ +faim_export const char *aim_odc_getsn(aim_conn_t *conn) +{ + struct aim_odc_intdata *intdata; + + if (!conn || !conn->internal) + return NULL; + + if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || + (conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)) + return NULL; + + intdata = (struct aim_odc_intdata *)conn->internal; + + return intdata->sn; +} + +/** + * Find the conn of a direct connection with the given buddy. + * + * @param sess The session. + * @param sn The screen name of the buddy whose direct connection you want to find. + * @return The conn for the direct connection with the given buddy, or NULL if no + * connection was found. + */ +faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn) +{ + aim_conn_t *cur; + struct aim_odc_intdata *intdata; + + if (!sess || !sn || !strlen(sn)) + return NULL; + + for (cur = sess->connlist; cur; cur = cur->next) { + if ((cur->type == AIM_CONN_TYPE_RENDEZVOUS) && (cur->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) { + intdata = cur->internal; + if (!aim_sncmp(intdata->sn, sn)) + return cur; + } + } + + return NULL; +} + +/** + * For those times when we want to open up the direct connection channel ourselves. + * + * You'll want to set up some kind of watcher on this socket. + * When the state changes, call aim_handlerendconnection with + * the connection returned by this. aim_handlerendconnection + * will accept the pending connection and stop listening. + * + * @param sess The session + * @param sn The screen name to connect to. + * @return The new connection. + */ +faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn) +{ + aim_conn_t *newconn; + aim_msgcookie_t *cookie; + struct aim_odc_intdata *priv; + int listenfd; + fu16_t port = 4443; + fu8_t localip[4]; + fu8_t ck[8]; + + if (aim_util_getlocalip(localip) == -1) + return NULL; + + if ((listenfd = listenestablish(port)) == -1) + return NULL; + + aim_im_sendch2_odcrequest(sess, ck, sn, localip, port); + + cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t)); + memcpy(cookie->cookie, ck, 8); + cookie->type = AIM_COOKIETYPE_OFTIM; + + /* this one is for the cookie */ + priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata)); + + memcpy(priv->cookie, ck, 8); + strncpy(priv->sn, sn, sizeof(priv->sn)); + cookie->data = priv; + aim_cachecookie(sess, cookie); + + /* XXX - switch to aim_cloneconn()? */ + if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) { + close(listenfd); + return NULL; + } + + /* this one is for the conn */ + priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata)); + + memcpy(priv->cookie, ck, 8); + strncpy(priv->sn, sn, sizeof(priv->sn)); + + newconn->fd = listenfd; + newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; + newconn->internal = priv; + newconn->lastactivity = time(NULL); + + return newconn; +} + +/** + * Connect directly to the given buddy for directim. + * + * This is a wrapper for aim_newconn. + * + * If addr is NULL, the socket is not created, but the connection is + * allocated and setup to connect. + * + * @param sess The Godly session. + * @param sn The screen name we're connecting to. I hope it's a girl... + * @param addr Address to connect to. + * @return The new connection. + */ +faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie) +{ + aim_conn_t *newconn; + struct aim_odc_intdata *intdata; + + if (!sess || !sn) + return NULL; + + if (!(intdata = calloc(1, sizeof(struct aim_odc_intdata)))) + return NULL; + memcpy(intdata->cookie, cookie, 8); + strncpy(intdata->sn, sn, sizeof(intdata->sn)); + if (addr) + strncpy(intdata->ip, addr, sizeof(intdata->ip)); + + /* XXX - verify that non-blocking connects actually work */ + if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr))) { + free(intdata); + return NULL; + } + + newconn->internal = intdata; + newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; + + return newconn; +} + +/** + * Sometimes you just don't know with these kinds of people. + * + * @param sess The session. + * @param conn The ODC connection of the incoming data. + * @param frr The frame allocated for the incoming data. + * @param bs It stands for "bologna sandwich." + * @return Return 0 if no errors, otherwise return the error number. + */ +static int handlehdr_odc(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *frr, aim_bstream_t *bs) +{ + aim_frame_t fr; + int ret = 0; + aim_rxcallback_t userfunc; + fu32_t payloadlength; + fu16_t flags, encoding; + char *snptr = NULL; + + fr.conn = conn; + + /* AAA - ugly */ + aim_bstream_setpos(bs, 20); + payloadlength = aimbs_get32(bs); + + aim_bstream_setpos(bs, 24); + encoding = aimbs_get16(bs); + + aim_bstream_setpos(bs, 30); + flags = aimbs_get16(bs); + + aim_bstream_setpos(bs, 36); + /* XXX - create an aimbs_getnullstr function? */ + snptr = aimbs_getstr(bs, 32); /* Next 32 bytes contain the sn, padded with null chars */ + + faimdprintf(sess, 2, "faim: OFT frame: handlehdr_odc: %04x / %04x / %s\n", payloadlength, flags, snptr); + + if (flags & 0x0008) { + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) + ret = userfunc(sess, &fr, snptr, 2); + } else if (flags & 0x0004) { + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) + ret = userfunc(sess, &fr, snptr, 1); + } else { + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) + ret = userfunc(sess, &fr, snptr, 0); + } + + if (payloadlength) { + char *msg; + int recvd = 0; + int i, isawaymsg; + + isawaymsg = flags & 0x0001; + + if (!(msg = calloc(1, payloadlength+1))) { + free(snptr); + return -ENOMEM; + } + + while (payloadlength - recvd) { + if (payloadlength - recvd >= 1024) + i = aim_recv(conn->fd, &msg[recvd], 1024); + else + i = aim_recv(conn->fd, &msg[recvd], payloadlength - recvd); + if (i <= 0) { + free(msg); + free(snptr); + return -1; + } + recvd = recvd + i; + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER))) + ret = userfunc(sess, &fr, snptr, (double)recvd / payloadlength); + } + + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING))) + ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding, isawaymsg); + + free(msg); + } + + free(snptr); + + return ret; +} + +faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn, const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename) +{ + struct aim_oft_info *new; + + if (!sess) + return NULL; + + if (!(new = (struct aim_oft_info *)calloc(1, sizeof(struct aim_oft_info)))) + return NULL; + + new->sess = sess; + if (cookie) + memcpy(new->cookie, cookie, 8); + if (ip) + new->clientip = strdup(ip); + if (sn) + new->sn = strdup(sn); + new->port = port; + new->fh.totfiles = 1; + new->fh.filesleft = 1; + new->fh.totparts = 1; + new->fh.partsleft = 1; + new->fh.totsize = size; + new->fh.size = size; + new->fh.modtime = modtime; + new->fh.checksum = 0xffff0000; + new->fh.rfrcsum = 0xffff0000; + new->fh.rfcsum = 0xffff0000; + new->fh.recvcsum = 0xffff0000; + strncpy(new->fh.idstring, "OFT_Windows ICBMFT V1.1 32", 31); + if (filename) + strncpy(new->fh.name, filename, 63); + + new->next = sess->oft_info; + sess->oft_info = new; + + return new; +} + +/** + * Remove the given oft_info struct from the oft_info linked list, and + * then free its memory. + * + * @param sess The session. + * @param oft_info The aim_oft_info struct that we're destroying. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info) +{ + aim_session_t *sess; + + if (!oft_info || !(sess = oft_info->sess)) + return -EINVAL; + + if (sess->oft_info && (sess->oft_info == oft_info)) { + sess->oft_info = sess->oft_info->next; + } else { + struct aim_oft_info *cur; + for (cur=sess->oft_info; (cur->next && (cur->next!=oft_info)); cur=cur->next); + if (cur->next) + cur->next = cur->next->next; + } + + free(oft_info->sn); + free(oft_info->proxyip); + free(oft_info->clientip); + free(oft_info->verifiedip); + free(oft_info); + + return 0; +} + +/** + * Creates a listener socket so the other dude can connect to us. + * + * You'll want to set up some kind of watcher on this socket. + * When the state changes, call aim_handlerendconnection with + * the connection returned by this. aim_handlerendconnection + * will accept the pending connection and stop listening. + * + * @param sess The session. + * @param oft_info File transfer information associated with this + * connection. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info) +{ + int listenfd; + + if (!oft_info) + return -EINVAL; + + if ((listenfd = listenestablish(oft_info->port)) == -1) + return 1; + + if (!(oft_info->conn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) { + close(listenfd); + return -ENOMEM; + } + + oft_info->conn->fd = listenfd; + oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE; + oft_info->conn->lastactivity = time(NULL); + + return 0; +} + +/** + * Extract an &aim_fileheader_t from the given buffer. + * + * @param bs The should be from an incoming rendezvous packet. + * @return A pointer to new struct on success, or NULL on error. + */ +static struct aim_fileheader_t *aim_oft_getheader(aim_bstream_t *bs) +{ + struct aim_fileheader_t *fh; + + if (!(fh = calloc(1, sizeof(struct aim_fileheader_t)))) + return NULL; + + /* The bstream should be positioned after the hdrtype. */ + aimbs_getrawbuf(bs, fh->bcookie, 8); + fh->encrypt = aimbs_get16(bs); + fh->compress = aimbs_get16(bs); + fh->totfiles = aimbs_get16(bs); + fh->filesleft = aimbs_get16(bs); + fh->totparts = aimbs_get16(bs); + fh->partsleft = aimbs_get16(bs); + fh->totsize = aimbs_get32(bs); + fh->size = aimbs_get32(bs); + fh->modtime = aimbs_get32(bs); + fh->checksum = aimbs_get32(bs); + fh->rfrcsum = aimbs_get32(bs); + fh->rfsize = aimbs_get32(bs); + fh->cretime = aimbs_get32(bs); + fh->rfcsum = aimbs_get32(bs); + fh->nrecvd = aimbs_get32(bs); + fh->recvcsum = aimbs_get32(bs); + aimbs_getrawbuf(bs, fh->idstring, 32); + fh->flags = aimbs_get8(bs); + fh->lnameoffset = aimbs_get8(bs); + fh->lsizeoffset = aimbs_get8(bs); + aimbs_getrawbuf(bs, fh->dummy, 69); + aimbs_getrawbuf(bs, fh->macfileinfo, 16); + fh->nencode = aimbs_get16(bs); + fh->nlanguage = aimbs_get16(bs); + aimbs_getrawbuf(bs, fh->name, 64); /* XXX - filenames longer than 64B */ + + return fh; +} + +/** + * Fills a buffer with network-order fh data + * + * @param bs A bstream to fill -- automatically initialized + * @param fh A struct aim_fileheader_t to get data from. + * @return Return non-zero on error. + */ +static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh) +{ + fu8_t *hdr; + + if (!bs || !fh) + return -EINVAL; + + if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8))) + return -ENOMEM; + + aim_bstream_init(bs, hdr, 0x100 - 8); + aimbs_putraw(bs, fh->bcookie, 8); + aimbs_put16(bs, fh->encrypt); + aimbs_put16(bs, fh->compress); + aimbs_put16(bs, fh->totfiles); + aimbs_put16(bs, fh->filesleft); + aimbs_put16(bs, fh->totparts); + aimbs_put16(bs, fh->partsleft); + aimbs_put32(bs, fh->totsize); + aimbs_put32(bs, fh->size); + aimbs_put32(bs, fh->modtime); + aimbs_put32(bs, fh->checksum); + aimbs_put32(bs, fh->rfrcsum); + aimbs_put32(bs, fh->rfsize); + aimbs_put32(bs, fh->cretime); + aimbs_put32(bs, fh->rfcsum); + aimbs_put32(bs, fh->nrecvd); + aimbs_put32(bs, fh->recvcsum); + aimbs_putraw(bs, fh->idstring, 32); + aimbs_put8(bs, fh->flags); + aimbs_put8(bs, fh->lnameoffset); + aimbs_put8(bs, fh->lsizeoffset); + aimbs_putraw(bs, fh->dummy, 69); + aimbs_putraw(bs, fh->macfileinfo, 16); + aimbs_put16(bs, fh->nencode); + aimbs_put16(bs, fh->nlanguage); + aimbs_putraw(bs, fh->name, 64); /* XXX - filenames longer than 64B */ + + return 0; +} + +/** + * Create an OFT packet based on the given information, and send it on its merry way. + * + * @param sess The session. + * @param type The subtype of the OFT packet we're sending. + * @param oft_info The aim_oft_info struct with the connection and OFT + * info we're sending. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info) +{ + aim_frame_t *fr; + + if (!sess || !oft_info || !oft_info->conn || (oft_info->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) + return -EINVAL; + +#if 0 + /* + * If you are receiving a file, the cookie should be null, if you are sending a + * file, the cookie should be the same as the one used in the ICBM negotiation + * SNACs. + */ + fh->lnameoffset = 0x1a; + fh->lsizeoffset = 0x10; + + /* apparently 0 is ASCII, 2 is UCS-2 */ + /* it is likely that 3 is ISO 8859-1 */ + /* I think "nlanguage" might be the same thing as "subenc" in im.c */ + fh->nencode = 0x0000; + fh->nlanguage = 0x0000; +#endif + + aim_oft_dirconvert_tostupid(oft_info->fh.name); + + if (!(fr = aim_tx_new(sess, oft_info->conn, AIM_FRAMETYPE_OFT, type, 0))) + return -ENOMEM; + + if (aim_oft_buildheader(&fr->data, &oft_info->fh) == -1) { + aim_frame_destroy(fr); + return -ENOMEM; + } + + memcpy(fr->hdr.rend.magic, "OFT2", 4); + fr->hdr.rend.hdrlen = aim_bstream_curpos(&fr->data); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Handle incoming data on a rendezvous connection. This is analogous to the + * consumesnac function in rxhandlers.c, and I really think this should probably + * be in rxhandlers.c as well, but I haven't finished cleaning everything up yet. + * + * @param sess The session. + * @param fr The frame allocated for the incoming data. + * @return Return 0 if the packet was handled correctly, otherwise return the + * error number. + */ +faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr) +{ + aim_conn_t *conn = fr->conn; + int ret = 1; + + if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { + if (fr->hdr.rend.type == 0x0001) + ret = handlehdr_odc(sess, conn, fr, &fr->data); + else + faimdprintf(sess, 0, "faim: ODC directim frame unknown, type is %04x\n", fr->hdr.rend.type); + + } else { + aim_rxcallback_t userfunc; + struct aim_fileheader_t *header = aim_oft_getheader(&fr->data); + aim_oft_dirconvert_fromstupid(header->name); /* XXX - This should be client-side */ + + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type))) + ret = userfunc(sess, fr, conn, header->bcookie, header); + + free(header); + } + + if (ret == -1) + aim_conn_close(conn); + + return ret; +} diff --git a/libfaim/icq.c b/libfaim/icq.c new file mode 100644 index 0000000..cced59c --- /dev/null +++ b/libfaim/icq.c @@ -0,0 +1,684 @@ +/* + * Family 0x0015 - Encapsulated ICQ. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x003c); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x003e); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_export int aim_icq_hideip(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2+4+2+2+2+4; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x0424); /* shrug. */ + aimbs_putle16(&fr->data, 0x0001); + aimbs_putle16(&fr->data, 0x0001); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Change your ICQ password. + * + * @param sess The oscar session + * @param passwd The new password. If this is longer than 8 characters it + * will be truncated. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen, passwdlen; + + if (!passwd) + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + passwdlen = strlen(passwd); + if (passwdlen > MAXICQPASSLEN) + passwdlen = MAXICQPASSLEN; + bslen = 2+4+2+2+2+2+passwdlen+1; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x042e); /* shrug. */ + aimbs_putle16(&fr->data, passwdlen+1); + aimbs_putraw(&fr->data, passwd, passwdlen); + aimbs_putle8(&fr->data, '\0'); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + struct aim_icq_info *info; + + if (!uin || uin[0] < '0' || uin[0] > '9') + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2 + 2 + 4; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x04b2); /* shrug. */ + aimbs_putle32(&fr->data, atoi(uin)); + + aim_tx_enqueue(sess, fr); + + /* Keep track of this request and the ICQ number and request ID */ + info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info)); + info->reqid = snacid; + info->uin = atoi(uin); + info->next = sess->icq_info; + sess->icq_info = info; + + return 0; +} + +faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + struct aim_icq_info *info; + + if (!uin || uin[0] < '0' || uin[0] > '9') + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2 + 2 + 4; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x04ba); /* shrug. */ + aimbs_putle32(&fr->data, atoi(uin)); + + aim_tx_enqueue(sess, fr); + + /* Keep track of this request and the ICQ number and request ID */ + info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info)); + info->reqid = snacid; + info->uin = atoi(uin); + info->next = sess->icq_info; + sess->icq_info = info; + + return 0; +} + +faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!uin || uin[0] < '0' || uin[0] > '9') + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2 + 2 + 4; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x051f); /* shrug. */ + aimbs_putle32(&fr->data, atoi(uin)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_export int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!xml || !strlen(xml)) + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 10 + 2 + strlen(xml) + 1; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x0998); /* shrug. */ + aimbs_putle16(&fr->data, strlen(xml) + 1); + aimbs_putraw(&fr->data, xml, strlen(xml) + 1); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Send an SMS message. This is the non-US way. The US-way is to IM + * their cell phone number (+19195551234). + * + * We basically construct and send an XML message. The format is: + * <icq_sms_message> + * <destination>full_phone_without_leading_+</destination> + * <text>message</text> + * <codepage>1252</codepage> + * <senders_UIN>self_uin</senders_UIN> + * <senders_name>self_name</senders_name> + * <delivery_receipt>Yes|No</delivery_receipt> + * <time>Wkd, DD Mmm YYYY HH:MM:SS TMZ</time> + * </icq_sms_message> + * + * Yeah hi Peter, whaaaat's happening. If there's any way to use + * a codepage other than 1252 that would be great. Thaaaanks. + */ +faim_export int aim_icq_sendsms(aim_session_t *sess, const char *name, const char *msg, const char *alias) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen, xmllen; + char *xml, timestr[30]; + time_t t; + struct tm *tm; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + if (!name || !msg || !alias) + return -EINVAL; + + time(&t); + tm = gmtime(&t); + strftime(timestr, 30, "%a, %d %b %Y %T %Z", tm); + + /* The length of xml included the null terminating character */ + xmllen = 225 + strlen(name) + strlen(msg) + strlen(sess->sn) + strlen(alias) + strlen(timestr) + 1; + + if (!(xml = (char *)malloc(xmllen*sizeof(char)))) + return -ENOMEM; + snprintf(xml, xmllen, "<icq_sms_message>\n" + "\t<destination>%s</destination>\n" + "\t<text>%s</text>\n" + "\t<codepage>1252</codepage>\n" + "\t<senders_UIN>%s</senders_UIN>\n" + "\t<senders_name>%s</senders_name>\n" + "\t<delivery_receipt>Yes</delivery_receipt>\n" + "\t<time>%s</time>\n" + "</icq_sms_message>\n", + name, msg, sess->sn, alias, timestr); + + bslen = 37 + xmllen; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) { + free(xml); + return -ENOMEM; + } + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + + /* From libicq200-0.3.2/src/SNAC-SRV.cpp */ + aimbs_putle16(&fr->data, 0x8214); + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, 0x0016); + aimbs_put32(&fr->data, 0x00000000); + aimbs_put32(&fr->data, 0x00000000); + aimbs_put32(&fr->data, 0x00000000); + aimbs_put32(&fr->data, 0x00000000); + + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, xmllen); + aimbs_putraw(&fr->data, xml, xmllen); + + aim_tx_enqueue(sess, fr); + + free(xml); + + return 0; +} + +static void aim_icq_freeinfo(struct aim_icq_info *info) { + int i; + + if (!info) + return; + free(info->nick); + free(info->first); + free(info->last); + free(info->email); + free(info->homecity); + free(info->homestate); + free(info->homephone); + free(info->homefax); + free(info->homeaddr); + free(info->mobile); + free(info->homezip); + free(info->personalwebpage); + if (info->email2) + for (i = 0; i < info->numaddresses; i++) + free(info->email2[i]); + free(info->email2); + free(info->workcity); + free(info->workstate); + free(info->workphone); + free(info->workfax); + free(info->workaddr); + free(info->workzip); + free(info->workcompany); + free(info->workdivision); + free(info->workposition); + free(info->workwebpage); + free(info->info); + free(info); +} + +/** + * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet. + */ +static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_tlvlist_t *tl; + aim_tlv_t *datatlv; + aim_bstream_t qbs; + fu32_t ouruin; + fu16_t cmdlen, cmd, reqid; + + if (!(tl = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tl, 0x0001, 1))) { + aim_tlvlist_free(&tl); + faimdprintf(sess, 0, "corrupt ICQ response\n"); + return 0; + } + + aim_bstream_init(&qbs, datatlv->value, datatlv->length); + + cmdlen = aimbs_getle16(&qbs); + ouruin = aimbs_getle32(&qbs); + cmd = aimbs_getle16(&qbs); + reqid = aimbs_getle16(&qbs); + + faimdprintf(sess, 1, "icq response: %d bytes, %ld, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid); + + if (cmd == 0x0041) { /* offline message */ + struct aim_icq_offlinemsg msg; + aim_rxcallback_t userfunc; + + memset(&msg, 0, sizeof(msg)); + + msg.sender = aimbs_getle32(&qbs); + msg.year = aimbs_getle16(&qbs); + msg.month = aimbs_getle8(&qbs); + msg.day = aimbs_getle8(&qbs); + msg.hour = aimbs_getle8(&qbs); + msg.minute = aimbs_getle8(&qbs); + msg.type = aimbs_getle8(&qbs); + msg.flags = aimbs_getle8(&qbs); + msg.msglen = aimbs_getle16(&qbs); + msg.msg = aimbs_getstr(&qbs, msg.msglen); + + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG))) + ret = userfunc(sess, rx, &msg); + + free(msg.msg); + + } else if (cmd == 0x0042) { + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE))) + ret = userfunc(sess, rx); + + } else if (cmd == 0x07da) { /* information */ + fu16_t subtype; + struct aim_icq_info *info; + aim_rxcallback_t userfunc; + + subtype = aimbs_getle16(&qbs); + aim_bstream_advance(&qbs, 1); /* 0x0a */ + + /* find other data from the same request */ + for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next); + if (!info) { + info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info)); + info->reqid = reqid; + info->next = sess->icq_info; + sess->icq_info = info; + } + + switch (subtype) { + case 0x00a0: { /* hide ip status */ + /* nothing */ + } break; + + case 0x00aa: { /* password change status */ + /* nothing */ + } break; + + case 0x00c8: { /* general and "home" information */ + info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homecity = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homestate = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homephone = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homefax = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homeaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->mobile = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homezip = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homecountry = aimbs_getle16(&qbs); + /* 0x0a 00 02 00 */ + /* 1 byte timezone? */ + /* 1 byte hide email flag? */ + } break; + + case 0x00dc: { /* personal information */ + info->age = aimbs_getle8(&qbs); + info->unknown = aimbs_getle8(&qbs); + info->gender = aimbs_getle8(&qbs); + info->personalwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->birthyear = aimbs_getle16(&qbs); + info->birthmonth = aimbs_getle8(&qbs); + info->birthday = aimbs_getle8(&qbs); + info->language1 = aimbs_getle8(&qbs); + info->language2 = aimbs_getle8(&qbs); + info->language3 = aimbs_getle8(&qbs); + /* 0x00 00 01 00 00 01 00 00 00 00 00 */ + } break; + + case 0x00d2: { /* work information */ + info->workcity = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workstate = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workphone = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workfax = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workzip = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workcountry = aimbs_getle16(&qbs); + info->workcompany = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workdivision = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workposition = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + aim_bstream_advance(&qbs, 2); /* 0x01 00 */ + info->workwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + } break; + + case 0x00e6: { /* additional personal information */ + info->info = aimbs_getstr(&qbs, aimbs_getle16(&qbs)-1); + } break; + + case 0x00eb: { /* email address(es) */ + int i; + info->numaddresses = aimbs_getle16(&qbs); + info->email2 = (char **)calloc(info->numaddresses, sizeof(char *)); + for (i = 0; i < info->numaddresses; i++) { + info->email2[i] = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + if (i+1 != info->numaddresses) + aim_bstream_advance(&qbs, 1); /* 0x00 */ + } + } break; + + case 0x00f0: { /* personal interests */ + } break; + + case 0x00fa: { /* past background and current organizations */ + } break; + + case 0x0104: { /* alias info */ + info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + aim_bstream_advance(&qbs, aimbs_getle16(&qbs)); /* email address? */ + /* Then 0x00 02 00 */ + } break; + + case 0x010e: { /* unknown */ + /* 0x00 00 */ + } break; + + case 0x019a: { /* simple info */ + aim_bstream_advance(&qbs, 2); + info->uin = aimbs_getle32(&qbs); + info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + /* Then 0x00 02 00 00 00 00 00 */ + } break; + } /* End switch statement */ + + if (!(snac->flags & 0x0001)) { + if (subtype != 0x0104) + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO))) + ret = userfunc(sess, rx, info); + + if (info->uin && info->nick) + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS))) + ret = userfunc(sess, rx, info); + + if (sess->icq_info == info) { + sess->icq_info = info->next; + } else { + struct aim_icq_info *cur; + for (cur=sess->icq_info; (cur->next && (cur->next!=info)); cur=cur->next); + if (cur->next) + cur->next = cur->next->next; + } + aim_icq_freeinfo(info); + } + } + + aim_tlvlist_free(&tl); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return icqresponse(sess, mod, rx, snac, bs); + + return 0; +} + +static void icq_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + struct aim_icq_info *del; + + while (sess->icq_info) { + del = sess->icq_info; + sess->icq_info = sess->icq_info->next; + aim_icq_freeinfo(del); + } + + return; +} + +faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0015; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x047c; + mod->flags = 0; + strncpy(mod->name, "icq", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = icq_shutdown; + + return 0; +} diff --git a/libfaim/im.c b/libfaim/im.c new file mode 100644 index 0000000..ebe64fc --- /dev/null +++ b/libfaim/im.c @@ -0,0 +1,2298 @@ +/* + * Family 0x0004 - Routines for sending/receiving Instant Messages. + * + * Note the term ICBM (Inter-Client Basic Message) which blankets + * all types of genericly routed through-server messages. Within + * the ICBM types (family 4), a channel is defined. Each channel + * represents a different type of message. Channel 1 is used for + * what would commonly be called an "instant message". Channel 2 + * is used for negotiating "rendezvous". These transactions end in + * something more complex happening, such as a chat invitation, or + * a file transfer. Channel 3 is used for chat messages (not in + * the same family as these channels). Channel 4 is used for + * various ICQ messages. Examples are normal messages, URLs, and + * old-style authorization. + * + * In addition to the channel, every ICBM contains a cookie. For + * standard IMs, these are only used for error messages. However, + * the more complex rendezvous messages make suitably more complex + * use of this field. + * + * TODO: Split this up into an im.c file an an icbm.c file. It + * will be beautiful, you'll see. + * + * Need to rename all the mpmsg messages to aim_im_bleh. + * + * Make sure aim_conn_findbygroup is used by all functions. + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#ifdef _WIN32 +#include "win32dep.h" +#endif + +/** + * Add a standard ICBM header to the given bstream with the given + * information. + * + * @param bs The bstream to write the ICBM header to. + * @param c c is for cookie, and cookie is for me. + * @param ch The ICBM channel (1 through 4). + * @param sn Null-terminated scrizeen nizame. + * @return The number of bytes written. It's really not useful. + */ +static int aim_im_puticbm(aim_bstream_t *bs, const fu8_t *c, fu16_t ch, const char *sn) +{ + aimbs_putraw(bs, c, 8); + aimbs_put16(bs, ch); + aimbs_put8(bs, strlen(sn)); + aimbs_putraw(bs, sn, strlen(sn)); + return 8+2+1+strlen(sn); +} + +/* + * Takes a msghdr (and a length) and returns a client type + * code. Note that this is *only a guess* and has a low likelihood + * of actually being accurate. + * + * Its based on experimental data, with the help of Eric Warmenhoven + * who seems to have collected a wide variety of different AIM clients. + * + * + * Heres the current collection: + * 0501 0003 0101 0101 01 AOL Mobile Communicator, WinAIM 1.0.414 + * 0501 0003 0101 0201 01 WinAIM 2.0.847, 2.1.1187, 3.0.1464, + * 4.3.2229, 4.4.2286 + * 0501 0004 0101 0102 0101 WinAIM 4.1.2010, libfaim (right here) + * 0501 0003 0101 02 WinAIM 5 + * 0501 0001 01 iChat x.x, mobile buddies + * 0501 0001 0101 01 AOL v6.0, CompuServe 2000 v6.0, any TOC client + * 0501 0002 0106 WinICQ 5.45.1.3777.85 + * + * Note that in this function, only the feature bytes are tested, since + * the rest will always be the same. + * + */ +faim_export fu16_t aim_im_fingerprint(const fu8_t *msghdr, int len) +{ + static const struct { + fu16_t clientid; + int len; + fu8_t data[10]; + } fingerprints[] = { + /* AOL Mobile Communicator, WinAIM 1.0.414 */ + { AIM_CLIENTTYPE_MC, + 3, {0x01, 0x01, 0x01}}, + + /* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */ + { AIM_CLIENTTYPE_WINAIM, + 3, {0x01, 0x01, 0x02}}, + + /* WinAIM 4.1.2010, libfaim */ + { AIM_CLIENTTYPE_WINAIM41, + 4, {0x01, 0x01, 0x01, 0x02}}, + + /* AOL v6.0, CompuServe 2000 v6.0, any TOC client */ + { AIM_CLIENTTYPE_AOL_TOC, + 1, {0x01}}, + + { 0, 0} + }; + int i; + + if (!msghdr || (len <= 0)) + return AIM_CLIENTTYPE_UNKNOWN; + + for (i = 0; fingerprints[i].len; i++) { + if (fingerprints[i].len != len) + continue; + if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0) + return fingerprints[i].clientid; + } + + return AIM_CLIENTTYPE_UNKNOWN; +} + +/** + * Subtype 0x0002 - Set ICBM parameters. + * + * I definitly recommend sending this. If you don't, you'll be stuck + * with the rather unreasonable defaults. + * + */ +faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!params) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid); + + /* This is read-only (see Parameter Reply). Must be set to zero here. */ + aimbs_put16(&fr->data, 0x0000); + + /* These are all read-write */ + aimbs_put32(&fr->data, params->flags); + aimbs_put16(&fr->data, params->maxmsglen); + aimbs_put16(&fr->data, params->maxsenderwarn); + aimbs_put16(&fr->data, params->maxrecverwarn); + aimbs_put32(&fr->data, params->minmsginterval); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0004 - Request ICBM parameter information. + * + */ +faim_export int aim_im_reqparams(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + return aim_genericreq_n_snacid(sess, conn, 0x0004, 0x0004); +} + +/** + * Subtype 0x0005 - Receive parameter information. + * + */ +static int aim_im_paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + struct aim_icbmparameters params; + + params.maxchan = aimbs_get16(bs); + params.flags = aimbs_get32(bs); + params.maxmsglen = aimbs_get16(bs); + params.maxsenderwarn = aimbs_get16(bs); + params.maxrecverwarn = aimbs_get16(bs); + params.minmsginterval = aimbs_get32(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, ¶ms); + + return 0; +} + +/** + * Subtype 0x0006 - Send an ICBM (instant message). + * + * + * Possible flags: + * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse + * AIM_IMFLAGS_ACK -- Requests that the server send an ack + * when the message is received (of type 0x0004/0x000c) + * AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are + * online (probably ICQ only). + * AIM_IMFLAGS_UNICODE--Instead of ASCII7, the passed message is + * made up of UNICODE duples. If you set + * this, you'd better be damn sure you know + * what you're doing. + * AIM_IMFLAGS_ISO_8859_1 -- The message contains the ASCII8 subset + * known as ISO-8859-1. + * + * Generally, you should use the lowest encoding possible to send + * your message. If you only use basic punctuation and the generic + * Latin alphabet, use ASCII7 (no flags). If you happen to use non-ASCII7 + * characters, but they are all clearly defined in ISO-8859-1, then + * use that. Keep in mind that not all characters in the PC ASCII8 + * character set are defined in the ISO standard. For those cases (most + * notably when the (r) symbol is used), you must use the full UNICODE + * encoding for your message. In UNICODE mode, _all_ characters must + * occupy 16bits, including ones that are not special. (Remember that + * the first 128 UNICODE symbols are equivelent to ASCII7, however they + * must be prefixed with a zero high order byte.) + * + * I strongly discourage the use of UNICODE mode, mainly because none + * of the clients I use can parse those messages (and besides that, + * wchars are difficult and non-portable to handle in most UNIX environments). + * If you really need to include special characters, use the HTML UNICODE + * entities. These are of the form ߪ where 2026 is the hex + * representation of the UNICODE index (in this case, UNICODE + * "Horizontal Ellipsis", or 133 in in ASCII8). + * + * Implementation note: Since this is one of the most-used functions + * in all of libfaim, it is written with performance in mind. As such, + * it is not as clear as it could be in respect to how this message is + * supposed to be layed out. Most obviously, tlvlists should be used + * instead of writing out the bytes manually. + * + * XXX - more precise verification that we never send SNACs larger than 8192 + * XXX - check SNAC size for multipart + * + */ +faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + fu8_t ck[8]; + int i, msgtlvlen; + static const fu8_t deffeatures[] = { 0x01, 0x01, 0x01, 0x02 }; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!args) + return -EINVAL; + + if (args->flags & AIM_IMFLAGS_MULTIPART) { + if (args->mpmsg->numparts <= 0) + return -EINVAL; + } else { + if (!args->msg || (args->msglen <= 0)) + return -EINVAL; + + if (args->msglen >= MAXMSGLEN) + return -E2BIG; + } + + /* Painfully calculate the size of the message TLV */ + msgtlvlen = 1 + 1; /* 0501 */ + + if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) + msgtlvlen += 2 + args->featureslen; + else + msgtlvlen += 2 + sizeof(deffeatures); + + if (args->flags & AIM_IMFLAGS_MULTIPART) { + aim_mpmsg_section_t *sec; + + for (sec = args->mpmsg->parts; sec; sec = sec->next) { + msgtlvlen += 2 /* 0101 */ + 2 /* block len */; + msgtlvlen += 4 /* charset */ + sec->datalen; + } + + } else { + msgtlvlen += 2 /* 0101 */ + 2 /* block len */; + msgtlvlen += 4 /* charset */ + args->msglen; + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128))) + return -ENOMEM; + + /* XXX - should be optional */ + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* + * Generate a random message cookie + * + * We could cache these like we do SNAC IDs. (In fact, it + * might be a good idea.) In the message error functions, + * the 8byte message cookie is returned as well as the + * SNAC ID. + * + */ + for (i = 0; i < 8; i++) + ck[i] = (fu8_t)rand(); + + /* ICBM header */ + aim_im_puticbm(&fr->data, ck, 0x0001, args->destsn); + + /* Message TLV (type 0x0002) */ + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, msgtlvlen); + + /* Features TLV (type 0x0501) */ + aimbs_put16(&fr->data, 0x0501); + if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) { + aimbs_put16(&fr->data, args->featureslen); + aimbs_putraw(&fr->data, args->features, args->featureslen); + } else { + aimbs_put16(&fr->data, sizeof(deffeatures)); + aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures)); + } + + if (args->flags & AIM_IMFLAGS_MULTIPART) { + aim_mpmsg_section_t *sec; + + /* Insert each message part in a TLV (type 0x0101) */ + for (sec = args->mpmsg->parts; sec; sec = sec->next) { + aimbs_put16(&fr->data, 0x0101); + aimbs_put16(&fr->data, sec->datalen + 4); + aimbs_put16(&fr->data, sec->charset); + aimbs_put16(&fr->data, sec->charsubset); + aimbs_putraw(&fr->data, sec->data, sec->datalen); + } + + } else { + + /* Insert message text in a TLV (type 0x0101) */ + aimbs_put16(&fr->data, 0x0101); + + /* Message block length */ + aimbs_put16(&fr->data, args->msglen + 0x04); + + /* Character set */ + aimbs_put16(&fr->data, args->charset); + aimbs_put16(&fr->data, args->charsubset); + + /* Message. Not terminated */ + aimbs_putraw(&fr->data, args->msg, args->msglen); + } + + /* Set the Autoresponse flag */ + if (args->flags & AIM_IMFLAGS_AWAY) { + aimbs_put16(&fr->data, 0x0004); + aimbs_put16(&fr->data, 0x0000); + } else if (args->flags & AIM_IMFLAGS_ACK) { + /* Set the Request Acknowledge flag */ + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0000); + } + + if (args->flags & AIM_IMFLAGS_OFFLINE) { + aimbs_put16(&fr->data, 0x0006); + aimbs_put16(&fr->data, 0x0000); + } + + /* + * Set the I HAVE A REALLY PURTY ICON flag. + * XXX - This should really only be sent on initial + * IMs and when you change your icon. + */ + if (args->flags & AIM_IMFLAGS_HASICON) { + aimbs_put16(&fr->data, 0x0008); + aimbs_put16(&fr->data, 0x000c); + aimbs_put32(&fr->data, args->iconlen); + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, args->iconsum); + aimbs_put32(&fr->data, args->iconstamp); + } + + /* + * Set the Buddy Icon Requested flag. + * XXX - Everytime? Surely not... + */ + if (args->flags & AIM_IMFLAGS_BUDDYREQ) { + aimbs_put16(&fr->data, 0x0009); + aimbs_put16(&fr->data, 0x0000); + } + + aim_tx_enqueue(sess, fr); + + /* clean out SNACs over 60sec old */ + aim_cleansnacs(sess, 60); + + return 0; +} + +/* + * Simple wrapper for aim_im_sendch1_ext() + * + * You cannot use aim_send_im if you need the HASICON flag. You must + * use aim_im_sendch1_ext directly for that. + * + * aim_send_im also cannot be used if you require UNICODE messages, because + * that requires an explicit message length. Use aim_im_sendch1_ext(). + * + */ +faim_export int aim_im_sendch1(aim_session_t *sess, const char *sn, fu16_t flags, const char *msg) +{ + struct aim_sendimext_args args; + + args.destsn = sn; + args.flags = flags; + args.msg = msg; + args.msglen = strlen(msg); + args.charset = 0x0000; + args.charsubset = 0x0000; + + /* Make these don't get set by accident -- they need aim_im_sendch1_ext */ + args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART); + + return aim_im_sendch1_ext(sess, &args); +} + +/** + * Subtype 0x0006 - Send your icon to a given user. + * + * This is also performance sensitive. (If you can believe it...) + * + */ +faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + fu8_t ck[8]; + int i; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN)) + return -EINVAL; + + for (i = 0; i < 8; i++) + ck[i] = (fu8_t)rand(); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, ck, 0x0002, sn); + + /* + * TLV t(0005) + * + * Encompasses everything below. + */ + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT)); + + aimbs_put16(&fr->data, 0x0000); + aimbs_putraw(&fr->data, ck, 8); + aim_putcap(&fr->data, AIM_CAPS_BUDDYICON); + + /* TLV t(000a) */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* TLV t(000f) */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* TLV t(2711) */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, 4+4+4+iconlen+strlen(AIM_ICONIDENT)); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, iconsum); + aimbs_put32(&fr->data, iconlen); + aimbs_put32(&fr->data, stamp); + aimbs_putraw(&fr->data, icon, iconlen); + aimbs_putraw(&fr->data, AIM_ICONIDENT, strlen(AIM_ICONIDENT)); + + /* TLV t(0003) */ + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0006 - Send a rich text message. + * + * This only works for ICQ 2001b (thats 2001 not 2000). Better, only + * send it to clients advertising the RTF capability. In fact, if you send + * it to a client that doesn't support that capability, the server will gladly + * bounce it back to you. + * + * You'd think this would be in icq.c, but, well, I'm trying to stick with + * the one-group-per-file scheme as much as possible. This could easily + * be an exception, since Rendezvous IMs are external of the Oscar core, + * and therefore are undefined. Really I just need to think of a good way to + * make an interface similar to what AOL actually uses. But I'm not using COM. + * + */ +faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + fu8_t ck[8]; + const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */ + int i, servdatalen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!args || !args->destsn || !args->rtfmsg) + return -EINVAL; + + servdatalen = 2+2+16+2+4+1+2 + 2+2+4+4+4 + 2+4+2+strlen(args->rtfmsg)+1 + 4+4+4+strlen(rtfcap)+1; + + for (i = 0; i < 8; i++) + ck[i] = (fu8_t)rand(); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, ck, 0x0002, args->destsn); + + /* TLV t(0005) - Encompasses everything below. */ + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 2+8+16 + 2+2+2 + 2+2 + 2+2+servdatalen); + + aimbs_put16(&fr->data, 0x0000); + aimbs_putraw(&fr->data, ck, 8); + aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY); + + /* t(000a) l(0002) v(0001) */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* t(000f) l(0000) v() */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* Service Data TLV */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, servdatalen); + + aimbs_putle16(&fr->data, 11 + 16 /* 11 + (sizeof CLSID) */); + aimbs_putle16(&fr->data, 9); + aim_putcap(&fr->data, AIM_CAPS_EMPTY); + aimbs_putle16(&fr->data, 0); + aimbs_putle32(&fr->data, 0); + aimbs_putle8(&fr->data, 0); + aimbs_putle16(&fr->data, 0x03ea); /* trid1 */ + + aimbs_putle16(&fr->data, 14); + aimbs_putle16(&fr->data, 0x03eb); /* trid2 */ + aimbs_putle32(&fr->data, 0); + aimbs_putle32(&fr->data, 0); + aimbs_putle32(&fr->data, 0); + + aimbs_putle16(&fr->data, 0x0001); + aimbs_putle32(&fr->data, 0); + aimbs_putle16(&fr->data, strlen(args->rtfmsg)+1); + aimbs_putraw(&fr->data, args->rtfmsg, strlen(args->rtfmsg)+1); + + aimbs_putle32(&fr->data, args->fgcolor); + aimbs_putle32(&fr->data, args->bgcolor); + aimbs_putle32(&fr->data, strlen(rtfcap)+1); + aimbs_putraw(&fr->data, rtfcap, strlen(rtfcap)+1); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send an "I want to directly connect to you" message + * + */ +faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, fu8_t *cookie, const char *sn, const fu8_t *ip, fu16_t port) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + fu8_t ck[8]; + aim_tlvlist_t *tl = NULL, *itl = NULL; + int hdrlen, i; + fu8_t *hdr; + aim_bstream_t hdrbs; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* + * Generate a random message cookie + * + * This cookie needs to be alphanumeric and NULL-terminated to be + * TOC-compatible. + * + * XXX - have I mentioned these should be generated in msgcookie.c? + * + */ + for (i = 0; i < 7; i++) + ck[i] = 0x30 + ((fu8_t) rand() % 10); + ck[7] = '\0'; + + if (cookie) + memcpy(cookie, ck, 8); + + /* ICBM header */ + aim_im_puticbm(&fr->data, ck, 0x0002, sn); + + aim_tlvlist_add_noval(&tl, 0x0003); + + hdrlen = 2+8+16+6+8+6+4; + hdr = malloc(hdrlen); + aim_bstream_init(&hdrbs, hdr, hdrlen); + + aimbs_put16(&hdrbs, 0x0000); + aimbs_putraw(&hdrbs, ck, 8); + aim_putcap(&hdrbs, AIM_CAPS_DIRECTIM); + + aim_tlvlist_add_16(&itl, 0x000a, 0x0001); + aim_tlvlist_add_raw(&itl, 0x0003, 4, ip); + aim_tlvlist_add_16(&itl, 0x0005, port); + aim_tlvlist_add_noval(&itl, 0x000f); + + aim_tlvlist_write(&hdrbs, &itl); + + aim_tlvlist_add_raw(&tl, 0x0005, aim_bstream_curpos(&hdrbs), hdr); + + aim_tlvlist_write(&fr->data, &tl); + + free(hdr); + aim_tlvlist_free(&itl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send an "I want to send you this file" message + * + */ +faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl=NULL, *subtl=NULL; + int i; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info) + return -EINVAL; + + /* XXX - Should be like "21CBF95" and null terminated */ + for (i = 0; i < 7; i++) + oft_info->cookie[i] = 0x30 + ((fu8_t)rand() % 10); + oft_info->cookie[7] = '\0'; + + { /* Create the subTLV chain */ + fu8_t *buf; + int buflen; + aim_bstream_t bs; + + aim_tlvlist_add_16(&subtl, 0x000a, 0x0001); + aim_tlvlist_add_noval(&subtl, 0x000f); +/* aim_tlvlist_add_raw(&subtl, 0x000e, 2, "en"); + aim_tlvlist_add_raw(&subtl, 0x000d, 8, "us-ascii"); + aim_tlvlist_add_raw(&subtl, 0x000c, 24, "Please accept this file."); */ + if (oft_info->clientip) { + fu8_t ip[4]; + char *nexttoken; + int i = 0; + nexttoken = strtok(oft_info->clientip, "."); + while (nexttoken && i<4) { + ip[i] = atoi(nexttoken); + nexttoken = strtok(NULL, "."); + i++; + } + aim_tlvlist_add_raw(&subtl, 0x0003, 4, ip); + } + aim_tlvlist_add_16(&subtl, 0x0005, oft_info->port); + + /* TLV t(2711) */ + buflen = 2+2+4+strlen(oft_info->fh.name)+1; + buf = malloc(buflen); + aim_bstream_init(&bs, buf, buflen); + aimbs_put16(&bs, (oft_info->fh.totfiles > 1) ? 0x0002 : 0x0001); + aimbs_put16(&bs, oft_info->fh.totfiles); + aimbs_put32(&bs, oft_info->fh.totsize); + + /* Filename - NULL terminated, for some odd reason */ + aimbs_putraw(&bs, oft_info->fh.name, strlen(oft_info->fh.name)); + aimbs_put8(&bs, 0x00); + + aim_tlvlist_add_raw(&subtl, 0x2711, bs.len, bs.data); + free(buf); + } + + { /* Create the main TLV chain */ + fu8_t *buf; + int buflen; + aim_bstream_t bs; + + /* TLV t(0005) - Encompasses everything from above. Gee. */ + buflen = 2+8+16+aim_tlvlist_size(&subtl); + buf = malloc(buflen); + aim_bstream_init(&bs, buf, buflen); + aimbs_put16(&bs, AIM_RENDEZVOUS_PROPOSE); + aimbs_putraw(&bs, oft_info->cookie, 8); + aim_putcap(&bs, AIM_CAPS_SENDFILE); + aim_tlvlist_write(&bs, &subtl); + aim_tlvlist_free(&subtl); + aim_tlvlist_add_raw(&tl, 0x0005, bs.len, bs.data); + free(buf); + + /* TLV t(0003) - Request an ack */ + aim_tlvlist_add_noval(&tl, 0x0003); + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + aim_tlvlist_size(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, AIM_SNACFLAGS_DESTRUCTOR, oft_info->cookie, sizeof(oft_info->cookie)); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn); + + /* All that crap from above (the 0x0005 TLV and the 0x0003 TLV) */ + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send an "I will accept this file" message? + * + * @param rendid Capability type (AIM_CAPS_GETFILE or AIM_CAPS_SENDFILE) + */ +faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *oft_info) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn); + + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 0x001a); + aimbs_put16(&fr->data, AIM_RENDEZVOUS_ACCEPT); + aimbs_putraw(&fr->data, oft_info->cookie, 8); + aim_putcap(&fr->data, AIM_CAPS_SENDFILE); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send a "cancel this file transfer" message? + * + */ +faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn); + + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 0x001a); + aimbs_put16(&fr->data, AIM_RENDEZVOUS_CANCEL); + aimbs_putraw(&fr->data, oft_info->cookie, 8); + aim_putcap(&fr->data, AIM_CAPS_SENDFILE); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Request the status message of the given ICQ user. + * + * @param sess The oscar session. + * @param sn The UIN of the user of whom you wish to request info. + * @param type The type of info you wish to request. This should be the current + * state of the user, as one of the AIM_ICQ_STATE_* defines. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int i; + fu8_t ck[8]; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn) + return -EINVAL; + + for (i = 0; i < 8; i++) + ck[i] = (fu8_t)rand(); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, ck, 0x0002, sn); + + /* TLV t(0005) - Encompasses almost everything below. */ + aimbs_put16(&fr->data, 0x0005); /* T */ + aimbs_put16(&fr->data, 0x005e); /* L */ + { /* V */ + aimbs_put16(&fr->data, 0x0000); + + /* Cookie */ + aimbs_putraw(&fr->data, ck, 8); + + /* Put the 16 byte server relay capability */ + aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY); + + /* TLV t(000a) */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* TLV t(000f) */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* TLV t(2711) */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, 0x0036); + { /* V */ + aimbs_putle16(&fr->data, 0x001b); /* L */ + aimbs_putle16(&fr->data, 0x0008); /* XXX - Protocol version */ + aim_putcap(&fr->data, AIM_CAPS_EMPTY); + aimbs_putle16(&fr->data, 0x0000); /* Unknown */ + aimbs_putle16(&fr->data, 0x0003); /* Client features? */ + aimbs_putle16(&fr->data, 0x0000); /* Unknown */ + aimbs_putle8(&fr->data, 0x00); /* Unkizown */ + aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */ + + aimbs_putle16(&fr->data, 0x000e); /* L */ + aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + + /* The type of status message being requested */ + if (type & AIM_ICQ_STATE_CHAT) + aimbs_putle16(&fr->data, 0x03ec); + else if(type & AIM_ICQ_STATE_DND) + aimbs_putle16(&fr->data, 0x03eb); + else if(type & AIM_ICQ_STATE_OUT) + aimbs_putle16(&fr->data, 0x03ea); + else if(type & AIM_ICQ_STATE_BUSY) + aimbs_putle16(&fr->data, 0x03e9); + else if(type & AIM_ICQ_STATE_AWAY) + aimbs_putle16(&fr->data, 0x03e8); + + aimbs_putle16(&fr->data, 0x0000); /* Status? */ + aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */ + aimbs_putle16(&fr->data, 0x0001); /* L */ + aimbs_putle8(&fr->data, 0x00); /* String of length L */ + } /* End TLV t(2711) */ + } /* End TLV t(0005) */ + + /* TLV t(0003) */ + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send an ICQ-esque ICBM. + * + * This can be used to send an ICQ authorization reply (deny or grant). It is the "old way." + * The new way is to use SSI. I like the new way a lot better. This seems like such a hack, + * mostly because it's in network byte order. Figuring this stuff out sometimes takes a while, + * but thats ok, because it gives me time to try to figure out what kind of drugs the AOL people + * were taking when they merged the two protocols. + * + * @param sn The destination screen name. + * @param type The type of message. 0x0007 for authorization denied. 0x0008 for authorization granted. + * @param message The message you want to send, it should be null terminated. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_im_sendch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int i; + char ck[8]; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002))) + return -EINVAL; + + if (!sn || !type || !message) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+3+strlen(sn)+12+strlen(message)+1+4))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* Cookie */ + for (i=0; i<8; i++) + ck[i] = (fu8_t)rand(); + + /* ICBM header */ + aim_im_puticbm(&fr->data, ck, 0x0004, sn); + + /* + * TLV t(0005) + * + * ICQ data (the UIN and the message). + */ + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 4 + 2+2+strlen(message)+1); + + /* + * Your UIN + */ + aimbs_putle32(&fr->data, atoi(sess->sn)); + + /* + * TLV t(type) l(strlen(message)+1) v(message+NULL) + */ + aimbs_putle16(&fr->data, type); + aimbs_putle16(&fr->data, strlen(message)+1); + aimbs_putraw(&fr->data, message, strlen(message)+1); + + /* + * TLV t(0006) l(0000) v() + */ + aimbs_put16(&fr->data, 0x0006); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * XXX - I don't see when this would ever get called... + */ +static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int i, ret = 0; + aim_rxcallback_t userfunc; + fu8_t cookie[8]; + fu16_t channel; + aim_tlvlist_t *tlvlist; + char *sn; + int snlen; + fu16_t icbmflags = 0; + fu8_t flag1 = 0, flag2 = 0; + fu8_t *msg = NULL; + aim_tlv_t *msgblock; + + /* ICBM Cookie. */ + for (i = 0; i < 8; i++) + cookie[i] = aimbs_get8(bs); + + /* Channel ID */ + channel = aimbs_get16(bs); + + if (channel != 0x01) { + faimdprintf(sess, 0, "icbm: ICBM recieved on unsupported channel. Ignoring. (chan = %04x)\n", channel); + return 0; + } + + snlen = aimbs_get8(bs); + sn = aimbs_getstr(bs, snlen); + + tlvlist = aim_tlvlist_read(bs); + + if (aim_tlv_gettlv(tlvlist, 0x0003, 1)) + icbmflags |= AIM_IMFLAGS_ACK; + if (aim_tlv_gettlv(tlvlist, 0x0004, 1)) + icbmflags |= AIM_IMFLAGS_AWAY; + + if ((msgblock = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { + aim_bstream_t mbs; + int featurelen, msglen; + + aim_bstream_init(&mbs, msgblock->value, msgblock->length); + + aimbs_get8(&mbs); + aimbs_get8(&mbs); + for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--) + aimbs_get8(&mbs); + aimbs_get8(&mbs); + aimbs_get8(&mbs); + + msglen = aimbs_get16(&mbs) - 4; /* final block length */ + + flag1 = aimbs_get16(&mbs); + flag2 = aimbs_get16(&mbs); + + msg = aimbs_getstr(&mbs, msglen); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, msg, icbmflags, flag1, flag2); + + free(sn); + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Ahh, the joys of nearly ridiculous over-engineering. + * + * Not only do AIM ICBM's support multiple channels. Not only do they + * support multiple character sets. But they support multiple character + * sets / encodings within the same ICBM. + * + * These multipart messages allow for complex space savings techniques, which + * seem utterly unnecessary by today's standards. In fact, there is only + * one client still in popular use that still uses this method: AOL for the + * Macintosh, Version 5.0. Obscure, yes, I know. + * + * In modern (non-"legacy") clients, if the user tries to send a character + * that is not ISO-8859-1 or ASCII, the client will send the entire message + * as UNICODE, meaning that every character in the message will occupy the + * full 16 bit UNICODE field, even if the high order byte would be zero. + * Multipart messages prevent this wasted space by allowing the client to + * only send the characters in UNICODE that need to be sent that way, and + * the rest of the message can be sent in whatever the native character + * set is (probably ASCII). + * + * An important note is that sections will be displayed in the order that + * they appear in the ICBM. There is no facility for merging or rearranging + * sections at run time. So if you have, say, ASCII then UNICODE then ASCII, + * you must supply two ASCII sections with a UNICODE in the middle, and incur + * the associated overhead. + * + * Normally I would have laughed and given a firm 'no' to supporting this + * seldom-used feature, but something is attracting me to it. In the future, + * it may be possible to abuse this to send mixed-media messages to other + * open source clients (like encryption or something) -- see faimtest for + * examples of how to do this. + * + * I would definitly recommend avoiding this feature unless you really + * know what you are doing, and/or you have something neat to do with it. + * + */ +faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm) +{ + + memset(mpm, 0, sizeof(aim_mpmsg_t)); + + return 0; +} + +static int mpmsg_addsection(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, fu8_t *data, fu16_t datalen) +{ + aim_mpmsg_section_t *sec; + + if (!(sec = malloc(sizeof(aim_mpmsg_section_t)))) + return -1; + + sec->charset = charset; + sec->charsubset = charsubset; + sec->data = data; + sec->datalen = datalen; + sec->next = NULL; + + if (!mpm->parts) + mpm->parts = sec; + else { + aim_mpmsg_section_t *cur; + + for (cur = mpm->parts; cur->next; cur = cur->next) + ; + cur->next = sec; + } + + mpm->numparts++; + + return 0; +} + +faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, const fu8_t *data, fu16_t datalen) +{ + fu8_t *dup; + + if (!(dup = malloc(datalen))) + return -1; + memcpy(dup, data, datalen); + + if (mpmsg_addsection(sess, mpm, charset, charsubset, dup, datalen) == -1) { + free(dup); + return -1; + } + + return 0; +} + +/* XXX - should provide a way of saying ISO-8859-1 specifically */ +faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii) +{ + fu8_t *dup; + + if (!(dup = strdup(ascii))) + return -1; + + if (mpmsg_addsection(sess, mpm, 0x0000, 0x0000, dup, strlen(ascii)) == -1) { + free(dup); + return -1; + } + + return 0; +} + +faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const fu16_t *unicode, fu16_t unicodelen) +{ + fu8_t *buf; + aim_bstream_t bs; + int i; + + if (!(buf = malloc(unicodelen * 2))) + return -1; + + aim_bstream_init(&bs, buf, unicodelen * 2); + + /* We assume unicode is in /host/ byte order -- convert to network */ + for (i = 0; i < unicodelen; i++) + aimbs_put16(&bs, unicode[i]); + + if (mpmsg_addsection(sess, mpm, 0x0002, 0x0000, buf, aim_bstream_curpos(&bs)) == -1) { + free(buf); + return -1; + } + + return 0; +} + +faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm) +{ + aim_mpmsg_section_t *cur; + + for (cur = mpm->parts; cur; ) { + aim_mpmsg_section_t *tmp; + + tmp = cur->next; + free(cur->data); + free(cur); + cur = tmp; + } + + mpm->numparts = 0; + mpm->parts = NULL; + + return; +} + +/* + * Start by building the multipart structures, then pick the first + * human-readable section and stuff it into args->msg so no one gets + * suspicious. + * + */ +static int incomingim_ch1_parsemsgs(aim_session_t *sess, fu8_t *data, int len, struct aim_incomingim_ch1_args *args) +{ + static const fu16_t charsetpri[] = { + 0x0000, /* ASCII first */ + 0x0003, /* then ISO-8859-1 */ + 0x0002, /* UNICODE as last resort */ + }; + static const int charsetpricount = 3; + int i; + aim_bstream_t mbs; + aim_mpmsg_section_t *sec; + + aim_bstream_init(&mbs, data, len); + + while (aim_bstream_empty(&mbs)) { + fu16_t msglen, flag1, flag2; + fu8_t *msgbuf; + + aimbs_get8(&mbs); /* 01 */ + aimbs_get8(&mbs); /* 01 */ + + /* Message string length, including character set info. */ + msglen = aimbs_get16(&mbs); + + /* Character set info */ + flag1 = aimbs_get16(&mbs); + flag2 = aimbs_get16(&mbs); + + /* Message. */ + msglen -= 4; + + /* + * For now, we don't care what the encoding is. Just copy + * it into a multipart struct and deal with it later. However, + * always pad the ending with a NULL. This makes it easier + * to treat ASCII sections as strings. It won't matter for + * UNICODE or binary data, as you should never read past + * the specified data length, which will not include the pad. + * + * XXX - There's an API bug here. For sending, the UNICODE is + * given in host byte order (aim_mpmsg_addunicode), but here + * the received messages are given in network byte order. + * + */ + msgbuf = aimbs_getstr(&mbs, msglen); + mpmsg_addsection(sess, &args->mpmsg, flag1, flag2, msgbuf, msglen); + + } /* while */ + + args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */ + + /* + * Clients that support multiparts should never use args->msg, as it + * will point to an arbitrary section. + * + * Here, we attempt to provide clients that do not support multipart + * messages with something to look at -- hopefully a human-readable + * string. But, failing that, a UNICODE message, or nothing at all. + * + * Which means that even if args->msg is NULL, it does not mean the + * message was blank. + * + */ + for (i = 0; i < charsetpricount; i++) { + for (sec = args->mpmsg.parts; sec; sec = sec->next) { + + if (sec->charset != charsetpri[i]) + continue; + + /* Great. We found one. Fill it in. */ + args->charset = sec->charset; + args->charsubset = sec->charsubset; + + /* Set up the simple flags */ + if (args->charset == 0x0000) + ; /* ASCII */ + else if (args->charset == 0x0002) + args->icbmflags |= AIM_IMFLAGS_UNICODE; + else if (args->charset == 0x0003) + args->icbmflags |= AIM_IMFLAGS_ISO_8859_1; + else if (args->charset == 0xffff) + ; /* no encoding (yeep!) */ + + if (args->charsubset == 0x0000) + ; /* standard subencoding? */ + else if (args->charsubset == 0x000b) + args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH; + else if (args->charsubset == 0xffff) + ; /* no subencoding */ + + args->msg = sec->data; + args->msglen = sec->datalen; + + return 0; + } + } + + /* No human-readable sections found. Oh well. */ + args->charset = args->charsubset = 0xffff; + args->msg = NULL; + args->msglen = 0; + + return 0; +} + +static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_bstream_t *bs, fu8_t *cookie) +{ + fu16_t type, length; + aim_rxcallback_t userfunc; + int ret = 0; + struct aim_incomingim_ch1_args args; + int endpos; + + memset(&args, 0, sizeof(args)); + + aim_mpmsg_init(sess, &args.mpmsg); + + /* + * This used to be done using tlvchains. For performance reasons, + * I've changed it to process the TLVs in-place. This avoids lots + * of per-IM memory allocations. + */ + while (aim_bstream_empty(bs)) { + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + + endpos = aim_bstream_curpos(bs) + length; + + if (type == 0x0002) { /* Message Block */ + + /* + * This TLV consists of the following: + * - 0501 -- Unknown + * - Features: Don't know how to interpret these + * - 0101 -- Unknown + * - Message + * + */ + + aimbs_get8(bs); /* 05 */ + aimbs_get8(bs); /* 01 */ + + args.featureslen = aimbs_get16(bs); + /* XXX XXX this is all evil! */ + args.features = bs->data + bs->offset; + aim_bstream_advance(bs, args.featureslen); + args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES; + + /* + * The rest of the TLV contains one or more message + * blocks... + */ + incomingim_ch1_parsemsgs(sess, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args); + + } else if (type == 0x0003) { /* Server Ack Requested */ + + args.icbmflags |= AIM_IMFLAGS_ACK; + + } else if (type == 0x0004) { /* Message is Auto Response */ + + args.icbmflags |= AIM_IMFLAGS_AWAY; + + } else if (type == 0x0006) { /* Message was received offline. */ + + /* XXX - not sure if this actually gets sent. */ + args.icbmflags |= AIM_IMFLAGS_OFFLINE; + + } else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */ + + args.iconlen = aimbs_get32(bs); + aimbs_get16(bs); /* 0x0001 */ + args.iconsum = aimbs_get16(bs); + args.iconstamp = aimbs_get32(bs); + + /* + * This looks to be a client bug. MacAIM 4.3 will + * send this tag, but with all zero values, in the + * first message of a conversation. This makes no + * sense whatsoever, so I'm going to say its a bug. + * + * You really shouldn't advertise a zero-length icon + * anyway. + * + */ + if (args.iconlen) + args.icbmflags |= AIM_IMFLAGS_HASICON; + + } else if (type == 0x0009) { + + args.icbmflags |= AIM_IMFLAGS_BUDDYREQ; + + } else if (type == 0x000b) { /* Non-direct connect typing notification */ + + args.icbmflags |= AIM_IMFLAGS_TYPINGNOT; + + } else if (type == 0x0017) { + + args.extdatalen = length; + args.extdata = aimbs_getraw(bs, args.extdatalen); + + } else { + faimdprintf(sess, 0, "incomingim_ch1: unknown TLV 0x%04x (len %d)\n", type, length); + } + + /* + * This is here to protect ourselves from ourselves. That + * is, if something above doesn't completly parse its value + * section, or, worse, overparses it, this will set the + * stream where it needs to be in order to land on the next + * TLV when the loop continues. + * + */ + aim_bstream_setpos(bs, endpos); + } + + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, userinfo, &args); + + aim_mpmsg_free(sess, &args.mpmsg); + free(args.extdata); + + return ret; +} + +static void incomingim_ch2_buddylist(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + + /* + * This goes like this... + * + * group name length + * group name + * num of buddies in group + * buddy name length + * buddy name + * buddy name length + * buddy name + * ... + * group name length + * group name + * num of buddies in group + * buddy name length + * buddy name + * ... + * ... + */ + while (servdata && aim_bstream_empty(servdata)) { + fu16_t gnlen, numb; + int i; + char *gn; + + gnlen = aimbs_get16(servdata); + gn = aimbs_getstr(servdata, gnlen); + numb = aimbs_get16(servdata); + + for (i = 0; i < numb; i++) { + fu16_t bnlen; + char *bn; + + bnlen = aimbs_get16(servdata); + bn = aimbs_getstr(servdata, bnlen); + + faimdprintf(sess, 0, "got a buddy list from %s: group %s, buddy %s\n", userinfo->sn, gn, bn); + + free(bn); + } + + free(gn); + } + + return; +} + +static void incomingim_ch2_buddyicon_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + + free(args->info.icon.icon); + + return; +} + +static void incomingim_ch2_buddyicon(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + + if (servdata) { + args->info.icon.checksum = aimbs_get32(servdata); + args->info.icon.length = aimbs_get32(servdata); + args->info.icon.timestamp = aimbs_get32(servdata); + args->info.icon.icon = aimbs_getraw(servdata, args->info.icon.length); + } + + args->destructor = (void *)incomingim_ch2_buddyicon_free; + + return; +} + +static void incomingim_ch2_chat_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + + /* XXX - aim_chat_roominfo_free() */ + free(args->info.chat.roominfo.name); + + return; +} + +static void incomingim_ch2_chat(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + + /* + * Chat room info. + */ + if (servdata) + aim_chat_readroominfo(servdata, &args->info.chat.roominfo); + + args->destructor = (void *)incomingim_ch2_chat_free; + + return; +} + +static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + + free((char *)args->info.rtfmsg.rtfmsg); + + return; +} + +/* + * The relationship between AIM_CAPS_ICQSERVERRELAY and AIM_CAPS_ICQRTF is + * kind of odd. This sends the client ICQRTF since that is all that I've seen + * SERVERRELAY used for. + * + * Note that this is all little-endian. Cringe. + * + */ +static void incomingim_ch2_icqserverrelay(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + fu16_t hdrlen, anslen, msglen; + fu16_t msgtype; + + hdrlen = aimbs_getle16(servdata); + aim_bstream_advance(servdata, hdrlen); + + hdrlen = aimbs_getle16(servdata); + aim_bstream_advance(servdata, hdrlen); + + msgtype = aimbs_getle16(servdata); + + anslen = aimbs_getle32(servdata); + aim_bstream_advance(servdata, anslen); + + msglen = aimbs_getle16(servdata); + args->info.rtfmsg.rtfmsg = aimbs_getstr(servdata, msglen); + + args->info.rtfmsg.fgcolor = aimbs_getle32(servdata); + args->info.rtfmsg.bgcolor = aimbs_getle32(servdata); + + hdrlen = aimbs_getle32(servdata); + aim_bstream_advance(servdata, hdrlen); + + /* XXX - This is such a hack. */ + args->reqclass = AIM_CAPS_ICQRTF; + + args->destructor = (void *)incomingim_ch2_icqserverrelay_free; + + return; +} + +static void incomingim_ch2_sendfile_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + free(args->info.sendfile.filename); +} + +static void incomingim_ch2_sendfile(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + + args->destructor = (void *)incomingim_ch2_sendfile_free; + + /* Maybe there is a better way to tell what kind of sendfile + * this is? Maybe TLV t(000a)? */ + if (servdata) { /* Someone is sending us a file */ + int flen; + + /* subtype is one of AIM_OFT_SUBTYPE_* */ + args->info.sendfile.subtype = aimbs_get16(servdata); + args->info.sendfile.totfiles = aimbs_get16(servdata); + args->info.sendfile.totsize = aimbs_get32(servdata); + + /* + * I hope to God I'm right when I guess that there is a + * 32 char max filename length for single files. I think + * OFT tends to do that. Gotta love inconsistency. I saw + * a 26 byte filename? + */ + /* AAA - create an aimbs_getnullstr function (don't anymore)(maybe) */ + /* Use an inelegant way of getting the null-terminated filename, + * since there's no easy bstream routine. */ + for (flen = 0; aimbs_get8(servdata); flen++); + aim_bstream_advance(servdata, -flen -1); + args->info.sendfile.filename = aimbs_getstr(servdata, flen); + + /* There is sometimes more after the null-terminated filename, + * but I'm unsure of its format. */ + /* I don't believe him. */ + } + + return; +} + +typedef void (*ch2_args_destructor_t)(aim_session_t *sess, struct aim_incomingim_ch2_args *args); + +static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, fu8_t *cookie) +{ + aim_rxcallback_t userfunc; + aim_tlv_t *block1, *servdatatlv; + aim_tlvlist_t *list2; + struct aim_incomingim_ch2_args args; + aim_bstream_t bbs, sdbs, *sdbsptr = NULL; + fu8_t *cookie2; + int ret = 0; + + char proxyip[30] = {""}; + char clientip[30] = {""}; + char verifiedip[30] = {""}; + + memset(&args, 0, sizeof(args)); + + /* + * There's another block of TLVs embedded in the type 5 here. + */ + block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1); + aim_bstream_init(&bbs, block1->value, block1->length); + + /* + * First two bytes represent the status of the connection. + * + * 0 is a request, 1 is a cancel, 2 is an accept + */ + args.status = aimbs_get16(&bbs); + + /* + * Next comes the cookie. Should match the ICBM cookie. + */ + cookie2 = aimbs_getraw(&bbs, 8); + if (memcmp(cookie, cookie2, 8) != 0) + faimdprintf(sess, 0, "rend: warning cookies don't match!\n"); + memcpy(args.cookie, cookie2, 8); + free(cookie2); + + /* + * The next 16bytes are a capability block so we can + * identify what type of rendezvous this is. + */ + args.reqclass = aim_locate_getcaps(sess, &bbs, 0x10); + + /* + * What follows may be TLVs or nothing, depending on the + * purpose of the message. + * + * Ack packets for instance have nothing more to them. + */ + list2 = aim_tlvlist_read(&bbs); + + /* + * IP address to proxy the file transfer through. + * + * XXX - I don't like this. Maybe just read in an int? Or inet_ntoa... + */ + if (aim_tlv_gettlv(list2, 0x0002, 1)) { + aim_tlv_t *iptlv; + + iptlv = aim_tlv_gettlv(list2, 0x0002, 1); + if (iptlv->length == 4) + snprintf(proxyip, sizeof(proxyip), "%hhu.%hhu.%hhu.%hhu", + iptlv->value[0], iptlv->value[1], + iptlv->value[2], iptlv->value[3]); + } + + /* + * IP address from the perspective of the client. + */ + if (aim_tlv_gettlv(list2, 0x0003, 1)) { + aim_tlv_t *iptlv; + + iptlv = aim_tlv_gettlv(list2, 0x0003, 1); + if (iptlv->length == 4) + snprintf(clientip, sizeof(clientip), "%hhu.%hhu.%hhu.%hhu", + iptlv->value[0], iptlv->value[1], + iptlv->value[2], iptlv->value[3]); + } + + /* + * Verified IP address (from the perspective of Oscar). + * + * This is added by the server. + */ + if (aim_tlv_gettlv(list2, 0x0004, 1)) { + aim_tlv_t *iptlv; + + iptlv = aim_tlv_gettlv(list2, 0x0004, 1); + if (iptlv->length == 4) + snprintf(verifiedip, sizeof(verifiedip), "%hhu.%hhu.%hhu.%hhu", + iptlv->value[0], iptlv->value[1], + iptlv->value[2], iptlv->value[3]); + } + + /* + * Port number for something. + */ + if (aim_tlv_gettlv(list2, 0x0005, 1)) + args.port = aim_tlv_get16(list2, 0x0005, 1); + + /* + * Something to do with ft -- two bytes + * 0x0001 - "I want to send you this file" + * 0x0002 - "I will accept this file from you" + */ + if (aim_tlv_gettlv(list2, 0x000a, 1)) + ; + + /* + * Error code. + */ + if (aim_tlv_gettlv(list2, 0x000b, 1)) + args.errorcode = aim_tlv_get16(list2, 0x000b, 1); + + /* + * Invitation message / chat description. + */ + if (aim_tlv_gettlv(list2, 0x000c, 1)) + args.msg = aim_tlv_getstr(list2, 0x000c, 1); + + /* + * Character set. + */ + if (aim_tlv_gettlv(list2, 0x000d, 1)) + args.encoding = aim_tlv_getstr(list2, 0x000d, 1); + + /* + * Language. + */ + if (aim_tlv_gettlv(list2, 0x000e, 1)) + args.language = aim_tlv_getstr(list2, 0x000e, 1); + + /* + * Unknown -- no value + * + * Maybe means we should connect directly to transfer the file? + */ + if (aim_tlv_gettlv(list2, 0x000f, 1)) + ; + + /* + * Unknown -- no value + * + * Maybe means we should proxy the file transfer through an AIM server? + */ + if (aim_tlv_gettlv(list2, 0x0010, 1)) + ; + + if (strlen(proxyip)) + args.proxyip = (char *)proxyip; + if (strlen(clientip)) + args.clientip = (char *)clientip; + if (strlen(verifiedip)) + args.verifiedip = (char *)verifiedip; + + /* + * This must be present in PROPOSALs, but will probably not + * exist in CANCELs and ACCEPTs. + * + * Service Data blocks are module-specific in format. + */ + if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) { + + aim_bstream_init(&sdbs, servdatatlv->value, servdatatlv->length); + sdbsptr = &sdbs; + } + + /* + * The rest of the handling depends on what type it is. + * + * Not all of them have special handling (yet). + */ + if (args.reqclass & AIM_CAPS_BUDDYICON) + incomingim_ch2_buddyicon(sess, mod, rx, snac, userinfo, &args, sdbsptr); + else if (args.reqclass & AIM_CAPS_SENDBUDDYLIST) + incomingim_ch2_buddylist(sess, mod, rx, snac, userinfo, &args, sdbsptr); + else if (args.reqclass & AIM_CAPS_CHAT) + incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, sdbsptr); + else if (args.reqclass & AIM_CAPS_ICQSERVERRELAY) + incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr); + else if (args.reqclass & AIM_CAPS_SENDFILE) + incomingim_ch2_sendfile(sess, mod, rx, snac, userinfo, &args, sdbsptr); + + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, userinfo, &args); + + + if (args.destructor) + ((ch2_args_destructor_t)args.destructor)(sess, &args); + + free((char *)args.msg); + free((char *)args.encoding); + free((char *)args.language); + + aim_tlvlist_free(&list2); + + return ret; +} + +static int incomingim_ch4(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, fu8_t *cookie) +{ + aim_bstream_t meat; + aim_rxcallback_t userfunc; + aim_tlv_t *block; + struct aim_incomingim_ch4_args args; + int ret = 0; + + /* + * Make a bstream for the meaty part. Yum. Meat. + */ + if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1))) + return -1; + aim_bstream_init(&meat, block->value, block->length); + + args.uin = aimbs_getle32(&meat); + args.type = aimbs_getle8(&meat); + args.flags = aimbs_getle8(&meat); + args.msglen = aimbs_getle16(&meat); + args.msg = aimbs_getraw(&meat, args.msglen); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, userinfo, &args); + + free(args.msg); + + return ret; +} + +/* + * Subtype 0x0007 + * + * It can easily be said that parsing ICBMs is THE single + * most difficult thing to do in the in AIM protocol. In + * fact, I think I just did say that. + * + * Below is the best damned solution I've come up with + * over the past sixteen months of battling with it. This + * can parse both away and normal messages from every client + * I have access to. Its not fast, its not clean. But it works. + * + */ +static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int i, ret = 0; + fu8_t cookie[8]; + fu16_t channel; + aim_userinfo_t userinfo; + + memset(&userinfo, 0x00, sizeof(aim_userinfo_t)); + + /* + * Read ICBM Cookie. And throw away. + */ + for (i = 0; i < 8; i++) + cookie[i] = aimbs_get8(bs); + + /* + * Channel ID. + * + * Channel 0x0001 is the message channel. It is + * used to send basic ICBMs. + * + * Channel 0x0002 is the Rendevous channel, which + * is where Chat Invitiations and various client-client + * connection negotiations come from. + * + * Channel 0x0003 is used for chat messages. + * + * Channel 0x0004 is used for ICQ authorization, or + * possibly any system notice. + * + */ + channel = aimbs_get16(bs); + + /* + * Extract the standard user info block. + * + * Note that although this contains TLVs that appear contiguous + * with the TLVs read below, they are two different pieces. The + * userinfo block contains the number of TLVs that contain user + * information, the rest are not even though there is no seperation. + * You can start reading the message TLVs after aim_info_extract() + * parses out the standard userinfo block. + * + * That also means that TLV types can be duplicated between the + * userinfo block and the rest of the message, however there should + * never be two TLVs of the same type in one block. + * + */ + aim_info_extract(sess, bs, &userinfo); + + /* + * From here on, its depends on what channel we're on. + * + * Technically all channels have a TLV list have this, however, + * for the common channel 1 case, in-place parsing is used for + * performance reasons (less memory allocation). + */ + if (channel == 1) { + + ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie); + + } else if (channel == 2) { + aim_tlvlist_t *tlvlist; + + /* + * Read block of TLVs (not including the userinfo data). All + * further data is derived from what is parsed here. + */ + tlvlist = aim_tlvlist_read(bs); + + ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie); + + aim_tlvlist_free(&tlvlist); + + } else if (channel == 4) { + aim_tlvlist_t *tlvlist; + + tlvlist = aim_tlvlist_read(bs); + ret = incomingim_ch4(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie); + aim_tlvlist_free(&tlvlist); + + } else { + faimdprintf(sess, 0, "icbm: ICBM received on an unsupported channel. Ignoring. (chan = %04x)\n", channel); + } + + aim_info_free(&userinfo); + + return ret; +} + +/* + * Subtype 0x0008 - Send a warning to sn. + * + * Flags: + * AIM_WARN_ANON Send as an anonymous (doesn't count as much) + * + * returns -1 on error (couldn't alloc packet), 0 on success. + * + */ +faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu32_t flags) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(sn)+13))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid); + + aimbs_put16(&fr->data, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Subtype 0x000a */ +static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t channel, nummissed, reason; + aim_userinfo_t userinfo; + + while (aim_bstream_empty(bs)) { + + channel = aimbs_get16(bs); + aim_info_extract(sess, bs, &userinfo); + nummissed = aimbs_get16(bs); + reason = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason); + + aim_info_free(&userinfo); + } + + return ret; +} + +/* + * Subtype 0x000b + * + * Possible codes: + * AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support" + * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer" + * AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers" + * + */ +faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, fu16_t code) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sender)+6))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid); + + aimbs_putraw(&fr->data, cookie, 8); + + aimbs_put16(&fr->data, 0x0002); /* channel */ + aimbs_put8(&fr->data, strlen(sender)); + aimbs_putraw(&fr->data, sender, strlen(sender)); + + aim_tlvlist_add_16(&tl, 0x0003, code); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x000b - Receive the response from an ICQ status message request. + * + * This contains the ICQ status message. Go figure. + * + */ +static int clientautoresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t channel, reason; + char *sn; + fu8_t *ck, snlen; + + ck = aimbs_getraw(bs, 8); + channel = aimbs_get16(bs); + snlen = aimbs_get8(bs); + sn = aimbs_getstr(bs, snlen); + reason = aimbs_get16(bs); + + if (channel == 0x0002) { /* File transfer declined */ + aimbs_get16(bs); /* Unknown */ + aimbs_get16(bs); /* Unknown */ + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, reason, ck); + } else if (channel == 0x0004) { /* ICQ message */ + switch (reason) { + case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */ + fu8_t statusmsgtype, *msg; + fu16_t len; + fu32_t state; + + len = aimbs_getle16(bs); /* Should be 0x001b */ + aim_bstream_advance(bs, len); /* Unknown */ + + len = aimbs_getle16(bs); /* Should be 0x000e */ + aim_bstream_advance(bs, len); /* Unknown */ + + statusmsgtype = aimbs_getle8(bs); + switch (statusmsgtype) { + case 0xe8: + state = AIM_ICQ_STATE_AWAY; + break; + case 0xe9: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY; + break; + case 0xea: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT; + break; + case 0xeb: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY; + break; + case 0xec: + state = AIM_ICQ_STATE_CHAT; + break; + default: + state = 0; + break; + } + + aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */ + aimbs_getle16(bs); /* Unknown - 0x0000 */ + aimbs_getle16(bs); /* Unknown - 0x0000 */ + + len = aimbs_getle16(bs); + msg = aimbs_getraw(bs, len); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, reason, state, msg); + + free(msg); + } break; + + default: { + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, reason); + } break; + } /* end switch */ + } + + free(ck); + free(sn); + + return ret; +} + +/* + * Subtype 0x000c - Receive an ack after sending an ICBM. + * + * You have to have send the message with the AIM_IMFLAGS_ACK flag set + * (TLV t(0003)). The ack contains the ICBM header of the message you + * sent. + * + */ +static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + fu16_t ch; + fu8_t *ck; + char *sn; + int ret = 0; + + ck = aimbs_getraw(bs, 8); + ch = aimbs_get16(bs); + sn = aimbs_getstr(bs, aimbs_get8(bs)); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, ch, sn); + + free(sn); + free(ck); + + return ret; +} + +/* + * Subtype 0x0014 - Send a mini typing notification (mtn) packet. + * + * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, + * and Gaim 0.60 and newer. + * + */ +faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, const char *sn, fu16_t type2) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002))) + return -EINVAL; + + if (!sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+11+strlen(sn)+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0014, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0014, 0x0000, snacid); + + /* + * 8 days of light + * Er, that is to say, 8 bytes of 0's + */ + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + + /* + * Type 1 (should be 0x0001 for mtn) + */ + aimbs_put16(&fr->data, type1); + + /* + * Dest sn + */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + /* + * Type 2 (should be 0x0000, 0x0001, or 0x0002 for mtn) + */ + aimbs_put16(&fr->data, type2); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0014 - Receive a mini typing notification (mtn) packet. + * + * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, + * and Gaim 0.60 and newer. + * + */ +static int mtn_receive(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + char *sn; + fu8_t snlen; + fu16_t type1, type2; + + aim_bstream_advance(bs, 8); /* Unknown - All 0's */ + type1 = aimbs_get16(bs); + snlen = aimbs_get8(bs); + sn = aimbs_getstr(bs, snlen); + type2 = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, type1, sn, type2); + + free(sn); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0005) + return aim_im_paraminfo(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0006) + return outgoingim(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return incomingim(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000a) + return missedcall(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000b) + return clientautoresp(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000c) + return msgack(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0014) + return mtn_receive(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0004; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "messaging", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/invite.c b/libfaim/invite.c new file mode 100644 index 0000000..687cafb --- /dev/null +++ b/libfaim/invite.c @@ -0,0 +1,34 @@ +/* + * Family 0x0006 - This isn't really ever used by anyone anymore. + * + * Once upon a time, there used to be a menu item in AIM clients that + * said something like "Invite a friend to use AIM..." and then it would + * ask for an email address and it would sent a mail to them saying + * how perfectly wonderful the AIM service is and why you should use it + * and click here if you hate the person who sent this to you and want to + * complain and yell at them in a small box with pretty fonts. + * + * I could've sworn libfaim had this implemented once, a long long time ago, + * but I can't find it. + * + * I'm mainly adding this so that I can keep advertising that we support + * group 6, even though we don't. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +faim_internal int invite_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0006; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "invite", sizeof(mod->name)); + mod->snachandler = NULL; + + return 0; +} diff --git a/libfaim/locate.c b/libfaim/locate.c new file mode 100644 index 0000000..72460c5 --- /dev/null +++ b/libfaim/locate.c @@ -0,0 +1,1319 @@ +/* + * Family 0x0002 - Locate. + * + * The functions here are responsible for requesting and parsing information- + * gathering SNACs. Or something like that. This family contains the SNACs + * for getting and setting info, away messages, directory profile thingy, etc. + */ + +#define FAIM_INTERNAL +#include <aim.h> +#ifdef _WIN32 +#include "win32dep.h" +#endif + +/* + * Capability blocks. + * + * These are CLSIDs. They should actually be of the form: + * + * {0x0946134b, 0x4c7f, 0x11d1, + * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}}, + * + * But, eh. + */ +static const struct { + fu32_t flag; + fu8_t data[16]; +} aim_caps[] = { + + /* + * These are in ascending numerical order. + */ + + /* + * Perhaps better called AIM_CAPS_SHORTCAPS + */ + {AIM_CAPS_ICHAT, + {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SECUREIM, + {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_VIDEO, + {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * Not really sure about this one. In an email from + * 26 Sep 2003, Matthew Sachs suggested that, "this + * is probably the capability for the SMS features." + */ + {AIM_CAPS_SMS, + {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_GENERICUNKNOWN, + {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_GENERICUNKNOWN, + {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_GENERICUNKNOWN, + {0x09, 0x46, 0xf0, 0x05, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_HIPTOP, + {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_VOICE, + {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SENDFILE, + {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ICQ_DIRECT, + {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_DIRECTIM, + {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_BUDDYICON, + {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * Windows AIM calls this "Add-ins," which is probably more accurate + */ + {AIM_CAPS_SAVESTOCKS, + {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_GETFILE, + {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ICQSERVERRELAY, + {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * Indeed, there are two of these. The former appears to be correct, + * but in some versions of winaim, the second one is set. Either they + * forgot to fix endianness, or they made a typo. It really doesn't + * matter which. + */ + {AIM_CAPS_GAMES, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + {AIM_CAPS_GAMES2, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SENDBUDDYLIST, + {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * Setting this lets AIM users receive messages from ICQ users, and ICQ + * users receive messages from AIM users. It also lets ICQ users show + * up in buddy lists for AIM users, and AIM users show up in buddy lists + * for ICQ users. And ICQ privacy/invisibility acts like AIM privacy, + * in that if you add a user to your deny list, you will not be able to + * see them as online (previous you could still see them, but they + * couldn't see you. + */ + {AIM_CAPS_INTEROPERATE, + {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ICQUTF8, + {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ICQUTF8OLD, + {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8, + 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}}, + + /* + * Chat is oddball. + */ + {AIM_CAPS_CHAT, + {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + {AIM_CAPS_ICQ2GO, + {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd, + 0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}}, + */ + + {AIM_CAPS_ICQRTF, + {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, + 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}}, + + /* This is added by the servers and it only shows up for ourselves... */ + {AIM_CAPS_GENERICUNKNOWN, + {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, + 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}}, + + {AIM_CAPS_APINFO, + {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6, + 0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}}, + + {AIM_CAPS_TRILLIANCRYPT, + {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb, + 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}}, + + {AIM_CAPS_EMPTY, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + + {AIM_CAPS_LAST} +}; + +/* + * Add the userinfo to our linked list. If we already have userinfo + * for this buddy, then just overwrite parts of the old data. + * @param userinfo Contains the new information for the buddy. + */ +static void aim_locate_adduserinfo(aim_session_t *sess, aim_userinfo_t *userinfo) { + aim_userinfo_t *cur; + + cur = aim_locate_finduserinfo(sess, userinfo->sn); + + if (cur == NULL) { + cur = (aim_userinfo_t *)calloc(1, sizeof(aim_userinfo_t)); + cur->sn = strdup(userinfo->sn); + cur->next = sess->locate.userinfo; + sess->locate.userinfo = cur; + } + + cur->warnlevel = userinfo->warnlevel; + cur->idletime = userinfo->idletime; + if (userinfo->flags != 0) + cur->flags = userinfo->flags; + if (userinfo->createtime != 0) + cur->createtime = userinfo->createtime; + if (userinfo->membersince != 0) + cur->membersince = userinfo->membersince; + if (userinfo->onlinesince != 0) + cur->onlinesince = userinfo->onlinesince; + if (userinfo->sessionlen != 0) + cur->sessionlen = userinfo->sessionlen; + if (userinfo->capabilities != 0) + cur->capabilities = userinfo->capabilities; + cur->present |= userinfo->present; + + if (userinfo->iconcsumlen > 0) { + free(cur->iconcsum); + cur->iconcsum = (fu8_t *)malloc(userinfo->iconcsumlen); + memcpy(cur->iconcsum, userinfo->iconcsum, userinfo->iconcsumlen); + cur->iconcsumlen = userinfo->iconcsumlen; + } + + if (userinfo->info != NULL) { + free(cur->info); + free(cur->info_encoding); + cur->info = (char *)malloc(userinfo->info_len); + memcpy(cur->info, userinfo->info, userinfo->info_len); + cur->info_encoding = strdup(userinfo->info_encoding); + cur->info_len = userinfo->info_len; + } + + if (userinfo->away != NULL) { + free(cur->away); + free(cur->away_encoding); + cur->away = (char *)malloc(userinfo->away_len); + memcpy(cur->away, userinfo->away, userinfo->away_len); + cur->away_encoding = strdup(userinfo->away_encoding); + cur->away_len = userinfo->away_len; + } +} + +static void aim_locate_dorequest(aim_session_t *sess) { + struct userinfo_node *cur = sess->locate.torequest; + + if (cur == NULL) + return; + + if (sess->locate.waiting_for_response == TRUE) + return; + + sess->locate.waiting_for_response = TRUE; + aim_locate_getinfoshort(sess, cur->sn, 0x00000003); + + /* Move this node to the "requested" queue */ + sess->locate.torequest = cur->next; + cur->next = sess->locate.requested; + sess->locate.requested = cur; +} + +/** + * Remove this screen name from our queue. If this info was resquested + * by our info request queue, then pop the next element off of the queue. + * + * @param sess The aim session. + * @param sn Screen name of the info we just received. + * @return True if the request was explicit (client requested the info), + * false if the request was implicit (libfaim request the info). + */ +static int aim_locate_gotuserinfo(aim_session_t *sess, const char *sn) { + struct userinfo_node *cur, *del; + int was_explicit = TRUE; + + while ((sess->locate.requested != NULL) && (aim_sncmp(sn, sess->locate.requested->sn) == 0)) { + del = sess->locate.requested; + sess->locate.requested = del->next; + was_explicit = FALSE; + free(del->sn); + free(del); + } + + cur = sess->locate.requested; + while ((cur != NULL) && (cur->next != NULL)) { + if (aim_sncmp(sn, cur->next->sn) == 0) { + del = cur->next; + cur->next = del->next; + was_explicit = FALSE; + free(del->sn); + free(del); + } else + cur = cur->next; + } + + if (!was_explicit) { + sess->locate.waiting_for_response = FALSE; + aim_locate_dorequest(sess); + } + + return was_explicit; +} + +faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn) { + struct userinfo_node *cur; + + /* Make sure we aren't already requesting info for this buddy */ + cur = sess->locate.torequest; + while (cur != NULL) { + if (aim_sncmp(sn, cur->sn) == 0) + return; + cur = cur->next; + } + + /* Add a new node to our request queue */ + cur = (struct userinfo_node *)malloc(sizeof(struct userinfo_node)); + cur->sn = strdup(sn); + cur->next = sess->locate.torequest; + sess->locate.torequest = cur; + + /* Actually request some info up in this piece */ + aim_locate_dorequest(sess); +} + +faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn) { + aim_userinfo_t *cur = sess->locate.userinfo; + + while (cur != NULL) { + if (aim_sncmp(cur->sn, sn) == 0) + return cur; + cur = cur->next; + } + + return NULL; +} + +faim_internal fu32_t aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len) +{ + fu32_t flags = 0; + int offset; + + for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) { + fu8_t *cap; + int i, identified; + + cap = aimbs_getraw(bs, 0x10); + + for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { + if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) { + flags |= aim_caps[i].flag; + identified++; + break; /* should only match once... */ + } + } + + if (!identified) + faimdprintf(sess, 0, "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", + cap[0], cap[1], cap[2], cap[3], + cap[4], cap[5], + cap[6], cap[7], + cap[8], cap[9], + cap[10], cap[11], cap[12], cap[13], + cap[14], cap[15]); + + free(cap); + } + + return flags; +} + +faim_internal fu32_t aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len) +{ + fu32_t flags = 0; + int offset; + + for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x02) { + fu8_t *cap; + int i, identified; + + cap = aimbs_getraw(bs, 0x02); + + for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { + if (memcmp(&aim_caps[i].data[2], cap, 0x02) == 0) { + flags |= aim_caps[i].flag; + identified++; + break; /* should only match once... */ + } + } + + if (!identified) + faimdprintf(sess, 0, "unknown short capability: {%02x%02x}\n", cap[0], cap[1]); + + free(cap); + } + + return flags; +} + +faim_internal int aim_putcap(aim_bstream_t *bs, fu32_t caps) +{ + int i; + + if (!bs) + return -EINVAL; + + for (i = 0; aim_bstream_empty(bs); i++) { + + if (aim_caps[i].flag == AIM_CAPS_LAST) + break; + + if (caps & aim_caps[i].flag) + aimbs_putraw(bs, aim_caps[i].data, 0x10); + + } + + return 0; +} + +static void dumptlv(aim_session_t *sess, fu16_t type, aim_bstream_t *bs, fu8_t len) +{ + int i; + + if (!sess || !bs || !len) + return; + + faimdprintf(sess, 0, "userinfo: type =0x%04x\n", type); + faimdprintf(sess, 0, "userinfo: length=0x%04x\n", len); + faimdprintf(sess, 0, "userinfo: value:\n"); + + for (i = 0; i < len; i++) { + if ((i % 8) == 0) + faimdprintf(sess, 0, "\nuserinfo: "); + faimdprintf(sess, 0, "0x%2x ", aimbs_get8(bs)); + } + + faimdprintf(sess, 0, "\n"); + + return; +} + +faim_internal void aim_info_free(aim_userinfo_t *info) +{ + free(info->sn); + free(info->iconcsum); + free(info->info); + free(info->info_encoding); + free(info->avail); + free(info->avail_encoding); + free(info->away); + free(info->away_encoding); +} + +/* + * AIM is fairly regular about providing user info. This is a generic + * routine to extract it in its standard form. + */ +faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo) +{ + int curtlv, tlvcnt; + fu8_t snlen; + + if (!bs || !outinfo) + return -EINVAL; + + /* Clear out old data first */ + memset(outinfo, 0x00, sizeof(aim_userinfo_t)); + + /* + * Screen name. Stored as an unterminated string prepended with a + * byte containing its length. + */ + snlen = aimbs_get8(bs); + outinfo->sn = aimbs_getstr(bs, snlen); + + /* + * Warning Level. Stored as an unsigned short. + */ + outinfo->warnlevel = aimbs_get16(bs); + + /* + * TLV Count. Unsigned short representing the number of + * Type-Length-Value triples that follow. + */ + tlvcnt = aimbs_get16(bs); + + /* + * Parse out the Type-Length-Value triples as they're found. + */ + for (curtlv = 0; curtlv < tlvcnt; curtlv++) { + int endpos; + fu16_t type, length; + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + + endpos = aim_bstream_curpos(bs) + length; + + if (type == 0x0001) { + /* + * Type = 0x0001: User flags + * + * Specified as any of the following ORed together: + * 0x0001 Trial (user less than 60days) + * 0x0002 Unknown bit 2 + * 0x0004 AOL Main Service user + * 0x0008 Unknown bit 4 + * 0x0010 Free (AIM) user + * 0x0020 Away + * 0x0400 ActiveBuddy + * + */ + outinfo->flags = aimbs_get16(bs); + outinfo->present |= AIM_USERINFO_PRESENT_FLAGS; + + } else if (type == 0x0002) { + /* + * Type = 0x0002: Account creation time. + * + * The time/date that the user originally registered for + * the service, stored in time_t format. + * + * I'm not sure how this differs from type 5 ("member + * since"). + * + * Note: This is the field formerly known as "member + * since". All these years and I finally found out + * that I got the name wrong. + */ + outinfo->createtime = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME; + + } else if (type == 0x0003) { + /* + * Type = 0x0003: On-Since date. + * + * The time/date that the user started their current + * session, stored in time_t format. + */ + outinfo->onlinesince = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE; + + } else if (type == 0x0004) { + /* + * Type = 0x0004: Idle time. + * + * Number of minutes since the user actively used the + * service. + * + * Note that the client tells the server when to start + * counting idle times, so this may or may not be + * related to reality. + */ + outinfo->idletime = aimbs_get16(bs); + outinfo->present |= AIM_USERINFO_PRESENT_IDLE; + + } else if (type == 0x0005) { + /* + * Type = 0x0005: Member since date. + * + * The time/date that the user originally registered for + * the service, stored in time_t format. + * + * This is sometimes sent instead of type 2 ("account + * creation time"), particularly in the self-info. + * And particularly for ICQ? + */ + outinfo->membersince = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE; + + } else if (type == 0x0006) { + /* + * Type = 0x0006: ICQ Online Status + * + * ICQ's Away/DND/etc "enriched" status. Some decoding + * of values done by Scott <darkagl@pcnet.com> + */ + aimbs_get16(bs); + outinfo->icqinfo.status = aimbs_get16(bs); + outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS; + + } else if (type == 0x0008) { + /* + * Type = 0x0008 + * + * Client type, or some such. + */ + + } else if (type == 0x000a) { + /* + * Type = 0x000a + * + * ICQ User IP Address. + * Ahh, the joy of ICQ security. + */ + outinfo->icqinfo.ipaddr = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR; + + } else if (type == 0x000c) { + /* + * Type = 0x000c + * + * random crap containing the IP address, + * apparently a port number, and some Other Stuff. + * + * Format is: + * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43 + * + * + */ + aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25); + outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA; + + } else if (type == 0x000d) { + /* + * Type = 0x000d + * + * OSCAR Capability information. + * + */ + outinfo->capabilities |= aim_locate_getcaps(sess, bs, length); + outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; + + } else if (type == 0x000e) { + /* + * Type = 0x000e + * + * AOL capability information. + * + */ + + } else if ((type == 0x000f) || (type == 0x0010)) { + /* + * Type = 0x000f: Session Length. (AIM) + * Type = 0x0010: Session Length. (AOL) + * + * The duration, in seconds, of the user's current + * session. + * + * Which TLV type this comes in depends on the + * service the user is using (AIM or AOL). + * + */ + outinfo->sessionlen = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN; + + } else if (type == 0x0019) { + /* + * Type = 0x0019 + * + * OSCAR short capability information. A shortened + * form of the normal capabilities. + */ + outinfo->capabilities |= aim_locate_getcaps_short(sess, bs, length); + outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; + + } else if (type == 0x001b) { + /* + * Type = 0x001a + * + * AOL short capability information. A shortened + * form of the normal capabilities. + */ + + } else if (type == 0x001b) { + /* + * Type = 0x0019 + * + * Encryption certification MD5 checksum. + */ + + } else if (type == 0x001d) { + /* + * Type = 0x001d + * + * Buddy icon information and available messages. + * + * This almost seems like the AIM protocol guys gave + * the iChat guys a Type, and the iChat guys tried to + * cram as much cool shit into it as possible. Then + * the Windows AIM guys were like, "hey, that's + * pretty neat, let's copy those prawns." + * + * In that spirit, this can contain a custom message, + * kind of like an away message, but you're not away + * (it's called an "available" message). Or it can + * contain information about the buddy icon the user + * has stored on the server. + */ + int type2, number, length2; + + while (aim_bstream_curpos(bs) < endpos) { + type2 = aimbs_get16(bs); + number = aimbs_get8(bs); + length2 = aimbs_get8(bs); + + switch (type2) { + case 0x0000: { /* This is an official buddy icon? */ + /* This is always 5 bytes of "0x02 01 d2 04 72"? */ + aim_bstream_advance(bs, length2); + } break; + + case 0x0001: { /* A buddy icon checksum */ + if ((length2 > 0) && (number == 0x01)) { + free(outinfo->iconcsum); + outinfo->iconcsum = aimbs_getraw(bs, length2); + outinfo->iconcsumlen = length2; + } else + aim_bstream_advance(bs, length2); + } break; + + case 0x0002: { /* An available message */ + if (length2 > 4) { + free(outinfo->avail); + outinfo->avail_len = aimbs_get16(bs); + outinfo->avail = aimbs_getstr(bs, outinfo->avail_len); + if (aimbs_get16(bs) == 0x0001) { /* We have an encoding */ + aimbs_get16(bs); + outinfo->avail_encoding = aimbs_getstr(bs, aimbs_get16(bs)); + } else { + /* No explicit encoding, client should use UTF-8 */ + outinfo->avail_encoding = NULL; + } + } else + aim_bstream_advance(bs, length2); + } break; + + default: { + aim_bstream_advance(bs, length2); + } break; + } + } + + } else if (type == 0x001e) { + /* + * Type 30: Unknown. + * + * Always four bytes, but it doesn't look like an int. + */ + + } else if (type == 0x001f) { + /* + * Type 31: Unknown. + * + * Seen on a buddy using DeadAIM. Data was 4 bytes: + * 0x00 00 00 10 + */ + + } else { + + /* + * Reaching here indicates that either AOL has + * added yet another TLV for us to deal with, + * or the parsing has gone Terribly Wrong. + * + * Either way, inform the owner and attempt + * recovery. + * + */ + faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n"); + faimdprintf(sess, 0, "userinfo: sn =%s\n", outinfo->sn); + dumptlv(sess, type, bs, length); + } + + /* Save ourselves. */ + aim_bstream_setpos(bs, endpos); + } + + aim_locate_adduserinfo(sess, outinfo); + + return 0; +} + +/* + * Inverse of aim_info_extract() + */ +faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info) +{ + aim_tlvlist_t *tlvlist = NULL; + + if (!bs || !info) + return -EINVAL; + + aimbs_put8(bs, strlen(info->sn)); + aimbs_putraw(bs, info->sn, strlen(info->sn)); + + aimbs_put16(bs, info->warnlevel); + + if (info->present & AIM_USERINFO_PRESENT_FLAGS) + aim_tlvlist_add_16(&tlvlist, 0x0001, info->flags); + if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE) + aim_tlvlist_add_32(&tlvlist, 0x0002, info->membersince); + if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) + aim_tlvlist_add_32(&tlvlist, 0x0003, info->onlinesince); + if (info->present & AIM_USERINFO_PRESENT_IDLE) + aim_tlvlist_add_16(&tlvlist, 0x0004, info->idletime); + +/* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */ +#if ICQ_OSCAR_SUPPORT + if (atoi(info->sn) != 0) { + if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) + aim_tlvlist_add_16(&tlvlist, 0x0006, info->icqinfo.status); + if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR) + aim_tlvlist_add_32(&tlvlist, 0x000a, info->icqinfo.ipaddr); + } +#endif + + if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) + aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities); + + if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) + aim_tlvlist_add_32(&tlvlist, (fu16_t)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen); + + aimbs_put16(bs, aim_tlvlist_count(&tlvlist)); + aim_tlvlist_write(bs, &tlvlist); + aim_tlvlist_free(&tlvlist); + + return 0; +} + +/* + * Subtype 0x0001 + */ +static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + aim_snac_t *snac2; + fu16_t reason; + char *sn; + int was_explicit; + + if (!(snac2 = aim_remsnac(sess, snac->id))) { + faimdprintf(sess, 0, "faim: locate.c, error(): received response from unknown request!\n"); + return 0; + } + + if ((snac2->family != 0x0002) && (snac2->type != 0x0015)) { + faimdprintf(sess, 0, "faim: locate.c, error(): received response from invalid request! %d\n", snac2->family); + return 0; + } + + if (!(sn = snac2->data)) { + faimdprintf(sess, 0, "faim: locate.c, error(): received response from request without a screen name!\n"); + return 0; + } + + reason = aimbs_get16(bs); + + /* + * Remove this screen name from our queue. If the client requested + * this buddy's info explicitly, then notify them that we do not have + * info for this buddy. + */ + was_explicit = aim_locate_gotuserinfo(sess, sn); + if (was_explicit == TRUE) + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, reason, sn); + + if (snac2) + free(snac2->data); + free(snac2); + + return ret; +} + +/* + * Subtype 0x0002 + * + * Request Location services rights. + * + */ +faim_export int aim_locate_reqrights(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) + return -EINVAL; + + return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_REQRIGHTS); +} + +/* + * Subtype 0x0003 + * + * Normally contains: + * t(0001) - short containing max profile length (value = 1024) + * t(0002) - short - unknown (value = 16) [max MIME type length?] + * t(0003) - short - unknown (value = 10) + * t(0004) - short - unknown (value = 2048) [ICQ only?] + */ +static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_tlvlist_t *tlvlist; + aim_rxcallback_t userfunc; + int ret = 0; + fu16_t maxsiglen = 0; + + tlvlist = aim_tlvlist_read(bs); + + if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) + maxsiglen = aim_tlv_get16(tlvlist, 0x0001, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, maxsiglen); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0004 + * + * Gives BOS your profile. + * + * profile_encoding and awaymsg_encoding MUST be set if profile or + * away are set, respectively, and their value may or may not be + * restricted to a few choices. I am currently aware of: + * + * us-ascii Just that + * unicode-2-0 UCS2-BE + * + * profile_len and awaymsg_len MUST be set similarly, and they MUST + * be the length of their respective strings in bytes. + * + * To get the previous behavior of awaymsg == "" un-setting the away + * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the + * obvious equivalent). + * + */ +faim_export int aim_locate_setprofile(aim_session_t *sess, + const char *profile_encoding, const char *profile, const int profile_len, + const char *awaymsg_encoding, const char *awaymsg, const int awaymsg_len) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + char *encoding; + static const char defencoding[] = {"text/aolrtf; charset=\"%s\""}; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) + return -EINVAL; + + if (!profile && !awaymsg) + return -EINVAL; + + if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) { + return -EINVAL; + } + + /* Build the packet first to get real length */ + if (profile) { + /* no + 1 here because of %s */ + encoding = malloc(strlen(defencoding) + strlen(profile_encoding)); + if (encoding == NULL) { + return -ENOMEM; + } + snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding); + aim_tlvlist_add_raw(&tl, 0x0001, strlen(encoding), encoding); + aim_tlvlist_add_raw(&tl, 0x0002, profile_len, profile); + free(encoding); + } + + /* + * So here's how this works: + * - You are away when you have a non-zero-length type 4 TLV stored. + * - You become unaway when you clear the TLV with a zero-length + * type 4 TLV. + * - If you do not send the type 4 TLV, your status does not change + * (that is, if you were away, you'll remain away). + */ + if (awaymsg) { + if (awaymsg_len) { + encoding = malloc(strlen(defencoding) + strlen(awaymsg_encoding)); + if (encoding == NULL) { + return -ENOMEM; + } + snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding); + aim_tlvlist_add_raw(&tl, 0x0003, strlen(encoding), encoding); + aim_tlvlist_add_raw(&tl, 0x0004, awaymsg_len, awaymsg); + free(encoding); + } else + aim_tlvlist_add_noval(&tl, 0x0004); + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0004 - Set your client's capabilities. + */ +faim_export int aim_locate_setcaps(aim_session_t *sess, fu32_t caps) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) + return -EINVAL; + + aim_tlvlist_add_caps(&tl, 0x0005, caps); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0005 - Request info of another AIM user. + * + * @param sn The screenname whose info you wish to request. + * @param infotype The type of info you wish to request. + * 0x0001 - Info/profile + * 0x0003 - Away message + * 0x0004 - Capabilities + */ +faim_export int aim_locate_getinfo(aim_session_t *sess, const char *sn, fu16_t infotype) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid); + aimbs_put16(&fr->data, infotype); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Subtype 0x0006 */ +static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + aim_userinfo_t *userinfo, *userinfo2; + aim_tlvlist_t *tlvlist; + aim_tlv_t *tlv = NULL; + int was_explicit; + + userinfo = (aim_userinfo_t *)malloc(sizeof(aim_userinfo_t)); + aim_info_extract(sess, bs, userinfo); + tlvlist = aim_tlvlist_read(bs); + + /* Profile will be 1 and 2 */ + userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1); + if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { + userinfo->info = (char *)malloc(tlv->length); + memcpy(userinfo->info, tlv->value, tlv->length); + userinfo->info_len = tlv->length; + } + + /* Away message will be 3 and 4 */ + userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1); + if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { + userinfo->away = (char *)malloc(tlv->length); + memcpy(userinfo->away, tlv->value, tlv->length); + userinfo->away_len = tlv->length; + } + + /* Caps will be 5 */ + if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) { + aim_bstream_t cbs; + aim_bstream_init(&cbs, tlv->value, tlv->length); + userinfo->capabilities = aim_locate_getcaps(sess, &cbs, tlv->length); + userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; + } + aim_tlvlist_free(&tlvlist); + + aim_locate_adduserinfo(sess, userinfo); + userinfo2 = aim_locate_finduserinfo(sess, userinfo->sn); + aim_info_free(userinfo); + free(userinfo); + + /* + * Remove this screen name from our queue. If the client requested + * this buddy's info explicitly, then notify them that we have info + * for this buddy. + */ + was_explicit = aim_locate_gotuserinfo(sess, userinfo2->sn); + if (was_explicit == TRUE) + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, userinfo2); + + return ret; +} + +/* + * Subtype 0x0009 - Set directory profile data. + * + * This is not the same as aim_location_setprofile! + * privacy: 1 to allow searching, 0 to disallow. + * + */ +faim_export int aim_locate_setdirinfo(aim_session_t *sess, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, fu16_t privacy) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) + return -EINVAL; + + aim_tlvlist_add_16(&tl, 0x000a, privacy); + + if (first) + aim_tlvlist_add_raw(&tl, 0x0001, strlen(first), first); + if (last) + aim_tlvlist_add_raw(&tl, 0x0002, strlen(last), last); + if (middle) + aim_tlvlist_add_raw(&tl, 0x0003, strlen(middle), middle); + if (maiden) + aim_tlvlist_add_raw(&tl, 0x0004, strlen(maiden), maiden); + + if (state) + aim_tlvlist_add_raw(&tl, 0x0007, strlen(state), state); + if (city) + aim_tlvlist_add_raw(&tl, 0x0008, strlen(city), city); + + if (nickname) + aim_tlvlist_add_raw(&tl, 0x000c, strlen(nickname), nickname); + if (zip) + aim_tlvlist_add_raw(&tl, 0x000d, strlen(zip), zip); + + if (street) + aim_tlvlist_add_raw(&tl, 0x0021, strlen(street), street); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x000b - Huh? What is this? + */ +faim_export int aim_locate_000b(aim_session_t *sess, const char *sn) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x000f + * + * XXX pass these in better + * + */ +faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) + return -EINVAL; + + /* ?? privacy ?? */ + aim_tlvlist_add_16(&tl, 0x000a, privacy); + + if (interest1) + aim_tlvlist_add_raw(&tl, 0x0000b, strlen(interest1), interest1); + if (interest2) + aim_tlvlist_add_raw(&tl, 0x0000b, strlen(interest2), interest2); + if (interest3) + aim_tlvlist_add_raw(&tl, 0x0000b, strlen(interest3), interest3); + if (interest4) + aim_tlvlist_add_raw(&tl, 0x0000b, strlen(interest4), interest4); + if (interest5) + aim_tlvlist_add_raw(&tl, 0x0000b, strlen(interest5), interest5); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0015 - Request the info a user using the short method. This is + * what iChat uses. It normally is VERY leniently rate limited. + * + * @param sn The screen name whose info you wish to request. + * @param flags The bitmask which specifies the type of info you wish to request. + * 0x00000001 - Info/profile. + * 0x00000002 - Away message. + * 0x00000004 - Capabilities. + * 0x00000008 - Certification. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, fu32_t flags) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0015, 0x0000, sn, strlen(sn)+1); + + aim_putsnac(&fr->data, 0x0002, 0x0015, 0x0000, snacid); + aimbs_put32(&fr->data, flags); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0001) + return error(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0003) + return rights(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0006) + return userinfo(sess, mod, rx, snac, bs); + + return 0; +} + +static void locate_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + aim_userinfo_t *del; + + while (sess->locate.userinfo) { + del = sess->locate.userinfo; + sess->locate.userinfo = sess->locate.userinfo->next; + aim_info_free(del); + free(del); + } +} + +faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = AIM_CB_FAM_LOC; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "locate", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = locate_shutdown; + + return 0; +} diff --git a/libfaim/md5.c b/libfaim/md5.c new file mode 100644 index 0000000..e627358 --- /dev/null +++ b/libfaim/md5.c @@ -0,0 +1,392 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#ifdef TEST +/* + * Compile with -DTEST to create a self-contained executable test program. + * The test program should print out the same values as given in section + * A.5 of RFC 1321, reproduced below. + */ +#include <string.h> +main() +{ + static const char *const test[7] = { + "", /*d41d8cd98f00b204e9800998ecf8427e*/ + "945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/ + "abc", /*900150983cd24fb0d6963f7d28e17f72*/ + "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/ + "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + /*d174ab98d277d9f5a5611c2c9f419d9f*/ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/ + }; + int i; + + for (i = 0; i < 7; ++i) { + md5_state_t state; + md5_byte_t digest[16]; + int di; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i])); + md5_finish(&state, digest); + printf("MD5 (\"%s\") = ", test[i]); + for (di = 0; di < 16; ++di) + printf("%02x", digest[di]); + printf("\n"); + } + return 0; +} +#endif /* TEST */ + + +/* + * For reference, here is the program that computed the T values. + */ +#if 0 +#include <math.h> +main() +{ + int i; + for (i = 1; i <= 64; ++i) { + unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i))); + printf("#define T%d 0x%08lx\n", i, v); + } + return 0; +} +#endif +/* + * End of T computation program. + */ +#define T1 0xd76aa478 +#define T2 0xe8c7b756 +#define T3 0x242070db +#define T4 0xc1bdceee +#define T5 0xf57c0faf +#define T6 0x4787c62a +#define T7 0xa8304613 +#define T8 0xfd469501 +#define T9 0x698098d8 +#define T10 0x8b44f7af +#define T11 0xffff5bb1 +#define T12 0x895cd7be +#define T13 0x6b901122 +#define T14 0xfd987193 +#define T15 0xa679438e +#define T16 0x49b40821 +#define T17 0xf61e2562 +#define T18 0xc040b340 +#define T19 0x265e5a51 +#define T20 0xe9b6c7aa +#define T21 0xd62f105d +#define T22 0x02441453 +#define T23 0xd8a1e681 +#define T24 0xe7d3fbc8 +#define T25 0x21e1cde6 +#define T26 0xc33707d6 +#define T27 0xf4d50d87 +#define T28 0x455a14ed +#define T29 0xa9e3e905 +#define T30 0xfcefa3f8 +#define T31 0x676f02d9 +#define T32 0x8d2a4c8a +#define T33 0xfffa3942 +#define T34 0x8771f681 +#define T35 0x6d9d6122 +#define T36 0xfde5380c +#define T37 0xa4beea44 +#define T38 0x4bdecfa9 +#define T39 0xf6bb4b60 +#define T40 0xbebfbc70 +#define T41 0x289b7ec6 +#define T42 0xeaa127fa +#define T43 0xd4ef3085 +#define T44 0x04881d05 +#define T45 0xd9d4d039 +#define T46 0xe6db99e5 +#define T47 0x1fa27cf8 +#define T48 0xc4ac5665 +#define T49 0xf4292244 +#define T50 0x432aff97 +#define T51 0xab9423a7 +#define T52 0xfc93a039 +#define T53 0x655b59c3 +#define T54 0x8f0ccc92 +#define T55 0xffeff47d +#define T56 0x85845dd1 +#define T57 0x6fa87e4f +#define T58 0xfe2ce6e0 +#define T59 0xa3014314 +#define T60 0x4e0811a1 +#define T61 0xf7537e82 +#define T62 0xbd3af235 +#define T63 0x2ad7d2bb +#define T64 0xeb86d391 + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; + +#ifndef ARCH_IS_BIG_ENDIAN +# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */ +#endif +#if ARCH_IS_BIG_ENDIAN + + /* + * On big-endian machines, we must arrange the bytes in the right + * order. (This also works on machines of unknown byte order.) + */ + md5_word_t X[16]; + const md5_byte_t *xp = data; + int i; + + for (i = 0; i < 16; ++i, xp += 4) + X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + +#else /* !ARCH_IS_BIG_ENDIAN */ + + /* + * On little-endian machines, we can process properly aligned data + * without copying it. + */ + md5_word_t xbuf[16]; + const md5_word_t *X; + + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } +#endif + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = 0xefcdab89; + pms->abcd[2] = 0x98badcfe; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/libfaim/md5.h b/libfaim/md5.h new file mode 100644 index 0000000..501cdbe --- /dev/null +++ b/libfaim/md5.h @@ -0,0 +1,93 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This code has some adaptations for the Ghostscript environment, but it + * will compile and run correctly in any environment with 8-bit chars and + * 32-bit ints. Specifically, it assumes that if the following are + * defined, they have the same meaning as in Ghostscript: P1, P2, P3, + * ARCH_IS_BIG_ENDIAN. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +#ifdef P1 +void md5_init(P1(md5_state_t *pms)); +#else +void md5_init(md5_state_t *pms); +#endif + +/* Append a string to the message. */ +#ifdef P3 +void md5_append(P3(md5_state_t *pms, const md5_byte_t *data, int nbytes)); +#else +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); +#endif + +/* Finish the message and return the digest. */ +#ifdef P2 +void md5_finish(P2(md5_state_t *pms, md5_byte_t digest[16])); +#else +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); +#endif + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/libfaim/misc.c b/libfaim/misc.c new file mode 100644 index 0000000..6263f86 --- /dev/null +++ b/libfaim/misc.c @@ -0,0 +1,147 @@ +/* + * misc.c + * + * Random stuff. Basically just a few functions for sending + * simple SNACs, and then the generic error handler. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * Generic routine for sending commands. + * + * I know I can do this in a smarter way...but I'm not thinking straight + * right now... + * + * I had one big function that handled all three cases, but then it broke + * and I split it up into three. But then I fixed it. I just never went + * back to the single. I don't see any advantage to doing it either way. + * + */ +faim_internal int aim_genericreq_n(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype) +{ + aim_frame_t *fr; + aim_snacid_t snacid = 0x00000000; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10))) + return -ENOMEM; + + aim_putsnac(&fr->data, family, subtype, 0x0000, snacid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_internal int aim_genericreq_n_snacid(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); + aim_putsnac(&fr->data, family, subtype, 0x0000, snacid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_internal int aim_genericreq_l(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu32_t *longdata) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!longdata) + return aim_genericreq_n(sess, conn, family, subtype); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, family, subtype, 0x0000, snacid); + aimbs_put32(&fr->data, *longdata); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_internal int aim_genericreq_s(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu16_t *shortdata) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!shortdata) + return aim_genericreq_n(sess, conn, family, subtype); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, family, subtype, 0x0000, snacid); + aimbs_put16(&fr->data, *shortdata); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Should be generic enough to handle the errors for all groups. + * + */ +static int generror(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + int error = 0; + aim_rxcallback_t userfunc; + aim_snac_t *snac2; + + snac2 = aim_remsnac(sess, snac->id); + + if (aim_bstream_empty(bs)) + error = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, error, snac2 ? snac2->data : NULL); + + if (snac2) + free(snac2->data); + free(snac2); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0001) + return generror(sess, mod, rx, snac, bs); + else if ((snac->family == 0xffff) && (snac->subtype == 0xffff)) { + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx); + } + + return 0; +} + +faim_internal int misc_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0xffff; + mod->version = 0x0000; + mod->flags = AIM_MODFLAG_MULTIFAMILY; + strncpy(mod->name, "misc", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/msgcookie.c b/libfaim/msgcookie.c new file mode 100644 index 0000000..e2039ae --- /dev/null +++ b/libfaim/msgcookie.c @@ -0,0 +1,193 @@ +/* + * Cookie Caching stuff. Adam wrote this, apparently just some + * derivatives of n's SNAC work. I cleaned it up, added comments. + * + */ + +/* + * I'm assuming that cookies are type-specific. that is, we can have + * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we + * lose some error checking. if we assume cookies are not type-specific and are + * wrong, we get quirky behavior when cookies step on each others' toes. + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/** + * aim_cachecookie - appends a cookie to the cookie list + * @sess: session to add to + * @cookie: pointer to struct to append + * + * if cookie->cookie for type cookie->type is found, updates the + * ->addtime of the found structure; otherwise adds the given cookie + * to the cache + * + * returns -1 on error, 0 on append, 1 on update. the cookie you pass + * in may be free'd, so don't count on its value after calling this! + * + */ +faim_internal int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie) +{ + aim_msgcookie_t *newcook; + + if (!sess || !cookie) + return -EINVAL; + + newcook = aim_checkcookie(sess, cookie->cookie, cookie->type); + + if (newcook == cookie) { + newcook->addtime = time(NULL); + return 1; + } else if (newcook) + aim_cookie_free(sess, newcook); + + cookie->addtime = time(NULL); + + cookie->next = sess->msgcookies; + sess->msgcookies = cookie; + + return 0; +} + +/** + * aim_uncachecookie - grabs a cookie from the cookie cache (removes it from the list) + * @sess: session to grab cookie from + * @cookie: cookie string to look for + * @type: cookie type to look for + * + * takes a cookie string and a cookie type and finds the cookie struct associated with that duple, removing it from the cookie list ikn the process. + * + * if found, returns the struct; if none found (or on error), returns NULL: + */ +faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, fu8_t *cookie, int type) +{ + aim_msgcookie_t *cur, **prev; + + if (!cookie || !sess->msgcookies) + return NULL; + + for (prev = &sess->msgcookies; (cur = *prev); ) { + if ((cur->type == type) && + (memcmp(cur->cookie, cookie, 8) == 0)) { + *prev = cur->next; + return cur; + } + prev = &cur->next; + } + + return NULL; +} + +/** + * aim_mkcookie - generate an aim_msgcookie_t *struct from a cookie string, a type, and a data pointer. + * @c: pointer to the cookie string array + * @type: cookie type to use + * @data: data to be cached with the cookie + * + * returns NULL on error, a pointer to the newly-allocated cookie on + * success. + * + */ +faim_internal aim_msgcookie_t *aim_mkcookie(fu8_t *c, int type, void *data) +{ + aim_msgcookie_t *cookie; + + if (!c) + return NULL; + + if (!(cookie = calloc(1, sizeof(aim_msgcookie_t)))) + return NULL; + + cookie->data = data; + cookie->type = type; + memcpy(cookie->cookie, c, 8); + + return cookie; +} + +/** + * aim_checkcookie - check to see if a cookietuple has been cached + * @sess: session to check for the cookie in + * @cookie: pointer to the cookie string array + * @type: type of the cookie to look for + * + * this returns a pointer to the cookie struct (still in the list) on + * success; returns NULL on error/not found + * + */ + +faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const fu8_t *cookie, int type) +{ + aim_msgcookie_t *cur; + + for (cur = sess->msgcookies; cur; cur = cur->next) { + if ((cur->type == type) && + (memcmp(cur->cookie, cookie, 8) == 0)) + return cur; + } + + return NULL; +} + +#if 0 /* debugging feature */ +faim_internal int aim_dumpcookie(aim_session_t *sess, aim_msgcookie_t *cookie) +{ + + if (!cookie) + return -EINVAL; + + faimdprintf(sess, 0, "\tCookie at %p: %d/%s with %p, next %p\n", cookie, + cookie->type, cookie->cookie, cookie->data, cookie->next); + + return 0; +} +#endif + +/** + * aim_cookie_free - free an aim_msgcookie_t struct + * @sess: session to remove the cookie from + * @cookiep: the address of a pointer to the cookie struct to remove + * + * this function removes the cookie *cookie from teh list of cookies + * in sess, and then frees all memory associated with it. including + * its data! if you want to use the private data after calling this, + * make sure you copy it first. + * + * returns -1 on error, 0 on success. + * + */ +faim_internal int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie) +{ + aim_msgcookie_t *cur, **prev; + + if (!sess || !cookie) + return -EINVAL; + + for (prev = &sess->msgcookies; (cur = *prev); ) { + if (cur == cookie) + *prev = cur->next; + else + prev = &cur->next; + } + + free(cookie->data); + free(cookie); + + return 0; +} + +/* XXX I hate switch */ +faim_internal int aim_msgcookie_gettype(int reqclass) +{ + /* XXX: hokey-assed. needs fixed. */ + switch(reqclass) { + case AIM_CAPS_BUDDYICON: return AIM_COOKIETYPE_OFTICON; + case AIM_CAPS_VOICE: return AIM_COOKIETYPE_OFTVOICE; + case AIM_CAPS_DIRECTIM: return AIM_COOKIETYPE_OFTIMAGE; + case AIM_CAPS_CHAT: return AIM_COOKIETYPE_CHAT; + case AIM_CAPS_GETFILE: return AIM_COOKIETYPE_OFTGET; + case AIM_CAPS_SENDFILE: return AIM_COOKIETYPE_OFTSEND; + default: return AIM_COOKIETYPE_UNKNOWN; + } +} diff --git a/libfaim/odir.c b/libfaim/odir.c new file mode 100644 index 0000000..2cefa3b --- /dev/null +++ b/libfaim/odir.c @@ -0,0 +1,245 @@ +/* + * Family 0x000f - Newer Search Method + * + * Used for searching for other AIM users by email address, name, + * location, commmon interests, and a few other similar things. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/** + * Subtype 0x0002 - Submit a User Search Request + * + * Search for an AIM screen name based on their email address. + * + * @param sess The oscar session. + * @param region Should be "us-ascii" unless you know what you're doing. + * @param email The email address you want to search for. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_odir_email(aim_session_t *sess, const char *region, const char *email) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region || !email) + return -EINVAL; + + /* Create a TLV chain, write it to the outgoing frame, then free the chain */ + aim_tlvlist_add_raw(&tl, 0x001c, strlen(region), region); + aim_tlvlist_add_16(&tl, 0x000a, 0x0001); /* Type of search */ + aim_tlvlist_add_raw(&tl, 0x0005, strlen(email), email); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + + +/** + * Subtype 0x0002 - Submit a User Search Request + * + * Search for an AIM screen name based on various info + * about the person. + * + * @param sess The oscar session. + * @param region Should be "us-ascii" unless you know what you're doing. + * @param first The first name of the person you want to search for. + * @param middle The middle name of the person you want to search for. + * @param last The last name of the person you want to search for. + * @param maiden The maiden name of the person you want to search for. + * @param nick The nick name of the person you want to search for. + * @param city The city where the person you want to search for resides. + * @param state The state where the person you want to search for resides. + * @param country The country where the person you want to search for resides. + * @param zip The zip code where the person you want to search for resides. + * @param address The street address where the person you want to seach for resides. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_odir_name(aim_session_t *sess, const char *region, const char *first, const char *middle, const char *last, const char *maiden, const char *nick, const char *city, const char *state, const char *country, const char *zip, const char *address) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region) + return -EINVAL; + + /* Create a TLV chain, write it to the outgoing frame, then free the chain */ + aim_tlvlist_add_raw(&tl, 0x001c, strlen(region), region); + aim_tlvlist_add_16(&tl, 0x000a, 0x0000); /* Type of search */ + if (first) + aim_tlvlist_add_raw(&tl, 0x0001, strlen(first), first); + if (last) + aim_tlvlist_add_raw(&tl, 0x0002, strlen(last), last); + if (middle) + aim_tlvlist_add_raw(&tl, 0x0003, strlen(middle), middle); + if (maiden) + aim_tlvlist_add_raw(&tl, 0x0004, strlen(maiden), maiden); + if (country) + aim_tlvlist_add_raw(&tl, 0x0006, strlen(country), country); + if (state) + aim_tlvlist_add_raw(&tl, 0x0007, strlen(state), state); + if (city) + aim_tlvlist_add_raw(&tl, 0x0008, strlen(city), city); + if (nick) + aim_tlvlist_add_raw(&tl, 0x000c, strlen(nick), nick); + if (zip) + aim_tlvlist_add_raw(&tl, 0x000d, strlen(zip), zip); + if (address) + aim_tlvlist_add_raw(&tl, 0x0021, strlen(address), address); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + + +/** + * Subtype 0x0002 - Submit a User Search Request + * + * @param sess The oscar session. + * @param interest1 An interest you want to search for. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_odir_interest(aim_session_t *sess, const char *region, const char *interest) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region) + return -EINVAL; + + /* Create a TLV chain, write it to the outgoing frame, then free the chain */ + aim_tlvlist_add_raw(&tl, 0x001c, strlen(region), region); + aim_tlvlist_add_16(&tl, 0x000a, 0x0001); /* Type of search */ + if (interest) + aim_tlvlist_add_raw(&tl, 0x0001, strlen(interest), interest); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + + +/** + * Subtype 0x0003 - Receive Reply From a User Search + * + */ +static int parseresults(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t tmp, numresults; + struct aim_odir *results = NULL; + + tmp = aimbs_get16(bs); /* Unknown */ + tmp = aimbs_get16(bs); /* Unknown */ + aim_bstream_advance(bs, tmp); + + numresults = aimbs_get16(bs); /* Number of results to follow */ + + /* Allocate a linked list, 1 node per result */ + while (numresults) { + struct aim_odir *new; + aim_tlvlist_t *tl = aim_tlvlist_readnum(bs, aimbs_get16(bs)); + new = (struct aim_odir *)malloc(sizeof(struct aim_odir)); + new->first = aim_tlv_getstr(tl, 0x0001, 1); + new->last = aim_tlv_getstr(tl, 0x0002, 1); + new->middle = aim_tlv_getstr(tl, 0x0003, 1); + new->maiden = aim_tlv_getstr(tl, 0x0004, 1); + new->email = aim_tlv_getstr(tl, 0x0005, 1); + new->country = aim_tlv_getstr(tl, 0x0006, 1); + new->state = aim_tlv_getstr(tl, 0x0007, 1); + new->city = aim_tlv_getstr(tl, 0x0008, 1); + new->sn = aim_tlv_getstr(tl, 0x0009, 1); + new->interest = aim_tlv_getstr(tl, 0x000b, 1); + new->nick = aim_tlv_getstr(tl, 0x000c, 1); + new->zip = aim_tlv_getstr(tl, 0x000d, 1); + new->region = aim_tlv_getstr(tl, 0x001c, 1); + new->address = aim_tlv_getstr(tl, 0x0021, 1); + new->next = results; + results = new; + numresults--; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, results); + + /* Now free everything from above */ + while (results) { + struct aim_odir *del = results; + results = results->next; + free(del->first); + free(del->last); + free(del->middle); + free(del->maiden); + free(del->email); + free(del->country); + free(del->state); + free(del->city); + free(del->sn); + free(del->interest); + free(del->nick); + free(del->zip); + free(del->region); + free(del->address); + free(del); + } + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return parseresults(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int odir_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000f; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "odir", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/oscar.c b/libfaim/oscar.c new file mode 100644 index 0000000..04ab724 --- /dev/null +++ b/libfaim/oscar.c @@ -0,0 +1,6711 @@ +/* + * gaim + * + * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * Some code copyright (C) 1999-2001, Eric Warmenhoven + * Some code copyright (C) 2001-2003, Sean Egan + * Some code copyright (C) 2001-2003, Mark Doliner <thekingant@users.sourceforge.net> + * + * Most libfaim code copyright (C) 1998-2001 Adam Fritzler <afritz@auk.cx> + * Some libfaim code copyright (C) 2001-2003 Mark Doliner <thekingant@users.sourceforge.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 "internal.h" + +#include "account.h" +#include "accountopt.h" +#include "buddyicon.h" +#include "conversation.h" +#include "core.h" +#include "debug.h" +#include "ft.h" +#include "imgstore.h" +#include "multi.h" +#include "notify.h" +#include "privacy.h" +#include "prpl.h" +#include "proxy.h" +#include "request.h" +#include "util.h" + +#include "aim.h" +#include "md5.h" + +#define UC_AOL 0x02 +#define UC_ADMIN 0x04 +#define UC_UNCONFIRMED 0x08 +#define UC_NORMAL 0x10 +#define UC_AB 0x20 +#define UC_WIRELESS 0x40 +#define UC_HIPTOP 0x80 + +#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3" + +#define OSCAR_CONNECT_STEPS 6 + +static GaimPlugin *my_protocol = NULL; + +static int caps_aim = AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_INTEROPERATE | AIM_CAPS_ICHAT; +static int caps_icq = AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_ICQUTF8 | AIM_CAPS_INTEROPERATE | AIM_CAPS_ICHAT; + +static fu8_t features_aim[] = {0x01, 0x01, 0x01, 0x02}; +static fu8_t features_icq[] = {0x01, 0x06}; + +typedef struct _OscarData OscarData; +struct _OscarData { + aim_session_t *sess; + aim_conn_t *conn; + + guint cnpa; + guint paspa; + guint emlpa; + guint icopa; + + gboolean iconconnecting; + gboolean set_icon; + + GSList *create_rooms; + + gboolean conf; + gboolean reqemail; + gboolean setemail; + char *email; + gboolean setnick; + char *newsn; + gboolean chpass; + char *oldp; + char *newp; + + GSList *oscar_chats; + GSList *direct_ims; + GSList *file_transfers; + GHashTable *buddyinfo; + GSList *requesticon; + + gboolean killme; + gboolean icq; + guint icontimer; + guint getblisttimer; + + struct { + guint maxwatchers; /* max users who can watch you */ + guint maxbuddies; /* max users you can watch */ + guint maxgroups; /* max groups in server list */ + guint maxpermits; /* max users on permit list */ + guint maxdenies; /* max users on deny list */ + guint maxsiglen; /* max size (bytes) of profile */ + guint maxawaymsglen; /* max size (bytes) of posted away message */ + } rights; +}; + +struct create_room { + char *name; + int exchange; +}; + +struct chat_connection { + char *name; + char *show; /* AOL did something funny to us */ + fu16_t exchange; + fu16_t instance; + int fd; /* this is redundant since we have the conn below */ + aim_conn_t *conn; + int inpa; + int id; + GaimConnection *gc; /* i hate this. */ + GaimConversation *cnv; /* bah. */ + int maxlen; + int maxvis; +}; + +struct direct_im { + GaimConnection *gc; + char name[80]; + int watcher; + aim_conn_t *conn; + gboolean connected; +}; + +struct ask_direct { + GaimConnection *gc; + char *sn; + char ip[64]; + fu8_t cookie[8]; +}; + +/* + * Various PRPL-specific buddy info that we want to keep track of + * Some other info is maintained by locate.c, and I'd like to move + * the rest of this to libfaim, mostly im.c + */ +struct buddyinfo { + gboolean typingnot; + gchar *availmsg; + fu32_t ipaddr; + + unsigned long ico_me_len; + unsigned long ico_me_csum; + time_t ico_me_time; + gboolean ico_informed; + + unsigned long ico_len; + unsigned long ico_csum; + time_t ico_time; + gboolean ico_need; + gboolean ico_sent; +}; + +struct name_data { + GaimConnection *gc; + gchar *name; + gchar *nick; +}; + +static char *msgerrreason[] = { + N_("Invalid error"), + N_("Invalid SNAC"), + N_("Rate to host"), + N_("Rate to client"), + N_("Not logged in"), + N_("Service unavailable"), + N_("Service not defined"), + N_("Obsolete SNAC"), + N_("Not supported by host"), + N_("Not supported by client"), + N_("Refused by client"), + N_("Reply too big"), + N_("Responses lost"), + N_("Request denied"), + N_("Busted SNAC payload"), + N_("Insufficient rights"), + N_("In local permit/deny"), + N_("Too evil (sender)"), + N_("Too evil (receiver)"), + N_("User temporarily unavailable"), + N_("No match"), + N_("List overflow"), + N_("Request ambiguous"), + N_("Queue full"), + N_("Not while on AOL") +}; +static int msgerrreasonlen = 25; + +/* All the libfaim->gaim callback functions */ +static int gaim_parse_auth_resp (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_login (aim_session_t *, aim_frame_t *, ...); +static int gaim_handle_redirect (aim_session_t *, aim_frame_t *, ...); +static int gaim_info_change (aim_session_t *, aim_frame_t *, ...); +static int gaim_account_confirm (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_oncoming (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_offgoing (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_misses (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_clientauto (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_userinfo (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_motd (aim_session_t *, aim_frame_t *, ...); +static int gaim_chatnav_info (aim_session_t *, aim_frame_t *, ...); +static int gaim_conv_chat_join (aim_session_t *, aim_frame_t *, ...); +static int gaim_conv_chat_leave (aim_session_t *, aim_frame_t *, ...); +static int gaim_conv_chat_info_update (aim_session_t *, aim_frame_t *, ...); +static int gaim_conv_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...); +static int gaim_email_parseupdate(aim_session_t *, aim_frame_t *, ...); +static int gaim_icon_error (aim_session_t *, aim_frame_t *, ...); +static int gaim_icon_parseicon (aim_session_t *, aim_frame_t *, ...); +static int oscar_icon_req (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_msgack (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_evilnotify (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_searcherror(aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_searchreply(aim_session_t *, aim_frame_t *, ...); +static int gaim_bosrights (aim_session_t *, aim_frame_t *, ...); +static int gaim_connerr (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_admin (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_bos (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_chatnav (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_chat (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_email (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_icon (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_msgerr (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_mtn (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_locerr (aim_session_t *, aim_frame_t *, ...); +static int gaim_icbm_param_info (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...); +static int gaim_memrequest (aim_session_t *, aim_frame_t *, ...); +static int gaim_selfinfo (aim_session_t *, aim_frame_t *, ...); +static int gaim_offlinemsg (aim_session_t *, aim_frame_t *, ...); +static int gaim_offlinemsgdone (aim_session_t *, aim_frame_t *, ...); +static int gaim_icqalias (aim_session_t *, aim_frame_t *, ...); +static int gaim_icqinfo (aim_session_t *, aim_frame_t *, ...); +static int gaim_popup (aim_session_t *, aim_frame_t *, ...); +#ifndef NOSSI +static int gaim_ssi_parseerr (aim_session_t *, aim_frame_t *, ...); +static int gaim_ssi_parserights (aim_session_t *, aim_frame_t *, ...); +static int gaim_ssi_parselist (aim_session_t *, aim_frame_t *, ...); +static int gaim_ssi_parseack (aim_session_t *, aim_frame_t *, ...); +static int gaim_ssi_authgiven (aim_session_t *, aim_frame_t *, ...); +static int gaim_ssi_authrequest (aim_session_t *, aim_frame_t *, ...); +static int gaim_ssi_authreply (aim_session_t *, aim_frame_t *, ...); +static int gaim_ssi_gotadded (aim_session_t *, aim_frame_t *, ...); +#endif + +/* for DirectIM/image transfer */ +static int gaim_odc_initiate (aim_session_t *, aim_frame_t *, ...); +static int gaim_odc_incoming (aim_session_t *, aim_frame_t *, ...); +static int gaim_odc_typing (aim_session_t *, aim_frame_t *, ...); +static int gaim_odc_update_ui (aim_session_t *, aim_frame_t *, ...); + +/* for file transfer */ +static int oscar_sendfile_estblsh(aim_session_t *, aim_frame_t *, ...); +static int oscar_sendfile_prompt (aim_session_t *, aim_frame_t *, ...); +static int oscar_sendfile_ack (aim_session_t *, aim_frame_t *, ...); +static int oscar_sendfile_done (aim_session_t *, aim_frame_t *, ...); + +/* for icons */ +static gboolean gaim_icon_timerfunc(gpointer data); + +/* prpl actions - remove this at some point */ +/* Because I don't like forward declarations? I think that was why... */ +static void oscar_set_info(GaimConnection *gc, const char *text); + +static void oscar_free_name_data(struct name_data *data) { + g_free(data->name); + g_free(data->nick); + g_free(data); +} + +static void oscar_free_buddyinfo(void *data) { + struct buddyinfo *bi = data; + g_free(bi->availmsg); + g_free(bi); +} + +static fu32_t oscar_encoding_check(const char *utf8) +{ + int i = 0; + fu32_t encodingflag = 0; + + /* Determine how we can send this message. Per the warnings elsewhere + * in this file, these little checks determine the simplest encoding + * we can use for a given message send using it. */ + while (utf8[i]) { + if ((unsigned char)utf8[i] > 0x7f) { + /* not ASCII! */ + encodingflag = AIM_IMFLAGS_ISO_8859_1; + break; + } + i++; + } + while (utf8[i]) { + /* ISO-8859-1 is 0x00-0xbf in the first byte + * followed by 0xc0-0xc3 in the second */ + if ((unsigned char)utf8[i] < 0x80) { + i++; + continue; + } else if (((unsigned char)utf8[i] & 0xfc) == 0xc0 && + ((unsigned char)utf8[i + 1] & 0xc0) == 0x80) { + i += 2; + continue; + } + encodingflag = AIM_IMFLAGS_UNICODE; + break; + } + + return encodingflag; +} + +static fu32_t oscar_encoding_parse(const char *enc) +{ + char *charset; + + /* If anything goes wrong, fall back on ASCII and print a message */ + if (enc == NULL) { + gaim_debug(GAIM_DEBUG_WARNING, "oscar", + "Encoding was null, that's odd\n"); + return 0; + } + charset = strstr(enc, "charset="); + if (charset == NULL) { + gaim_debug(GAIM_DEBUG_WARNING, "oscar", + "No charset specified for info, assuming ASCII\n"); + return 0; + } + charset += 8; + if (!strcmp(charset, "\"us-ascii\"") || !strcmp(charset, "\"utf-8\"")) { + /* UTF-8 is our native charset, ASCII is a proper subset */ + return 0; + } else if (!strcmp(charset, "\"iso-8859-1\"")) { + return AIM_IMFLAGS_ISO_8859_1; + } else if (!strcmp(charset, "\"unicode-2-0\"")) { + return AIM_IMFLAGS_UNICODE; + } else { + gaim_debug(GAIM_DEBUG_WARNING, "oscar", + "Unrecognized character set '%s', using ASCII\n", charset); + return 0; + } +} + +gchar *oscar_encoding_to_utf8(const char *encoding, char *text, int textlen) +{ + gchar *utf8 = NULL; + int flags = oscar_encoding_parse(encoding); + + switch (flags) { + case 0: + utf8 = g_strndup(text, textlen); + break; + case AIM_IMFLAGS_ISO_8859_1: + utf8 = g_convert(text, textlen, "UTF-8", "ISO-8859-1", NULL, NULL, NULL); + break; + case AIM_IMFLAGS_UNICODE: + utf8 = g_convert(text, textlen, "UTF-8", "UCS-2BE", NULL, NULL, NULL); + break; + } + + return utf8; +} + +static struct direct_im *find_direct_im(OscarData *od, const char *who) { + GSList *d = od->direct_ims; + struct direct_im *m = NULL; + + while (d) { + m = (struct direct_im *)d->data; + if (!aim_sncmp(who, m->name)) + return m; + d = d->next; + } + + return NULL; +} + +static char *extract_name(const char *name) { + char *tmp, *x; + int i, j; + + if (!name) + return NULL; + + x = strchr(name, '-'); + + if (!x) return NULL; + x = strchr(++x, '-'); + if (!x) return NULL; + tmp = g_strdup(++x); + + for (i = 0, j = 0; x[i]; i++) { + char hex[3]; + if (x[i] != '%') { + tmp[j++] = x[i]; + continue; + } + strncpy(hex, x + ++i, 2); hex[2] = 0; + i++; + tmp[j++] = strtol(hex, NULL, 16); + } + + tmp[j] = 0; + return tmp; +} + +static struct chat_connection *find_oscar_chat(GaimConnection *gc, int id) { + GSList *g = ((OscarData *)gc->proto_data)->oscar_chats; + struct chat_connection *c = NULL; + + while (g) { + c = (struct chat_connection *)g->data; + if (c->id == id) + break; + g = g->next; + c = NULL; + } + + return c; +} + +static struct chat_connection *find_oscar_chat_by_conn(GaimConnection *gc, + aim_conn_t *conn) { + GSList *g = ((OscarData *)gc->proto_data)->oscar_chats; + struct chat_connection *c = NULL; + + while (g) { + c = (struct chat_connection *)g->data; + if (c->conn == conn) + break; + g = g->next; + c = NULL; + } + + return c; +} + +static void gaim_odc_disconnect(aim_session_t *sess, aim_conn_t *conn) { + GaimConnection *gc = sess->aux_data; + OscarData *od = (OscarData *)gc->proto_data; + GaimConversation *cnv; + struct direct_im *dim; + char *sn; + char buf[256]; + + sn = g_strdup(aim_odc_getsn(conn)); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "%s disconnected Direct IM.\n", sn); + + dim = find_direct_im(od, sn); + od->direct_ims = g_slist_remove(od->direct_ims, dim); + gaim_input_remove(dim->watcher); + + if (dim->connected) + g_snprintf(buf, sizeof buf, _("Direct IM with %s closed"), sn); + else + g_snprintf(buf, sizeof buf, _("Direct IM with %s failed"), sn); + + cnv = gaim_find_conversation_with_account(sn, gaim_connection_get_account(gc)); + if (cnv) + gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL)); + + gaim_conversation_update_progress(cnv, 0); + + g_free(dim); /* I guess? I don't see it anywhere else... -- mid */ + g_free(sn); + + return; +} + +static void oscar_callback(gpointer data, gint source, GaimInputCondition condition) { + aim_conn_t *conn = (aim_conn_t *)data; + aim_session_t *sess = aim_conn_getsess(conn); + GaimConnection *gc = sess ? sess->aux_data : NULL; + OscarData *od; + + if (!gc) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "oscar callback for closed connection (1).\n"); + return; + } + + od = (OscarData *)gc->proto_data; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + /* oh boy. this is probably bad. i guess the only thing we + * can really do is return? */ + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "oscar callback for closed connection (2).\n"); + gaim_debug(GAIM_DEBUG_MISC, "oscar", "gc = %p\n", gc); + return; + } + + if (condition & GAIM_INPUT_READ) { + if (conn->type == AIM_CONN_TYPE_LISTENER) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "got information on rendezvous listener\n"); + if (aim_handlerendconnect(od->sess, conn) < 0) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "connection error (rendezvous listener)\n"); + aim_conn_kill(od->sess, &conn); + } + } else { + if (aim_get_command(od->sess, conn) >= 0) { + aim_rxdispatch(od->sess); + if (od->killme) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", "Waiting to be destroyed\n"); + return; + } + } else { + if ((conn->type == AIM_CONN_TYPE_BOS) || + !(aim_getconn_type(od->sess, AIM_CONN_TYPE_BOS))) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "major connection error\n"); + gaim_connection_error(gc, _("Disconnected.")); + } else if (conn->type == AIM_CONN_TYPE_CHAT) { + struct chat_connection *c = find_oscar_chat_by_conn(gc, conn); + char *buf; + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "disconnected from chat room %s\n", c->name); + c->conn = NULL; + if (c->inpa > 0) + gaim_input_remove(c->inpa); + c->inpa = 0; + c->fd = -1; + aim_conn_kill(od->sess, &conn); + buf = g_strdup_printf(_("You have been disconnected from chat room %s."), c->name); + gaim_notify_error(gc, NULL, buf, NULL); + g_free(buf); + } else if (conn->type == AIM_CONN_TYPE_CHATNAV) { + if (od->cnpa > 0) + gaim_input_remove(od->cnpa); + od->cnpa = 0; + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "removing chatnav input watcher\n"); + while (od->create_rooms) { + struct create_room *cr = od->create_rooms->data; + g_free(cr->name); + od->create_rooms = + g_slist_remove(od->create_rooms, cr); + g_free(cr); + gaim_notify_error(gc, NULL, + _("Chat is currently unavailable"), + NULL); + } + aim_conn_kill(od->sess, &conn); + } else if (conn->type == AIM_CONN_TYPE_AUTH) { + if (od->paspa > 0) + gaim_input_remove(od->paspa); + od->paspa = 0; + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "removing authconn input watcher\n"); + aim_conn_kill(od->sess, &conn); + } else if (conn->type == AIM_CONN_TYPE_EMAIL) { + if (od->emlpa > 0) + gaim_input_remove(od->emlpa); + od->emlpa = 0; + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "removing email input watcher\n"); + aim_conn_kill(od->sess, &conn); + } else if (conn->type == AIM_CONN_TYPE_ICON) { + if (od->icopa > 0) + gaim_input_remove(od->icopa); + od->icopa = 0; + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "removing icon input watcher\n"); + aim_conn_kill(od->sess, &conn); + } else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) { + if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) + gaim_odc_disconnect(od->sess, conn); + aim_conn_kill(od->sess, &conn); + } else { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "holy crap! generic connection error! %hu\n", + conn->type); + aim_conn_kill(od->sess, &conn); + } + } + } + } +} + +static void oscar_debug(aim_session_t *sess, int level, const char *format, va_list va) { + GaimConnection *gc = sess->aux_data; + gchar *s = g_strdup_vprintf(format, va); + gchar *buf; + + buf = g_strdup_printf("%s %d: %s", gaim_account_get_username(gaim_connection_get_account(gc)), level, s); + gaim_debug(GAIM_DEBUG_INFO, "oscar", buf); + if (buf[strlen(buf)-1] != '\n') + gaim_debug(GAIM_DEBUG_INFO, NULL, "\n"); + g_free(buf); + g_free(s); +} + +static void oscar_login_connect(gpointer data, gint source, GaimInputCondition cond) +{ + GaimConnection *gc = data; + OscarData *od; + aim_session_t *sess; + aim_conn_t *conn; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + close(source); + return; + } + + od = gc->proto_data; + sess = od->sess; + conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH); + conn->fd = source; + + if (source < 0) { + gaim_connection_error(gc, _("Couldn't connect to host")); + return; + } + + aim_conn_completeconnect(sess, conn); + gc->inpa = gaim_input_add(conn->fd, GAIM_INPUT_READ, oscar_callback, conn); + aim_request_login(sess, conn, gaim_account_get_username(gaim_connection_get_account(gc))); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Screen name sent, waiting for response\n"); + gaim_connection_update_progress(gc, _("Screen name sent"), 1, OSCAR_CONNECT_STEPS); +} + +static void oscar_login(GaimAccount *account) { + aim_session_t *sess; + aim_conn_t *conn; + GaimConnection *gc = gaim_account_get_connection(account); + OscarData *od = gc->proto_data = g_new0(OscarData, 1); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", "oscar_login: gc = %p\n", gc); + + if (isdigit(*(gaim_account_get_username(account)))) { + od->icq = TRUE; + } else { + gc->flags |= GAIM_CONNECTION_HTML; + gc->flags |= GAIM_CONNECTION_AUTO_RESP; + } + od->buddyinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, oscar_free_buddyinfo); + + sess = g_new0(aim_session_t, 1); + aim_session_init(sess, TRUE, 0); + aim_setdebuggingcb(sess, oscar_debug); + /* + * We need an immediate queue because we don't use a while-loop + * to see if things need to be sent. + */ + aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL); + od->sess = sess; + sess->aux_data = gc; + + conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL); + if (conn == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "internal connection error\n"); + gaim_connection_error(gc, _("Unable to login to AIM")); + return; + } + + aim_conn_addhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0); + aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0); + aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0); + + conn->status |= AIM_CONN_STATUS_INPROGRESS; + if (gaim_proxy_connect(account, gaim_account_get_string(account, "server", FAIM_LOGIN_SERVER), + gaim_account_get_int(account, "port", FAIM_LOGIN_PORT), + oscar_login_connect, gc) < 0) { + gaim_connection_error(gc, _("Couldn't connect to host")); + return; + } + + gaim_connection_update_progress(gc, _("Connecting"), 0, OSCAR_CONNECT_STEPS); +} + +static void oscar_close(GaimConnection *gc) { + OscarData *od = (OscarData *)gc->proto_data; + + while (od->oscar_chats) { + struct chat_connection *n = od->oscar_chats->data; + if (n->inpa > 0) + gaim_input_remove(n->inpa); + g_free(n->name); + g_free(n->show); + od->oscar_chats = g_slist_remove(od->oscar_chats, n); + g_free(n); + } + while (od->direct_ims) { + struct direct_im *n = od->direct_ims->data; + if (n->watcher > 0) + gaim_input_remove(n->watcher); + od->direct_ims = g_slist_remove(od->direct_ims, n); + g_free(n); + } +/* BBB */ + while (od->file_transfers) { + GaimXfer *xfer; + xfer = (GaimXfer *)od->file_transfers->data; + gaim_xfer_destroy(xfer); + } + while (od->requesticon) { + char *sn = od->requesticon->data; + od->requesticon = g_slist_remove(od->requesticon, sn); + free(sn); + } + g_hash_table_destroy(od->buddyinfo); + while (od->create_rooms) { + struct create_room *cr = od->create_rooms->data; + g_free(cr->name); + od->create_rooms = g_slist_remove(od->create_rooms, cr); + g_free(cr); + } + if (od->email) + g_free(od->email); + if (od->newp) + g_free(od->newp); + if (od->oldp) + g_free(od->oldp); + if (gc->inpa > 0) + gaim_input_remove(gc->inpa); + if (od->cnpa > 0) + gaim_input_remove(od->cnpa); + if (od->paspa > 0) + gaim_input_remove(od->paspa); + if (od->emlpa > 0) + gaim_input_remove(od->emlpa); + if (od->icopa > 0) + gaim_input_remove(od->icopa); + if (od->icontimer > 0) + g_source_remove(od->icontimer); + if (od->getblisttimer) + g_source_remove(od->getblisttimer); + aim_session_kill(od->sess); + g_free(od->sess); + od->sess = NULL; + g_free(gc->proto_data); + gc->proto_data = NULL; + gaim_debug(GAIM_DEBUG_INFO, "oscar", "Signed off.\n"); +} + +static void oscar_bos_connect(gpointer data, gint source, GaimInputCondition cond) { + GaimConnection *gc = data; + OscarData *od; + aim_session_t *sess; + aim_conn_t *bosconn; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + close(source); + return; + } + + od = gc->proto_data; + sess = od->sess; + bosconn = od->conn; + bosconn->fd = source; + + if (source < 0) { + gaim_connection_error(gc, _("Could Not Connect")); + return; + } + + aim_conn_completeconnect(sess, bosconn); + gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ, oscar_callback, bosconn); + + gaim_connection_update_progress(gc, + _("Connection established, cookie sent"), 4, OSCAR_CONNECT_STEPS); +} + +/* BBB */ +/* + * This little area in oscar.c is the nexus of file transfer code, + * so I wrote a little explanation of what happens. I am such a + * ninja. + * + * The series of events for a file send is: + * -Create xfer and call gaim_xfer_request (this happens in oscar_ask_sendfile) + * -User chooses a file and oscar_xfer_init is called. It establishs a + * listening socket, then asks the remote user to connect to us (and + * gives them the file name, port, IP, etc.) + * -They connect to us and we send them an AIM_CB_OFT_PROMPT (this happens + * in oscar_sendfile_estblsh) + * -They send us an AIM_CB_OFT_ACK and then we start sending data + * -When we finish, they send us an AIM_CB_OFT_DONE and they close the + * connection. + * -We get drunk because file transfer kicks ass. + * + * The series of events for a file receive is: + * -Create xfer and call gaim_xfer request (this happens in incomingim_chan2) + * -Gaim user selects file to name and location to save file to and + * oscar_xfer_init is called + * -It connects to the remote user using the IP they gave us earlier + * -After connecting, they send us an AIM_CB_OFT_PROMPT. In reply, we send + * them an AIM_CB_OFT_ACK. + * -They begin to send us lots of raw data. + * -When they finish sending data we send an AIM_CB_OFT_DONE and then close + * the connectionn. + */ +static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition); + +/* XXX - This function is pretty ugly */ +static void oscar_xfer_init(GaimXfer *xfer) +{ + struct aim_oft_info *oft_info = xfer->data; + GaimConnection *gc = oft_info->sess->aux_data; + OscarData *od = gc->proto_data; + + if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { + int i; + + xfer->filename = g_path_get_basename(xfer->local_filename); + strncpy(oft_info->fh.name, xfer->filename, 64); + oft_info->fh.totsize = gaim_xfer_get_size(xfer); + oft_info->fh.size = gaim_xfer_get_size(xfer); + oft_info->fh.checksum = aim_oft_checksum_file(xfer->local_filename); + + /* + * First try the port specified earlier (5190). If that fails, + * increment by 1 and try again. + */ + aim_sendfile_listen(od->sess, oft_info); + for (i=0; (i<5 && !oft_info->conn); i++) { + xfer->local_port = oft_info->port = oft_info->port + 1; + aim_sendfile_listen(od->sess, oft_info); + } + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "port is %d, ip is %s\n", + xfer->local_port, oft_info->clientip); + if (oft_info->conn) { + xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn); + aim_im_sendch2_sendfile_ask(od->sess, oft_info); + aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED, oscar_sendfile_estblsh, 0); + } else { + gaim_notify_error(gc, NULL, _("File Transfer Aborted"), + _("Unable to establish listener socket.")); + /* XXX - The below line causes a crash because the transfer is canceled before the "Ok" callback on the file selection thing exists, I think */ + /* gaim_xfer_cancel_remote(xfer); */ + } + } else if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) { + oft_info->conn = aim_newconn(od->sess, AIM_CONN_TYPE_RENDEZVOUS, NULL); + if (oft_info->conn) { + oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE; + aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_PROMPT, oscar_sendfile_prompt, 0); + oft_info->conn->fd = xfer->fd = gaim_proxy_connect(gaim_connection_get_account(gc), xfer->remote_ip, xfer->remote_port, + oscar_sendfile_connected, xfer); + if (xfer->fd == -1) { + gaim_notify_error(gc, NULL, _("File Transfer Aborted"), + _("Unable to establish file descriptor.")); + /* gaim_xfer_cancel_remote(xfer); */ + } + } else { + gaim_notify_error(gc, NULL, _("File Transfer Aborted"), + _("Unable to create new connection.")); + /* gaim_xfer_cancel_remote(xfer); */ + /* Try a different port? Ask them to connect to us? */ + } + + } +} + +static void oscar_xfer_start(GaimXfer *xfer) +{ + + gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_xfer_start\n"); + /* I'm pretty sure we don't need to do jack here. Nor Jill. */ +} + +static void oscar_xfer_end(GaimXfer *xfer) +{ + struct aim_oft_info *oft_info = xfer->data; + GaimConnection *gc = oft_info->sess->aux_data; + OscarData *od = gc->proto_data; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_xfer_end\n"); + + if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) { + oft_info->fh.nrecvd = gaim_xfer_get_bytes_sent(xfer); + aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_DONE, oft_info); + } + + aim_conn_kill(oft_info->sess, &oft_info->conn); + aim_oft_destroyinfo(oft_info); + xfer->data = NULL; + od->file_transfers = g_slist_remove(od->file_transfers, xfer); +} + +static void oscar_xfer_cancel_send(GaimXfer *xfer) +{ + struct aim_oft_info *oft_info = xfer->data; + GaimConnection *gc = oft_info->sess->aux_data; + OscarData *od = gc->proto_data; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "AAA - in oscar_xfer_cancel_send\n"); + + aim_im_sendch2_sendfile_cancel(oft_info->sess, oft_info); + + aim_conn_kill(oft_info->sess, &oft_info->conn); + aim_oft_destroyinfo(oft_info); + xfer->data = NULL; + od->file_transfers = g_slist_remove(od->file_transfers, xfer); +} + +static void oscar_xfer_cancel_recv(GaimXfer *xfer) +{ + struct aim_oft_info *oft_info = xfer->data; + GaimConnection *gc = oft_info->sess->aux_data; + OscarData *od = gc->proto_data; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "AAA - in oscar_xfer_cancel_recv\n"); + + aim_im_sendch2_sendfile_cancel(oft_info->sess, oft_info); + + aim_conn_kill(oft_info->sess, &oft_info->conn); + aim_oft_destroyinfo(oft_info); + xfer->data = NULL; + od->file_transfers = g_slist_remove(od->file_transfers, xfer); +} + +static void oscar_xfer_ack(GaimXfer *xfer, const char *buffer, size_t size) +{ + struct aim_oft_info *oft_info = xfer->data; + + if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { + /* + * If we're done sending, intercept the socket from the core ft code + * and wait for the other guy to send the "done" OFT packet. + */ + if (gaim_xfer_get_bytes_remaining(xfer) <= 0) { + gaim_input_remove(xfer->watcher); + xfer->watcher = gaim_input_add(xfer->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn); + xfer->fd = 0; + gaim_xfer_set_completed(xfer, TRUE); + } + } else if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) { + /* Update our rolling checksum. Like Walmart, yo. */ + oft_info->fh.recvcsum = aim_oft_checksum_chunk(buffer, size, oft_info->fh.recvcsum); + } +} + +static GaimXfer *oscar_find_xfer_by_cookie(GSList *fts, const char *ck) +{ + GaimXfer *xfer; + struct aim_oft_info *oft_info; + + while (fts) { + xfer = fts->data; + oft_info = xfer->data; + + if (oft_info && !strcmp(ck, oft_info->cookie)) + return xfer; + + fts = g_slist_next(fts); + } + + return NULL; +} + +static GaimXfer *oscar_find_xfer_by_conn(GSList *fts, aim_conn_t *conn) +{ + GaimXfer *xfer; + struct aim_oft_info *oft_info; + + while (fts) { + xfer = fts->data; + oft_info = xfer->data; + + if (oft_info && (conn == oft_info->conn)) + return xfer; + + fts = g_slist_next(fts); + } + + return NULL; +} + +static void oscar_ask_sendfile(GaimConnection *gc, const char *destsn) { + OscarData *od = (OscarData *)gc->proto_data; + GaimXfer *xfer; + struct aim_oft_info *oft_info; + + /* You want to send a file to someone else, you're so generous */ + + /* Build the file transfer handle */ + xfer = gaim_xfer_new(gaim_connection_get_account(gc), GAIM_XFER_SEND, destsn); + xfer->local_port = 5190; + + /* Create the oscar-specific data */ + oft_info = aim_oft_createinfo(od->sess, NULL, destsn, xfer->local_ip, xfer->local_port, 0, 0, NULL); + xfer->data = oft_info; + + /* Setup our I/O op functions */ + gaim_xfer_set_init_fnc(xfer, oscar_xfer_init); + gaim_xfer_set_start_fnc(xfer, oscar_xfer_start); + gaim_xfer_set_end_fnc(xfer, oscar_xfer_end); + gaim_xfer_set_cancel_send_fnc(xfer, oscar_xfer_cancel_send); + gaim_xfer_set_cancel_recv_fnc(xfer, oscar_xfer_cancel_recv); + gaim_xfer_set_ack_fnc(xfer, oscar_xfer_ack); + + /* Keep track of this transfer for later */ + od->file_transfers = g_slist_append(od->file_transfers, xfer); + + /* Now perform the request */ + gaim_xfer_request(xfer); +} + +static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + GaimAccount *account = gc->account; + aim_conn_t *bosconn; + char *host; int port; + int i, rc; + va_list ap; + struct aim_authresp_info *info; + + port = gaim_account_get_int(account, "port", FAIM_LOGIN_PORT); + + va_start(ap, fr); + info = va_arg(ap, struct aim_authresp_info *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "inside auth_resp (Screen name: %s)\n", info->sn); + + if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) { + char buf[256]; + switch (info->errorcode) { + case 0x05: + /* Incorrect nick/password */ + gc->wants_to_die = TRUE; + gaim_connection_error(gc, _("Incorrect nickname or password.")); + break; + case 0x11: + /* Suspended account */ + gc->wants_to_die = TRUE; + gaim_connection_error(gc, _("Your account is currently suspended.")); + break; + case 0x14: + /* service temporarily unavailable */ + gaim_connection_error(gc, _("The AOL Instant Messenger service is temporarily unavailable.")); + break; + case 0x18: + /* connecting too frequently */ + gc->wants_to_die = TRUE; + gaim_connection_error(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer.")); + break; + case 0x1c: + /* client too old */ + gc->wants_to_die = TRUE; + g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), GAIM_WEBSITE); + gaim_connection_error(gc, buf); + break; + default: + gaim_connection_error(gc, _("Authentication failed")); + break; + } + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Login Error Code 0x%04hx\n", info->errorcode); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Error URL: %s\n", info->errorurl); + od->killme = TRUE; + return 1; + } + + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "Reg status: %hu\n", info->regstatus); + + if (info->email) { + gaim_debug(GAIM_DEBUG_MISC, "oscar", "Email: %s\n", info->email); + } else { + gaim_debug(GAIM_DEBUG_MISC, "oscar", "Email is NULL\n"); + } + + gaim_debug(GAIM_DEBUG_MISC, "oscar", "BOSIP: %s\n", info->bosip); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Closing auth connection...\n"); + aim_conn_kill(sess, &fr->conn); + + bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL); + if (bosconn == NULL) { + gaim_connection_error(gc, _("Internal Error")); + od->killme = TRUE; + return 0; + } + + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0); + aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, gaim_bosrights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_CLIENTAUTORESP, gaim_parse_clientauto, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, gaim_parse_evilnotify, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, AIM_CB_LOK_ERROR, gaim_parse_searcherror, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, 0x0003, gaim_parse_searchreply, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parse_mtn, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parse_userinfo, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, gaim_parse_msgack, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0); + aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, gaim_icbm_param_info, 0); + aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, 0x0009, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, gaim_memrequest, 0); + aim_conn_addhandler(sess, bosconn, 0x0001, 0x000f, gaim_selfinfo, 0); + aim_conn_addhandler(sess, bosconn, 0x0001, 0x0021, oscar_icon_req,0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_POP, 0x0002, gaim_popup, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS, gaim_icqalias, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0); +#ifndef NOSSI + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ERROR, gaim_ssi_parseerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_NOLIST, gaim_ssi_parselist, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTH, gaim_ssi_authgiven, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREQ, gaim_ssi_authrequest, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREP, gaim_ssi_authreply, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ADDED, gaim_ssi_gotadded, 0); +#endif + + od->conn = bosconn; + for (i = 0; i < (int)strlen(info->bosip); i++) { + if (info->bosip[i] == ':') { + port = atoi(&(info->bosip[i+1])); + break; + } + } + host = g_strndup(info->bosip, i); + bosconn->status |= AIM_CONN_STATUS_INPROGRESS; + rc = gaim_proxy_connect(gc->account, host, port, oscar_bos_connect, gc); + g_free(host); + if (rc < 0) { + gaim_connection_error(gc, _("Could Not Connect")); + od->killme = TRUE; + return 0; + } + aim_sendcookie(sess, bosconn, info->cookielen, info->cookie); + gaim_input_remove(gc->inpa); + + gaim_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS); + + return 1; +} + +/* XXX - Should use gaim_url_fetch for the below stuff */ +struct pieceofcrap { + GaimConnection *gc; + unsigned long offset; + unsigned long len; + char *modname; + int fd; + aim_conn_t *conn; + unsigned int inpa; +}; + +static void damn_you(gpointer data, gint source, GaimInputCondition c) +{ + struct pieceofcrap *pos = data; + OscarData *od = pos->gc->proto_data; + char in = '\0'; + int x = 0; + unsigned char m[17]; + + while (read(pos->fd, &in, 1) == 1) { + if (in == '\n') + x++; + else if (in != '\r') + x = 0; + if (x == 2) + break; + in = '\0'; + } + if (in != '\n') { + char buf[256]; + g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. You may want to use TOC until " + "this is fixed. Check %s for updates."), GAIM_WEBSITE); + gaim_notify_warning(pos->gc, NULL, + _("Gaim was unable to get a valid AIM login hash."), + buf); + gaim_input_remove(pos->inpa); + close(pos->fd); + g_free(pos); + return; + } + read(pos->fd, m, 16); + m[16] = '\0'; + gaim_debug(GAIM_DEBUG_MISC, "oscar", "Sending hash: "); + for (x = 0; x < 16; x++) + gaim_debug(GAIM_DEBUG_MISC, NULL, "%02hhx ", (unsigned char)m[x]); + + gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); + gaim_input_remove(pos->inpa); + close(pos->fd); + aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH); + g_free(pos); +} + +static void straight_to_hell(gpointer data, gint source, GaimInputCondition cond) { + struct pieceofcrap *pos = data; + gchar *buf; + + pos->fd = source; + + if (source < 0) { + buf = g_strdup_printf(_("You may be disconnected shortly. You may want to use TOC until " + "this is fixed. Check %s for updates."), GAIM_WEBSITE); + gaim_notify_warning(pos->gc, NULL, + _("Gaim was unable to get a valid AIM login hash."), + buf); + g_free(buf); + if (pos->modname) + g_free(pos->modname); + g_free(pos); + return; + } + + buf = g_strdup_printf("GET " AIMHASHDATA "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n", + pos->offset, pos->len, pos->modname ? pos->modname : ""); + write(pos->fd, buf, strlen(buf)); + g_free(buf); + if (pos->modname) + g_free(pos->modname); + pos->inpa = gaim_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos); + return; +} + +/* size of icbmui.ocm, the largest module in AIM 3.5 */ +#define AIM_MAX_FILE_SIZE 98304 + +int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + struct pieceofcrap *pos; + fu32_t offset, len; + char *modname; + + va_start(ap, fr); + offset = va_arg(ap, fu32_t); + len = va_arg(ap, fu32_t); + modname = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "offset: %u, len: %u, file: %s\n", + offset, len, (modname ? modname : "aim.exe")); + + if (len == 0) { + gaim_debug(GAIM_DEBUG_MISC, "oscar", "len is 0, hashing NULL\n"); + aim_sendmemblock(sess, fr->conn, offset, len, NULL, + AIM_SENDMEMBLOCK_FLAG_ISREQUEST); + return 1; + } + /* uncomment this when you're convinced it's right. remember, it's been wrong before. + if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) { + char *buf; + int i = 8; + if (modname) + i += strlen(modname); + buf = g_malloc(i); + i = 0; + if (modname) { + memcpy(buf, modname, strlen(modname)); + i += strlen(modname); + } + buf[i++] = offset & 0xff; + buf[i++] = (offset >> 8) & 0xff; + buf[i++] = (offset >> 16) & 0xff; + buf[i++] = (offset >> 24) & 0xff; + buf[i++] = len & 0xff; + buf[i++] = (len >> 8) & 0xff; + buf[i++] = (len >> 16) & 0xff; + buf[i++] = (len >> 24) & 0xff; + gaim_debug(GAIM_DEBUG_MISC, "oscar", "len + offset is invalid, " + "hashing request\n"); + aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST); + g_free(buf); + return 1; + } + */ + + pos = g_new0(struct pieceofcrap, 1); + pos->gc = sess->aux_data; + pos->conn = fr->conn; + + pos->offset = offset; + pos->len = len; + pos->modname = modname ? g_strdup(modname) : NULL; + + if (gaim_proxy_connect(pos->gc->account, "gaim.sourceforge.net", 80, straight_to_hell, pos) != 0) { + char buf[256]; + if (pos->modname) + g_free(pos->modname); + g_free(pos); + g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. You may want to use TOC until " + "this is fixed. Check %s for updates."), GAIM_WEBSITE); + gaim_notify_warning(pos->gc, NULL, + _("Gaim was unable to get a valid login hash."), + buf); + } + + return 1; +} + +static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + GaimAccount *account = gaim_connection_get_account(gc); + GaimAccount *ac = gaim_connection_get_account(gc); +#if 0 + struct client_info_s info = {"gaim", 7, 3, 2003, "us", "en", 0x0004, 0x0000, 0x04b}; +#endif + va_list ap; + char *key; + + va_start(ap, fr); + key = va_arg(ap, char *); + va_end(ap); + + if (od->icq) { + struct client_info_s info = CLIENTINFO_ICQ_KNOWNGOOD; + aim_send_login(sess, fr->conn, gaim_account_get_username(ac), + gaim_account_get_password(account), &info, key); + } else { + struct client_info_s info = CLIENTINFO_AIM_KNOWNGOOD; + aim_send_login(sess, fr->conn, gaim_account_get_username(ac), + gaim_account_get_password(account), &info, key); + } + + gaim_connection_update_progress(gc, _("Password sent"), 2, OSCAR_CONNECT_STEPS); + + return 1; +} + +static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + struct chat_connection *chatcon; + static int id = 1; + + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_conv_chat_join, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_conv_chat_leave, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_conv_chat_info_update, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_conv_chat_incoming_msg, 0); + + aim_clientready(sess, fr->conn); + + chatcon = find_oscar_chat_by_conn(gc, fr->conn); + chatcon->id = id; + chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show); + + return 1; +} + +static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) { + + aim_conn_addhandler(sess, fr->conn, 0x000d, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0); + + aim_clientready(sess, fr->conn); + + aim_chatnav_reqrights(sess, fr->conn); + + return 1; +} + +static int conninitdone_email(aim_session_t *sess, aim_frame_t *fr, ...) { + + aim_conn_addhandler(sess, fr->conn, 0x0018, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_EML, AIM_CB_EML_MAILSTATUS, gaim_email_parseupdate, 0); + + aim_email_sendcookies(sess); + aim_email_activate(sess); + aim_clientready(sess, fr->conn); + + return 1; +} + +static int conninitdone_icon(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + + aim_conn_addhandler(sess, fr->conn, 0x0018, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ICO, AIM_CB_ICO_ERROR, gaim_icon_error, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ICO, AIM_CB_ICO_RESPONSE, gaim_icon_parseicon, 0); + + aim_clientready(sess, fr->conn); + + od->iconconnecting = FALSE; + + if (od->icontimer) + g_source_remove(od->icontimer); + od->icontimer = g_timeout_add(100, gaim_icon_timerfunc, gc); + + return 1; +} + +static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond) { + GaimConnection *gc = data; + OscarData *od; + aim_session_t *sess; + aim_conn_t *tstconn; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + close(source); + return; + } + + od = gc->proto_data; + sess = od->sess; + tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV); + tstconn->fd = source; + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to chatnav server\n"); + return; + } + + aim_conn_completeconnect(sess, tstconn); + od->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn); + gaim_debug(GAIM_DEBUG_INFO, "oscar", "chatnav: connected\n"); +} + +static void oscar_auth_connect(gpointer data, gint source, GaimInputCondition cond) +{ + GaimConnection *gc = data; + OscarData *od; + aim_session_t *sess; + aim_conn_t *tstconn; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + close(source); + return; + } + + od = gc->proto_data; + sess = od->sess; + tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH); + tstconn->fd = source; + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to authorizer\n"); + return; + } + + aim_conn_completeconnect(sess, tstconn); + od->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn); + gaim_debug(GAIM_DEBUG_INFO, "oscar", "admin: connected\n"); +} + +static void oscar_chat_connect(gpointer data, gint source, GaimInputCondition cond) +{ + struct chat_connection *ccon = data; + GaimConnection *gc = ccon->gc; + OscarData *od; + aim_session_t *sess; + aim_conn_t *tstconn; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + close(source); + g_free(ccon->show); + g_free(ccon->name); + g_free(ccon); + return; + } + + od = gc->proto_data; + sess = od->sess; + tstconn = ccon->conn; + tstconn->fd = source; + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + g_free(ccon->show); + g_free(ccon->name); + g_free(ccon); + return; + } + + aim_conn_completeconnect(sess, ccon->conn); + ccon->inpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn); + od->oscar_chats = g_slist_append(od->oscar_chats, ccon); +} + +static void oscar_email_connect(gpointer data, gint source, GaimInputCondition cond) { + GaimConnection *gc = data; + OscarData *od; + aim_session_t *sess; + aim_conn_t *tstconn; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + close(source); + return; + } + + od = gc->proto_data; + sess = od->sess; + tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_EMAIL); + tstconn->fd = source; + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to email server\n"); + return; + } + + aim_conn_completeconnect(sess, tstconn); + od->emlpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "email: connected\n"); +} + +static void oscar_icon_connect(gpointer data, gint source, GaimInputCondition cond) { + GaimConnection *gc = data; + OscarData *od; + aim_session_t *sess; + aim_conn_t *tstconn; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + close(source); + return; + } + + od = gc->proto_data; + sess = od->sess; + tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_ICON); + tstconn->fd = source; + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to icon server\n"); + return; + } + + aim_conn_completeconnect(sess, tstconn); + od->icopa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn); + gaim_debug(GAIM_DEBUG_INFO, "oscar", "icon: connected\n"); +} + +/* Hrmph. I don't know how to make this look better. --mid */ +static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + GaimAccount *account = gaim_connection_get_account(gc); + aim_conn_t *tstconn; + int i; + char *host; + int port; + va_list ap; + struct aim_redirect_data *redir; + + port = gaim_account_get_int(account, "port", FAIM_LOGIN_PORT); + + va_start(ap, fr); + redir = va_arg(ap, struct aim_redirect_data *); + va_end(ap); + + for (i = 0; i < (int)strlen(redir->ip); i++) { + if (redir->ip[i] == ':') { + port = atoi(&(redir->ip[i+1])); + break; + } + } + host = g_strndup(redir->ip, i); + + switch(redir->group) { + case 0x7: /* Authorizer */ + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Reconnecting with authorizor...\n"); + tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL); + if (tstconn == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to reconnect with authorizer\n"); + g_free(host); + return 1; + } + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0); + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_admin, 0); + + tstconn->status |= AIM_CONN_STATUS_INPROGRESS; + if (gaim_proxy_connect(account, host, port, oscar_auth_connect, gc) != 0) { + aim_conn_kill(sess, &tstconn); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to reconnect with authorizer\n"); + g_free(host); + return 1; + } + aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie); + break; + + case 0xd: /* ChatNav */ + tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL); + if (tstconn == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to chatnav server\n"); + g_free(host); + return 1; + } + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0); + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0); + + tstconn->status |= AIM_CONN_STATUS_INPROGRESS; + if (gaim_proxy_connect(account, host, port, oscar_chatnav_connect, gc) != 0) { + aim_conn_kill(sess, &tstconn); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to chatnav server\n"); + g_free(host); + return 1; + } + aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie); + break; + + case 0xe: { /* Chat */ + struct chat_connection *ccon; + + tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL); + if (tstconn == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to chat server\n"); + g_free(host); + return 1; + } + + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0); + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0); + + ccon = g_new0(struct chat_connection, 1); + ccon->conn = tstconn; + ccon->gc = gc; + ccon->fd = -1; + ccon->name = g_strdup(redir->chat.room); + ccon->exchange = redir->chat.exchange; + ccon->instance = redir->chat.instance; + ccon->show = extract_name(redir->chat.room); + + ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS; + if (gaim_proxy_connect(account, host, port, oscar_chat_connect, ccon) != 0) { + aim_conn_kill(sess, &tstconn); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to chat server\n"); + g_free(host); + g_free(ccon->show); + g_free(ccon->name); + g_free(ccon); + return 1; + } + aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Connected to chat room %s exchange %hu\n", + ccon->name, ccon->exchange); + } break; + + case 0x0010: { /* icon */ + if (!(tstconn = aim_newconn(sess, AIM_CONN_TYPE_ICON, NULL))) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to icon server\n"); + g_free(host); + return 1; + } + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0); + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_icon, 0); + + tstconn->status |= AIM_CONN_STATUS_INPROGRESS; + if (gaim_proxy_connect(account, host, port, oscar_icon_connect, gc) != 0) { + aim_conn_kill(sess, &tstconn); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to icon server\n"); + g_free(host); + return 1; + } + aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie); + } break; + + case 0x0018: { /* email */ + if (!(tstconn = aim_newconn(sess, AIM_CONN_TYPE_EMAIL, NULL))) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to email server\n"); + g_free(host); + return 1; + } + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0); + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_email, 0); + + tstconn->status |= AIM_CONN_STATUS_INPROGRESS; + if (gaim_proxy_connect(account, host, port, oscar_email_connect, gc) != 0) { + aim_conn_kill(sess, &tstconn); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unable to connect to email server\n"); + g_free(host); + return 1; + } + aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie); + } break; + + default: /* huh? */ + gaim_debug(GAIM_DEBUG_WARNING, "oscar", + "got redirect for unknown service 0x%04hx\n", + redir->group); + break; + } + + g_free(host); + return 1; +} + +static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + struct buddyinfo *bi; + time_t time_idle = 0, signon = 0; + int type = 0; + int caps = 0; + va_list ap; + aim_userinfo_t *info; + + va_start(ap, fr); + info = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) + caps = info->capabilities; + if (info->flags & AIM_FLAG_ACTIVEBUDDY) + type |= UC_AB; + if (caps & AIM_CAPS_HIPTOP) + type |= UC_HIPTOP; + + if (info->present & AIM_USERINFO_PRESENT_FLAGS) { + if (info->flags & AIM_FLAG_UNCONFIRMED) + type |= UC_UNCONFIRMED; + if (info->flags & AIM_FLAG_ADMINISTRATOR) + type |= UC_ADMIN; + if (info->flags & AIM_FLAG_AOL) + type |= UC_AOL; + if (info->flags & AIM_FLAG_FREE) + type |= UC_NORMAL; + if (info->flags & AIM_FLAG_AWAY) + type |= UC_UNAVAILABLE; + if (info->flags & AIM_FLAG_WIRELESS) + type |= UC_WIRELESS; + } + if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) { + type = (info->icqinfo.status << 16); + if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) && + (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) { + type |= UC_UNAVAILABLE; + } + } + + if (caps & AIM_CAPS_ICQ_DIRECT) + caps ^= AIM_CAPS_ICQ_DIRECT; + + if (info->present & AIM_USERINFO_PRESENT_IDLE) { + time(&time_idle); + time_idle -= info->idletime*60; + } + + if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) + signon = info->onlinesince; + else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) + signon = time(NULL) - info->sessionlen; + + if (!aim_sncmp(gaim_account_get_username(gaim_connection_get_account(gc)), info->sn)) + gaim_connection_set_display_name(gc, info->sn); + + bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(gc->account, info->sn)); + if (!bi) { + bi = g_new0(struct buddyinfo, 1); + g_hash_table_insert(od->buddyinfo, g_strdup(gaim_normalize(gc->account, info->sn)), bi); + } + bi->typingnot = FALSE; + bi->ico_informed = FALSE; + bi->ipaddr = info->icqinfo.ipaddr; + + /* Available message stuff */ + free(bi->availmsg); + if (info->avail != NULL) + if (info->avail_encoding) { + gchar *enc = g_strdup_printf("charset=\"%s\"", info->avail_encoding); + bi->availmsg = oscar_encoding_to_utf8(enc, info->avail, info->avail_len); + g_free(enc); + } else { + /* No explicit encoding means utf8. Yay. */ + bi->availmsg = g_strdup(info->avail); + } + else + bi->availmsg = NULL; + + /* Server stored icon stuff */ + if (info->iconcsumlen) { + const char *filename = NULL, *saved_b16 = NULL; + char *b16 = NULL; + GaimBuddy *b = NULL; + + b16 = gaim_base16_encode(info->iconcsum, info->iconcsumlen); + b = gaim_find_buddy(gc->account, info->sn); + /* + * If for some reason the checksum is valid, but cached file is not.. + * we want to know. + */ + filename = gaim_buddy_get_setting(b, "buddy_icon"); + if (filename != NULL) { + if (g_file_test(filename, G_FILE_TEST_EXISTS)) + saved_b16 = gaim_buddy_get_setting(b, "icon_checksum"); + } else + saved_b16 = NULL; + + if (!b16 || !saved_b16 || strcmp(b16, saved_b16)) { + GSList *cur = od->requesticon; + while (cur && aim_sncmp((char *)cur->data, info->sn)) + cur = cur->next; + if (!cur) { + od->requesticon = g_slist_append(od->requesticon, g_strdup(gaim_normalize(gc->account, info->sn))); + if (od->icontimer) + g_source_remove(od->icontimer); + od->icontimer = g_timeout_add(500, gaim_icon_timerfunc, gc); + } + } + g_free(b16); + } + + serv_got_update(gc, info->sn, 1, (info->warnlevel/10.0) + 0.5, signon, time_idle, type); + + return 1; +} + +static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + va_list ap; + aim_userinfo_t *info; + + va_start(ap, fr); + info = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + serv_got_update(gc, info->sn, 0, 0, 0, 0, 0); + + g_hash_table_remove(od->buddyinfo, gaim_normalize(gc->account, info->sn)); + + return 1; +} + +static void cancel_direct_im(struct ask_direct *d) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", "Freeing DirectIM prompts.\n"); + + g_free(d->sn); + g_free(d); +} + +static void oscar_odc_callback(gpointer data, gint source, GaimInputCondition condition) { + struct direct_im *dim = data; + GaimConnection *gc = dim->gc; + OscarData *od = gc->proto_data; + GaimConversation *cnv; + char buf[256]; + struct sockaddr name; + socklen_t name_len = 1; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + g_free(dim); + return; + } + + if (source < 0) { + g_free(dim); + return; + } + + dim->conn->fd = source; + aim_conn_completeconnect(od->sess, dim->conn); + cnv = gaim_conversation_new(GAIM_CONV_IM, dim->gc->account, dim->name); + + /* This is the best way to see if we're connected or not */ + if (getpeername(source, &name, &name_len) == 0) { + g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), dim->name); + dim->connected = TRUE; + gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL)); + } + od->direct_ims = g_slist_append(od->direct_ims, dim); + + dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn); +} + +/* BBB */ +/* + * This is called after a remote AIM user has connected to us. We + * want to do some voodoo with the socket file descriptors, add a + * callback or two, and then send the AIM_CB_OFT_PROMPT. + */ +static int oscar_sendfile_estblsh(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = (OscarData *)gc->proto_data; + GaimXfer *xfer; + struct aim_oft_info *oft_info; + va_list ap; + aim_conn_t *conn, *listenerconn; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "AAA - in oscar_sendfile_estblsh\n"); + va_start(ap, fr); + conn = va_arg(ap, aim_conn_t *); + listenerconn = va_arg(ap, aim_conn_t *); + va_end(ap); + + if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, listenerconn))) + return 1; + + if (!(oft_info = xfer->data)) + return 1; + + /* Stop watching listener conn; watch transfer conn instead */ + gaim_input_remove(xfer->watcher); + aim_conn_kill(sess, &listenerconn); + + oft_info->conn = conn; + xfer->fd = oft_info->conn->fd; + + aim_conn_addhandler(sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK, oscar_sendfile_ack, 0); + aim_conn_addhandler(sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE, oscar_sendfile_done, 0); + xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn); + + /* Inform the other user that we are connected and ready to transfer */ + aim_oft_sendheader(sess, AIM_CB_OFT_PROMPT, oft_info); + + return 0; +} + +/* + * This is the gaim callback passed to gaim_proxy_connect when connecting to another AIM + * user in order to transfer a file. + */ +static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition) { + GaimXfer *xfer; + struct aim_oft_info *oft_info; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "AAA - in oscar_sendfile_connected\n"); + if (!(xfer = data)) + return; + if (!(oft_info = xfer->data)) + return; + if (source < 0) + return; + + xfer->fd = source; + oft_info->conn->fd = source; + + aim_conn_completeconnect(oft_info->sess, oft_info->conn); + xfer->watcher = gaim_input_add(xfer->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn); + + /* Inform the other user that we are connected and ready to transfer */ + aim_im_sendch2_sendfile_accept(oft_info->sess, oft_info); + + return; +} + +/* + * This is called when a buddy sends us some file info. This happens when they + * are sending a file to you, and you have just established a connection to them. + * You should send them the exact same info except use the real cookie. We also + * get like totally ready to like, receive the file, kay? + */ +static int oscar_sendfile_prompt(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + GaimXfer *xfer; + struct aim_oft_info *oft_info; + va_list ap; + aim_conn_t *conn; + fu8_t *cookie; + struct aim_fileheader_t *fh; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "AAA - in oscar_sendfile_prompt\n"); + va_start(ap, fr); + conn = va_arg(ap, aim_conn_t *); + cookie = va_arg(ap, fu8_t *); + fh = va_arg(ap, struct aim_fileheader_t *); + va_end(ap); + + if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn))) + return 1; + + if (!(oft_info = xfer->data)) + return 1; + + /* We want to stop listening with a normal thingy */ + gaim_input_remove(xfer->watcher); + xfer->watcher = 0; + + /* They sent us some information about the file they're sending */ + memcpy(&oft_info->fh, fh, sizeof(*fh)); + + /* Fill in the cookie */ + memcpy(&oft_info->fh.bcookie, oft_info->cookie, 8); + + /* XXX - convert the name from UTF-8 to UCS-2 if necessary, and pass the encoding to the call below */ + aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_ACK, oft_info); + gaim_xfer_start(xfer, xfer->fd, NULL, 0); + + return 0; +} + +/* + * We are sending a file to someone else. They have just acknowledged our + * prompt, so we want to start sending data like there's no tomorrow. + */ +static int oscar_sendfile_ack(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + GaimXfer *xfer; + va_list ap; + aim_conn_t *conn; + fu8_t *cookie; + struct aim_fileheader_t *fh; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_sendfile_ack\n"); + va_start(ap, fr); + conn = va_arg(ap, aim_conn_t *); + cookie = va_arg(ap, fu8_t *); + fh = va_arg(ap, struct aim_fileheader_t *); + va_end(ap); + + if (!(xfer = oscar_find_xfer_by_cookie(od->file_transfers, cookie))) + return 1; + + /* We want to stop listening with a normal thingy */ + gaim_input_remove(xfer->watcher); + xfer->watcher = 0; + + gaim_xfer_start(xfer, xfer->fd, NULL, 0); + + return 0; +} + +/* + * We just sent a file to someone. They said they got it and everything, + * so we can close our direct connection and what not. + */ +static int oscar_sendfile_done(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + GaimXfer *xfer; + va_list ap; + aim_conn_t *conn; + fu8_t *cookie; + struct aim_fileheader_t *fh; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_sendfile_done\n"); + va_start(ap, fr); + conn = va_arg(ap, aim_conn_t *); + cookie = va_arg(ap, fu8_t *); + fh = va_arg(ap, struct aim_fileheader_t *); + va_end(ap); + + if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn))) + return 1; + + xfer->fd = conn->fd; + gaim_xfer_end(xfer); + + return 0; +} + +static void accept_direct_im(struct ask_direct *d) { + GaimConnection *gc = d->gc; + OscarData *od; + struct direct_im *dim; + char *host; int port = 4443; + int i, rc; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + cancel_direct_im(d); + return; + } + + od = (OscarData *)gc->proto_data; + gaim_debug(GAIM_DEBUG_INFO, "oscar", "Accepted DirectIM.\n"); + + dim = find_direct_im(od, d->sn); + if (dim) { + cancel_direct_im(d); /* 40 */ + return; + } + dim = g_new0(struct direct_im, 1); + dim->gc = d->gc; + g_snprintf(dim->name, sizeof dim->name, "%s", d->sn); + + dim->conn = aim_odc_connect(od->sess, d->sn, NULL, d->cookie); + if (!dim->conn) { + g_free(dim); + cancel_direct_im(d); + return; + } + + aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, + gaim_odc_incoming, 0); + aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, + gaim_odc_typing, 0); + aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, + gaim_odc_update_ui, 0); + for (i = 0; i < (int)strlen(d->ip); i++) { + if (d->ip[i] == ':') { + port = atoi(&(d->ip[i+1])); + break; + } + } + host = g_strndup(d->ip, i); + dim->conn->status |= AIM_CONN_STATUS_INPROGRESS; + rc = gaim_proxy_connect(gc->account, host, port, oscar_odc_callback, dim); + g_free(host); + if (rc < 0) { + aim_conn_kill(od->sess, &dim->conn); + g_free(dim); + cancel_direct_im(d); + return; + } + + cancel_direct_im(d); + + return; +} + +static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + gchar *tmp; + GaimConvImFlags flags = 0; + gsize convlen; + GError *err = NULL; + struct buddyinfo *bi; + const char *iconfile; + + bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(gc->account, userinfo->sn)); + if (!bi) { + bi = g_new0(struct buddyinfo, 1); + g_hash_table_insert(od->buddyinfo, g_strdup(gaim_normalize(gc->account, userinfo->sn)), bi); + } + + if (args->icbmflags & AIM_IMFLAGS_AWAY) + flags |= GAIM_CONV_IM_AUTO_RESP; + + if (args->icbmflags & AIM_IMFLAGS_TYPINGNOT) + bi->typingnot = TRUE; + else + bi->typingnot = FALSE; + + if ((args->icbmflags & AIM_IMFLAGS_HASICON) && (args->iconlen) && (args->iconsum) && (args->iconstamp)) { + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "%s has an icon\n", userinfo->sn); + if ((args->iconlen != bi->ico_len) || (args->iconsum != bi->ico_csum) || (args->iconstamp != bi->ico_time)) { + bi->ico_need = TRUE; + bi->ico_len = args->iconlen; + bi->ico_csum = args->iconsum; + bi->ico_time = args->iconstamp; + } + } + + if ((iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc))) && + (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) && !bi->ico_sent && bi->ico_informed) { + FILE *file; + struct stat st; + + if (!stat(iconfile, &st)) { + char *buf = g_malloc(st.st_size); + file = fopen(iconfile, "rb"); + if (file) { + int len = fread(buf, 1, st.st_size, file); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Sending buddy icon to %s (%d bytes, " + "%lu reported)\n", + userinfo->sn, len, st.st_size); + aim_im_sendch2_icon(sess, userinfo->sn, buf, st.st_size, + st.st_mtime, aimutil_iconsum(buf, st.st_size)); + fclose(file); + } else + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Can't open buddy icon file!\n"); + g_free(buf); + } else + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Can't stat buddy icon file!\n"); + } + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "Character set is %hu %hu\n", args->charset, args->charsubset); + if (args->icbmflags & AIM_IMFLAGS_UNICODE) { + /* This message is marked as UNICODE, so we have to + * convert it to utf-8 before handing it to the gaim core. + * This conversion should *never* fail, if it does it + * means that either the incoming ICBM is corrupted or + * there is something we don't understand about it. + * For the record, AIM Unicode is big-endian UCS-2 */ + + gaim_debug(GAIM_DEBUG_INFO, "oscar", "Received UNICODE IM\n"); + + if (!args->msg || !args->msglen) + return 1; + + tmp = g_convert(args->msg, args->msglen, "UTF-8", "UCS-2BE", NULL, &convlen, &err); + if (err) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Unicode IM conversion: %s\n", err->message); + tmp = g_strdup(_("(There was an error receiving this message)")); + g_error_free(err); + } + } else { + /* This will get executed for both AIM_IMFLAGS_ISO_8859_1 and + * unflagged messages, which are ASCII. That's OK because + * ASCII is a strict subset of ISO-8859-1; this should + * help with compatibility with old, broken versions of + * gaim (everything before 0.60) and other broken clients + * that will happily send ISO-8859-1 without marking it as + * such */ + if (args->icbmflags & AIM_IMFLAGS_ISO_8859_1) + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Received ISO-8859-1 IM\n"); + + if (!args->msg || !args->msglen) + return 1; + + tmp = g_convert(args->msg, args->msglen, "UTF-8", "ISO-8859-1", NULL, &convlen, &err); + if (err) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ISO-8859-1 IM conversion: %s\n", err->message); + tmp = g_strdup(_("(There was an error receiving this message)")); + g_error_free(err); + } + } + + /* gaim_str_strip_cr(tmp); */ + serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL)); + g_free(tmp); + + return 1; +} + +static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + const char *username = gaim_account_get_username(gaim_connection_get_account(gc)); + + if (!args) + return 0; + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "rendezvous with %s, status is %hu\n", + userinfo->sn, args->status); + + if (args->reqclass & AIM_CAPS_CHAT) { + char *name; + GHashTable *components; + + if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange || !args->msg) + return 1; + components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + g_free); + name = extract_name(args->info.chat.roominfo.name); + g_hash_table_replace(components, g_strdup("room"), g_strdup(name ? name : args->info.chat.roominfo.name)); + g_hash_table_replace(components, g_strdup("exchange"), g_strdup_printf("%d", args->info.chat.roominfo.exchange)); + serv_got_chat_invite(gc, + name ? name : args->info.chat.roominfo.name, + userinfo->sn, + args->msg, + components); + if (name) + g_free(name); + } else if (args->reqclass & AIM_CAPS_SENDFILE) { +/* BBB */ + if (args->status == AIM_RENDEZVOUS_PROPOSE) { + /* Someone wants to send a file (or files) to us */ + GaimXfer *xfer; + struct aim_oft_info *oft_info; + + if (!args->cookie || !args->port || !args->verifiedip || + !args->info.sendfile.filename || !args->info.sendfile.totsize || + !args->info.sendfile.totfiles || !args->reqclass) { + gaim_debug(GAIM_DEBUG_WARNING, "oscar", + "%s tried to send you a file with incomplete " + "information.\n", userinfo->sn); + if (args->proxyip) + gaim_debug(GAIM_DEBUG_WARNING, "oscar", + "IP for a proxy server was given. Gaim " + "does not support this yet.\n"); + return 1; + } + + if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR) { + /* last char of the ft req is a star, they are sending us a + * directory -- remove the star and trailing slash so we dont save + * directories that look like 'dirname\*' -- arl */ + char *tmp = strrchr(args->info.sendfile.filename, '\\'); + if (tmp && (tmp[1] == '*')) { + tmp[0] = '\0'; + } + } + + /* Build the file transfer handle */ + xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, userinfo->sn); + xfer->remote_ip = g_strdup(args->verifiedip); + xfer->remote_port = args->port; + gaim_xfer_set_filename(xfer, args->info.sendfile.filename); + gaim_xfer_set_size(xfer, args->info.sendfile.totsize); + + /* Create the oscar-specific data */ + oft_info = aim_oft_createinfo(od->sess, args->cookie, userinfo->sn, args->clientip, xfer->remote_port, 0, 0, NULL); + if (args->proxyip) + oft_info->proxyip = g_strdup(args->proxyip); + if (args->verifiedip) + oft_info->verifiedip = g_strdup(args->verifiedip); + xfer->data = oft_info; + + /* Setup our I/O op functions */ + gaim_xfer_set_init_fnc(xfer, oscar_xfer_init); + gaim_xfer_set_start_fnc(xfer, oscar_xfer_start); + gaim_xfer_set_end_fnc(xfer, oscar_xfer_end); + gaim_xfer_set_cancel_send_fnc(xfer, oscar_xfer_cancel_send); + gaim_xfer_set_cancel_recv_fnc(xfer, oscar_xfer_cancel_recv); + gaim_xfer_set_ack_fnc(xfer, oscar_xfer_ack); + + /* + * XXX - Should do something with args->msg, args->encoding, and args->language + * probably make it apply to all ch2 messages. + */ + + /* Keep track of this transfer for later */ + od->file_transfers = g_slist_append(od->file_transfers, xfer); + + /* Now perform the request */ + gaim_xfer_request(xfer); + } else if (args->status == AIM_RENDEZVOUS_CANCEL) { + /* The other user wants to cancel a file transfer */ + GaimXfer *xfer; + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "AAA - File transfer canceled by remote user\n"); + if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, args->cookie))) + gaim_xfer_cancel_remote(xfer); + } else if (args->status == AIM_RENDEZVOUS_ACCEPT) { + /* + * This gets sent by the receiver of a file + * as they connect directly to us. If we don't + * get this, then maybe a third party connected + * to us, and we shouldn't send them anything. + */ + } else { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "unknown rendezvous status!\n"); + } + } else if (args->reqclass & AIM_CAPS_GETFILE) { + } else if (args->reqclass & AIM_CAPS_VOICE) { + } else if (args->reqclass & AIM_CAPS_BUDDYICON) { + gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc), + userinfo->sn, args->info.icon.icon, + args->info.icon.length); + } else if (args->reqclass & AIM_CAPS_DIRECTIM) { + struct ask_direct *d = g_new0(struct ask_direct, 1); + char buf[256]; + + if (!args->verifiedip) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "directim kill blocked (%s)\n", userinfo->sn); + return 1; + } + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "%s received direct im request from %s (%s)\n", + username, userinfo->sn, args->verifiedip); + + d->gc = gc; + d->sn = g_strdup(userinfo->sn); + strncpy(d->ip, args->verifiedip, sizeof(d->ip)); + memcpy(d->cookie, args->cookie, 8); + g_snprintf(buf, sizeof buf, _("%s has just asked to directly connect to %s"), userinfo->sn, username); + + gaim_request_action(gc, NULL, buf, + _("This requires a direct connection between " + "the two computers and is necessary for IM " + "Images. Because your IP address will be " + "revealed, this may be considered a privacy " + "risk."), 0, d, 2, + _("Connect"), G_CALLBACK(accept_direct_im), + _("Cancel"), G_CALLBACK(cancel_direct_im)); + } else { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Unknown reqclass %hu\n", args->reqclass); + } + + return 1; +} + +/* + * Authorization Functions + * Most of these are callbacks from dialogs. They're used by both + * methods of authorization (SSI and old-school channel 4 ICBM) + */ +/* When you ask other people for authorization */ +static void gaim_auth_request(struct name_data *data, char *msg) { + GaimConnection *gc = data->gc; + + if (g_list_find(gaim_connections_get_all(), gc)) { + OscarData *od = gc->proto_data; + GaimBuddy *buddy = gaim_find_buddy(gc->account, data->name); + GaimGroup *group = gaim_find_buddys_group(buddy); + if (buddy && group) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding buddy %s to group %s\n", + buddy->name, group->name); + aim_ssi_sendauthrequest(od->sess, data->name, msg ? msg : _("Please authorize me so I can add you to my buddy list.")); + if (!aim_ssi_itemlist_finditem(od->sess->ssi.local, group->name, buddy->name, AIM_SSI_TYPE_BUDDY)) + aim_ssi_addbuddy(od->sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 1); + } + } +} + +static void gaim_auth_request_msgprompt(struct name_data *data) { + gaim_request_input(data->gc, NULL, _("Authorization Request Message:"), + NULL, _("Please authorize me!"), TRUE, FALSE, + _("OK"), G_CALLBACK(gaim_auth_request), + _("Cancel"), G_CALLBACK(oscar_free_name_data), + data); +} + +static void gaim_auth_dontrequest(struct name_data *data) { + GaimConnection *gc = data->gc; + + if (g_list_find(gaim_connections_get_all(), gc)) { + /* OscarData *od = gc->proto_data; */ + /* XXX - Take the buddy out of our buddy list */ + } + + oscar_free_name_data(data); +} + +static void gaim_auth_sendrequest(GaimConnection *gc, const char *name) { + struct name_data *data = g_new(struct name_data, 1); + GaimBuddy *buddy; + gchar *dialog_msg, *nombre; + + buddy = gaim_find_buddy(gc->account, name); + if (buddy && (gaim_get_buddy_alias_only(buddy))) + nombre = g_strdup_printf("%s (%s)", name, gaim_get_buddy_alias_only(buddy)); + else + nombre = NULL; + + dialog_msg = g_strdup_printf(_("The user %s requires authorization before being added to a buddy list. Do you want to send an authorization request?"), (nombre ? nombre : name)); + data->gc = gc; + data->name = g_strdup(name); + data->nick = NULL; + + gaim_request_action(gc, NULL, _("Request Authorization"), dialog_msg, + 0, data, 2, + _("Request Authorization"), + G_CALLBACK(gaim_auth_request_msgprompt), + _("Cancel"), G_CALLBACK(gaim_auth_dontrequest)); + + g_free(dialog_msg); + g_free(nombre); +} + +/* When other people ask you for authorization */ +static void gaim_auth_grant(struct name_data *data) { + GaimConnection *gc = data->gc; + + if (g_list_find(gaim_connections_get_all(), gc)) { + OscarData *od = gc->proto_data; +#ifdef NOSSI + GaimBuddy *buddy; + gchar message; + message = 0; + buddy = gaim_find_buddy(gc->account, data->name); + aim_im_sendch4(od->sess, data->name, AIM_ICQMSG_AUTHGRANTED, &message); + gaim_account_notify_added(gc->account, NULL, data->name, (buddy ? gaim_get_buddy_alias_only(buddy) : NULL), NULL); +#else + aim_ssi_sendauthreply(od->sess, data->name, 0x01, NULL); +#endif + } + + oscar_free_name_data(data); +} + +/* When other people ask you for authorization */ +static void gaim_auth_dontgrant(struct name_data *data, char *msg) { + GaimConnection *gc = data->gc; + + if (g_list_find(gaim_connections_get_all(), gc)) { + OscarData *od = gc->proto_data; +#ifdef NOSSI + aim_im_sendch4(od->sess, data->name, AIM_ICQMSG_AUTHDENIED, msg ? msg : _("No reason given.")); +#else + aim_ssi_sendauthreply(od->sess, data->name, 0x00, msg ? msg : _("No reason given.")); +#endif + } +} + +static void gaim_auth_dontgrant_msgprompt(struct name_data *data) { + gaim_request_input(data->gc, NULL, _("Authorization Denied Message:"), + NULL, _("No reason given."), TRUE, FALSE, + _("OK"), G_CALLBACK(gaim_auth_dontgrant), + _("Cancel"), G_CALLBACK(oscar_free_name_data), + data); +} + +/* When someone sends you buddies */ +static void gaim_icq_buddyadd(struct name_data *data) { + GaimConnection *gc = data->gc; + + if (g_list_find(gaim_connections_get_all(), gc)) { + gaim_blist_request_add_buddy(gaim_connection_get_account(gc), data->name, NULL, data->nick); + } + + oscar_free_name_data(data); +} + +static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args, time_t t) { + GaimConnection *gc = sess->aux_data; + gchar **msg1, **msg2; + GError *err = NULL; + int i, numtoks; + + if (!args->type || !args->msg || !args->uin) + return 1; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Received a channel 4 message of type 0x%02hhx.\n", args->type); + + /* Split up the message at the delimeter character, then convert each string to UTF-8 */ + msg1 = g_strsplit(args->msg, "\376", 0); + for (numtoks=0; msg1[numtoks]; numtoks++); + msg2 = (gchar **)g_malloc((numtoks+1)*sizeof(gchar *)); + for (i=0; msg1[i]; i++) { + gaim_str_strip_cr(msg1[i]); + msg2[i] = g_convert(msg1[i], strlen(msg1[i]), "UTF-8", "ISO-8859-1", NULL, NULL, &err); + if (err) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Error converting a string from ISO-8859-1 to " + "UTF-8 in oscar ICBM channel 4 parsing\n"); + g_error_free(err); + } + } + msg2[i] = NULL; + + switch (args->type) { + case 0x01: { /* MacICQ message or basic offline message */ + if (i >= 1) { + gchar *uin = g_strdup_printf("%u", args->uin); + if (t) { /* This is an offline message */ + /* I think this timestamp is in UTC, or something */ + serv_got_im(gc, uin, msg2[0], 0, t); + } else { /* This is a message from MacICQ/Miranda */ + serv_got_im(gc, uin, msg2[0], 0, time(NULL)); + } + g_free(uin); + } + } break; + + case 0x04: { /* Someone sent you a URL */ + if (i >= 2) { + if (msg2[1] != NULL) { + gchar *uin = g_strdup_printf("%u", args->uin); + gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>", + msg2[1], + (msg2[0] && msg2[0][0]) ? msg2[0] : msg2[1]); + serv_got_im(gc, uin, message, 0, time(NULL)); + g_free(uin); + g_free(message); + } + } + } break; + + case 0x06: { /* Someone requested authorization */ + if (i >= 6) { + struct name_data *data = g_new(struct name_data, 1); + gchar *dialog_msg = g_strdup_printf(_("The user %u wants to add you to their buddy list for the following reason:\n%s"), args->uin, msg2[5] ? msg2[5] : _("No reason given.")); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Received an authorization request from UIN %u\n", + args->uin); + data->gc = gc; + data->name = g_strdup_printf("%u", args->uin); + data->nick = NULL; + + gaim_request_action(gc, NULL, _("Authorization Request"), + dialog_msg, 0, data, 2, + _("Authorize"), + G_CALLBACK(gaim_auth_grant), + _("Deny"), + G_CALLBACK(gaim_auth_dontgrant_msgprompt)); + g_free(dialog_msg); + } + } break; + + case 0x07: { /* Someone has denied you authorization */ + if (i >= 1) { + gchar *dialog_msg = g_strdup_printf(_("The user %u has denied your request to add them to your buddy list for the following reason:\n%s"), args->uin, msg2[0] ? msg2[0] : _("No reason given.")); + gaim_notify_info(gc, NULL, _("ICQ authorization denied."), + dialog_msg); + g_free(dialog_msg); + } + } break; + + case 0x08: { /* Someone has granted you authorization */ + gchar *dialog_msg = g_strdup_printf(_("The user %u has granted your request to add them to your buddy list."), args->uin); + gaim_notify_info(gc, NULL, "ICQ authorization accepted.", + dialog_msg); + g_free(dialog_msg); + } break; + + case 0x09: { /* Message from the Godly ICQ server itself, I think */ + if (i >= 5) { + gchar *dialog_msg = g_strdup_printf(_("You have received a special message\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]); + gaim_notify_info(gc, NULL, "ICQ Server Message", dialog_msg); + g_free(dialog_msg); + } + } break; + + case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */ + if (i >= 6) { + gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ page\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]); + gaim_notify_info(gc, NULL, "ICQ Page", dialog_msg); + g_free(dialog_msg); + } + } break; + + case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */ + if (i >= 6) { + gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ email from %s [%s]\n\nMessage is:\n%s"), msg2[0], msg2[3], msg2[5]); + gaim_notify_info(gc, NULL, "ICQ Email", dialog_msg); + g_free(dialog_msg); + } + } break; + + case 0x12: { + /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */ + /* Someone added you to their buddy list? */ + } break; + + case 0x13: { /* Someone has sent you some ICQ buddies */ + int i, num; + gchar **text; + text = g_strsplit(args->msg, "\376", 0); + if (text) { + num = 0; + for (i=0; i<strlen(text[0]); i++) + num = num*10 + text[0][i]-48; + for (i=0; i<num; i++) { + struct name_data *data = g_new(struct name_data, 1); + gchar *message = g_strdup_printf(_("ICQ user %u has sent you a buddy: %s (%s)"), args->uin, text[i*2+2], text[i*2+1]); + data->gc = gc; + data->name = g_strdup(text[i*2+1]); + data->nick = g_strdup(text[i*2+2]); + + gaim_request_action(gc, NULL, message, + _("Do you want to add this buddy " + "to your buddy list?"), + 0, data, 2, + _("Add"), G_CALLBACK(gaim_icq_buddyadd), + _("Decline"), G_CALLBACK(oscar_free_name_data)); + g_free(message); + } + g_strfreev(text); + } + } break; + + case 0x1a: { /* Someone has sent you a greeting card or requested buddies? */ + /* This is boring and silly. */ + } break; + + default: { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Received a channel 4 message of unknown type " + "(type 0x%02hhx).\n", args->type); + } break; + } + + g_strfreev(msg1); + g_strfreev(msg2); + + return 1; +} + +static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) { + fu16_t channel; + int ret = 0; + aim_userinfo_t *userinfo; + va_list ap; + + va_start(ap, fr); + channel = (fu16_t)va_arg(ap, unsigned int); + userinfo = va_arg(ap, aim_userinfo_t *); + + switch (channel) { + case 1: { /* standard message */ + struct aim_incomingim_ch1_args *args; + args = va_arg(ap, struct aim_incomingim_ch1_args *); + ret = incomingim_chan1(sess, fr->conn, userinfo, args); + } break; + + case 2: { /* rendevous */ + struct aim_incomingim_ch2_args *args; + args = va_arg(ap, struct aim_incomingim_ch2_args *); + ret = incomingim_chan2(sess, fr->conn, userinfo, args); + } break; + + case 4: { /* ICQ */ + struct aim_incomingim_ch4_args *args; + args = va_arg(ap, struct aim_incomingim_ch4_args *); + ret = incomingim_chan4(sess, fr->conn, userinfo, args, 0); + } break; + + default: { + gaim_debug(GAIM_DEBUG_WARNING, "oscar", + "ICBM received on unsupported channel (channel " + "0x%04hx).", channel); + } break; + } + + va_end(ap); + + return ret; +} + +static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) { + char *buf; + va_list ap; + fu16_t chan, nummissed, reason; + aim_userinfo_t *userinfo; + + va_start(ap, fr); + chan = (fu16_t)va_arg(ap, unsigned int); + userinfo = va_arg(ap, aim_userinfo_t *); + nummissed = (fu16_t)va_arg(ap, unsigned int); + reason = (fu16_t)va_arg(ap, unsigned int); + va_end(ap); + + switch(reason) { + case 0: /* Invalid (0) */ + buf = g_strdup_printf( + ngettext( + "You missed %hu message from %s because it was invalid.", + "You missed %hu messages from %s because they were invalid.", + nummissed), + nummissed, + userinfo->sn); + break; + case 1: /* Message too large */ + buf = g_strdup_printf( + ngettext( + "You missed %hu message from %s because it was too large.", + "You missed %hu messages from %s because they were too large.", + nummissed), + nummissed, + userinfo->sn); + break; + case 2: /* Rate exceeded */ + buf = g_strdup_printf( + ngettext( + "You missed %hu message from %s because the rate limit has been exceeded.", + "You missed %hu messages from %s because the rate limit has been exceeded.", + nummissed), + nummissed, + userinfo->sn); + break; + case 3: /* Evil Sender */ + buf = g_strdup_printf( + ngettext( + "You missed %hu message from %s because he/she was too evil.", + "You missed %hu messages from %s because he/she was too evil.", + nummissed), + nummissed, + userinfo->sn); + break; + case 4: /* Evil Receiver */ + buf = g_strdup_printf( + ngettext( + "You missed %hu message from %s because you are too evil.", + "You missed %hu messages from %s because you are too evil.", + nummissed), + nummissed, + userinfo->sn); + break; + default: + buf = g_strdup_printf( + ngettext( + "You missed %hu message from %s for an unknown reason.", + "You missed %hu messages from %s for an unknown reason.", + nummissed), + nummissed, + userinfo->sn); + break; + } + gaim_notify_error(sess->aux_data, NULL, buf, NULL); + g_free(buf); + + return 1; +} + +static char *gaim_icq_status(int state) { + /* Make a cute little string that shows the status of the dude or dudet */ + if (state & AIM_ICQ_STATE_CHAT) + return g_strdup_printf(_("Free For Chat")); + else if (state & AIM_ICQ_STATE_DND) + return g_strdup_printf(_("Do Not Disturb")); + else if (state & AIM_ICQ_STATE_OUT) + return g_strdup_printf(_("Not Available")); + else if (state & AIM_ICQ_STATE_BUSY) + return g_strdup_printf(_("Occupied")); + else if (state & AIM_ICQ_STATE_AWAY) + return g_strdup_printf(_("Away")); + else if (state & AIM_ICQ_STATE_WEBAWARE) + return g_strdup_printf(_("Web Aware")); + else if (state & AIM_ICQ_STATE_INVISIBLE) + return g_strdup_printf(_("Invisible")); + else + return g_strdup_printf(_("Online")); +} + +static int gaim_parse_clientauto_ch2(aim_session_t *sess, const char *who, fu16_t reason, const char *cookie) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + +/* BBB */ + switch (reason) { + case 3: { /* Decline sendfile. */ + GaimXfer *xfer; + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "AAA - Other user declined file transfer\n"); + if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, cookie))) + gaim_xfer_cancel_remote(xfer); + } break; + + default: { + gaim_debug(GAIM_DEBUG_WARNING, "oscar", + "Received an unknown rendezvous client auto-response " + "from %s. Type 0x%04hx\n", who, reason); + } + + } + + return 0; +} + +static int gaim_parse_clientauto_ch4(aim_session_t *sess, char *who, fu16_t reason, fu32_t state, char *msg) { + GaimConnection *gc = sess->aux_data; + + switch(reason) { + case 0x0003: { /* Reply from an ICQ status message request */ + char *status_msg = gaim_icq_status(state); + char *dialog_msg, **splitmsg; + + /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */ + splitmsg = g_strsplit(msg, "\r\n", 0); + + dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<HR>%s"), who, status_msg, g_strjoinv("<BR>", splitmsg)); + gaim_notify_formatted(gc, NULL, _("Buddy Information"), NULL, dialog_msg, NULL, NULL); + + g_free(status_msg); + g_free(dialog_msg); + g_strfreev(splitmsg); + } break; + + default: { + gaim_debug(GAIM_DEBUG_WARNING, "oscar", + "Received an unknown client auto-response from %s. " + "Type 0x%04hx\n", who, reason); + } break; + } /* end of switch */ + + return 0; +} + +static int gaim_parse_clientauto(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + fu16_t chan, reason; + char *who; + + va_start(ap, fr); + chan = (fu16_t)va_arg(ap, unsigned int); + who = va_arg(ap, char *); + reason = (fu16_t)va_arg(ap, unsigned int); + + if (chan == 0x0002) { /* File transfer declined */ + char *cookie = va_arg(ap, char *); + return gaim_parse_clientauto_ch2(sess, who, reason, cookie); + } else if (chan == 0x0004) { /* ICQ message */ + fu32_t state = 0; + char *msg = NULL; + if (reason == 0x0003) { + state = va_arg(ap, fu32_t); + msg = va_arg(ap, char *); + } + return gaim_parse_clientauto_ch4(sess, who, reason, state, msg); + } + + va_end(ap); + + return 1; +} + +static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + fu16_t reason; + char *m; + + va_start(ap, fr); + reason = (fu16_t) va_arg(ap, unsigned int); + va_end(ap); + + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "snac threw error (reason 0x%04hx: %s)\n", reason, + (reason < msgerrreasonlen) ? msgerrreason[reason] : "unknown"); + + m = g_strdup_printf(_("SNAC threw error: %s\n"), + reason < msgerrreasonlen ? _(msgerrreason[reason]) : _("Unknown error")); + gaim_notify_error(sess->aux_data, NULL, m, NULL); + g_free(m); + + return 1; +} + +static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) { +#if 0 + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + GaimXfer *xfer; +#endif + va_list ap; + fu16_t reason; + char *data, *buf; + + va_start(ap, fr); + reason = (fu16_t)va_arg(ap, unsigned int); + data = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Message error with data %s and reason %hu\n", data, reason); + +/* BBB */ +#if 0 + /* If this was a file transfer request, data is a cookie */ + if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, data))) { + gaim_xfer_cancel_remote(xfer); + return 1; + } +#endif + + /* Data is assumed to be the destination sn */ + buf = g_strdup_printf(_("Your message to %s did not get sent:"), data); + gaim_notify_error(sess->aux_data, NULL, buf, + (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("No reason given.")); + g_free(buf); + + return 1; +} + +static int gaim_parse_mtn(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + va_list ap; + fu16_t type1, type2; + char *sn; + + va_start(ap, fr); + type1 = (fu16_t) va_arg(ap, unsigned int); + sn = va_arg(ap, char *); + type2 = (fu16_t) va_arg(ap, unsigned int); + va_end(ap); + + switch (type2) { + case 0x0000: { /* Text has been cleared */ + serv_got_typing_stopped(gc, sn); + } break; + + case 0x0001: { /* Paused typing */ + serv_got_typing(gc, sn, 0, GAIM_TYPED); + } break; + + case 0x0002: { /* Typing */ + serv_got_typing(gc, sn, 0, GAIM_TYPING); + } break; + + default: { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", "Received unknown typing notification message from %s. Type1 is 0x%04x and type2 is 0x%04hx.\n", sn, type1, type2); + } break; + } + + return 1; +} + +/* + * We get this error when there was an error in the locate family. This + * happens when you request info of someone who is offline. + */ +static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) { + gchar *buf; + va_list ap; + fu16_t reason; + char *destn; + + va_start(ap, fr); + reason = (fu16_t) va_arg(ap, unsigned int); + destn = va_arg(ap, char *); + va_end(ap); + + if (destn != NULL) { + buf = g_strdup_printf(_("User information for %s unavailable:"), destn); + gaim_notify_error(sess->aux_data, NULL, buf, + (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("No reason given.")); + g_free(buf); + } + + return 1; +} + +#if 0 +static char *images(int flags) { + static char buf[1024]; + g_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s", + (flags & AIM_FLAG_ACTIVEBUDDY) ? "<IMG SRC=\"ab_icon.gif\">" : "", + (flags & AIM_FLAG_UNCONFIRMED) ? "<IMG SRC=\"dt_icon.gif\">" : "", + (flags & AIM_FLAG_AOL) ? "<IMG SRC=\"aol_icon.gif\">" : "", + (flags & AIM_FLAG_ICQ) ? "<IMG SRC=\"icq_icon.gif\">" : "", + (flags & AIM_FLAG_ADMINISTRATOR) ? "<IMG SRC=\"admin_icon.gif\">" : "", + (flags & AIM_FLAG_FREE) ? "<IMG SRC=\"free_icon.gif\">" : "", + (flags & AIM_FLAG_WIRELESS) ? "<IMG SRC=\"wireless_icon.gif\">" : ""); + return buf; +} +#endif + +static char *caps_string(guint caps) +{ + static char buf[512], *tmp; + int count = 0, i = 0; + guint bit = 1; + + if (!caps) { + return NULL; + } else while (bit <= AIM_CAPS_LAST) { + if (bit & caps) { + switch (bit) { + case AIM_CAPS_BUDDYICON: + tmp = _("Buddy Icon"); + break; + case AIM_CAPS_VOICE: + tmp = _("Voice"); + break; + case AIM_CAPS_DIRECTIM: + tmp = _("AIM Direct IM"); + break; + case AIM_CAPS_CHAT: + tmp = _("Chat"); + break; + case AIM_CAPS_GETFILE: + tmp = _("Get File"); + break; + case AIM_CAPS_SENDFILE: + tmp = _("Send File"); + break; + case AIM_CAPS_GAMES: + case AIM_CAPS_GAMES2: + tmp = _("Games"); + break; + case AIM_CAPS_SAVESTOCKS: + tmp = _("Add-Ins"); + break; + case AIM_CAPS_SENDBUDDYLIST: + tmp = _("Send Buddy List"); + break; + case AIM_CAPS_ICQ_DIRECT: + tmp = _("ICQ Direct Connect"); + break; + case AIM_CAPS_APINFO: + tmp = _("AP User"); + break; + case AIM_CAPS_ICQRTF: + tmp = _("ICQ RTF"); + break; + case AIM_CAPS_EMPTY: + tmp = _("Nihilist"); + break; + case AIM_CAPS_ICQSERVERRELAY: + tmp = _("ICQ Server Relay"); + break; + case AIM_CAPS_ICQUTF8OLD: + tmp = _("Old ICQ UTF8"); + break; + case AIM_CAPS_TRILLIANCRYPT: + tmp = _("Trillian Encryption"); + break; + case AIM_CAPS_ICQUTF8: + tmp = _("ICQ UTF8"); + break; + case AIM_CAPS_HIPTOP: + tmp = _("Hiptop"); + break; + case AIM_CAPS_SECUREIM: + tmp = _("Security Enabled"); + break; + case AIM_CAPS_VIDEO: + tmp = _("Video Chat"); + break; + default: + tmp = NULL; + break; + } + if (tmp) + i += g_snprintf(buf + i, sizeof(buf) - i, "%s%s", (count ? ", " : ""), + tmp); + count++; + } + bit <<= 1; + } + return buf; +} + +static int gaim_parse_userinfo(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + GString *text; + gchar *info_utf8 = NULL, *away_utf8 = NULL; + const char *final = NULL; + va_list ap; + aim_userinfo_t *userinfo; + + va_start(ap, fr); + userinfo = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + text = g_string_new(""); + g_string_append_printf(text, _("Username: <b>%s</b><br>\n"), userinfo->sn); + g_string_append_printf(text, _("Warning Level: <b>%d%%</b><br>\n"), (int)((userinfo->warnlevel/10.0) + 0.5)); + + if (userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) + g_string_append_printf(text, _("Online Since: <b>%s</b><br>\n"), + asctime(localtime((time_t *)&userinfo->onlinesince))); + + if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) + g_string_append_printf(text, _("Member Since: <b>%s</b><br>\n"), + asctime(localtime((time_t *)&userinfo->membersince))); + + if (userinfo->present & AIM_USERINFO_PRESENT_IDLE) { + gchar *itime = gaim_str_seconds_to_string(userinfo->idletime*60); + g_string_append_printf(text, _("Idle: <b>%s</b>"), itime); + g_free(itime); + } else + g_string_append_printf(text, _("Idle: <b>Active</b>")); + + if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) { + away_utf8 = oscar_encoding_to_utf8(userinfo->away_encoding, userinfo->away, userinfo->away_len); + if (away_utf8 != NULL) { + g_string_append_printf(text, "<hr>%s", away_utf8); + g_free(away_utf8); + } + } + + if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) { + info_utf8 = oscar_encoding_to_utf8(userinfo->info_encoding, userinfo->info, userinfo->info_len); + if (info_utf8 != NULL) { + g_string_append_printf(text, "<hr>%s", info_utf8); + g_free(info_utf8); + } + } + + final = gaim_str_sub_away_formatters(text->str, gaim_account_get_username(gaim_connection_get_account(gc))); + g_string_free(text, TRUE); + gaim_notify_formatted(gc, NULL, _("Buddy Information"), NULL, final, NULL, NULL); + + return 1; +} + +static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) { + char *msg; + fu16_t id; + va_list ap; + + va_start(ap, fr); + id = (fu16_t) va_arg(ap, unsigned int); + msg = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "MOTD: %s (%hu)\n", msg ? msg : "Unknown", id); + if (id < 4) + gaim_notify_warning(sess->aux_data, NULL, + _("Your AIM connection may be lost."), NULL); + + return 1; +} + +static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + fu16_t type; + GaimConnection *gc = sess->aux_data; + OscarData *od = (OscarData *)gc->proto_data; + + va_start(ap, fr); + type = (fu16_t) va_arg(ap, unsigned int); + + switch(type) { + case 0x0002: { + fu8_t maxrooms; + struct aim_chat_exchangeinfo *exchanges; + int exchangecount, i; + + maxrooms = (fu8_t) va_arg(ap, unsigned int); + exchangecount = va_arg(ap, int); + exchanges = va_arg(ap, struct aim_chat_exchangeinfo *); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "chat info: Chat Rights:\n"); + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "chat info: \tMax Concurrent Rooms: %hhd\n", maxrooms); + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "chat info: \tExchange List: (%d total)\n", exchangecount); + for (i = 0; i < exchangecount; i++) + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "chat info: \t\t%hu %s\n", + exchanges[i].number, exchanges[i].name ? exchanges[i].name : ""); + while (od->create_rooms) { + struct create_room *cr = od->create_rooms->data; + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "creating room %s\n", cr->name); + aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange); + g_free(cr->name); + od->create_rooms = g_slist_remove(od->create_rooms, cr); + g_free(cr); + } + } + break; + case 0x0008: { + char *fqcn, *name, *ck; + fu16_t instance, flags, maxmsglen, maxoccupancy, unknown, exchange; + fu8_t createperms; + fu32_t createtime; + + fqcn = va_arg(ap, char *); + instance = (fu16_t)va_arg(ap, unsigned int); + exchange = (fu16_t)va_arg(ap, unsigned int); + flags = (fu16_t)va_arg(ap, unsigned int); + createtime = va_arg(ap, fu32_t); + maxmsglen = (fu16_t)va_arg(ap, unsigned int); + maxoccupancy = (fu16_t)va_arg(ap, unsigned int); + createperms = (fu8_t)va_arg(ap, unsigned int); + unknown = (fu16_t)va_arg(ap, unsigned int); + name = va_arg(ap, char *); + ck = va_arg(ap, char *); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "created room: %s %hu %hu %hu %u %hu %hu %hhu %hu %s %s\n", + fqcn, + exchange, instance, flags, + createtime, + maxmsglen, maxoccupancy, createperms, unknown, + name, ck); + aim_chat_join(od->sess, od->conn, exchange, ck, instance); + } + break; + default: + gaim_debug(GAIM_DEBUG_WARNING, "oscar", + "chatnav info: unknown type (%04hx)\n", type); + break; + } + + va_end(ap); + + return 1; +} + +static int gaim_conv_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + int count, i; + aim_userinfo_t *info; + GaimConnection *g = sess->aux_data; + + struct chat_connection *c = NULL; + + va_start(ap, fr); + count = va_arg(ap, int); + info = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + c = find_oscar_chat_by_conn(g, fr->conn); + if (!c) + return 1; + + for (i = 0; i < count; i++) + gaim_conv_chat_add_user(GAIM_CONV_CHAT(c->cnv), info[i].sn, NULL); + + return 1; +} + +static int gaim_conv_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + int count, i; + aim_userinfo_t *info; + GaimConnection *g = sess->aux_data; + + struct chat_connection *c = NULL; + + va_start(ap, fr); + count = va_arg(ap, int); + info = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + c = find_oscar_chat_by_conn(g, fr->conn); + if (!c) + return 1; + + for (i = 0; i < count; i++) + gaim_conv_chat_remove_user(GAIM_CONV_CHAT(c->cnv), info[i].sn, NULL); + + return 1; +} + +static int gaim_conv_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + aim_userinfo_t *userinfo; + struct aim_chat_roominfo *roominfo; + char *roomname; + int usercount; + char *roomdesc; + fu16_t unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen; + fu32_t creationtime; + GaimConnection *gc = sess->aux_data; + struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn); + + va_start(ap, fr); + roominfo = va_arg(ap, struct aim_chat_roominfo *); + roomname = va_arg(ap, char *); + usercount= va_arg(ap, int); + userinfo = va_arg(ap, aim_userinfo_t *); + roomdesc = va_arg(ap, char *); + unknown_c9 = (fu16_t)va_arg(ap, unsigned int); + creationtime = va_arg(ap, fu32_t); + maxmsglen = (fu16_t)va_arg(ap, unsigned int); + unknown_d2 = (fu16_t)va_arg(ap, unsigned int); + unknown_d5 = (fu16_t)va_arg(ap, unsigned int); + maxvisiblemsglen = (fu16_t)va_arg(ap, unsigned int); + va_end(ap); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "inside chat_info_update (maxmsglen = %hu, maxvislen = %hu)\n", + maxmsglen, maxvisiblemsglen); + + ccon->maxlen = maxmsglen; + ccon->maxvis = maxvisiblemsglen; + + return 1; +} + +static int gaim_conv_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + va_list ap; + aim_userinfo_t *info; + char *msg; + struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn); + + va_start(ap, fr); + info = va_arg(ap, aim_userinfo_t *); + msg = va_arg(ap, char *); + va_end(ap); + + serv_got_chat_in(gc, ccon->id, info->sn, 0, msg, time((time_t)NULL)); + + return 1; +} + +static int gaim_email_parseupdate(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + GaimConnection *gc = sess->aux_data; + struct aim_emailinfo *emailinfo; + int havenewmail; + char *alertitle, *alerturl; + + va_start(ap, fr); + emailinfo = va_arg(ap, struct aim_emailinfo *); + havenewmail = va_arg(ap, int); + alertitle = va_arg(ap, char *); + alerturl = va_arg(ap, char *); + va_end(ap); + + if (emailinfo && gaim_account_get_check_mail(gc->account)) { + gchar *to = g_strdup_printf("%s@%s", gaim_account_get_username(gaim_connection_get_account(gc)), emailinfo->domain); + if (emailinfo->unread && havenewmail) + gaim_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL, (const char **)&to, (const char **)&emailinfo->url, NULL, NULL); + g_free(to); + } + + if (alertitle) + gaim_debug(GAIM_DEBUG_MISC, "oscar", "Got an alert '%s' %s\n", alertitle, alerturl ? alerturl : ""); + + return 1; +} + +static int gaim_icon_error(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + char *sn; + + sn = od->requesticon->data; + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "removing %s from hash table\n", sn); + od->requesticon = g_slist_remove(od->requesticon, sn); + free(sn); + + if (od->icontimer) + g_source_remove(od->icontimer); + od->icontimer = g_timeout_add(500, gaim_icon_timerfunc, gc); + + return 1; +} + +static int gaim_icon_parseicon(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + GSList *cur; + va_list ap; + char *sn; + fu8_t *iconcsum, *icon; + fu16_t iconcsumlen, iconlen; + + va_start(ap, fr); + sn = va_arg(ap, char *); + iconcsum = va_arg(ap, fu8_t *); + iconcsumlen = va_arg(ap, int); + icon = va_arg(ap, fu8_t *); + iconlen = va_arg(ap, int); + va_end(ap); + + if (iconlen > 0) { + char *b16; + GaimBuddy *b = gaim_find_buddy(gc->account, sn); + gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc), + sn, icon, iconlen); + b16 = gaim_base16_encode(iconcsum, iconcsumlen); + if (b16) { + gaim_buddy_set_setting(b, "icon_checksum", b16); + gaim_blist_save(); + g_free(b16); + } + } + + cur = od->requesticon; + while (cur) { + char *cursn = cur->data; + if (!aim_sncmp(cursn, sn)) { + od->requesticon = g_slist_remove(od->requesticon, cursn); + free(cursn); + cur = od->requesticon; + } else + cur = cur->next; + } + + if (od->icontimer) + g_source_remove(od->icontimer); + od->icontimer = g_timeout_add(250, gaim_icon_timerfunc, gc); + + return 1; +} + +static gboolean gaim_icon_timerfunc(gpointer data) { + GaimConnection *gc = data; + OscarData *od = gc->proto_data; + aim_userinfo_t *userinfo; + aim_conn_t *conn; + + conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_ICON); + if (!conn) { + if (!od->iconconnecting) { + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_ICON); + od->iconconnecting = TRUE; + } + return FALSE; + } + + if (od->set_icon) { + struct stat st; + const char *iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc)); + if (iconfile == NULL) { + /* Set an empty icon, or something */ + } else if (!stat(iconfile, &st)) { + char *buf = g_malloc(st.st_size); + FILE *file = fopen(iconfile, "rb"); + if (file) { + fread(buf, 1, st.st_size, file); + fclose(file); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Uploading icon to icon server\n"); + aim_bart_upload(od->sess, buf, st.st_size); + } else + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Can't open buddy icon file!\n"); + g_free(buf); + } else { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Can't stat buddy icon file!\n"); + } + od->set_icon = FALSE; + } + + if (!od->requesticon) { + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "no more icons to request\n"); + return FALSE; + } + + userinfo = aim_locate_finduserinfo(od->sess, (char *)od->requesticon->data); + if ((userinfo != NULL) && (userinfo->iconcsumlen > 0)) { + aim_bart_request(od->sess, od->requesticon->data, userinfo->iconcsum, userinfo->iconcsumlen); + return FALSE; + } else { + char *sn = od->requesticon->data; + od->requesticon = g_slist_remove(od->requesticon, sn); + free(sn); + } + + return TRUE; +} + +/* + * Recieved in response to an IM sent with the AIM_IMFLAGS_ACK option. + */ +static int gaim_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + fu16_t type; + char *sn; + + va_start(ap, fr); + type = (fu16_t) va_arg(ap, unsigned int); + sn = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", "Sent message to %s.\n", sn); + + return 1; +} + +static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) { + static const char *codes[5] = { + "invalid", + "change", + "warning", + "limit", + "limit cleared", + }; + va_list ap; + fu16_t code, rateclass; + fu32_t windowsize, clear, alert, limit, disconnect, currentavg, maxavg; + + va_start(ap, fr); + code = (fu16_t)va_arg(ap, unsigned int); + rateclass= (fu16_t)va_arg(ap, unsigned int); + windowsize = va_arg(ap, fu32_t); + clear = va_arg(ap, fu32_t); + alert = va_arg(ap, fu32_t); + limit = va_arg(ap, fu32_t); + disconnect = va_arg(ap, fu32_t); + currentavg = va_arg(ap, fu32_t); + maxavg = va_arg(ap, fu32_t); + va_end(ap); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "rate %s (param ID 0x%04hx): curavg = %u, maxavg = %u, alert at %u, " + "clear warning at %u, limit at %u, disconnect at %u (window size = %u)\n", + (code < 5) ? codes[code] : codes[0], + rateclass, + currentavg, maxavg, + alert, clear, + limit, disconnect, + windowsize); + + /* XXX fix these values */ + if (code == AIM_RATE_CODE_CHANGE) { + if (currentavg >= clear) + aim_conn_setlatency(fr->conn, 0); + } else if (code == AIM_RATE_CODE_WARNING) { + aim_conn_setlatency(fr->conn, windowsize/4); + } else if (code == AIM_RATE_CODE_LIMIT) { + gaim_notify_error(sess->aux_data, NULL, _("Rate limiting error."), + _("The last action you attempted could not be " + "performed because you are over the rate limit. " + "Please wait 10 seconds and try again.")); + aim_conn_setlatency(fr->conn, windowsize/2); + } else if (code == AIM_RATE_CODE_CLEARLIMIT) { + aim_conn_setlatency(fr->conn, 0); + } + + return 1; +} + +static int gaim_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + fu16_t newevil; + aim_userinfo_t *userinfo; + GaimConnection *gc = sess->aux_data; + + va_start(ap, fr); + newevil = (fu16_t) va_arg(ap, unsigned int); + userinfo = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + serv_got_eviled(gc, (userinfo && userinfo->sn) ? userinfo->sn : NULL, (newevil/10.0) + 0.5); + + return 1; +} + +static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + aim_userinfo_t *info; + GaimConnection *gc = sess->aux_data; + + va_start(ap, fr); + info = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + gc->evil = (info->warnlevel/10.0) + 0.5; + + if (info->onlinesince) + gc->login_time_official = info->onlinesince; + + return 1; +} + +static int gaim_connerr(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + va_list ap; + fu16_t code; + char *msg; + + va_start(ap, fr); + code = (fu16_t)va_arg(ap, int); + msg = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Disconnected. Code is 0x%04x and msg is %s\n", code, msg); + if ((fr) && (fr->conn) && (fr->conn->type == AIM_CONN_TYPE_BOS)) { + if (code == 0x0001) { + gc->wants_to_die = TRUE; + gaim_connection_error(gc, _("You have been disconnected because you have signed on with this screen name at another location.")); + } else { + gaim_connection_error(gc, _("You have been signed off for an unknown reason.")); + } + od->killme = TRUE; + } + + return 1; +} + +static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + + aim_reqpersonalinfo(sess, fr->conn); + +#ifndef NOSSI + gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: requesting rights and list\n"); + aim_ssi_reqrights(sess); + aim_ssi_reqdata(sess); +#endif + + aim_locate_reqrights(sess); + aim_buddylist_reqrights(sess, fr->conn); + aim_im_reqparams(sess); + aim_bos_reqrights(sess, fr->conn); /* XXX - Don't call this with ssi */ + +#ifdef NOSSI + gaim_debug(GAIM_DEBUG_INFO, "oscar", "bos: requesting rights\n"); + aim_bos_reqrights(sess, fr->conn); + aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS); + aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE | AIM_PRIVFLAGS_ALLOWMEMBERSINCE); +#endif + + gaim_connection_update_progress(gc, _("Finalizing connection"), 5, OSCAR_CONNECT_STEPS); + + return 1; +} + +static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0003, gaim_info_change, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0005, gaim_info_change, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0007, gaim_account_confirm, 0); + + aim_clientready(sess, fr->conn); + gaim_debug(GAIM_DEBUG_INFO, "oscar", "connected to admin\n"); + + if (od->chpass) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", "changing password\n"); + aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp); + g_free(od->oldp); + od->oldp = NULL; + g_free(od->newp); + od->newp = NULL; + od->chpass = FALSE; + } + if (od->setnick) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", "formatting screenname\n"); + aim_admin_setnick(sess, fr->conn, od->newsn); + g_free(od->newsn); + od->newsn = NULL; + od->setnick = FALSE; + } + if (od->conf) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", "confirming account\n"); + aim_admin_reqconfirm(sess, fr->conn); + od->conf = FALSE; + } + if (od->reqemail) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", "requesting email\n"); + aim_admin_getinfo(sess, fr->conn, 0x0011); + od->reqemail = FALSE; + } + if (od->setemail) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", "setting email\n"); + aim_admin_setemail(sess, fr->conn, od->email); + g_free(od->email); + od->email = NULL; + od->setemail = FALSE; + } + + return 1; +} + +static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) { + struct aim_icbmparameters *params; + va_list ap; + + va_start(ap, fr); + params = va_arg(ap, struct aim_icbmparameters *); + va_end(ap); + + /* XXX - evidently this crashes on solaris. i have no clue why + gaim_debug(GAIM_DEBUG_MISC, "oscar", "ICBM Parameters: maxchannel = %hu, default flags = 0x%08lx, max msg len = %hu, " + "max sender evil = %f, max receiver evil = %f, min msg interval = %u\n", + params->maxchan, params->flags, params->maxmsglen, + ((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0, + params->minmsginterval); + */ + + /* Maybe senderwarn and recverwarn should be user preferences... */ + params->flags = 0x0000000b; + params->maxmsglen = 8000; + params->minmsginterval = 0; + + aim_im_setparams(sess, params); + + return 1; +} + +static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...) +{ + GaimConnection *gc = sess->aux_data; + OscarData *od = (OscarData *)gc->proto_data; + va_list ap; + fu16_t maxsiglen; + + va_start(ap, fr); + maxsiglen = (fu16_t) va_arg(ap, int); + va_end(ap); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "locate rights: max sig len = %d\n", maxsiglen); + + od->rights.maxsiglen = od->rights.maxawaymsglen = (guint)maxsiglen; + + if (od->icq) + aim_locate_setcaps(od->sess, caps_icq); + else + aim_locate_setcaps(od->sess, caps_aim); + oscar_set_info(gc, gc->account->user_info); + + return 1; +} + +static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + fu16_t maxbuddies, maxwatchers; + GaimConnection *gc = sess->aux_data; + OscarData *od = (OscarData *)gc->proto_data; + + va_start(ap, fr); + maxbuddies = (fu16_t) va_arg(ap, unsigned int); + maxwatchers = (fu16_t) va_arg(ap, unsigned int); + va_end(ap); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "buddy list rights: Max buddies = %hu / Max watchers = %hu\n", maxbuddies, maxwatchers); + + od->rights.maxbuddies = (guint)maxbuddies; + od->rights.maxwatchers = (guint)maxwatchers; + + return 1; +} + +static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = (OscarData *)gc->proto_data; + va_list ap; + fu16_t maxpermits, maxdenies; + + va_start(ap, fr); + maxpermits = (fu16_t) va_arg(ap, unsigned int); + maxdenies = (fu16_t) va_arg(ap, unsigned int); + va_end(ap); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "BOS rights: Max permit = %hu / Max deny = %hu\n", maxpermits, maxdenies); + + od->rights.maxpermits = (guint)maxpermits; + od->rights.maxdenies = (guint)maxdenies; + + gaim_connection_set_state(gc, GAIM_CONNECTED); + serv_finish_login(gc); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", "buddy list loaded\n"); + + aim_clientready(sess, fr->conn); + aim_srv_setavailmsg(sess, NULL); + aim_srv_setidle(sess, 0); + + if (od->icq) { + aim_icq_reqofflinemsgs(sess); + aim_icq_hideip(sess); + } + + aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV); + if (sess->authinfo->email) + aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_EMAIL); + + return 1; +} + +static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + struct aim_icq_offlinemsg *msg; + struct aim_incomingim_ch4_args args; + time_t t; + + va_start(ap, fr); + msg = va_arg(ap, struct aim_icq_offlinemsg *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Received offline message. Converting to channel 4 ICBM...\n"); + args.uin = msg->sender; + args.type = msg->type; + args.flags = msg->flags; + args.msglen = msg->msglen; + args.msg = msg->msg; + t = gaim_time_build(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0); + incomingim_chan4(sess, fr->conn, NULL, &args, t); + + return 1; +} + +static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...) +{ + aim_icq_ackofflinemsgs(sess); + return 1; +} + +#if 0 +/* + * Update, 2003-11-09: + * Joseph S. Myers, a gcc dude, fixed this for gcc 3.4! Rock on! + * + * It may not be my place to do this, but... + * I feel pretty strongly that the "last 2 digits" warning is ridiculously + * stupid, and should not exist for % switches (%x in our case) that request + * a year in the preferred representation for the current locale. For that + * reason I've chosen to not use this workaround (n., see kluge). + * + * I have a date. I want to show it to the user in the "preferred" way. + * Whether that displays a 2 digit year is perfectly fine--after all, it's + * what the locale wanted. + * + * If I have a necessity for a full representation of the year in the current + * locale, then I'll use a switch that returns a full representation of the + * year. + * + * If you think the preferred locale should show 4 digits instead of 2 digits + * (because you're anal, or whatever), then change the f***ing locale. + * + * I guess the bottom line is--I'm trying to show a date to the user how they + * prefer to see it, why the hell does gcc want me to change that? + * + * See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=3190 + * See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=8714 + */ + +/* + * This function was recommended by the STRFTIME(3) man page to remove the + * "last 2 digits" warning. + */ +static size_t my_strftime(char *s, size_t max, const char *fmt, + const struct tm *tm) +{ + return strftime(s, max, fmt, tm); +} +#endif + +static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...) +{ + GaimConnection *gc = sess->aux_data; + gchar *buf, *tmp, *utf8; + gchar who[16]; + GaimBuddy *buddy; + gchar *primary; + va_list ap; + struct aim_icq_info *info; + + va_start(ap, fr); + info = va_arg(ap, struct aim_icq_info *); + va_end(ap); + + if (!info->uin) + return 0; + + g_snprintf(who, sizeof(who), "%u", info->uin); + buf = g_strdup_printf("<b>%s:</b> %s", _("UIN"), who); + if (info->nick && info->nick[0] && (utf8 = gaim_utf8_try_convert(info->nick))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Nick"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->first && info->first[0] && (utf8 = gaim_utf8_try_convert(info->first))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("First Name"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->last && info->last[0] && (utf8 = gaim_utf8_try_convert(info->last))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Last Name"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->email && info->email[0] && (utf8 = gaim_utf8_try_convert(info->email))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Email Address"), ":</b> <a href=\"mailto:", utf8, "\">", utf8, "</a>", NULL); g_free(tmp); g_free(utf8); + } + if (info->numaddresses && info->email2) { + int i; + for (i = 0; i < info->numaddresses; i++) { + if (info->email2[i] && info->email2[i][0] && (utf8 = gaim_utf8_try_convert(info->email2[i]))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Email Address"), ":</b> <a href=\"mailto:", utf8, "\">", utf8, "</a>", NULL); g_free(tmp); g_free(utf8); + } + } + } + if (info->mobile && info->mobile[0] && (utf8 = gaim_utf8_try_convert(info->mobile))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Mobile Phone"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->gender) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Gender"), ":</b> ", info->gender==1 ? _("Female") : _("Male"), NULL); g_free(tmp); + } + if (info->birthyear || info->birthmonth || info->birthday) { + char date[30]; + struct tm tm; + tm.tm_mday = (int)info->birthday; + tm.tm_mon = (int)info->birthmonth-1; + tm.tm_year = (int)info->birthyear-1900; + strftime(date, sizeof(date), "%x", &tm); + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Birthday"), ":</b> ", date, NULL); g_free(tmp); + } + if (info->age) { + char age[5]; + snprintf(age, sizeof(age), "%hhd", info->age); + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Age"), ":</b> ", age, NULL); g_free(tmp); + } + if (info->personalwebpage && info->personalwebpage[0] && (utf8 = gaim_utf8_try_convert(info->personalwebpage))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Personal Web Page"), ":</b> <a href=\"", utf8, "\">", utf8, "</a>", NULL); g_free(tmp); g_free(utf8); + } + if (info->info && info->info[0] && (utf8 = gaim_utf8_try_convert(info->info))) { + tmp = buf; buf = g_strconcat(tmp, "<hr><b>", _("Additional Information"), ":</b><br>", utf8, NULL); g_free(tmp); g_free(utf8); + } + tmp = buf; buf = g_strconcat(tmp, "<hr>\n", NULL); g_free(tmp); + if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) { + tmp = buf; buf = g_strconcat(tmp, "<b>", _("Home Address"), ":</b>", NULL); g_free(tmp); + if (info->homeaddr && info->homeaddr[0] && (utf8 = gaim_utf8_try_convert(info->homeaddr))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Address"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->homecity && info->homecity[0] && (utf8 = gaim_utf8_try_convert(info->homecity))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("City"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->homestate && info->homestate[0] && (utf8 = gaim_utf8_try_convert(info->homestate))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("State"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->homezip && info->homezip[0] && (utf8 = gaim_utf8_try_convert(info->homezip))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Zip Code"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + tmp = buf; buf = g_strconcat(tmp, "\n<hr>\n", NULL); g_free(tmp); + } + if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) { + tmp = buf; buf = g_strconcat(tmp, "<b>", _("Work Address"), ":</b>", NULL); g_free(tmp); + if (info->workaddr && info->workaddr[0] && (utf8 = gaim_utf8_try_convert(info->workaddr))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Address"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->workcity && info->workcity[0] && (utf8 = gaim_utf8_try_convert(info->workcity))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("City"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->workstate && info->workstate[0] && (utf8 = gaim_utf8_try_convert(info->workstate))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("State"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->workzip && info->workzip[0] && (utf8 = gaim_utf8_try_convert(info->workzip))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Zip Code"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + tmp = buf; buf = g_strconcat(tmp, "\n<hr>\n", NULL); g_free(tmp); + } + if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) { + tmp = buf; buf = g_strconcat(tmp, "<b>", _("Work Information"), ":</b>", NULL); g_free(tmp); + if (info->workcompany && info->workcompany[0] && (utf8 = gaim_utf8_try_convert(info->workcompany))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Company"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->workdivision && info->workdivision[0] && (utf8 = gaim_utf8_try_convert(info->workdivision))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Division"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->workposition && info->workposition[0] && (utf8 = gaim_utf8_try_convert(info->workposition))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Position"), ":</b> ", utf8, NULL); g_free(tmp); g_free(utf8); + } + if (info->workwebpage && info->workwebpage[0] && (utf8 = gaim_utf8_try_convert(info->workwebpage))) { + tmp = buf; buf = g_strconcat(tmp, "\n<br><b>", _("Web Page"), ":</b> <a href=\"", utf8, "\">", utf8, "</a>", NULL); g_free(tmp); g_free(utf8); + } + tmp = buf; buf = g_strconcat(tmp, "\n<hr>\n", NULL); g_free(tmp); + } + + buddy = gaim_find_buddy(gaim_connection_get_account(gc), who); + primary = g_strdup_printf(_("ICQ Info for %s"), gaim_get_buddy_alias(buddy)); + gaim_notify_formatted(gc, NULL, primary, NULL, buf, NULL, NULL); + g_free(primary); + g_free(buf); + + return 1; +} + +static int gaim_icqalias(aim_session_t *sess, aim_frame_t *fr, ...) +{ + GaimConnection *gc = sess->aux_data; + gchar who[16], *utf8; + GaimBuddy *b; + va_list ap; + struct aim_icq_info *info; + + va_start(ap, fr); + info = va_arg(ap, struct aim_icq_info *); + va_end(ap); + + if (info->uin && info->nick && info->nick[0] && (utf8 = gaim_utf8_try_convert(info->nick))) { + g_snprintf(who, sizeof(who), "%u", info->uin); + serv_got_alias(gc, who, utf8); + if ((b = gaim_find_buddy(gc->account, who))) { + gaim_buddy_set_setting(b, "servernick", utf8); + gaim_blist_save(); + } + g_free(utf8); + } + + return 1; +} + +static int gaim_popup(aim_session_t *sess, aim_frame_t *fr, ...) +{ + GaimConnection *gc = sess->aux_data; + gchar *text; + va_list ap; + char *msg, *url; + fu16_t wid, hei, delay; + + va_start(ap, fr); + msg = va_arg(ap, char *); + url = va_arg(ap, char *); + wid = (fu16_t) va_arg(ap, int); + hei = (fu16_t) va_arg(ap, int); + delay = (fu16_t) va_arg(ap, int); + va_end(ap); + + text = g_strdup_printf("%s<br><a href=\"%s\">%s</a>", msg, url, url); + gaim_notify_formatted(gc, NULL, _("Pop-Up Message"), NULL, text, NULL, NULL); + g_free(text); + + return 1; +} + +static int gaim_parse_searchreply(aim_session_t *sess, aim_frame_t *fr, ...) +{ + GaimConnection *gc = sess->aux_data; + gchar *secondary; + GString *text; + int i, num; + va_list ap; + char *email, *SNs; + + va_start(ap, fr); + email = va_arg(ap, char *); + num = va_arg(ap, int); + SNs = va_arg(ap, char *); + va_end(ap); + + secondary = g_strdup_printf(_("The following screennames are associated with %s"), email); + text = g_string_new(""); + for (i = 0; i < num; i++) + g_string_append_printf(text, "%s<br>", &SNs[i * (MAXSNLEN + 1)]); + gaim_notify_formatted(gc, NULL, _("Search Results"), secondary, text->str, NULL, NULL); + + g_free(secondary); + g_string_free(text, TRUE); + + return 1; +} + +static int gaim_parse_searcherror(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + char *email; + char *buf; + + va_start(ap, fr); + email = va_arg(ap, char *); + va_end(ap); + + buf = g_strdup_printf(_("No results found for email address %s"), email); + gaim_notify_error(sess->aux_data, NULL, buf, NULL); + g_free(buf); + + return 1; +} + +static int gaim_account_confirm(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + fu16_t status; + va_list ap; + char msg[256]; + + va_start(ap, fr); + status = (fu16_t) va_arg(ap, unsigned int); /* status code of confirmation request */ + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "account confirmation returned status 0x%04x (%s)\n", status, + status ? "unknown" : "email sent"); + if (!status) { + g_snprintf(msg, sizeof(msg), _("You should receive an email asking to confirm %s."), + gaim_account_get_username(gaim_connection_get_account(gc))); + gaim_notify_info(gc, NULL, _("Account Confirmation Requested"), msg); + } + + return 1; +} + +static int gaim_info_change(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + va_list ap; + fu16_t perms, err; + char *url, *sn, *email; + int change; + + va_start(ap, fr); + change = va_arg(ap, int); + perms = (fu16_t) va_arg(ap, unsigned int); + err = (fu16_t) va_arg(ap, unsigned int); + url = va_arg(ap, char *); + sn = va_arg(ap, char *); + email = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, sn=%s, email=%s\n", + change ? "change" : "request", perms, err, url, sn, email); + + if (err && url) { + char *dialog_msg; + char *dialog_top = g_strdup_printf(_("Error Changing Account Info")); + switch (err) { + case 0x0001: { + dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name differs from the original."), err); + } break; + case 0x0006: { + dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name ends in a space."), err); + } break; + case 0x000b: { + dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name is too long."), err); + } break; + case 0x001d: { + dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because there is already a request pending for this screen name."), err); + } break; + case 0x0021: { + dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address has too many screen names associated with it."), err); + } break; + case 0x0023: { + dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address is invalid."), err); + } break; + default: { + dialog_msg = g_strdup_printf(_("Error 0x%04x: Unknown error."), err); + } break; + } + gaim_notify_error(gc, NULL, dialog_top, dialog_msg); + g_free(dialog_top); + g_free(dialog_msg); + return 1; + } + + if (sn) { + char *dialog_msg = g_strdup_printf(_("Your screen name is currently formatted as follows:\n%s"), sn); + gaim_notify_info(gc, NULL, _("Account Info"), dialog_msg); + g_free(dialog_msg); + } + + if (email) { + char *dialog_msg = g_strdup_printf(_("The email address for %s is %s"), + gaim_account_get_username(gaim_connection_get_account(gc)), email); + gaim_notify_info(gc, NULL, _("Account Info"), dialog_msg); + g_free(dialog_msg); + } + + return 1; +} + +static void oscar_keepalive(GaimConnection *gc) { + OscarData *od = (OscarData *)gc->proto_data; + aim_flap_nop(od->sess, od->conn); +} + +static int oscar_send_typing(GaimConnection *gc, const char *name, int typing) { + OscarData *od = (OscarData *)gc->proto_data; + struct direct_im *dim = find_direct_im(od, name); + if (dim) + if (typing == GAIM_TYPING) + aim_odc_send_typing(od->sess, dim->conn, 0x0002); + else if (typing == GAIM_TYPED) + aim_odc_send_typing(od->sess, dim->conn, 0x0001); + else + aim_odc_send_typing(od->sess, dim->conn, 0x0000); + else { + /* Don't send if this turkey is in our deny list */ + GSList *list; + for (list=gc->account->deny; (list && aim_sncmp(name, list->data)); list=list->next); + if (!list) { + struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(gc->account, name)); + if (bi && bi->typingnot) { + if (typing == GAIM_TYPING) + aim_im_sendmtn(od->sess, 0x0001, name, 0x0002); + else if (typing == GAIM_TYPED) + aim_im_sendmtn(od->sess, 0x0001, name, 0x0001); + else + aim_im_sendmtn(od->sess, 0x0001, name, 0x0000); + } + } + } + return 0; +} +static void oscar_ask_direct_im(GaimConnection *gc, const char *name); +static int gaim_odc_send_im(aim_session_t *, aim_conn_t *, const char *, GaimConvImFlags); + +static int oscar_send_im(GaimConnection *gc, const char *name, const char *message, GaimConvImFlags imflags) { + OscarData *od = (OscarData *)gc->proto_data; + struct direct_im *dim = find_direct_im(od, name); + int ret = 0; + GError *err = NULL; + const char *iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc)); + char *tmpmsg = NULL; + + if (dim && dim->connected) { + /* If we're directly connected, send a direct IM */ + ret = gaim_odc_send_im(od->sess, dim->conn, message, imflags); + } else if (imflags & GAIM_CONV_IM_IMAGES) { + /* Trying to send an IM image outside of a direct connection. */ + oscar_ask_direct_im(gc, name); + ret = -ENOTCONN; + } else { + struct buddyinfo *bi; + struct aim_sendimext_args args; + struct stat st; + gsize len; + + bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(gc->account, name)); + if (!bi) { + bi = g_new0(struct buddyinfo, 1); + g_hash_table_insert(od->buddyinfo, g_strdup(gaim_normalize(gc->account, name)), bi); + } + + args.flags = AIM_IMFLAGS_ACK | AIM_IMFLAGS_CUSTOMFEATURES; + if (od->icq) { + args.features = features_icq; + args.featureslen = sizeof(features_icq); + args.flags |= AIM_IMFLAGS_OFFLINE; + } else { + args.features = features_aim; + args.featureslen = sizeof(features_aim); + + if (imflags & GAIM_CONV_IM_AUTO_RESP) + args.flags |= AIM_IMFLAGS_AWAY; + } + + if (bi->ico_need) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Sending buddy icon request with message\n"); + args.flags |= AIM_IMFLAGS_BUDDYREQ; + bi->ico_need = FALSE; + } + + if (iconfile && !stat(iconfile, &st)) { + FILE *file = fopen(iconfile, "r"); + if (file) { + char *buf = g_malloc(st.st_size); + fread(buf, 1, st.st_size, file); + fclose(file); + + args.iconlen = st.st_size; + args.iconsum = aimutil_iconsum(buf, st.st_size); + args.iconstamp = st.st_mtime; + + if ((args.iconlen != bi->ico_me_len) || (args.iconsum != bi->ico_me_csum) || (args.iconstamp != bi->ico_me_time)) { + bi->ico_informed = FALSE; + bi->ico_sent = FALSE; + } + + if (!bi->ico_informed) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Claiming to have a buddy icon\n"); + args.flags |= AIM_IMFLAGS_HASICON; + bi->ico_me_len = args.iconlen; + bi->ico_me_csum = args.iconsum; + bi->ico_me_time = args.iconstamp; + bi->ico_informed = TRUE; + } + + g_free(buf); + } + } + + args.destsn = name; + + /* For ICQ send newlines as CR/LF, for AIM send newlines as <BR> */ + if (isdigit(name[0])) + tmpmsg = gaim_str_add_cr(message); + else + tmpmsg = gaim_strdup_withhtml(message); + len = strlen(tmpmsg); + + args.flags |= oscar_encoding_check(tmpmsg); + if (args.flags & AIM_IMFLAGS_UNICODE) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", "Sending Unicode IM\n"); + args.charset = 0x0002; + args.charsubset = 0x0000; + args.msg = g_convert(tmpmsg, len, "UCS-2BE", "UTF-8", NULL, &len, &err); + if (err) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Error converting a unicode message: %s\n", err->message); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "This really shouldn't happen!\n"); + /* We really shouldn't try to send the + * IM now, but I'm not sure what to do */ + g_error_free(err); + } + } else if (args.flags & AIM_IMFLAGS_ISO_8859_1) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Sending ISO-8859-1 IM\n"); + args.charset = 0x0003; + args.charsubset = 0x0000; + args.msg = g_convert(tmpmsg, len, "ISO-8859-1", "UTF-8", NULL, &len, &err); + if (err) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "conversion error: %s\n", err->message); + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Someone tell Ethan his 8859-1 detection is wrong\n"); + args.flags ^= AIM_IMFLAGS_ISO_8859_1 | AIM_IMFLAGS_UNICODE; + len = strlen(tmpmsg); + g_error_free(err); + args.msg = g_convert(tmpmsg, len, "UCS-2BE", "UTF8", NULL, &len, &err); + if (err) { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Error in unicode fallback: %s\n", err->message); + g_error_free(err); + } + } + } else { + args.charset = 0x0000; + args.charsubset = 0x0000; + args.msg = tmpmsg; + } + args.msglen = len; + + ret = aim_im_sendch1_ext(od->sess, &args); + } + + g_free(tmpmsg); + + if (ret >= 0) + return 1; + + return ret; +} + +static void oscar_get_info(GaimConnection *gc, const char *name) { + OscarData *od = (OscarData *)gc->proto_data; + + if (od->icq && isdigit(name[0])) + aim_icq_getallinfo(od->sess, name); + else + aim_locate_getinfoshort(od->sess, name, 0x00000003); +} + +static void oscar_get_away(GaimConnection *gc, const char *who) { + OscarData *od = (OscarData *)gc->proto_data; + if (od->icq) { + GaimBuddy *budlight = gaim_find_buddy(gc->account, who); + if (budlight) + if ((budlight->uc & 0xffff0000) >> 16) + aim_im_sendch2_geticqaway(od->sess, who, (budlight->uc & 0xffff0000) >> 16); + else + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Error: The user %s has no status message, therefore not requesting.\n", who); + else + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Error: Could not find %s in local buddy list, therefore unable to request status message.\n", who); + } else + aim_locate_getinfoshort(od->sess, who, 0x00000002); +} + +static void oscar_set_dir(GaimConnection *gc, const char *first, const char *middle, const char *last, + const char *maiden, const char *city, const char *state, const char *country, int web) { + /* XXX - some of these things are wrong, but i'm lazy */ + OscarData *od = (OscarData *)gc->proto_data; + aim_locate_setdirinfo(od->sess, first, middle, last, + maiden, NULL, NULL, city, state, NULL, 0, web); +} + +static void oscar_set_idle(GaimConnection *gc, int time) { + OscarData *od = (OscarData *)gc->proto_data; + aim_srv_setidle(od->sess, time); +} + +static void oscar_set_info(GaimConnection *gc, const char *text) { + OscarData *od = (OscarData *)gc->proto_data; + fu32_t flags = 0; + char *text_html = NULL; + char *msg = NULL; + gsize msglen = 0; + + if (od->rights.maxsiglen == 0) + gaim_notify_warning(gc, NULL, _("Unable to set AIM profile."), + _("You have probably requested to set your " + "profile before the login procedure completed. " + "Your profile remains unset; try setting it " + "again when you are fully connected.")); + + if (!text) { + aim_locate_setprofile(od->sess, NULL, "", 0, NULL, NULL, 0); + return; + } + + text_html = gaim_strdup_withhtml(text); + flags = oscar_encoding_check(text_html); + if (flags & AIM_IMFLAGS_UNICODE) { + msg = g_convert(text_html, strlen(text_html), "UCS-2BE", "UTF-8", NULL, &msglen, NULL); + aim_locate_setprofile(od->sess, "unicode-2-0", msg, (msglen > od->rights.maxsiglen ? od->rights.maxsiglen : msglen), NULL, NULL, 0); + g_free(msg); + } else if (flags & AIM_IMFLAGS_ISO_8859_1) { + msg = g_convert(text_html, strlen(text_html), "ISO-8859-1", "UTF-8", NULL, &msglen, NULL); + aim_locate_setprofile(od->sess, "iso-8859-1", msg, (msglen > od->rights.maxsiglen ? od->rights.maxsiglen : msglen), NULL, NULL, 0); + g_free(msg); + } else { + msglen = strlen(text_html); + aim_locate_setprofile(od->sess, "us-ascii", text_html, (msglen > od->rights.maxsiglen ? od->rights.maxsiglen : msglen), NULL, NULL, 0); + } + + if (msglen > od->rights.maxsiglen) { + gchar *errstr; + errstr = g_strdup_printf(ngettext("The maximum profile length of %d byte " + "has been exceeded. Gaim has truncated it for you.", + "The maximum profile length of %d bytes " + "has been exceeded. Gaim has truncated it for you.", + od->rights.maxsiglen), od->rights.maxsiglen); + gaim_notify_warning(gc, NULL, _("Profile too long."), errstr); + g_free(errstr); + } + + g_free(text_html); + + return; +} + +static void oscar_set_away_aim(GaimConnection *gc, OscarData *od, const char *text) +{ + fu32_t flags = 0; + gchar *text_html = NULL; + char *msg = NULL; + gsize msglen = 0; + + if (od->rights.maxawaymsglen == 0) + gaim_notify_warning(gc, NULL, _("Unable to set AIM away message."), + _("You have probably requested to set your " + "away message before the login procedure " + "completed. You remain in a \"present\" " + "state; try setting it again when you are " + "fully connected.")); + + if (gc->away) { + g_free(gc->away); + gc->away = NULL; + } + + if (!text) { + aim_locate_setprofile(od->sess, NULL, NULL, 0, NULL, "", 0); + return; + } + + text_html = gaim_strdup_withhtml(text); + flags = oscar_encoding_check(text_html); + if (flags & AIM_IMFLAGS_UNICODE) { + msg = g_convert(text_html, strlen(text_html), "UCS-2BE", "UTF-8", NULL, &msglen, NULL); + aim_locate_setprofile(od->sess, NULL, NULL, 0, "unicode-2-0", msg, + (msglen > od->rights.maxawaymsglen ? od->rights.maxawaymsglen : msglen)); + g_free(msg); + gc->away = g_strndup(text, od->rights.maxawaymsglen/2); + } else if (flags & AIM_IMFLAGS_ISO_8859_1) { + msg = g_convert(text_html, strlen(text_html), "ISO-8859-1", "UTF-8", NULL, &msglen, NULL); + aim_locate_setprofile(od->sess, NULL, NULL, 0, "iso-8859-1", msg, + (msglen > od->rights.maxawaymsglen ? od->rights.maxawaymsglen : msglen)); + g_free(msg); + gc->away = g_strndup(text_html, od->rights.maxawaymsglen); + } else { + msglen = strlen(text_html); + aim_locate_setprofile(od->sess, NULL, NULL, 0, "us-ascii", text_html, + (msglen > od->rights.maxawaymsglen ? od->rights.maxawaymsglen : msglen)); + gc->away = g_strndup(text_html, od->rights.maxawaymsglen); + } + + if (msglen > od->rights.maxawaymsglen) { + gchar *errstr; + + errstr = g_strdup_printf(ngettext("The maximum away message length of %d byte " + "has been exceeded. Gaim has truncated it for you.", + "The maximum away message length of %d bytes " + "has been exceeded. Gaim has truncated it for you.", + od->rights.maxawaymsglen), od->rights.maxawaymsglen); + gaim_notify_warning(gc, NULL, _("Away message too long."), errstr); + g_free(errstr); + } + + g_free(text_html); + return; +} + +static void oscar_set_away_icq(GaimConnection *gc, OscarData *od, const char *state, const char *message) +{ + GaimAccount *account = gaim_connection_get_account(gc); + if (gc->away) { + g_free(gc->away); + gc->away = NULL; + } + + if (strcmp(state, _("Invisible"))) { + if ((od->sess->ssi.received_data) && (aim_ssi_getpermdeny(od->sess->ssi.local) != account->perm_deny)) + aim_ssi_setpermdeny(od->sess, account->perm_deny, 0xffffffff); + account->perm_deny = 4; + } else { + if ((od->sess->ssi.received_data) && (aim_ssi_getpermdeny(od->sess->ssi.local) != 0x03)) + aim_ssi_setpermdeny(od->sess, 0x03, 0xffffffff); + account->perm_deny = 3; + } + + if (!strcmp(state, _("Online"))) + aim_setextstatus(od->sess, AIM_ICQ_STATE_NORMAL); + else if (!strcmp(state, _("Away"))) { + aim_setextstatus(od->sess, AIM_ICQ_STATE_AWAY); + gc->away = g_strdup(""); + } else if (!strcmp(state, _("Do Not Disturb"))) { + aim_setextstatus(od->sess, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY); + gc->away = g_strdup(""); + } else if (!strcmp(state, _("Not Available"))) { + aim_setextstatus(od->sess, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY); + gc->away = g_strdup(""); + } else if (!strcmp(state, _("Occupied"))) { + aim_setextstatus(od->sess, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY); + gc->away = g_strdup(""); + } else if (!strcmp(state, _("Free For Chat"))) { + aim_setextstatus(od->sess, AIM_ICQ_STATE_CHAT); + gc->away = g_strdup(""); + } else if (!strcmp(state, _("Invisible"))) { + aim_setextstatus(od->sess, AIM_ICQ_STATE_INVISIBLE); + gc->away = g_strdup(""); + } else if (!strcmp(state, GAIM_AWAY_CUSTOM)) { + if (message) { + aim_setextstatus(od->sess, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY); + gc->away = g_strdup(""); + } else { + aim_setextstatus(od->sess, AIM_ICQ_STATE_NORMAL); + } + } + + return; +} + +static void oscar_set_away(GaimConnection *gc, const char *state, const char *message) +{ + OscarData *od = (OscarData *)gc->proto_data; + + if (od->icq) + oscar_set_away_icq(gc, od, state, message); + else + oscar_set_away_aim(gc, od, message); + + return; +} + +static void oscar_warn(GaimConnection *gc, const char *name, int anon) { + OscarData *od = (OscarData *)gc->proto_data; + aim_im_warn(od->sess, od->conn, name, anon ? AIM_WARN_ANON : 0); +} + +static void oscar_dir_search(GaimConnection *gc, const char *first, const char *middle, const char *last, + const char *maiden, const char *city, const char *state, const char *country, const char *email) { + OscarData *od = (OscarData *)gc->proto_data; + if (strlen(email)) + aim_search_address(od->sess, od->conn, email); +} + +static void oscar_add_buddy(GaimConnection *gc, const char *name, GaimGroup *g) { + OscarData *od = (OscarData *)gc->proto_data; +#ifdef NOSSI + aim_add_buddy(od->sess, od->conn, name); +#else + if ((od->sess->ssi.received_data) && !(aim_ssi_itemlist_exists(od->sess->ssi.local, name))) { + GaimBuddy *buddy = gaim_find_buddy(gc->account, name); + GaimGroup *group = gaim_find_buddys_group(buddy); + if (buddy && group) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding buddy %s to group %s\n", name, group->name); + aim_ssi_addbuddy(od->sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 0); + } + } +#endif + if (od->icq) + aim_icq_getalias(od->sess, name); +} + +static void oscar_add_buddies(GaimConnection *gc, GList *buddies) { + OscarData *od = (OscarData *)gc->proto_data; +#ifdef NOSSI + char buf[MSG_LEN]; + int n=0; + while (buddies) { + if (n > MSG_LEN - 18) { + aim_buddylist_set(od->sess, od->conn, buf); + n = 0; + } + n += g_snprintf(buf + n, sizeof(buf) - n, "%s&", (char *)buddies->data); + buddies = buddies->next; + } + aim_buddylist_set(od->sess, od->conn, buf); +#else + if (od->sess->ssi.received_data) { + while (buddies) { + oscar_add_buddy(gc, (const char *)buddies->data, NULL); + buddies = buddies->next; + } + } +#endif +} + +static void oscar_remove_buddy(GaimConnection *gc, const char *name, const char *group) { + OscarData *od = (OscarData *)gc->proto_data; +#ifdef NOSSI + aim_remove_buddy(od->sess, od->conn, name); +#else + if (od->sess->ssi.received_data) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: deleting buddy %s from group %s\n", name, group); + aim_ssi_delbuddy(od->sess, name, group); + } +#endif +} + +static void oscar_remove_buddies(GaimConnection *gc, GList *buddies, const char *group) { + OscarData *od = (OscarData *)gc->proto_data; +#ifdef NOSSI + GList *cur; + for (cur=buddies; cur; cur=cur->next) + aim_remove_buddy(od->sess, od->conn, cur->data); +#else + if (od->sess->ssi.received_data) { + while (buddies) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: deleting buddy %s from group %s\n", (char *)buddies->data, group); + aim_ssi_delbuddy(od->sess, buddies->data, group); + buddies = buddies->next; + } + } +#endif +} + +#ifndef NOSSI +static void oscar_move_buddy(GaimConnection *gc, const char *name, const char *old_group, const char *new_group) { + OscarData *od = (OscarData *)gc->proto_data; + if (od->sess->ssi.received_data && strcmp(old_group, new_group)) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: moving buddy %s from group %s to group %s\n", name, old_group, new_group); + aim_ssi_movebuddy(od->sess, old_group, new_group, name); + } +} + +static void oscar_alias_buddy(GaimConnection *gc, const char *name, const char *alias) { + OscarData *od = (OscarData *)gc->proto_data; + if (od->sess->ssi.received_data) { + char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, name); + if (gname) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: changing the alias for buddy %s to %s\n", name, alias); + aim_ssi_aliasbuddy(od->sess, gname, name, alias); + } + } +} + +static void oscar_rename_group(GaimConnection *g, const char *old_group, const char *new_group, GList *members) { + OscarData *od = (OscarData *)g->proto_data; + + if (od->sess->ssi.received_data) { + if (aim_ssi_itemlist_finditem(od->sess->ssi.local, new_group, NULL, AIM_SSI_TYPE_GROUP)) { + oscar_remove_buddies(g, members, old_group); + oscar_add_buddies(g, members); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: moved all buddies from group %s to %s\n", old_group, new_group); + } else { + aim_ssi_rename_group(od->sess, old_group, new_group); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: renamed group %s to %s\n", old_group, new_group); + } + } +} + +static gboolean gaim_ssi_rerequestdata(gpointer data) { + aim_session_t *sess = data; + aim_ssi_reqdata(sess); + return FALSE; +} + +static int gaim_ssi_parseerr(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + va_list ap; + fu16_t reason; + + va_start(ap, fr); + reason = (fu16_t)va_arg(ap, unsigned int); + va_end(ap); + + gaim_debug(GAIM_DEBUG_ERROR, "oscar", "ssi: SNAC error %hu\n", reason); + + if (reason == 0x0005) { + gaim_notify_error(gc, NULL, _("Unable To Retrieve Buddy List"), + _("Gaim was temporarily unable to retrieve your buddy list from the AIM servers. Your buddy list is not lost, and will probably become available in a few hours.")); + od->getblisttimer = g_timeout_add(300000, gaim_ssi_rerequestdata, od->sess); + } + + /* Activate SSI */ + /* Sending the enable causes other people to be able to see you, and you to see them */ + /* Make sure your privacy setting/invisibility is set how you want it before this! */ + gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: activating server-stored buddy list\n"); + aim_ssi_enable(od->sess); + + return 1; +} + +static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = (OscarData *)gc->proto_data; + int numtypes, i; + fu16_t *maxitems; + va_list ap; + + va_start(ap, fr); + numtypes = va_arg(ap, int); + maxitems = va_arg(ap, fu16_t *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_MISC, "oscar", "ssi rights:"); + + for (i=0; i<numtypes; i++) + gaim_debug(GAIM_DEBUG_MISC, NULL, " max type 0x%04x=%hd,", + i, maxitems[i]); + + gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); + + if (numtypes >= 0) + od->rights.maxbuddies = maxitems[0]; + if (numtypes >= 1) + od->rights.maxgroups = maxitems[1]; + if (numtypes >= 2) + od->rights.maxpermits = maxitems[2]; + if (numtypes >= 3) + od->rights.maxdenies = maxitems[3]; + + return 1; +} + +static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + GaimAccount *account = gaim_connection_get_account(gc); + OscarData *od = (OscarData *)gc->proto_data; + struct aim_ssi_item *curitem; + int tmp; + gboolean export = FALSE; + /* XXX - use these? + va_list ap; + + va_start(ap, fr); + fmtver = (fu16_t)va_arg(ap, int); + numitems = (fu16_t)va_arg(ap, int); + items = va_arg(ap, struct aim_ssi_item); + timestamp = va_arg(ap, fu32_t); + va_end(ap); */ + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: syncing local list and server list\n"); + + /* Clean the buddy list */ + aim_ssi_cleanlist(sess); + + /* Add from server list to local list */ + for (curitem=sess->ssi.local; curitem; curitem=curitem->next) { + if ((curitem->name == NULL) || (g_utf8_validate(curitem->name, -1, NULL))) + switch (curitem->type) { + case 0x0000: { /* Buddy */ + if (curitem->name) { + char *gname = aim_ssi_itemlist_findparentname(sess->ssi.local, curitem->name); + char *gname_utf8 = gname ? gaim_utf8_try_convert(gname) : NULL; + char *alias = aim_ssi_getalias(sess->ssi.local, gname, curitem->name); + char *alias_utf8 = alias ? gaim_utf8_try_convert(alias) : NULL; + GaimBuddy *buddy = gaim_find_buddy(gc->account, curitem->name); + /* Should gname be freed here? -- elb */ + /* Not with the current code, but that might be cleaner -- med */ + free(alias); + if (buddy) { + /* Get server stored alias */ + if (alias_utf8) { + g_free(buddy->alias); + buddy->alias = g_strdup(alias_utf8); + } + } else { + GaimGroup *g; + buddy = gaim_buddy_new(gc->account, curitem->name, alias_utf8); + + if (!(g = gaim_find_group(gname_utf8 ? gname_utf8 : _("Orphans")))) { + g = gaim_group_new(gname_utf8 ? gname_utf8 : _("Orphans")); + gaim_blist_add_group(g, NULL); + } + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname_utf8 ? gname_utf8 : _("Orphans")); + gaim_blist_add_buddy(buddy, NULL, g, NULL); + export = TRUE; + } + g_free(gname_utf8); + g_free(alias_utf8); + } + } break; + + case 0x0001: { /* Group */ + /* Shouldn't add empty groups */ + } break; + + case 0x0002: { /* Permit buddy */ + if (curitem->name) { + /* if (!find_permdeny_by_name(gc->permit, curitem->name)) { AAA */ + GSList *list; + for (list=account->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next); + if (!list) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding permit buddy %s to local list\n", curitem->name); + gaim_privacy_permit_add(account, curitem->name, TRUE); + export = TRUE; + } + } + } break; + + case 0x0003: { /* Deny buddy */ + if (curitem->name) { + GSList *list; + for (list=account->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next); + if (!list) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding deny buddy %s to local list\n", curitem->name); + gaim_privacy_deny_add(account, curitem->name, TRUE); + export = TRUE; + } + } + } break; + + case 0x0004: { /* Permit/deny setting */ + if (curitem->data) { + fu8_t permdeny; + if ((permdeny = aim_ssi_getpermdeny(sess->ssi.local)) && (permdeny != account->perm_deny)) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, permdeny); + account->perm_deny = permdeny; + if (od->icq && account->perm_deny == 0x03) { + serv_set_away(gc, "Invisible", ""); + } + export = TRUE; + } + } + } break; + + case 0x0005: { /* Presence setting */ + /* We don't want to change Gaim's setting because it applies to all accounts */ + } break; + } /* End of switch on curitem->type */ + } /* End of for loop */ + + /* If changes were made, then flush buddy list to file */ + if (export) + gaim_blist_save(); + + { /* Add from local list to server list */ + GaimBlistNode *gnode, *cnode, *bnode; + GaimGroup *group; + GaimBuddy *buddy; + GaimBuddyList *blist; + GSList *cur; + + /* Buddies */ + if ((blist = gaim_get_blist())) + for (gnode = blist->root; gnode; gnode = gnode->next) { + if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) + continue; + group = (GaimGroup *)gnode; + for (cnode = gnode->child; cnode; cnode = cnode->next) { + if(!GAIM_BLIST_NODE_IS_CONTACT(cnode)) + continue; + for (bnode = cnode->child; bnode; bnode = bnode->next) { + if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) + continue; + buddy = (GaimBuddy *)bnode; + if (buddy->account == gc->account) { + const char *servernick = gaim_buddy_get_setting(buddy, "servernick"); + if (servernick) + serv_got_alias(gc, buddy->name, servernick); + + if (aim_ssi_itemlist_exists(sess->ssi.local, buddy->name)) { + /* Store local alias on server */ + char *alias = aim_ssi_getalias(sess->ssi.local, group->name, buddy->name); + if (!alias && buddy->alias && strlen(buddy->alias)) + aim_ssi_aliasbuddy(sess, group->name, buddy->name, buddy->alias); + free(alias); + } else { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding buddy %s from local list to server list\n", buddy->name); + aim_ssi_addbuddy(sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 0); + } + } + } + } + } + + /* Permit list */ + if (gc->account->permit) { + for (cur=gc->account->permit; cur; cur=cur->next) + if (!aim_ssi_itemlist_finditem(sess->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding permit %s from local list to server list\n", (char *)cur->data); + aim_ssi_addpermit(sess, cur->data); + } + } + + /* Deny list */ + if (gc->account->deny) { + for (cur=gc->account->deny; cur; cur=cur->next) + if (!aim_ssi_itemlist_finditem(sess->ssi.local, NULL, cur->data, AIM_SSI_TYPE_DENY)) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: adding deny %s from local list to server list\n", (char *)cur->data); + aim_ssi_adddeny(sess, cur->data); + } + } + /* Presence settings (idle time visibility) */ + if ((tmp = aim_ssi_getpresence(sess->ssi.local)) != 0xFFFFFFFF) + if (!(tmp & 0x400)) + aim_ssi_setpresence(sess, tmp | 0x400); + } /* end adding buddies from local list to server list */ + + /* Set our ICQ status */ + if (od->icq && !gc->away) { + aim_setextstatus(sess, AIM_ICQ_STATE_NORMAL); + } + + /* Activate SSI */ + /* Sending the enable causes other people to be able to see you, and you to see them */ + /* Make sure your privacy setting/invisibility is set how you want it before this! */ + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: activating server-stored buddy list\n"); + aim_ssi_enable(sess); + + return 1; +} + +static int gaim_ssi_parseack(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + va_list ap; + struct aim_ssi_tmp *retval; + + va_start(ap, fr); + retval = va_arg(ap, struct aim_ssi_tmp *); + va_end(ap); + + while (retval) { + gaim_debug(GAIM_DEBUG_MISC, "oscar", + "ssi: status is 0x%04hx for a 0x%04hx action with name %s\n", retval->ack, retval->action, retval->item ? (retval->item->name ? retval->item->name : "no name") : "no item"); + + if (retval->ack != 0xffff) + switch (retval->ack) { + case 0x0000: { /* added successfully */ + } break; + + case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */ + gchar *buf; + buf = g_strdup_printf(_("Could not add the buddy %s because you have too many buddies in your buddy list. Please remove one and try again."), (retval->name ? retval->name : _("(no name)"))); + gaim_notify_error(gc, NULL, _("Unable To Add"), buf); + g_free(buf); + } + + case 0x000e: { /* buddy requires authorization */ + if ((retval->action == AIM_CB_SSI_ADD) && (retval->name)) + gaim_auth_sendrequest(gc, retval->name); + } break; + + default: { /* La la la */ + gchar *buf; + gaim_debug(GAIM_DEBUG_ERROR, "oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval->action, retval->ack); + buf = g_strdup_printf(_("Could not add the buddy %s for an unknown reason. The most common reason for this is that you have the maximum number of allowed buddies in your buddy list."), (retval->name ? retval->name : _("(no name)"))); + gaim_notify_error(gc, NULL, _("Unable To Add"), buf); + g_free(buf); + /* XXX - Should remove buddy from local list */ + } break; + } + + retval = retval->next; + } + + return 1; +} + +static int gaim_ssi_authgiven(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + va_list ap; + char *sn, *msg; + gchar *dialog_msg, *nombre; + struct name_data *data; + GaimBuddy *buddy; + + va_start(ap, fr); + sn = va_arg(ap, char *); + msg = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: %s has given you permission to add him to your buddy list\n", sn); + + buddy = gaim_find_buddy(gc->account, sn); + if (buddy && (gaim_get_buddy_alias_only(buddy))) + nombre = g_strdup_printf("%s (%s)", sn, gaim_get_buddy_alias_only(buddy)); + else + nombre = g_strdup(sn); + + dialog_msg = g_strdup_printf(_("The user %s has given you permission to add you to their buddy list. Do you want to add them?"), nombre); + data = g_new(struct name_data, 1); + data->gc = gc; + data->name = g_strdup(sn); + data->nick = NULL; + + gaim_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg, + 0, data, + G_CALLBACK(gaim_icq_buddyadd), + G_CALLBACK(oscar_free_name_data)); + + g_free(dialog_msg); + g_free(nombre); + + return 1; +} + +static int gaim_ssi_authrequest(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + va_list ap; + char *sn, *msg; + gchar *dialog_msg, *nombre; + struct name_data *data; + GaimBuddy *buddy; + + va_start(ap, fr); + sn = va_arg(ap, char *); + msg = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: received authorization request from %s\n", sn); + + buddy = gaim_find_buddy(gc->account, sn); + if (buddy && (gaim_get_buddy_alias_only(buddy))) + nombre = g_strdup_printf("%s (%s)", sn, gaim_get_buddy_alias_only(buddy)); + else + nombre = g_strdup(sn); + + dialog_msg = g_strdup_printf(_("The user %s wants to add you to their buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given.")); + data = g_new(struct name_data, 1); + data->gc = gc; + data->name = g_strdup(sn); + data->nick = NULL; + + gaim_request_action(gc, NULL, _("Authorization Request"), dialog_msg, + 0, data, 2, + _("Authorize"), G_CALLBACK(gaim_auth_grant), + _("Deny"), G_CALLBACK(gaim_auth_dontgrant_msgprompt)); + + g_free(dialog_msg); + g_free(nombre); + + return 1; +} + +static int gaim_ssi_authreply(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + va_list ap; + char *sn, *msg; + gchar *dialog_msg, *nombre; + fu8_t reply; + GaimBuddy *buddy; + + va_start(ap, fr); + sn = va_arg(ap, char *); + reply = (fu8_t)va_arg(ap, int); + msg = va_arg(ap, char *); + va_end(ap); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: received authorization reply from %s. Reply is 0x%04hhx\n", sn, reply); + + buddy = gaim_find_buddy(gc->account, sn); + if (buddy && (gaim_get_buddy_alias_only(buddy))) + nombre = g_strdup_printf("%s (%s)", sn, gaim_get_buddy_alias_only(buddy)); + else + nombre = g_strdup(sn); + + if (reply) { + /* Granted */ + dialog_msg = g_strdup_printf(_("The user %s has granted your request to add them to your buddy list."), nombre); + gaim_notify_info(gc, NULL, _("Authorization Granted"), dialog_msg); + } else { + /* Denied */ + dialog_msg = g_strdup_printf(_("The user %s has denied your request to add them to your buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given.")); + gaim_notify_info(gc, NULL, _("Authorization Denied"), dialog_msg); + } + g_free(dialog_msg); + g_free(nombre); + + return 1; +} + +static int gaim_ssi_gotadded(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + va_list ap; + char *sn; + GaimBuddy *buddy; + + va_start(ap, fr); + sn = va_arg(ap, char *); + va_end(ap); + + buddy = gaim_find_buddy(gc->account, sn); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ssi: %s added you to their buddy list\n", sn); + gaim_account_notify_added(gc->account, NULL, sn, (buddy ? gaim_get_buddy_alias_only(buddy) : NULL), NULL); + + return 1; +} +#endif + +static GList *oscar_chat_info(GaimConnection *gc) { + GList *m = NULL; + struct proto_chat_entry *pce; + + pce = g_new0(struct proto_chat_entry, 1); + pce->label = _("Join what group:"); + pce->identifier = "room"; + m = g_list_append(m, pce); + + pce = g_new0(struct proto_chat_entry, 1); + pce->label = _("Exchange:"); + pce->identifier = "exchange"; + pce->is_int = TRUE; + pce->min = 4; + pce->max = 20; + m = g_list_append(m, pce); + + return m; +} + +static void oscar_join_chat(GaimConnection *g, GHashTable *data) { + OscarData *od = (OscarData *)g->proto_data; + aim_conn_t *cur; + char *name, *exchange; + + name = g_hash_table_lookup(data, "room"); + exchange = g_hash_table_lookup(data, "exchange"); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Attempting to join chat room %s.\n", name); + if ((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) { + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "chatnav exists, creating room\n"); + aim_chatnav_createroom(od->sess, cur, name, atoi(exchange)); + } else { + /* this gets tricky */ + struct create_room *cr = g_new0(struct create_room, 1); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "chatnav does not exist, opening chatnav\n"); + cr->exchange = atoi(exchange); + cr->name = g_strdup(name); + od->create_rooms = g_slist_append(od->create_rooms, cr); + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV); + } +} + +static void oscar_chat_invite(GaimConnection *g, int id, const char *message, const char *name) { + OscarData *od = (OscarData *)g->proto_data; + struct chat_connection *ccon = find_oscar_chat(g, id); + + if (!ccon) + return; + + aim_chat_invite(od->sess, od->conn, name, message ? message : "", + ccon->exchange, ccon->name, 0x0); +} + +static void oscar_chat_leave(GaimConnection *g, int id) { + OscarData *od = g ? (OscarData *)g->proto_data : NULL; + GSList *bcs = g->buddy_chats; + GaimConversation *b = NULL; + struct chat_connection *c = NULL; + int count = 0; + + while (bcs) { + count++; + b = (GaimConversation *)bcs->data; + if (id == gaim_conv_chat_get_id(GAIM_CONV_CHAT(b))) + break; + bcs = bcs->next; + b = NULL; + } + + if (!b) + return; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Attempting to leave room %s (currently in %d rooms)\n", b->name, count); + + c = find_oscar_chat(g, gaim_conv_chat_get_id(GAIM_CONV_CHAT(b))); + if (c != NULL) { + if (od) + od->oscar_chats = g_slist_remove(od->oscar_chats, c); + if (c->inpa > 0) + gaim_input_remove(c->inpa); + if (g && od->sess) + aim_conn_kill(od->sess, &c->conn); + g_free(c->name); + g_free(c->show); + g_free(c); + } + /* we do this because with Oscar it doesn't tell us we left */ + serv_got_chat_left(g, gaim_conv_chat_get_id(GAIM_CONV_CHAT(b))); +} + +static int oscar_chat_send(GaimConnection *g, int id, const char *message) { + OscarData *od = (OscarData *)g->proto_data; + GSList *bcs = g->buddy_chats; + GaimConversation *b = NULL; + struct chat_connection *c = NULL; + char *buf, *buf2; + int i, j; + + while (bcs) { + b = (GaimConversation *)bcs->data; + if (id == gaim_conv_chat_get_id(GAIM_CONV_CHAT(b))) + break; + bcs = bcs->next; + b = NULL; + } + if (!b) + return -EINVAL; + + bcs = od->oscar_chats; + while (bcs) { + c = (struct chat_connection *)bcs->data; + if (b == c->cnv) + break; + bcs = bcs->next; + c = NULL; + } + if (!c) + return -EINVAL; + + buf = g_malloc(strlen(message) * 4 + 1); + for (i = 0, j = 0; i < strlen(message); i++) { + if (message[i] == '\n') { + buf[j++] = '<'; + buf[j++] = 'B'; + buf[j++] = 'R'; + buf[j++] = '>'; + } else { + buf[j++] = message[i]; + } + } + buf[j] = '\0'; + + if (strlen(buf) > c->maxlen) + return -E2BIG; + + buf2 = gaim_markup_strip_html(buf); + if (strlen(buf2) > c->maxvis) { + g_free(buf2); + return -E2BIG; + } + g_free(buf2); + + aim_chat_send_im(od->sess, c->conn, 0, buf, strlen(buf)); + g_free(buf); + return 0; +} + +static const char *oscar_list_icon(GaimAccount *a, GaimBuddy *b) { + if (!b || (b && b->name && b->name[0] == '+')) { + if (a != NULL && isdigit(*gaim_account_get_username(a))) + return "icq"; + else + return "aim"; + } + + if (b != NULL && isdigit(b->name[0])) + return "icq"; + return "aim"; +} + +static void oscar_list_emblems(GaimBuddy *b, char **se, char **sw, char **nw, char **ne) +{ + GaimAccount *account = NULL; + GaimConnection *gc = NULL; + OscarData *od = NULL; + char *emblems[4] = {NULL,NULL,NULL,NULL}; + int i = 0; + aim_userinfo_t *userinfo = NULL; + + if (b != NULL) + account = b->account; + if (account != NULL) + gc = account->gc; + if (gc != NULL) + od = gc->proto_data; + + if (!GAIM_BUDDY_IS_ONLINE(b)) { + char *gname; + if ((b->name) && (od) && (od->sess->ssi.received_data) && + (gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, b->name)) && + (aim_ssi_waitingforauth(od->sess->ssi.local, gname, b->name))) { + emblems[i++] = "notauthorized"; + } else { + emblems[i++] = "offline"; + } + } + + if (b->name && (b->uc & 0xffff0000) && isdigit(b->name[0])) { + int uc = b->uc >> 16; + if (uc & AIM_ICQ_STATE_INVISIBLE) + emblems[i++] = "invisible"; + else if (uc & AIM_ICQ_STATE_CHAT) + emblems[i++] = "freeforchat"; + else if (uc & AIM_ICQ_STATE_DND) + emblems[i++] = "dnd"; + else if (uc & AIM_ICQ_STATE_OUT) + emblems[i++] = "na"; + else if (uc & AIM_ICQ_STATE_BUSY) + emblems[i++] = "occupied"; + else if (uc & AIM_ICQ_STATE_AWAY) + emblems[i++] = "away"; + } else { + if (b->uc & UC_UNAVAILABLE) + emblems[i++] = "away"; + } + if (b->uc & UC_WIRELESS) + emblems[i++] = "wireless"; + if (b->uc & UC_AOL) + emblems[i++] = "aol"; + if (b->uc & UC_ADMIN) + emblems[i++] = "admin"; + if (b->uc & UC_AB && i < 4) + emblems[i++] = "activebuddy"; + if (b->uc & UC_HIPTOP && i < 4) + emblems[i++] = "hiptop"; +/* if (b->uc & UC_UNCONFIRMED && i < 4) + emblems[i++] = "unconfirmed"; */ + + if ((i < 4) && (od != NULL)) { + userinfo = aim_locate_finduserinfo(od->sess, b->name); + if ((userinfo != NULL) && (userinfo->capabilities & AIM_CAPS_SECUREIM)) + emblems[i++] = "secure"; + } + + *se = emblems[0]; + *sw = emblems[1]; + *nw = emblems[2]; + *ne = emblems[3]; +} + +static char *oscar_tooltip_text(GaimBuddy *b) { + GaimConnection *gc = b->account->gc; + OscarData *od = gc->proto_data; + struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(b->account, b->name)); + aim_userinfo_t *userinfo = aim_locate_finduserinfo(od->sess, b->name); + gchar *tmp = NULL, *ret = g_strdup(""); + + if (GAIM_BUDDY_IS_ONLINE(b)) { + if (isdigit(b->name[0])) { + char *status; + status = gaim_icq_status((b->uc & 0xffff0000) >> 16); + tmp = ret; + ret = g_strconcat(tmp, _("<b>Status:</b> "), status, "\n", NULL); + g_free(tmp); + g_free(status); + } + + if (userinfo != NULL) { + char *tstr = gaim_str_seconds_to_string(time(NULL) - userinfo->onlinesince + + (gc->login_time_official ? gc->login_time_official - gc->login_time : 0)); + tmp = ret; + ret = g_strconcat(tmp, _("<b>Logged In:</b> "), tstr, "\n", NULL); + g_free(tmp); + g_free(tstr); + } + + if ((bi != NULL) && (bi->ipaddr)) { + char *tstr = g_strdup_printf("%hhu.%hhu.%hhu.%hhu", + (bi->ipaddr & 0xff000000) >> 24, + (bi->ipaddr & 0x00ff0000) >> 16, + (bi->ipaddr & 0x0000ff00) >> 8, + (bi->ipaddr & 0x000000ff)); + tmp = ret; + ret = g_strconcat(tmp, _("<b>IP Address:</b> "), tstr, "\n", NULL); + g_free(tmp); + g_free(tstr); + } + + if ((userinfo != NULL) && (userinfo->capabilities)) { + char *caps = caps_string(userinfo->capabilities); + tmp = ret; + ret = g_strconcat(tmp, _("<b>Capabilities:</b> "), caps, "\n", NULL); + g_free(tmp); + } + + if ((bi != NULL) && (bi->availmsg != NULL) && !(b->uc & UC_UNAVAILABLE)) { + gchar *escaped = g_markup_escape_text(bi->availmsg, strlen(bi->availmsg)); + tmp = ret; + ret = g_strconcat(tmp, _("<b>Available:</b> "), escaped, "\n", NULL); + g_free(tmp); + g_free(escaped); + } + + if ((userinfo != NULL) && (userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) { + gchar *away_utf8 = oscar_encoding_to_utf8(userinfo->away_encoding, userinfo->away, userinfo->away_len); + if (away_utf8 != NULL) { + gchar *tmp1, *tmp2; + const char *tmp3; + tmp1 = gaim_strreplace(away_utf8, "<BR>", "\n"); + tmp2 = gaim_markup_strip_html(tmp1); + g_free(tmp1); + tmp1 = g_markup_escape_text(tmp2, strlen(tmp2)); + g_free(tmp2); + tmp3 = gaim_str_sub_away_formatters(tmp1, gaim_account_get_username(gaim_connection_get_account(gc))); + g_free(tmp1); + tmp = ret; + ret = g_strconcat(tmp, _("<b>Away Message:</b> "), tmp3, "\n", NULL); + g_free(tmp); + g_free(away_utf8); + } + } + } else { + char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, b->name); + if (aim_ssi_waitingforauth(od->sess->ssi.local, gname, b->name)) { + tmp = ret; + ret = g_strconcat(tmp, _("<b>Status:</b> Not Authorized"), "\n", NULL); + g_free(tmp); + } else { + tmp = ret; + ret = g_strconcat(tmp, _("<b>Status:</b> Offline"), "\n", NULL); + g_free(tmp); + } + } + + /* remove the trailing newline character */ + if (ret) + ret[strlen(ret)-1] = '\0'; + return ret; +} + +static char *oscar_status_text(GaimBuddy *b) { + GaimConnection *gc = b->account->gc; + OscarData *od = gc->proto_data; + gchar *ret = NULL; + + if ((b->uc & UC_UNAVAILABLE) || (((b->uc & 0xffff0000) >> 16) & AIM_ICQ_STATE_CHAT)) { + if (isdigit(b->name[0])) + ret = gaim_icq_status((b->uc & 0xffff0000) >> 16); + else + ret = g_strdup(_("Away")); + } else if (GAIM_BUDDY_IS_ONLINE(b)) { + struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(b->account, b->name)); + if (bi->availmsg) + ret = g_markup_escape_text(bi->availmsg, strlen(bi->availmsg)); + } else { + char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, b->name); + if (aim_ssi_waitingforauth(od->sess->ssi.local, gname, b->name)) + ret = g_strdup(_("Not Authorized")); + else + ret = g_strdup(_("Offline")); + } + + return ret; +} + + +static int oscar_icon_req(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = gc->proto_data; + va_list ap; + fu16_t type; + fu8_t flags = 0, length = 0; + char *md5 = NULL; + + + va_start(ap, fr); + type = va_arg(ap, int); + + switch(type) { + case 0x0000: + case 0x0001: { + flags = va_arg(ap, int); + length = va_arg(ap, int); + md5 = va_arg(ap, char *); + + if (flags == 0x41) { + if (!aim_getconn_type(od->sess, AIM_CONN_TYPE_ICON) && !od->iconconnecting) { + od->iconconnecting = TRUE; + od->set_icon = TRUE; + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_ICON); + } else { + struct stat st; + const char *iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc)); + if (iconfile == NULL) { + /* Set an empty icon, or something */ + } else if (!stat(iconfile, &st)) { + char *buf = g_malloc(st.st_size); + FILE *file = fopen(iconfile, "rb"); + if (file) { + fread(buf, 1, st.st_size, file); + fclose(file); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Uploading icon to icon server\n"); + aim_bart_upload(od->sess, buf, st.st_size); + } else + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Can't open buddy icon file!\n"); + g_free(buf); + } else { + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Can't stat buddy icon file!\n"); + } + } + } else if (flags == 0x81) + aim_ssi_seticon(od->sess, md5, length); + } break; + + case 0x0002: { /* We just set an "available" message? */ + } break; + } + + va_end(ap); + + return 0; +} + +/* + * We have just established a socket with the other dude, so set up some handlers. + */ +static int gaim_odc_initiate(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + OscarData *od = (OscarData *)gc->proto_data; + GaimConversation *cnv; + struct direct_im *dim; + char buf[256]; + char *sn; + va_list ap; + aim_conn_t *newconn, *listenerconn; + + va_start(ap, fr); + newconn = va_arg(ap, aim_conn_t *); + listenerconn = va_arg(ap, aim_conn_t *); + va_end(ap); + + aim_conn_close(listenerconn); + aim_conn_kill(sess, &listenerconn); + + sn = g_strdup(aim_odc_getsn(newconn)); + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "DirectIM: initiate success to %s\n", sn); + dim = find_direct_im(od, sn); + + cnv = gaim_conversation_new(GAIM_CONV_IM, dim->gc->account, sn); + gaim_input_remove(dim->watcher); + dim->conn = newconn; + dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn); + dim->connected = TRUE; + g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), sn); + g_free(sn); + gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL)); + + aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_odc_incoming, 0); + aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_odc_typing, 0); + aim_conn_addhandler(sess, newconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, gaim_odc_update_ui, 0); + + return 1; +} + +/* + * This is called when each chunk of an image is received. It can be used to + * update a progress bar, or to eat lots of dry cat food. Wet cat food is + * nasty, you sicko. + */ +static int gaim_odc_update_ui(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + char *sn; + double percent; + GaimConnection *gc = sess->aux_data; + OscarData *od = (OscarData *)gc->proto_data; + GaimConversation *c; + struct direct_im *dim; + + va_start(ap, fr); + sn = va_arg(ap, char *); + percent = va_arg(ap, double); + va_end(ap); + + if (!(dim = find_direct_im(od, sn))) + return 1; + if (dim->watcher) { + gaim_input_remove(dim->watcher); /* Otherwise, the callback will callback */ + dim->watcher = 0; + } + /* XXX is this really necessary? */ + gaim_core_mainloop_finish_events(); + + c = gaim_find_conversation_with_account(sn, gaim_connection_get_account(gc)); + if (c != NULL) + gaim_conversation_update_progress(c, percent); + dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, + oscar_callback, dim->conn); + + return 1; +} + +/* + * This is called after a direct IM has been received in its entirety. This + * function is passed a long chunk of data which contains the IM with any + * data chunks (images) appended to it. + * + * This function rips out all the data chunks and creates an imgstore for + * each one. In order to do this, it first goes through the IM and takes + * out all the IMG tags. When doing so, it rewrites the original IMG tag + * with one compatable with the imgstore Gaim core code. For each one, we + * then read in chunks of data from the end of the message and actually + * create the img store using the given data. + * + * For somewhat easy reference, here's a sample message + * (without the whitespace and asterisks): + * + * <HTML><BODY BGCOLOR="#ffffff"> + * <FONT LANG="0"> + * This is a really stupid picture:<BR> + * <IMG SRC="Sample.jpg" ID="1" WIDTH="283" HEIGHT="212" DATASIZE="9894"><BR> + * Yeah it is<BR> + * Here is another one:<BR> + * <IMG SRC="Soap Bubbles.bmp" ID="2" WIDTH="256" HEIGHT="256" DATASIZE="65978"> + * </FONT> + * </BODY></HTML> + * <BINARY> + * <DATA ID="1" SIZE="9894">datadatadatadata</DATA> + * <DATA ID="2" SIZE="65978">datadatadatadata</DATA> + * </BINARY> + */ +static int gaim_odc_incoming(aim_session_t *sess, aim_frame_t *fr, ...) { + GaimConnection *gc = sess->aux_data; + GaimConvImFlags imflags = 0; + GString *newmsg = g_string_new(""); + GSList *images = NULL; + va_list ap; + const char *sn, *msg, *msgend, *binary; + size_t len; + int encoding, isawaymsg; + + va_start(ap, fr); + sn = va_arg(ap, const char *); + msg = va_arg(ap, const char *); + len = va_arg(ap, size_t); + encoding = va_arg(ap, int); + isawaymsg = va_arg(ap, int); + va_end(ap); + msgend = msg + len; + + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Got DirectIM message from %s\n", sn); + + if (isawaymsg) + imflags |= GAIM_CONV_IM_AUTO_RESP; + + /* message has a binary trailer */ + if ((binary = gaim_strcasestr(msg, "<binary>"))) { + GData *attribs; + const char *tmp, *start, *end, *last = NULL; + + tmp = msg; + + /* for each valid image tag... */ + while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) { + const char *id, *src, *datasize; + const char *tag = NULL, *data = NULL; + size_t size; + int imgid = 0; + + /* update the location of the last img tag */ + last = end; + + /* grab attributes */ + id = g_datalist_get_data(&attribs, "id"); + src = g_datalist_get_data(&attribs, "src"); + datasize = g_datalist_get_data(&attribs, "datasize"); + + /* if we have id & datasize, build the data tag */ + if (id && datasize) + tag = g_strdup_printf("<data id=\"%s\" size=\"%s\">", id, datasize); + + /* if we have a tag, find the start of the data */ + if (tag && (data = gaim_strcasestr(binary, tag))) + data += strlen(tag); + + /* check the data is here and store it */ + if (data + (size = atoi(datasize)) <= msgend) + imgid = gaim_imgstore_add(data, size, src); + + /* if we have a stored image... */ + if (imgid) { + /* append the message up to the tag */ + newmsg = g_string_append_len(newmsg, tmp, start - tmp); + + /* write the new image tag */ + g_string_append_printf(newmsg, "<IMG ID=\"%d\">", imgid); + + /* and record the image number */ + images = g_slist_append(images, GINT_TO_POINTER(imgid)); + } else { + /* otherwise, copy up to the end of the tag */ + newmsg = g_string_append_len(newmsg, tmp, (end + 1) - tmp); + } + + /* clear the attribute list */ + g_datalist_clear(&attribs); + + /* continue from the end of the tag */ + tmp = end + 1; + } + + /* append any remaining message data (without the > :-) */ + if (last++ && (last < binary)) + newmsg = g_string_append_len(newmsg, last, binary - last); + + /* set the flag if we caught any images */ + if (images) + imflags |= GAIM_CONV_IM_IMAGES; + } else { + g_string_append_len(newmsg, msg, len); + } + + /* XXX - I imagine Paco-Paco will want to do some voodoo with the encoding here */ + serv_got_im(gc, sn, newmsg->str, imflags, time(NULL)); + + /* free up the message */ + g_string_free(newmsg, TRUE); + + /* unref any images we allocated */ + if (images) { + GSList *tmp; + int id; + + for (tmp = images; tmp != NULL; tmp = tmp->next) { + id = GPOINTER_TO_INT(tmp->data); + gaim_imgstore_unref(id); + } + + g_slist_free(images); + } + + return 1; +} + +static int gaim_odc_typing(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + char *sn; + int typing; + GaimConnection *gc = sess->aux_data; + + va_start(ap, fr); + sn = va_arg(ap, char *); + typing = va_arg(ap, int); + va_end(ap); + + if (typing == 0x0002) { + /* I had to leave this. It's just too funny. It reminds me of my sister. */ + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "ohmigod! %s has started typing (DirectIM). He's going to send you a message! *squeal*\n", sn); + serv_got_typing(gc, sn, 0, GAIM_TYPING); + } else if (typing == 0x0001) + serv_got_typing(gc, sn, 0, GAIM_TYPED); + else + serv_got_typing_stopped(gc, sn); + return 1; +} + +static int gaim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *message, GaimConvImFlags imflags) { + char *buf; + size_t len; + int ret; + + if (imflags & GAIM_CONV_IM_IMAGES) { + GString *msg = g_string_new(""); + GString *data = g_string_new("<BINARY>"); + GData *attribs; + const char *tmp, *start, *end, *last = NULL; + int oscar_id = 0; + + tmp = message; + + /* for each valid IMG tag... */ + while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) { + GaimStoredImage *image = NULL; + const char *id; + + last = end; + id = g_datalist_get_data(&attribs, "id"); + + /* ... if it refers to a valid gaim image ... */ + if (id && (image = gaim_imgstore_get(atoi(id)))) { + /* ... append the message from start to the tag ... */ + msg = g_string_append_len(msg, tmp, start - tmp); + oscar_id++; + + /* ... insert a new img tag with the oscar id ... */ + if (image->filename) + g_string_append_printf(msg, + "<IMG SRC=\"file://%s\" ID=\"%d\" DATASIZE=\"%d\">", + image->filename, oscar_id, (int)image->size); + else + g_string_append_printf(msg, + "<IMG ID=\"%d\" DATASIZE=\"%d\">", + oscar_id, (int)image->size); + + /* ... and append the data to the binary section ... */ + g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%d\">", + oscar_id, (int)image->size); + data = g_string_append_len(data, image->data, image->size); + data = g_string_append(data, "</DATA>"); + } else { + /* ... otherwise, allow the possibly invalid img tag through. */ + /* should we do something else? */ + msg = g_string_append_len(msg, tmp, (end + 1) - tmp); + } + + g_datalist_clear(&attribs); + + /* continue from the end of the tag */ + tmp = end + 1; + } + + /* append any remaining message data (without the > :-) */ + if (last++ && *last) + msg = g_string_append(msg, last); + + /* if we inserted any images in the binary section, append it */ + if (oscar_id) { + msg = g_string_append_len(msg, data->str, data->len); + msg = g_string_append(msg, "</BINARY>"); + } + + len = msg->len; + buf = msg->str; + g_string_free(msg, FALSE); + g_string_free(data, TRUE); + } else { + len = strlen(message); + buf = g_memdup(message, len+1); + } + + /* XXX - The last parameter below is the encoding. Let Paco-Paco do something with it. */ + if (imflags & GAIM_CONV_IM_AUTO_RESP) + ret = aim_odc_send_im(sess, conn, buf, len, 0, 1); + else + ret = aim_odc_send_im(sess, conn, buf, len, 0, 0); + + g_free(buf); + + return ret; +} + +struct ask_do_dir_im { + char *who; + GaimConnection *gc; +}; + +static void oscar_cancel_direct_im(struct ask_do_dir_im *data) { + g_free(data->who); + g_free(data); +} + +static void oscar_direct_im(struct ask_do_dir_im *data) { + GaimConnection *gc = data->gc; + OscarData *od; + struct direct_im *dim; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + g_free(data->who); + g_free(data); + return; + } + + od = (OscarData *)gc->proto_data; + + dim = find_direct_im(od, data->who); + if (dim) { + if (!(dim->connected)) { /* We'll free the old, unconnected dim, and start over */ + od->direct_ims = g_slist_remove(od->direct_ims, dim); + gaim_input_remove(dim->watcher); + g_free(dim); + gaim_debug(GAIM_DEBUG_INFO, "oscar", + "Gave up on old direct IM, trying again\n"); + } else { + gaim_notify_error(gc, NULL, "DirectIM already open.", NULL); + g_free(data->who); + g_free(data); + return; + } + } + dim = g_new0(struct direct_im, 1); + dim->gc = gc; + g_snprintf(dim->name, sizeof dim->name, "%s", data->who); + + dim->conn = aim_odc_initiate(od->sess, data->who); + if (dim->conn != NULL) { + od->direct_ims = g_slist_append(od->direct_ims, dim); + dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, + oscar_callback, dim->conn); + aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED, + gaim_odc_initiate, 0); + } else { + gaim_notify_error(gc, NULL, _("Unable to open Direct IM"), NULL); + g_free(dim); + } + + g_free(data->who); + g_free(data); +} + +static void oscar_ask_direct_im(GaimConnection *gc, const char *who) { + gchar *buf; + struct ask_do_dir_im *data = g_new0(struct ask_do_dir_im, 1); + data->who = g_strdup(who); + data->gc = gc; + buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."), who); + + gaim_request_action(gc, NULL, buf, + _("Because this reveals your IP address, it " + "may be considered a privacy risk. Do you " + "wish to continue?"), + 0, data, 2, + _("Connect"), G_CALLBACK(oscar_direct_im), + _("Cancel"), G_CALLBACK(oscar_cancel_direct_im)); + g_free(buf); +} + +static void oscar_set_permit_deny(GaimConnection *gc) { + GaimAccount *account = gaim_connection_get_account(gc); + OscarData *od = (OscarData *)gc->proto_data; +#ifdef NOSSI + GSList *list, *g = gaim_blist_groups(), *g1; + char buf[MAXMSGLEN]; + int at; + + switch(account->perm_deny) { + case 1: + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, gaim_account_get_username(account)); + break; + case 2: + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, gaim_account_get_username(account)); + break; + case 3: + list = account->permit; + at = 0; + while (list) { + at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data); + list = list->next; + } + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf); + break; + case 4: + list = account->deny; + at = 0; + while (list) { + at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data); + list = list->next; + } + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf); + break; + case 5: + g1 = g; + at = 0; + while (g1) { + list = gaim_blist_members((struct group *)g->data); + GSList list1 = list; + while (list1) { + struct buddy *b = list1->data; + if(b->account == account) + at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", b->name); + list1 = list1->next; + } + g_slist_free(list); + g1 = g1->next; + } + g_slist_free(g); + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf); + break; + default: + break; + } + signoff_blocked(gc); +#else + if (od->sess->ssi.received_data) + aim_ssi_setpermdeny(od->sess, account->perm_deny, 0xffffffff); +#endif +} + +static void oscar_add_permit(GaimConnection *gc, const char *who) { +#ifdef NOSSI + if (gc->account->permdeny == 3) + oscar_set_permit_deny(gc); +#else + OscarData *od = (OscarData *)gc->proto_data; + gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: About to add a permit\n"); + if (od->sess->ssi.received_data) + aim_ssi_addpermit(od->sess, who); +#endif +} + +static void oscar_add_deny(GaimConnection *gc, const char *who) { +#ifdef NOSSI + if (gc->account->permdeny == 4) + oscar_set_permit_deny(gc); +#else + OscarData *od = (OscarData *)gc->proto_data; + gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: About to add a deny\n"); + if (od->sess->ssi.received_data) + aim_ssi_adddeny(od->sess, who); +#endif +} + +static void oscar_rem_permit(GaimConnection *gc, const char *who) { +#ifdef NOSSI + if (gc->account->permdeny == 3) + oscar_set_permit_deny(gc); +#else + OscarData *od = (OscarData *)gc->proto_data; + gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: About to delete a permit\n"); + if (od->sess->ssi.received_data) + aim_ssi_delpermit(od->sess, who); +#endif +} + +static void oscar_rem_deny(GaimConnection *gc, const char *who) { +#ifdef NOSSI + if (gc->account->permdeny == 4) + oscar_set_permit_deny(gc); +#else + OscarData *od = (OscarData *)gc->proto_data; + gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: About to delete a deny\n"); + if (od->sess->ssi.received_data) + aim_ssi_deldeny(od->sess, who); +#endif +} + +static GList *oscar_away_states(GaimConnection *gc) +{ + OscarData *od = gc->proto_data; + GList *m = NULL; + + if (!od->icq) + return g_list_append(m, GAIM_AWAY_CUSTOM); + + m = g_list_append(m, _("Online")); + m = g_list_append(m, _("Away")); + m = g_list_append(m, _("Do Not Disturb")); + m = g_list_append(m, _("Not Available")); + m = g_list_append(m, _("Occupied")); + m = g_list_append(m, _("Free For Chat")); + m = g_list_append(m, _("Invisible")); + + return m; +} + +static void oscar_ssi_editcomment(struct name_data *data, const char *text) { + OscarData *od = data->gc->proto_data; + GaimBuddy *b; + GaimGroup *g; + + if (!(b = gaim_find_buddy(gaim_connection_get_account(data->gc), data->name))) { + oscar_free_name_data(data); + return; + } + + if (!(g = gaim_find_buddys_group(b))) { + oscar_free_name_data(data); + return; + } + + aim_ssi_editcomment(od->sess, g->name, data->name, text); + oscar_free_name_data(data); +} + +static void oscar_buddycb_edit_comment(GaimConnection *gc, const char *name) { + OscarData *od = gc->proto_data; + struct name_data *data = g_new(struct name_data, 1); + GaimBuddy *b; + GaimGroup *g; + char *comment; + gchar *comment_utf8; + + if (!(b = gaim_find_buddy(gaim_connection_get_account(gc), name))) + return; + if (!(g = gaim_find_buddys_group(b))) + return; + comment = aim_ssi_getcomment(od->sess->ssi.local, g->name, name); + comment_utf8 = comment ? gaim_utf8_try_convert(comment) : NULL; + + data->gc = gc; + data->name = g_strdup(name); + data->nick = NULL; + + gaim_request_input(gc, NULL, _("Buddy Comment:"), NULL, + comment_utf8, TRUE, FALSE, + _("OK"), G_CALLBACK(oscar_ssi_editcomment), + _("Cancel"), G_CALLBACK(oscar_free_name_data), + data); + + free(comment); + g_free(comment_utf8); +} + +static GList *oscar_buddy_menu(GaimConnection *gc, const char *who) { + OscarData *od = gc->proto_data; + GList *m = NULL; + struct proto_buddy_menu *pbm; + + pbm = g_new0(struct proto_buddy_menu, 1); + pbm->label = _("Edit Buddy Comment"); + pbm->callback = oscar_buddycb_edit_comment; + pbm->gc = gc; + m = g_list_append(m, pbm); + + if (od->icq) { +#if 0 + pbm = g_new0(struct proto_buddy_menu, 1); + pbm->label = _("Get Status Msg"); + pbm->callback = oscar_get_icqstatusmsg; + pbm->gc = gc; + m = g_list_append(m, pbm); +#endif + } else { + GaimBuddy *b = gaim_find_buddy(gc->account, who); + aim_userinfo_t *userinfo; + + if (b) + userinfo = aim_locate_finduserinfo(od->sess, b->name); + + if (b && userinfo && aim_sncmp(gaim_account_get_username(gaim_connection_get_account(gc)), who) && GAIM_BUDDY_IS_ONLINE(b)) { + if (userinfo->capabilities & AIM_CAPS_DIRECTIM) { + pbm = g_new0(struct proto_buddy_menu, 1); + pbm->label = _("Direct IM"); + pbm->callback = oscar_ask_direct_im; + pbm->gc = gc; + m = g_list_append(m, pbm); + } + + if (userinfo->capabilities & AIM_CAPS_SENDFILE) { + pbm = g_new0(struct proto_buddy_menu, 1); + pbm->label = _("Send File"); + pbm->callback = oscar_ask_sendfile; + pbm->gc = gc; + m = g_list_append(m, pbm); + } +#if 0 + if (userinfo->capabilities & AIM_CAPS_GETFILE) { + pbm = g_new0(struct proto_buddy_menu, 1); + pbm->label = _("Get File"); + pbm->callback = oscar_ask_getfile; + pbm->gc = gc; + m = g_list_append(m, pbm); + } +#endif + } + } + + if (od->sess->ssi.received_data) { + char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, who); + if (gname && aim_ssi_waitingforauth(od->sess->ssi.local, gname, who)) { + pbm = g_new0(struct proto_buddy_menu, 1); + pbm->label = _("Re-request Authorization"); + pbm->callback = gaim_auth_sendrequest; + pbm->gc = gc; + m = g_list_append(m, pbm); + } + } + + return m; +} + +static void oscar_format_screenname(GaimConnection *gc, const char *nick) { + OscarData *od = gc->proto_data; + if (!aim_sncmp(gaim_account_get_username(gaim_connection_get_account(gc)), nick)) { + if (!aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH)) { + od->setnick = TRUE; + od->newsn = g_strdup(nick); + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } else { + aim_admin_setnick(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH), nick); + } + } else { + gaim_notify_error(gc, NULL, _("The new formatting is invalid."), + _("Screenname formatting can change only capitalization and whitespace.")); + } +} + +static void oscar_show_format_screenname(GaimConnection *gc) +{ + gaim_request_input(gc, NULL, _("New screenname formatting:"), NULL, + gaim_connection_get_display_name(gc), FALSE, FALSE, + _("OK"), G_CALLBACK(oscar_format_screenname), + _("Cancel"), NULL, + gc); +} + +static void oscar_confirm_account(GaimConnection *gc) +{ + OscarData *od = gc->proto_data; + aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH); + + if (conn) { + aim_admin_reqconfirm(od->sess, conn); + } else { + od->conf = TRUE; + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } +} + +static void oscar_show_email(GaimConnection *gc) +{ + OscarData *od = gc->proto_data; + aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH); + + if (conn) { + aim_admin_getinfo(od->sess, conn, 0x11); + } else { + od->reqemail = TRUE; + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } +} + +static void oscar_change_email(GaimConnection *gc, const char *email) +{ + OscarData *od = gc->proto_data; + aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH); + + if (conn) { + aim_admin_setemail(od->sess, conn, email); + } else { + od->setemail = TRUE; + od->email = g_strdup(email); + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } +} + +static void oscar_show_change_email(GaimConnection *gc) +{ + gaim_request_input(gc, NULL, _("Change Address To:"), NULL, NULL, + FALSE, FALSE, + _("OK"), G_CALLBACK(oscar_change_email), + _("Cancel"), NULL, + gc); +} + +static void oscar_show_awaitingauth(GaimConnection *gc) +{ + OscarData *od = gc->proto_data; + gchar *nombre, *text, *tmp; + GaimBlistNode *gnode, *cnode, *bnode; + int num=0; + + text = g_strdup(""); + + for (gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) { + GaimGroup *group = (GaimGroup *)gnode; + if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) + continue; + for (cnode = gnode->child; cnode; cnode = cnode->next) { + if(!GAIM_BLIST_NODE_IS_CONTACT(cnode)) + continue; + for (bnode = cnode->child; bnode; bnode = bnode->next) { + GaimBuddy *buddy = (GaimBuddy *)bnode; + if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) + continue; + if (buddy->account == gc->account && aim_ssi_waitingforauth(od->sess->ssi.local, group->name, buddy->name)) { + if (gaim_get_buddy_alias_only(buddy)) + nombre = g_strdup_printf(" %s (%s)", buddy->name, gaim_get_buddy_alias_only(buddy)); + else + nombre = g_strdup_printf(" %s", buddy->name); + tmp = g_strdup_printf("%s%s<br>", text, nombre); + g_free(text); + text = tmp; + g_free(nombre); + num++; + } + } + } + } + + if (!num) { + g_free(text); + text = g_strdup(_("<i>you are not waiting for authorization</i>")); + } + + gaim_notify_formatted(gc, NULL, _("You are awaiting authorization from " + "the following buddies"), _("You can re-request " + "authorization from these buddies by " + "right-clicking on them and selecting " + "\"Re-request Authorization.\""), text, NULL, NULL); + g_free(text); +} + +static void search_by_email_cb(GaimConnection *gc, const char *email) +{ + serv_dir_search(gc, "", "", "", "", "", "", "", email); +} + +static void oscar_show_find_email(GaimConnection *gc) +{ + gaim_request_input(gc, _("Find Buddy by E-mail"), + _("Search for a buddy by e-mail address"), + _("Type the e-mail address of the buddy you are " + "searching for."), + NULL, FALSE, FALSE, + _("Search"), G_CALLBACK(search_by_email_cb), + _("Cancel"), NULL, gc); +} + +#if 0 +static void oscar_setavailmsg(GaimConnection *gc, char *text) { + OscarData *od = (OscarData *)gc->proto_data; + + aim_locate_setprofile(od->sess, NULL, NULL, 0, NULL, "", 0); + aim_srv_setavailmsg(od->sess, text); +} + +static void oscar_show_setavailmsg(GaimConnection *gc) +{ + gaim_request_input(gc, NULL, _("Available Message:"), NULL, + _("I'm doing work and hoping for a distraction--IM me!"), + TRUE, FALSE, + _("OK"), G_CALLBACK(oscar_setavailmsg), + _("Cancel"), NULL, + gc); +} +#endif + +static void oscar_show_set_info(GaimConnection *gc) +{ + gaim_account_request_change_user_info(gaim_connection_get_account(gc)); +} + +static void oscar_change_pass(GaimConnection *gc) +{ + gaim_account_request_change_password(gaim_connection_get_account(gc)); +} + +static void oscar_show_chpassurl(GaimConnection *gc) +{ + OscarData *od = gc->proto_data; + gchar *substituted = gaim_strreplace(od->sess->authinfo->chpassurl, "%s", gaim_account_get_username(gaim_connection_get_account(gc))); + gaim_notify_uri(gc, substituted); + g_free(substituted); +} + +static void oscar_show_imforwardingurl(GaimConnection *gc) +{ + gaim_notify_uri(gc, "http://mymobile.aol.com/dbreg/register?action=imf&clientID=1"); +} + +static void oscar_set_icon(GaimConnection *gc, const char *iconfile) +{ + OscarData *od = gc->proto_data; + aim_session_t *sess = od->sess; + FILE *file; + struct stat st; + + if (iconfile == NULL) { + /* Set an empty icon, or something */ + } else if (!stat(iconfile, &st)) { + char *buf = g_malloc(st.st_size); + file = fopen(iconfile, "rb"); + if (file) { + md5_state_t *state; + char md5[16]; + int len = fread(buf, 1, st.st_size, file); + fclose(file); + state = g_malloc(sizeof(md5_state_t)); + md5_init(state); + md5_append(state, buf, len); + md5_finish(state, md5); + g_free(state); + aim_ssi_seticon(sess, md5, 16); + } else + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Can't open buddy icon file!\n"); + g_free(buf); + } else + gaim_debug(GAIM_DEBUG_ERROR, "oscar", + "Can't stat buddy icon file!\n"); +} + + +static GList *oscar_actions(GaimConnection *gc) +{ + OscarData *od = gc->proto_data; + struct proto_actions_menu *pam; + GList *m = NULL; + + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Set User Info"); + pam->callback = oscar_show_set_info; + pam->gc = gc; + m = g_list_append(m, pam); + +#if 0 + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Set Available Message"); + pam->callback = oscar_show_setavailmsg; + pam->gc = gc; + m = g_list_append(m, pam); +#endif + + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Change Password"); + pam->callback = oscar_change_pass; + pam->gc = gc; + m = g_list_append(m, pam); + + if (od->sess->authinfo->chpassurl) { + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Change Password (URL)"); + pam->callback = oscar_show_chpassurl; + pam->gc = gc; + m = g_list_append(m, pam); + } + + if (od->sess->authinfo->chpassurl) { + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Configure IM Forwarding (URL)"); + pam->callback = oscar_show_imforwardingurl; + pam->gc = gc; + m = g_list_append(m, pam); + } + + if (!od->icq) { + /* AIM actions */ + m = g_list_append(m, NULL); + + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Format Screenname"); + pam->callback = oscar_show_format_screenname; + pam->gc = gc; + m = g_list_append(m, pam); + + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Confirm Account"); + pam->callback = oscar_confirm_account; + pam->gc = gc; + m = g_list_append(m, pam); + + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Display Current Registered Address"); + pam->callback = oscar_show_email; + pam->gc = gc; + m = g_list_append(m, pam); + + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Change Current Registered Address"); + pam->callback = oscar_show_change_email; + pam->gc = gc; + m = g_list_append(m, pam); + } + + m = g_list_append(m, NULL); + + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Show Buddies Awaiting Authorization"); + pam->callback = oscar_show_awaitingauth; + pam->gc = gc; + m = g_list_append(m, pam); + + m = g_list_append(m, NULL); + + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Search for Buddy by Email"); + pam->callback = oscar_show_find_email; + pam->gc = gc; + m = g_list_append(m, pam); + +/* pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Search for Buddy by Information"); + pam->callback = show_find_info; + pam->gc = gc; + m = g_list_append(m, pam); */ + + return m; +} + +static void oscar_change_passwd(GaimConnection *gc, const char *old, const char *new) +{ + OscarData *od = gc->proto_data; + + if (od->icq) { + aim_icq_changepasswd(od->sess, new); + } else { + aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH); + if (conn) { + aim_admin_changepasswd(od->sess, conn, new, old); + } else { + od->chpass = TRUE; + od->oldp = g_strdup(old); + od->newp = g_strdup(new); + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } + } +} + +static void oscar_convo_closed(GaimConnection *gc, const char *who) +{ + OscarData *od = gc->proto_data; + struct direct_im *dim = find_direct_im(od, who); + + if (!dim) + return; + + od->direct_ims = g_slist_remove(od->direct_ims, dim); + gaim_input_remove(dim->watcher); + aim_conn_kill(od->sess, &dim->conn); + g_free(dim); +} + +static GaimPluginProtocolInfo prpl_info = +{ + GAIM_PROTO_OSCAR, + OPT_PROTO_MAIL_CHECK | OPT_PROTO_BUDDY_ICON | OPT_PROTO_IM_IMAGE, + NULL, + NULL, + oscar_list_icon, + oscar_list_emblems, + oscar_status_text, + oscar_tooltip_text, + oscar_away_states, + oscar_actions, + oscar_buddy_menu, + oscar_chat_info, + oscar_login, + oscar_close, + oscar_send_im, + oscar_set_info, + oscar_send_typing, + oscar_get_info, + oscar_set_away, + oscar_get_away, + oscar_set_dir, + NULL, + oscar_dir_search, + oscar_set_idle, + oscar_change_passwd, + oscar_add_buddy, + oscar_add_buddies, + oscar_remove_buddy, + oscar_remove_buddies, + oscar_add_permit, + oscar_add_deny, + oscar_rem_permit, + oscar_rem_deny, + oscar_set_permit_deny, + oscar_warn, + oscar_join_chat, + oscar_chat_invite, + oscar_chat_leave, + NULL, + oscar_chat_send, + oscar_keepalive, + NULL, + NULL, + NULL, +#ifndef NOSSI + oscar_alias_buddy, + oscar_move_buddy, + oscar_rename_group, +#else + NULL, + NULL, + NULL, +#endif + NULL, + oscar_convo_closed, + NULL, + oscar_set_icon +}; + +static GaimPluginInfo info = +{ + 2, /**< api_version */ + GAIM_PLUGIN_PROTOCOL, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + "prpl-oscar", /**< id */ + "AIM/ICQ", /**< name */ + VERSION, /**< version */ + /** summary */ + N_("AIM/ICQ Protocol Plugin"), + /** description */ + N_("AIM/ICQ Protocol Plugin"), + NULL, /**< author */ + GAIM_WEBSITE, /**< homepage */ + + NULL, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + &prpl_info /**< extra_info */ +}; + +static void +init_plugin(GaimPlugin *plugin) +{ + GaimAccountOption *option; + + option = gaim_account_option_string_new(_("Auth host"), "server", + "login.oscar.aol.com"); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + option); + + option = gaim_account_option_int_new(_("Auth port"), "port", 5190); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + option); + + my_protocol = plugin; +} + +GAIM_INIT_PLUGIN(oscar, init_plugin, info); diff --git a/libfaim/popups.c b/libfaim/popups.c new file mode 100644 index 0000000..c512fb8 --- /dev/null +++ b/libfaim/popups.c @@ -0,0 +1,64 @@ +/* + * Family 0x0008 - Popups. + * + * Popups are just what it sounds like. They're a way for the server to + * open up an informative box on the client's screen. + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * This is all there is to it. + * + * The message is probably HTML. + * + */ +static int parsepopup(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + aim_tlvlist_t *tl; + int ret = 0; + char *msg, *url; + fu16_t width, height, delay; + + tl = aim_tlvlist_read(bs); + + msg = aim_tlv_getstr(tl, 0x0001, 1); + url = aim_tlv_getstr(tl, 0x0002, 1); + width = aim_tlv_get16(tl, 0x0003, 1); + height = aim_tlv_get16(tl, 0x0004, 1); + delay = aim_tlv_get16(tl, 0x0005, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, msg, url, width, height, delay); + + aim_tlvlist_free(&tl); + free(msg); + free(url); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0002) + return parsepopup(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int popups_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0008; + mod->version = 0x0001; + mod->toolid = 0x0104; + mod->toolversion = 0x0001; + mod->flags = 0; + strncpy(mod->name, "popups", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/rxhandlers.c b/libfaim/rxhandlers.c new file mode 100644 index 0000000..8ecb963 --- /dev/null +++ b/libfaim/rxhandlers.c @@ -0,0 +1,585 @@ +/* + * rxhandlers.c + * + * This file contains most all of the incoming packet handlers, along + * with aim_rxdispatch(), the Rx dispatcher. Queue/list management is + * actually done in aim_rxqueue.c. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +struct aim_rxcblist_s { + fu16_t family; + fu16_t type; + aim_rxcallback_t handler; + fu16_t flags; + struct aim_rxcblist_s *next; +}; + +faim_internal aim_module_t *aim__findmodulebygroup(aim_session_t *sess, fu16_t group) +{ + aim_module_t *cur; + + for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { + if (cur->family == group) + return cur; + } + + return NULL; +} + +faim_internal aim_module_t *aim__findmodule(aim_session_t *sess, const char *name) +{ + aim_module_t *cur; + + for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { + if (strcmp(name, cur->name) == 0) + return cur; + } + + return NULL; +} + +faim_internal int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *)) +{ + aim_module_t *mod; + + if (!sess || !modfirst) + return -1; + + if (!(mod = malloc(sizeof(aim_module_t)))) + return -1; + memset(mod, 0, sizeof(aim_module_t)); + + if (modfirst(sess, mod) == -1) { + free(mod); + return -1; + } + + if (aim__findmodule(sess, mod->name)) { + if (mod->shutdown) + mod->shutdown(sess, mod); + free(mod); + return -1; + } + + mod->next = (aim_module_t *)sess->modlistv; + /* (aim_module_t *)sess->modlistv = mod; */ + sess->modlistv = mod; + + + faimdprintf(sess, 1, "registered module %s (family 0x%04x, version = 0x%04x, tool 0x%04x, tool version 0x%04x)\n", mod->name, mod->family, mod->version, mod->toolid, mod->toolversion); + + return 0; +} + +faim_internal void aim__shutdownmodules(aim_session_t *sess) +{ + aim_module_t *cur; + + for (cur = (aim_module_t *)sess->modlistv; cur; ) { + aim_module_t *tmp; + + tmp = cur->next; + + if (cur->shutdown) + cur->shutdown(sess, cur); + + free(cur); + + cur = tmp; + } + + sess->modlistv = NULL; + + return; +} + +static int consumesnac(aim_session_t *sess, aim_frame_t *rx) +{ + aim_module_t *cur; + aim_modsnac_t snac; + + if (aim_bstream_empty(&rx->data) < 10) + return 0; + + snac.family = aimbs_get16(&rx->data); + snac.subtype = aimbs_get16(&rx->data); + snac.flags = aimbs_get16(&rx->data); + snac.id = aimbs_get32(&rx->data); + + /* SNAC flags are apparently uniform across all SNACs, so we handle them here */ + if (snac.flags & 0x0001) { + /* + * This means the SNAC will be followed by another SNAC with + * related information. We don't need to do anything about + * this here. + */ + } + if (snac.flags & 0x8000) { + /* + * This packet contains the version of the family that this SNAC is + * in. You get this when your SSI module is version 2 or higher. + * For now we have no need for this, but you could always save + * it as a part of aim_modnsac_t, or something. The format is... + * 2 byte length of total mini-header (which is 6 bytes), then TLV + * of type 0x0001, length 0x0002, value is the 2 byte version + * number + */ + aim_bstream_advance(&rx->data, aimbs_get16(&rx->data)); + } + + for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { + + if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && + (cur->family != snac.family)) + continue; + + if (cur->snachandler(sess, cur, rx, &snac, &rx->data)) + return 1; + + } + + return 0; +} + +static int consumenonsnac(aim_session_t *sess, aim_frame_t *rx, fu16_t family, fu16_t subtype) +{ + aim_module_t *cur; + aim_modsnac_t snac; + + snac.family = family; + snac.subtype = subtype; + snac.flags = snac.id = 0; + + for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { + + if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && + (cur->family != snac.family)) + continue; + + if (cur->snachandler(sess, cur, rx, &snac, &rx->data)) + return 1; + + } + + return 0; +} + +static int negchan_middle(aim_session_t *sess, aim_frame_t *fr) +{ + aim_tlvlist_t *tlvlist; + char *msg = NULL; + fu16_t code = 0; + aim_rxcallback_t userfunc; + int ret = 1; + + if (aim_bstream_empty(&fr->data) == 0) { + /* XXX should do something with this */ + return 1; + } + + /* Used only by the older login protocol */ + /* XXX remove this special case? */ + if (fr->conn->type == AIM_CONN_TYPE_AUTH) + return consumenonsnac(sess, fr, 0x0017, 0x0003); + + tlvlist = aim_tlvlist_read(&fr->data); + + if (aim_tlv_gettlv(tlvlist, 0x0009, 1)) + code = aim_tlv_get16(tlvlist, 0x0009, 1); + + if (aim_tlv_gettlv(tlvlist, 0x000b, 1)) + msg = aim_tlv_getstr(tlvlist, 0x000b, 1); + + if ((userfunc = aim_callhandler(sess, fr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) + ret = userfunc(sess, fr, code, msg); + + aim_tlvlist_free(&tlvlist); + + free(msg); + + return ret; +} + +/* + * Bleck functions get called when there's no non-bleck functions + * around to cleanup the mess... + */ +faim_internal int bleck(aim_session_t *sess, aim_frame_t *frame, ...) +{ + fu16_t family, subtype; + fu16_t maxf, maxs; + + static const char *channels[6] = { + "Invalid (0)", + "FLAP Version", + "SNAC", + "Invalid (3)", + "Negotiation", + "FLAP NOP" + }; + static const int maxchannels = 5; + + /* XXX: this is ugly. and big just for debugging. */ + static const char *literals[14][25] = { + {"Invalid", + NULL + }, + {"General", + "Invalid", + "Error", + "Client Ready", + "Server Ready", + "Service Request", + "Redirect", + "Rate Information Request", + "Rate Information", + "Rate Information Ack", + NULL, + "Rate Information Change", + "Server Pause", + NULL, + "Server Resume", + "Request Personal User Information", + "Personal User Information", + "Evil Notification", + NULL, + "Migration notice", + "Message of the Day", + "Set Privacy Flags", + "Well Known URL", + "NOP" + }, + {"Location", + "Invalid", + "Error", + "Request Rights", + "Rights Information", + "Set user information", + "Request User Information", + "User Information", + "Watcher Sub Request", + "Watcher Notification" + }, + {"Buddy List Management", + "Invalid", + "Error", + "Request Rights", + "Rights Information", + "Add Buddy", + "Remove Buddy", + "Watcher List Query", + "Watcher List Response", + "Watcher SubRequest", + "Watcher Notification", + "Reject Notification", + "Oncoming Buddy", + "Offgoing Buddy" + }, + {"Messeging", + "Invalid", + "Error", + "Add ICBM Parameter", + "Remove ICBM Parameter", + "Request Parameter Information", + "Parameter Information", + "Outgoing Message", + "Incoming Message", + "Evil Request", + "Evil Reply", + "Missed Calls", + "Message Error", + "Host Ack" + }, + {"Advertisements", + "Invalid", + "Error", + "Request Ad", + "Ad Data (GIFs)" + }, + {"Invitation / Client-to-Client", + "Invalid", + "Error", + "Invite a Friend", + "Invitation Ack" + }, + {"Administrative", + "Invalid", + "Error", + "Information Request", + "Information Reply", + "Information Change Request", + "Information Chat Reply", + "Account Confirm Request", + "Account Confirm Reply", + "Account Delete Request", + "Account Delete Reply" + }, + {"Popups", + "Invalid", + "Error", + "Display Popup" + }, + {"BOS", + "Invalid", + "Error", + "Request Rights", + "Rights Response", + "Set group permission mask", + "Add permission list entries", + "Delete permission list entries", + "Add deny list entries", + "Delete deny list entries", + "Server Error" + }, + {"User Lookup", + "Invalid", + "Error", + "Search Request", + "Search Response" + }, + {"Stats", + "Invalid", + "Error", + "Set minimum report interval", + "Report Events" + }, + {"Translate", + "Invalid", + "Error", + "Translate Request", + "Translate Reply", + }, + {"Chat Navigation", + "Invalid", + "Error", + "Request rights", + "Request Exchange Information", + "Request Room Information", + "Request Occupant List", + "Search for Room", + "Outgoing Message", + "Incoming Message", + "Evil Request", + "Evil Reply", + "Chat Error", + } + }; + + maxf = sizeof(literals) / sizeof(literals[0]); + maxs = sizeof(literals[0]) / sizeof(literals[0][0]); + + if (frame->hdr.flap.type == 0x02) { + + family = aimbs_get16(&frame->data); + subtype = aimbs_get16(&frame->data); + + if ((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL)) + faimdprintf(sess, 0, "bleck: channel %s: null handler for %04x/%04x (%s)\n", channels[frame->hdr.flap.type], family, subtype, literals[family][subtype+1]); + else + faimdprintf(sess, 0, "bleck: channel %s: null handler for %04x/%04x (no literal)\n", channels[frame->hdr.flap.type], family, subtype); + } else { + + if (frame->hdr.flap.type <= maxchannels) + faimdprintf(sess, 0, "bleck: channel %s (0x%02x)\n", channels[frame->hdr.flap.type], frame->hdr.flap.type); + else + faimdprintf(sess, 0, "bleck: unknown channel 0x%02x\n", frame->hdr.flap.type); + + } + + return 1; +} + +faim_export int aim_conn_addhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type, aim_rxcallback_t newhandler, fu16_t flags) +{ + struct aim_rxcblist_s *newcb; + + if (!conn) + return -1; + + faimdprintf(sess, 1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type); + + if (!(newcb = (struct aim_rxcblist_s *)calloc(1, sizeof(struct aim_rxcblist_s)))) + return -1; + + newcb->family = family; + newcb->type = type; + newcb->flags = flags; + newcb->handler = newhandler ? newhandler : bleck; + newcb->next = NULL; + + if (!conn->handlerlist) + conn->handlerlist = (void *)newcb; + else { + struct aim_rxcblist_s *cur; + + for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur->next; cur = cur->next) + ; + cur->next = newcb; + } + + return 0; +} + +faim_export int aim_clearhandlers(aim_conn_t *conn) +{ + struct aim_rxcblist_s *cur; + + if (!conn) + return -1; + + for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; ) { + struct aim_rxcblist_s *tmp; + + tmp = cur->next; + free(cur); + cur = tmp; + } + conn->handlerlist = NULL; + + return 0; +} + +faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type) +{ + struct aim_rxcblist_s *cur; + + if (!conn) + return NULL; + + faimdprintf(sess, 1, "aim_callhandler: calling for %04x/%04x\n", family, type); + + for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; cur = cur->next) { + if ((cur->family == family) && (cur->type == type)) + return cur->handler; + } + + if (type == AIM_CB_SPECIAL_DEFAULT) { + faimdprintf(sess, 1, "aim_callhandler: no default handler for family 0x%04x\n", family); + return NULL; /* prevent infinite recursion */ + } + + faimdprintf(sess, 1, "aim_callhandler: no handler for 0x%04x/0x%04x\n", family, type); + + return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT); +} + +faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src) +{ + struct aim_rxcblist_s *cur; + + for (cur = (struct aim_rxcblist_s *)src->handlerlist; cur; cur = cur->next) { + aim_conn_addhandler(sess, dest, cur->family, cur->type, + cur->handler, cur->flags); + } + + return; +} + +faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn,fu16_t family, fu16_t type, aim_frame_t *ptr) +{ + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, conn, family, type))) + return userfunc(sess, ptr); + + return 1; /* XXX */ +} + +/* + * aim_rxdispatch() + * + * Basically, heres what this should do: + * 1) Determine correct packet handler for this packet + * 2) Mark the packet handled (so it can be dequeued in purge_queue()) + * 3) Send the packet to the packet handler + * 4) Go to next packet in the queue and start over + * 5) When done, run purge_queue() to purge handled commands + * + * TODO: Clean up. + * TODO: More support for mid-level handlers. + * TODO: Allow for NULL handlers. + * + */ +faim_export void aim_rxdispatch(aim_session_t *sess) +{ + int i; + aim_frame_t *cur; + + for (cur = sess->queue_incoming, i = 0; cur; cur = cur->next, i++) { + + /* + * XXX: This is still fairly ugly. + */ + + if (cur->handled) + continue; + + if (cur->hdrtype == AIM_FRAMETYPE_FLAP) { + if (cur->hdr.flap.type == 0x01) { + cur->handled = aim_callhandler_noparam(sess, cur->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, cur); /* XXX use consumenonsnac */ + continue; + + } else if (cur->hdr.flap.type == 0x02) { + if ((cur->handled = consumesnac(sess, cur))) + continue; + + } else if (cur->hdr.flap.type == 0x04) { + cur->handled = negchan_middle(sess, cur); + continue; + + } else if (cur->hdr.flap.type == 0x05) { + + } + + } else if (cur->hdrtype == AIM_FRAMETYPE_OFT) { + if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS) { + aim_rxdispatch_rendezvous(sess, cur); + cur->handled = 1; + continue; + + } else if (cur->conn->type == AIM_CONN_TYPE_LISTENER) { + /* not possible */ + faimdprintf(sess, 0, "rxdispatch called on LISTENER connection!\n"); + cur->handled = 1; + continue; + } + } + + if (!cur->handled) { + consumenonsnac(sess, cur, 0xffff, 0xffff); /* last chance! */ + cur->handled = 1; + } + } + + /* + * This doesn't have to be called here. It could easily be done + * by a seperate thread or something. It's an administrative operation, + * and can take a while. Though the less you call it the less memory + * you'll have :) + */ + aim_purge_rxqueue(sess); + + return; +} + +faim_internal int aim_parse_unknown(aim_session_t *sess, aim_frame_t *frame, ...) +{ + int i; + + faimdprintf(sess, 1, "\nRecieved unknown packet:"); + + for (i = 0; aim_bstream_empty(&frame->data); i++) { + if ((i % 8) == 0) + faimdprintf(sess, 1, "\n\t"); + + faimdprintf(sess, 1, "0x%2x ", aimbs_get8(&frame->data)); + } + + faimdprintf(sess, 1, "\n\n"); + + return 1; +} diff --git a/libfaim/rxqueue.c b/libfaim/rxqueue.c new file mode 100644 index 0000000..dead8d8 --- /dev/null +++ b/libfaim/rxqueue.c @@ -0,0 +1,290 @@ +/* + * rxqueue.c + * + * This file contains the management routines for the receive + * (incoming packet) queue. The actual packet handlers are in + * aim_rxhandlers.c. + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#ifndef _WIN32 +#include <sys/socket.h> +#endif + +/* + * + */ +faim_internal int aim_recv(int fd, void *buf, size_t count) +{ + int left, cur; + + for (cur = 0, left = count; left; ) { + int ret; + + ret = recv(fd, ((unsigned char *)buf)+cur, left, 0); + + /* Of course EOF is an error, only morons disagree with that. */ + if (ret <= 0) + return -1; + + cur += ret; + left -= ret; + } + + return cur; +} + +/* + * Read into a byte stream. Will not read more than count, but may read + * less if there is not enough room in the stream buffer. + */ +faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count) +{ + int red = 0; + + if (!bs || (fd < 0) || (count < 0)) + return -1; + + if (count > (bs->len - bs->offset)) + count = bs->len - bs->offset; /* truncate to remaining space */ + + if (count) { + + red = aim_recv(fd, bs->data + bs->offset, count); + + if (red <= 0) + return -1; + } + + bs->offset += red; + + return red; +} + +/** + * aim_frame_destroy - free aim_frame_t + * @frame: the frame to free + * + * returns -1 on error; 0 on success. + * + */ +faim_internal void aim_frame_destroy(aim_frame_t *frame) +{ + + free(frame->data.data); /* XXX aim_bstream_free */ + free(frame); + + return; +} + +/* + * Read a FLAP header from conn into fr, and return the number of bytes in the payload. + */ +static int aim_get_command_flap(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr) +{ + fu8_t flaphdr_raw[6]; + aim_bstream_t flaphdr; + fu16_t payloadlen; + + aim_bstream_init(&flaphdr, flaphdr_raw, sizeof(flaphdr_raw)); + + /* + * Read FLAP header. Six bytes: + * 0 char -- Always 0x2a + * 1 char -- Channel ID. Usually 2 -- 1 and 4 are used during login. + * 2 short -- Sequence number + * 4 short -- Number of data bytes that follow. + */ + if (aim_bstream_recv(&flaphdr, conn->fd, 6) < 6) { + aim_conn_close(conn); + return -1; + } + + aim_bstream_rewind(&flaphdr); + + /* + * This shouldn't happen unless the socket breaks, the server breaks, + * or we break. We must handle it just in case. + */ + if (aimbs_get8(&flaphdr) != 0x2a) { + fu8_t start; + + aim_bstream_rewind(&flaphdr); + start = aimbs_get8(&flaphdr); + faimdprintf(sess, 0, "FLAP framing disrupted (0x%02x)", start); + aim_conn_close(conn); + return -1; + } + + /* we're doing FLAP if we're here */ + fr->hdrtype = AIM_FRAMETYPE_FLAP; + + fr->hdr.flap.type = aimbs_get8(&flaphdr); + fr->hdr.flap.seqnum = aimbs_get16(&flaphdr); + payloadlen = aimbs_get16(&flaphdr); /* length of payload */ + + return payloadlen; +} + +/* + * Read a rendezvous header from conn into fr, and return the number of bytes in the payload. + */ +static int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr) +{ + fu8_t rendhdr_raw[8]; + aim_bstream_t rendhdr; + + aim_bstream_init(&rendhdr, rendhdr_raw, sizeof(rendhdr_raw)); + + if (aim_bstream_recv(&rendhdr, conn->fd, 8) < 8) { + aim_conn_close(conn); + return -1; + } + + aim_bstream_rewind(&rendhdr); + + fr->hdrtype = AIM_FRAMETYPE_OFT; /* a misnomer--rendezvous */ + + aimbs_getrawbuf(&rendhdr, fr->hdr.rend.magic, 4); + fr->hdr.rend.hdrlen = aimbs_get16(&rendhdr) - 8; + fr->hdr.rend.type = aimbs_get16(&rendhdr); + + return fr->hdr.rend.hdrlen; +} + +/* + * Grab a single command sequence off the socket, and enqueue it in the incoming event queue + * in a separate struct. + */ +faim_export int aim_get_command(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *newrx; + fu16_t payloadlen; + + if (!sess || !conn) + return -EINVAL; + + if (conn->fd == -1) + return -1; /* it's an aim_conn_close()'d connection */ + + if (conn->fd < 3) /* can happen when people abuse the interface */ + return -1; + + if (conn->status & AIM_CONN_STATUS_INPROGRESS) + return aim_conn_completeconnect(sess, conn); + + if (!(newrx = (aim_frame_t *)calloc(sizeof(aim_frame_t), 1))) + return -ENOMEM; + + /* + * Rendezvous (client to client) connections do not speak FLAP, so this + * function will break on them. + */ + if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) { + int ret = aim_get_command_rendezvous(sess, conn, newrx); + + if (ret < 0) { + free(newrx); + return -1; + } + + payloadlen = ret; + } else if (conn->type == AIM_CONN_TYPE_LISTENER) { + faimdprintf(sess, 0, "AIM_CONN_TYPE_LISTENER on fd %d\n", conn->fd); + free(newrx); + return -1; + } else + payloadlen = aim_get_command_flap(sess, conn, newrx); + + newrx->nofree = 0; /* free by default */ + + if (payloadlen) { + fu8_t *payload = NULL; + + if (!(payload = (fu8_t *) malloc(payloadlen))) { + aim_frame_destroy(newrx); + return -1; + } + + aim_bstream_init(&newrx->data, payload, payloadlen); + + /* read the payload */ + if (aim_bstream_recv(&newrx->data, conn->fd, payloadlen) < payloadlen) { + aim_frame_destroy(newrx); /* free's payload */ + aim_conn_close(conn); + return -1; + } + } else + aim_bstream_init(&newrx->data, NULL, 0); + + + aim_bstream_rewind(&newrx->data); + + newrx->conn = conn; + + newrx->next = NULL; /* this will always be at the bottom */ + + if (!sess->queue_incoming) + sess->queue_incoming = newrx; + else { + aim_frame_t *cur; + + for (cur = sess->queue_incoming; cur->next; cur = cur->next) + ; + cur->next = newrx; + } + + newrx->conn->lastactivity = time(NULL); + + return 0; +} + +/* + * Purge recieve queue of all handled commands (->handled==1). Also + * allows for selective freeing using ->nofree so that the client can + * keep the data for various purposes. + * + * If ->nofree is nonzero, the frame will be delinked from the global list, + * but will not be free'ed. The client _must_ keep a pointer to the + * data -- libfaim will not! If the client marks ->nofree but + * does not keep a pointer, it's lost forever. + * + */ +faim_export void aim_purge_rxqueue(aim_session_t *sess) +{ + aim_frame_t *cur, **prev; + + for (prev = &sess->queue_incoming; (cur = *prev); ) { + if (cur->handled) { + + *prev = cur->next; + + if (!cur->nofree) + aim_frame_destroy(cur); + + } else + prev = &cur->next; + } + + return; +} + +/* + * Since aim_get_command will aim_conn_kill dead connections, we need + * to clean up the rxqueue of unprocessed connections on that socket. + * + * XXX: this is something that was handled better in the old connection + * handling method, but eh. + */ +faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *currx; + + for (currx = sess->queue_incoming; currx; currx = currx->next) { + if ((!currx->handled) && (currx->conn == conn)) + currx->handled = 1; + } + return; +} diff --git a/libfaim/search.c b/libfaim/search.c new file mode 100644 index 0000000..452ebe5 --- /dev/null +++ b/libfaim/search.c @@ -0,0 +1,134 @@ +/* + * Family 0x000a - User Search. + * + * TODO: Add aim_usersearch_name() + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * Subtype 0x0001 + * + * XXX can this be integrated with the rest of the error handling? + */ +static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + aim_snac_t *snac2; + + /* XXX the modules interface should have already retrieved this for us */ + if (!(snac2 = aim_remsnac(sess, snac->id))) { + faimdprintf(sess, 2, "search error: couldn't get a snac for 0x%08lx\n", snac->id); + return 0; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, snac2->data /* address */); + + /* XXX freesnac()? */ + if (snac2) + free(snac2->data); + free(snac2); + + return ret; +} + +/* + * Subtype 0x0002 + * + */ +faim_export int aim_search_address(aim_session_t *sess, aim_conn_t *conn, const char *address) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !address) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, strdup(address), strlen(address)+1); + aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid); + + aimbs_putraw(&fr->data, address, strlen(address)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0003 + * + */ +static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int j = 0, m, ret = 0; + aim_tlvlist_t *tlvlist; + char *cur = NULL, *buf = NULL; + aim_rxcallback_t userfunc; + aim_snac_t *snac2; + char *searchaddr = NULL; + + if ((snac2 = aim_remsnac(sess, snac->id))) + searchaddr = (char *)snac2->data; + + tlvlist = aim_tlvlist_read(bs); + m = aim_tlvlist_count(&tlvlist); + + /* XXX uhm. + * This is the only place that uses something other than 1 for the 3rd + * parameter to aim_tlv_gettlv_whatever(). + */ + while ((cur = aim_tlv_getstr(tlvlist, 0x0001, j+1)) && j < m) { + buf = realloc(buf, (j+1) * (MAXSNLEN+1)); + + strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN); + free(cur); + + j++; + } + + aim_tlvlist_free(&tlvlist); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, searchaddr, j, buf); + + /* XXX freesnac()? */ + if (snac2) + free(snac2->data); + free(snac2); + + free(buf); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0001) + return error(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0003) + return reply(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000a; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "search", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/service.c b/libfaim/service.c new file mode 100644 index 0000000..cb8fd43 --- /dev/null +++ b/libfaim/service.c @@ -0,0 +1,1089 @@ +/* + * Family 0x0001 - This is a very special group. All connections support + * this group, as it does some particularly good things (like rate limiting). + */ + +#define FAIM_INTERNAL +#define FAIM_NEED_CONN_INTERNAL +#include <aim.h> + +#include "md5.h" + +/* Subtype 0x0002 - Client Online */ +faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!ins) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid); + + /* + * Send only the tool versions that the server cares about (that it + * marked as supporting in the server ready SNAC). + */ + for (sg = ins->groups; sg; sg = sg->next) { + aim_module_t *mod; + + if ((mod = aim__findmodulebygroup(sess, sg->group))) { + aimbs_put16(&fr->data, mod->family); + aimbs_put16(&fr->data, mod->version); + aimbs_put16(&fr->data, mod->toolid); + aimbs_put16(&fr->data, mod->toolversion); + } else + faimdprintf(sess, 1, "aim_clientready: server supports group 0x%04x but we don't!\n", sg->group); + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0003 - Host Online + * + * See comments in conn.c about how the group associations are supposed + * to work, and how they really work. + * + * This info probably doesn't even need to make it to the client. + * + * We don't actually call the client here. This starts off the connection + * initialization routine required by all AIM connections. The next time + * the client is called is the CONNINITDONE callback, which should be + * shortly after the rate information is acknowledged. + * + */ +static int hostonline(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + fu16_t *families; + int famcount; + + + if (!(families = malloc(aim_bstream_empty(bs)))) + return 0; + + for (famcount = 0; aim_bstream_empty(bs); famcount++) { + families[famcount] = aimbs_get16(bs); + aim_conn_addgroup(rx->conn, families[famcount]); + } + + free(families); + + + /* + * Next step is in the Host Versions handler. + * + * Note that we must send this before we request rates, since + * the format of the rate information depends on the versions we + * give it. + * + */ + aim_setversions(sess, rx->conn); + + return 1; +} + +/* Subtype 0x0004 - Service request */ +faim_export int aim_reqservice(aim_session_t *sess, aim_conn_t *conn, fu16_t serviceid) +{ + return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid); +} + +/* Subtype 0x0005 - Redirect */ +static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + struct aim_redirect_data redir; + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + aim_snac_t *origsnac = NULL; + int ret = 0; + + memset(&redir, 0, sizeof(redir)); + + tlvlist = aim_tlvlist_read(bs); + + if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) || + !aim_tlv_gettlv(tlvlist, 0x0005, 1) || + !aim_tlv_gettlv(tlvlist, 0x0006, 1)) { + aim_tlvlist_free(&tlvlist); + return 0; + } + + redir.group = aim_tlv_get16(tlvlist, 0x000d, 1); + redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1); + redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length; + redir.cookie = aim_tlv_getstr(tlvlist, 0x0006, 1); + + /* Fetch original SNAC so we can get csi if needed */ + origsnac = aim_remsnac(sess, snac->id); + + if ((redir.group == AIM_CONN_TYPE_CHAT) && origsnac) { + struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data; + + redir.chat.exchange = csi->exchange; + redir.chat.room = csi->name; + redir.chat.instance = csi->instance; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &redir); + + free((void *)redir.ip); + free((void *)redir.cookie); + + if (origsnac) + free(origsnac->data); + free(origsnac); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* Subtype 0x0006 - Request Rate Information. */ +faim_internal int aim_reqrates(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x0006); +} + +/* + * OSCAR defines several 'rate classes'. Each class has seperate + * rate limiting properties (limit level, alert level, disconnect + * level, etc), and a set of SNAC family/type pairs associated with + * it. The rate classes, their limiting properties, and the definitions + * of which SNACs are belong to which class, are defined in the + * Rate Response packet at login to each host. + * + * Logically, all rate offenses within one class count against further + * offenses for other SNACs in the same class (ie, sending messages + * too fast will limit the number of user info requests you can send, + * since those two SNACs are in the same rate class). + * + * Since the rate classes are defined dynamically at login, the values + * below may change. But they seem to be fairly constant. + * + * Currently, BOS defines five rate classes, with the commonly used + * members as follows... + * + * Rate class 0x0001: + * - Everything thats not in any of the other classes + * + * Rate class 0x0002: + * - Buddy list add/remove + * - Permit list add/remove + * - Deny list add/remove + * + * Rate class 0x0003: + * - User information requests + * - Outgoing ICBMs + * + * Rate class 0x0004: + * - A few unknowns: 2/9, 2/b, and f/2 + * + * Rate class 0x0005: + * - Chat room create + * - Outgoing chat ICBMs + * + * The only other thing of note is that class 5 (chat) has slightly looser + * limiting properties than class 3 (normal messages). But thats just a + * small bit of trivia for you. + * + * The last thing that needs to be learned about the rate limiting + * system is how the actual numbers relate to the passing of time. This + * seems to be a big mystery. + * + */ + +static void rc_addclass(struct rateclass **head, struct rateclass *inrc) +{ + struct rateclass *rc, *rc2; + + if (!(rc = malloc(sizeof(struct rateclass)))) + return; + + memcpy(rc, inrc, sizeof(struct rateclass)); + rc->next = NULL; + + for (rc2 = *head; rc2 && rc2->next; rc2 = rc2->next) + ; + + if (!rc2) + *head = rc; + else + rc2->next = rc; + + return; +} + +static struct rateclass *rc_findclass(struct rateclass **head, fu16_t id) +{ + struct rateclass *rc; + + for (rc = *head; rc; rc = rc->next) { + if (rc->classid == id) + return rc; + } + + return NULL; +} + +static void rc_addpair(struct rateclass *rc, fu16_t group, fu16_t type) +{ + struct snacpair *sp, *sp2; + + if (!(sp = malloc(sizeof(struct snacpair)))) + return; + memset(sp, 0, sizeof(struct snacpair)); + + sp->group = group; + sp->subtype = type; + sp->next = NULL; + + for (sp2 = rc->members; sp2 && sp2->next; sp2 = sp2->next) + ; + + if (!sp2) + rc->members = sp; + else + sp2->next = sp; + + return; +} + +/* Subtype 0x0007 - Rate Parameters */ +static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)rx->conn->inside; + fu16_t numclasses, i; + aim_rxcallback_t userfunc; + + + /* + * First are the parameters for each rate class. + */ + numclasses = aimbs_get16(bs); + for (i = 0; i < numclasses; i++) { + struct rateclass rc; + + memset(&rc, 0, sizeof(struct rateclass)); + + rc.classid = aimbs_get16(bs); + rc.windowsize = aimbs_get32(bs); + rc.clear = aimbs_get32(bs); + rc.alert = aimbs_get32(bs); + rc.limit = aimbs_get32(bs); + rc.disconnect = aimbs_get32(bs); + rc.current = aimbs_get32(bs); + rc.max = aimbs_get32(bs); + + /* + * The server will send an extra five bytes of parameters + * depending on the version we advertised in 1/17. If we + * didn't send 1/17 (evil!), then this will crash and you + * die, as it will default to the old version but we have + * the new version hardcoded here. + */ + if (mod->version >= 3) + aimbs_getrawbuf(bs, rc.unknown, sizeof(rc.unknown)); + + faimdprintf(sess, 1, "--- Adding rate class %d to connection type %d: window size = %ld, clear = %ld, alert = %ld, limit = %ld, disconnect = %ld, current = %ld, max = %ld\n", rx->conn->type, rc.classid, rc.windowsize, rc.clear, rc.alert, rc.limit, rc.disconnect, rc.current, rc.max); + + rc_addclass(&ins->rates, &rc); + } + + /* + * Then the members of each class. + */ + for (i = 0; i < numclasses; i++) { + fu16_t classid, count; + struct rateclass *rc; + int j; + + classid = aimbs_get16(bs); + count = aimbs_get16(bs); + + rc = rc_findclass(&ins->rates, classid); + + for (j = 0; j < count; j++) { + fu16_t group, subtype; + + group = aimbs_get16(bs); + subtype = aimbs_get16(bs); + + if (rc) + rc_addpair(rc, group, subtype); + } + } + + /* + * We don't pass the rate information up to the client, as it really + * doesn't care. The information is stored in the connection, however + * so that we can do more fun stuff later (not really). + */ + + /* + * Last step in the conn init procedure is to acknowledge that we + * agree to these draconian limitations. + */ + aim_rates_addparam(sess, rx->conn); + + /* + * Finally, tell the client it's ready to go... + */ + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE))) + userfunc(sess, rx); + + + return 1; +} + +/* Subtype 0x0008 - Add Rate Parameter */ +faim_internal int aim_rates_addparam(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + aim_frame_t *fr; + aim_snacid_t snacid; + struct rateclass *rc; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0008, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0008, 0x0000, snacid); + + for (rc = ins->rates; rc; rc = rc->next) + aimbs_put16(&fr->data, rc->classid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Subtype 0x0009 - Delete Rate Parameter */ +faim_internal int aim_rates_delparam(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + aim_frame_t *fr; + aim_snacid_t snacid; + struct rateclass *rc; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0009, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0009, 0x0000, snacid); + + for (rc = ins->rates; rc; rc = rc->next) + aimbs_put16(&fr->data, rc->classid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Subtype 0x000a - Rate Change */ +static int ratechange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t code, rateclass; + fu32_t currentavg, maxavg, windowsize, clear, alert, limit, disconnect; + + code = aimbs_get16(bs); + rateclass = aimbs_get16(bs); + + windowsize = aimbs_get32(bs); + clear = aimbs_get32(bs); + alert = aimbs_get32(bs); + limit = aimbs_get32(bs); + disconnect = aimbs_get32(bs); + currentavg = aimbs_get32(bs); + maxavg = aimbs_get32(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg); + + return ret; +} + +/* + * How Migrations work. + * + * The server sends a Server Pause message, which the client should respond to + * with a Server Pause Ack, which contains the families it needs on this + * connection. The server will send a Migration Notice with an IP address, and + * then disconnect. Next the client should open the connection and send the + * cookie. Repeat the normal login process and pretend this never happened. + * + * The Server Pause contains no data. + * + */ + +/* Subtype 0x000b - Service Pause */ +static int serverpause(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + return ret; +} + +/* + * Subtype 0x000c - Service Pause Acknowledgement + * + * It is rather important that aim_sendpauseack() gets called for the exact + * same connection that the Server Pause callback was called for, since + * libfaim extracts the data for the SNAC from the connection structure. + * + * Of course, if you don't do that, more bad things happen than just what + * libfaim can cause. + * + */ +faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1024))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x000c, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x000c, 0x0000, snacid); + + /* + * This list should have all the groups that the original + * Host Online / Server Ready said this host supports. And + * we want them all back after the migration. + */ + for (sg = ins->groups; sg; sg = sg->next) + aimbs_put16(&fr->data, sg->group); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Subtype 0x000d - Service Resume */ +static int serverresume(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + return ret; +} + +/* Subtype 0x000e - Request self-info */ +faim_export int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x000e); +} + +/* Subtype 0x000f - Self User Info */ +static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + aim_userinfo_t userinfo; + + aim_info_extract(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &userinfo); + + aim_info_free(&userinfo); + + return ret; +} + +/* Subtype 0x0010 - Evil Notification */ +static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t newevil; + aim_userinfo_t userinfo; + + memset(&userinfo, 0, sizeof(aim_userinfo_t)); + + newevil = aimbs_get16(bs); + + if (aim_bstream_empty(bs)) + aim_info_extract(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, newevil, &userinfo); + + aim_info_free(&userinfo); + + return ret; +} + +/* + * Subtype 0x0011 - Idle Notification + * + * Should set your current idle time in seconds. Note that this should + * never be called consecutively with a non-zero idle time. That makes + * OSCAR do funny things. Instead, just set it once you go idle, and then + * call it again with zero when you're back. + * + */ +faim_export int aim_srv_setidle(aim_session_t *sess, fu32_t idletime) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_BOS))) + return -EINVAL; + + return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime); +} + +/* + * Subtype 0x0012 - Service Migrate + * + * This is the final SNAC sent on the original connection during a migration. + * It contains the IP and cookie used to connect to the new server, and + * optionally a list of the SNAC groups being migrated. + * + */ +static int migrate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + int ret = 0; + fu16_t groupcount, i; + aim_tlvlist_t *tl; + char *ip = NULL; + aim_tlv_t *cktlv; + + /* + * Apparently there's some fun stuff that can happen right here. The + * migration can actually be quite selective about what groups it + * moves to the new server. When not all the groups for a connection + * are migrated, or they are all migrated but some groups are moved + * to a different server than others, it is called a bifurcated + * migration. + * + * Let's play dumb and not support that. + * + */ + groupcount = aimbs_get16(bs); + for (i = 0; i < groupcount; i++) { + fu16_t group; + + group = aimbs_get16(bs); + + faimdprintf(sess, 0, "bifurcated migration unsupported -- group 0x%04x\n", group); + } + + tl = aim_tlvlist_read(bs); + + if (aim_tlv_gettlv(tl, 0x0005, 1)) + ip = aim_tlv_getstr(tl, 0x0005, 1); + + cktlv = aim_tlv_gettlv(tl, 0x0006, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, ip, cktlv ? cktlv->value : NULL); + + aim_tlvlist_free(&tl); + free(ip); + + return ret; +} + +/* Subtype 0x0013 - Message of the Day */ +static int motd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + char *msg = NULL; + int ret = 0; + aim_tlvlist_t *tlvlist; + fu16_t id; + + /* + * Code. + * + * Valid values: + * 1 Mandatory upgrade + * 2 Advisory upgrade + * 3 System bulletin + * 4 Nothing's wrong ("top o the world" -- normal) + * 5 Lets-break-something. + * + */ + id = aimbs_get16(bs); + + /* + * TLVs follow + */ + tlvlist = aim_tlvlist_read(bs); + + msg = aim_tlv_getstr(tlvlist, 0x000b, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, id, msg); + + free(msg); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0014 - Set privacy flags + * + * Normally 0x03. + * + * Bit 1: Allows other AIM users to see how long you've been idle. + * Bit 2: Allows other AIM users to see how long you've been a member. + * + */ +faim_export int aim_bos_setprivacyflags(aim_session_t *sess, aim_conn_t *conn, fu32_t flags) +{ + return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags); +} + +/* + * Subtype 0x0016 - No-op + * + * WinAIM sends these every 4min or so to keep the connection alive. Its not + * really necessary. + * + * Wha? No? Since when? I think WinAIM sends an empty channel 3 + * SNAC as a no-op... + */ +faim_export int aim_nop(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x0016); +} + +/* + * Subtype 0x0017 - Set client versions + * + * If you've seen the clientonline/clientready SNAC you're probably + * wondering what the point of this one is. And that point seems to be + * that the versions in the client online SNAC are sent too late for the + * server to be able to use them to change the protocol for the earlier + * login packets (client versions are sent right after Host Online is + * received, but client online versions aren't sent until quite a bit later). + * We can see them already making use of this by changing the format of + * the rate information based on what version of group 1 we advertise here. + * + */ +faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!ins) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0017, 0x0000, snacid); + + /* + * Send only the versions that the server cares about (that it + * marked as supporting in the server ready SNAC). + */ + for (sg = ins->groups; sg; sg = sg->next) { + aim_module_t *mod; + + if ((mod = aim__findmodulebygroup(sess, sg->group))) { + aimbs_put16(&fr->data, mod->family); + aimbs_put16(&fr->data, mod->version); + } else + faimdprintf(sess, 1, "aim_setversions: server supports group 0x%04x but we don't!\n", sg->group); + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Subtype 0x0018 - Host versions */ +static int hostversions(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int vercount; + fu8_t *versions; + + /* This is frivolous. (Thank you SmarterChild.) */ + vercount = aim_bstream_empty(bs)/4; + versions = aimbs_getraw(bs, aim_bstream_empty(bs)); + free(versions); + + /* + * Now request rates. + */ + aim_reqrates(sess, rx->conn); + + return 1; +} + +/* + * Subtype 0x001e - Extended Status + * + * Sets your ICQ status (available, away, do not disturb, etc.) + * + * These are the same TLVs seen in user info. You can + * also set 0x0008 and 0x000c. + */ +faim_export int aim_setextstatus(aim_session_t *sess, fu32_t status) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + fu32_t data; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + data = AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_WEBAWARE | status; /* yay for error checking ;^) */ + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 8))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid); + + aim_tlvlist_add_32(&tl, 0x0006, data); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x001e - Extended Status. + * + * Sets your "available" message. This is currently only supported by iChat + * and Gaim. + * + * These are the same TLVs seen in user info. You can + * also set 0x0008 and 0x000c. + */ +faim_export int aim_srv_setavailmsg(aim_session_t *sess, char *msg) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0001))) + return -EINVAL; + + if (msg != NULL) { + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + strlen(msg) + 8))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid); + + aimbs_put16(&fr->data, 0x001d); /* userinfo TLV type */ + aimbs_put16(&fr->data, strlen(msg)+8); /* total length of userinfo TLV data */ + aimbs_put16(&fr->data, 0x0002); + aimbs_put8(&fr->data, 0x04); + aimbs_put8(&fr->data, strlen(msg)+4); + aimbs_put16(&fr->data, strlen(msg)); + aimbs_putraw(&fr->data, msg, strlen(msg)); + aimbs_put16(&fr->data, 0x0000); + } else { + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + 8))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid); + + aimbs_put16(&fr->data, 0x001d); + aimbs_put16(&fr->data, 0x0008); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0404); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Starting this past week (26 Mar 2001, say), AOL has started sending + * this nice little extra SNAC. AFAIK, it has never been used until now. + * + * The request contains eight bytes. The first four are an offset, the + * second four are a length. + * + * The offset is an offset into aim.exe when it is mapped during execution + * on Win32. So far, AOL has only been requesting bytes in static regions + * of memory. (I won't put it past them to start requesting data in + * less static regions -- regions that are initialized at run time, but still + * before the client recieves this request.) + * + * When the client recieves the request, it adds it to the current ds + * (0x00400000) and dereferences it, copying the data into a buffer which + * it then runs directly through the MD5 hasher. The 16 byte output of + * the hash is then sent back to the server. + * + * If the client does not send any data back, or the data does not match + * the data that the specific client should have, the client will get the + * following message from "AOL Instant Messenger": + * "You have been disconnected from the AOL Instant Message Service (SM) + * for accessing the AOL network using unauthorized software. You can + * download a FREE, fully featured, and authorized client, here + * http://www.aol.com/aim/download2.html" + * The connection is then closed, recieving disconnect code 1, URL + * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html. + * + * Note, however, that numerous inconsistencies can cause the above error, + * not just sending back a bad hash. Do not immediatly suspect this code + * if you get disconnected. AOL and the open/free software community have + * played this game for a couple years now, generating the above message + * on numerous ocassions. + * + * Anyway, neener. We win again. + * + */ +/* Subtype 0x001f - Client verification */ +static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu32_t offset, len; + aim_tlvlist_t *list; + char *modname; + + offset = aimbs_get32(bs); + len = aimbs_get32(bs); + list = aim_tlvlist_read(bs); + + modname = aim_tlv_getstr(list, 0x0001, 1); + + faimdprintf(sess, 1, "data at 0x%08lx (%d bytes) of requested\n", offset, len, modname ? modname : "aim.exe"); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, offset, len, modname); + + free(modname); + aim_tlvlist_free(&list); + + return ret; +} + +#if 0 +static void dumpbox(aim_session_t *sess, unsigned char *buf, int len) +{ + int i; + + if (!sess || !buf || !len) + return; + + faimdprintf(sess, 1, "\nDump of %d bytes at %p:", len, buf); + + for (i = 0; i < len; i++) { + if ((i % 8) == 0) + faimdprintf(sess, 1, "\n\t"); + + faimdprintf(sess, 1, "0x%2x ", buf[i]); + } + + faimdprintf(sess, 1, "\n\n"); + + return; +} +#endif + +/* Subtype 0x0020 - Client verification reply */ +faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, fu32_t offset, fu32_t len, const fu8_t *buf, fu8_t flag) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0020, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0001, 0x0020, 0x0000, snacid); + aimbs_put16(&fr->data, 0x0010); /* md5 is always 16 bytes */ + + if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */ + + aimbs_putraw(&fr->data, buf, 0x10); + + } else if (buf && (len > 0)) { /* use input buffer */ + md5_state_t state; + md5_byte_t digest[0x10]; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)buf, len); + md5_finish(&state, digest); + + aimbs_putraw(&fr->data, (fu8_t *)digest, 0x10); + + } else if (len == 0) { /* no length, just hash NULL (buf is optional) */ + md5_state_t state; + fu8_t nil = '\0'; + md5_byte_t digest[0x10]; + + /* + * These MD5 routines are stupid in that you have to have + * at least one append. So thats why this doesn't look + * real logical. + */ + md5_init(&state); + md5_append(&state, (const md5_byte_t *)&nil, 0); + md5_finish(&state, digest); + + aimbs_putraw(&fr->data, (fu8_t *)digest, 0x10); + + } else { + + /* + * This data is correct for AIM 3.5.1670. + * + * Using these blocks is as close to "legal" as you can get + * without using an AIM binary. + * + */ + if ((offset == 0x03ffffff) && (len == 0x03ffffff)) { + +#if 1 /* with "AnrbnrAqhfzcd" */ + aimbs_put32(&fr->data, 0x44a95d26); + aimbs_put32(&fr->data, 0xd2490423); + aimbs_put32(&fr->data, 0x93b8821f); + aimbs_put32(&fr->data, 0x51c54b01); +#else /* no filename */ + aimbs_put32(&fr->data, 0x1df8cbae); + aimbs_put32(&fr->data, 0x5523b839); + aimbs_put32(&fr->data, 0xa0e10db3); + aimbs_put32(&fr->data, 0xa46d3b39); +#endif + + } else if ((offset == 0x00001000) && (len == 0x00000000)) { + + aimbs_put32(&fr->data, 0xd41d8cd9); + aimbs_put32(&fr->data, 0x8f00b204); + aimbs_put32(&fr->data, 0xe9800998); + aimbs_put32(&fr->data, 0xecf8427e); + + } else + faimdprintf(sess, 0, "sendmemblock: WARNING: unknown hash request\n"); + + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0021 - Receive our extended status + * + * This is used for iChat's "available" messages, and maybe ICQ extended + * status messages? It's also used to tell the client whether or not it + * needs to upload an SSI buddy icon... who engineers this stuff, anyway? + */ +static int aim_parse_extstatus(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t type; + fu8_t flags, length; + + type = aimbs_get16(bs); + flags = aimbs_get8(bs); + length = aimbs_get8(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { + switch (type) { + case 0x0000: + case 0x0001: { /* buddy icon checksum */ + /* not sure what the difference between 1 and 0 is */ + fu8_t *md5 = aimbs_getraw(bs, length); + ret = userfunc(sess, rx, type, flags, length, md5); + free(md5); + } break; + case 0x0002: { /* available message */ + /* there is a second length that is just for the message */ + char *msg = aimbs_getstr(bs, aimbs_get16(bs)); + ret = userfunc(sess, rx, msg); + free(msg); + } break; + } + } + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return hostonline(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0005) + return redirect(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return rateresp(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000a) + return ratechange(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000b) + return serverpause(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000d) + return serverresume(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000f) + return selfinfo(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0010) + return evilnotify(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0012) + return migrate(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0013) + return motd(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0018) + return hostversions(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x001f) + return memrequest(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0021) + return aim_parse_extstatus(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0001; + mod->version = 0x0003; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "service", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/snac.c b/libfaim/snac.c new file mode 100644 index 0000000..969c1bb --- /dev/null +++ b/libfaim/snac.c @@ -0,0 +1,148 @@ +/* + * + * Various SNAC-related dodads... + * + * outstanding_snacs is a list of aim_snac_t structs. A SNAC should be added + * whenever a new SNAC is sent and it should remain in the list until the + * response for it has been receieved. + * + * cleansnacs() should be called periodically by the client in order + * to facilitate the aging out of unreplied-to SNACs. This can and does + * happen, so it should be handled. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * Called from aim_session_init() to initialize the hash. + */ +faim_internal void aim_initsnachash(aim_session_t *sess) +{ + int i; + + for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) + sess->snac_hash[i] = NULL; + + return; +} + +faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const fu16_t family, const fu16_t type, const fu16_t flags, const void *data, const int datalen) +{ + aim_snac_t snac; + + snac.id = sess->snacid_next++; + snac.family = family; + snac.type = type; + snac.flags = flags; + + if (datalen) { + if (!(snac.data = malloc(datalen))) + return 0; /* er... */ + memcpy(snac.data, data, datalen); + } else + snac.data = NULL; + + return aim_newsnac(sess, &snac); +} + +/* + * Clones the passed snac structure and caches it in the + * list/hash. + */ +faim_internal aim_snacid_t aim_newsnac(aim_session_t *sess, aim_snac_t *newsnac) +{ + aim_snac_t *snac; + int index; + + if (!newsnac) + return 0; + + if (!(snac = malloc(sizeof(aim_snac_t)))) + return 0; + memcpy(snac, newsnac, sizeof(aim_snac_t)); + snac->issuetime = time(NULL); + + index = snac->id % FAIM_SNAC_HASH_SIZE; + + snac->next = (aim_snac_t *)sess->snac_hash[index]; + sess->snac_hash[index] = (void *)snac; + + return snac->id; +} + +/* + * Finds a snac structure with the passed SNAC ID, + * removes it from the list/hash, and returns a pointer to it. + * + * The returned structure must be freed by the caller. + * + */ +faim_internal aim_snac_t *aim_remsnac(aim_session_t *sess, aim_snacid_t id) +{ + aim_snac_t *cur, **prev; + int index; + + index = id % FAIM_SNAC_HASH_SIZE; + + for (prev = (aim_snac_t **)&sess->snac_hash[index]; (cur = *prev); ) { + if (cur->id == id) { + *prev = cur->next; + if (cur->flags & AIM_SNACFLAGS_DESTRUCTOR) { + free(cur->data); + cur->data = NULL; + } + return cur; + } else + prev = &cur->next; + } + + return cur; +} + +/* + * This is for cleaning up old SNACs that either don't get replies or + * a reply was never received for. Garabage collection. Plain and simple. + * + * maxage is the _minimum_ age in seconds to keep SNACs. + * + */ +faim_export void aim_cleansnacs(aim_session_t *sess, int maxage) +{ + int i; + + for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) { + aim_snac_t *cur, **prev; + time_t curtime; + + if (!sess->snac_hash[i]) + continue; + + curtime = time(NULL); /* done here in case we waited for the lock */ + + for (prev = (aim_snac_t **)&sess->snac_hash[i]; (cur = *prev); ) { + if ((curtime - cur->issuetime) > maxage) { + + *prev = cur->next; + + free(cur->data); + free(cur); + } else + prev = &cur->next; + } + } + + return; +} + +faim_internal int aim_putsnac(aim_bstream_t *bs, fu16_t family, fu16_t subtype, fu16_t flags, aim_snacid_t snacid) +{ + + aimbs_put16(bs, family); + aimbs_put16(bs, subtype); + aimbs_put16(bs, flags); + aimbs_put32(bs, snacid); + + return 10; +} diff --git a/libfaim/ssi.c b/libfaim/ssi.c new file mode 100644 index 0000000..96dba67 --- /dev/null +++ b/libfaim/ssi.c @@ -0,0 +1,1963 @@ +/* + * Family 0x0013 - Server-Side/Stored Information. + * + * Relatively new facility that allows certain types of information, such as + * a user's buddy list, permit/deny list, and permit/deny preferences, to be + * stored on the server, so that they can be accessed from any client. + * + * We keep 2 copies of SSI data: + * 1) An exact copy of what is stored on the AIM servers. + * 2) A local copy that we make changes to, and then send diffs + * between this and the exact copy to keep them in sync. + * + * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list + * that is given to them (i.e. they don't send SNACs). + * + * The SNAC sending and receiving functions are lower down in the file, and + * they're simpler. They are in the order of the subtypes they deal with, + * starting with the request rights function (subtype 0x0002), then parse + * rights (subtype 0x0003), then--well, you get the idea. + * + * This is entirely too complicated. + * You don't know the half of it. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/** + * Locally rebuild the 0x00c8 TLV in the additional data of the given group. + * + * @param list A pointer to a pointer to the current list of items. + * @param name A null terminated string containing the group name, or NULL + * if you want to modify the master group. + * @return Return a pointer to the modified item. + */ +static struct aim_ssi_item *aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name) +{ + int newlen; + struct aim_ssi_item *cur, *group; + + if (!list) + return NULL; + + /* Find the group */ + if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP))) + return NULL; + + /* Find the length for the new additional data */ + newlen = 0; + if (group->gid == 0x0000) { + for (cur=list; cur; cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000)) + newlen += 2; + } else { + for (cur=list; cur; cur=cur->next) + if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) + newlen += 2; + } + + /* Build the new TLV list */ + if (newlen > 0) { + fu8_t *newdata; + + if (!(newdata = (fu8_t *)malloc((newlen)*sizeof(fu8_t)))) + return NULL; + newlen = 0; + if (group->gid == 0x0000) { + for (cur=list; cur; cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000)) + newlen += aimutil_put16(newdata+newlen, cur->gid); + } else { + for (cur=list; cur; cur=cur->next) + if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) + newlen += aimutil_put16(newdata+newlen, cur->bid); + } + aim_tlvlist_replace_raw(&group->data, 0x00c8, newlen, newdata); + + free(newdata); + } + + return group; +} + +/** + * Locally add a new item to the given item list. + * + * @param list A pointer to a pointer to the current list of items. + * @param name A null terminated string of the name of the new item, or NULL if the + * item should have no name. + * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something. + * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something. + * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc. + * @param data The additional data for the new item. + * @return A pointer to the newly created item. + */ +static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, fu16_t gid, fu16_t bid, fu16_t type, aim_tlvlist_t *data) +{ + int i; + struct aim_ssi_item *cur, *new; + + if (!list) + return NULL; + + if (!(new = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item)))) + return NULL; + + /* Set the name */ + if (name) { + new->name = (char *)malloc((strlen(name)+1)*sizeof(char)); + strcpy(new->name, name); + } else + new->name = NULL; + + /* Set the group ID# and buddy ID# */ + new->gid = gid; + new->bid = bid; + if (type == AIM_SSI_TYPE_GROUP) { + if ((new->gid == 0xFFFF) && name) { + do { + new->gid += 0x0001; + for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid)) + i=1; + } while (i); + } + } else { + if (new->bid == 0xFFFF) { + do { + new->bid += 0x0001; + for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next) + if ((cur->bid == new->bid) && (cur->gid == new->gid)) + i=1; + } while (i); + } + } + + /* Set the type */ + new->type = type; + + /* Set the TLV list */ + new->data = aim_tlvlist_copy(data); + + /* Add the item to the list in the correct numerical position. Fancy, eh? */ + if (*list) { + if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) { + new->next = *list; + *list = new; + } else { + struct aim_ssi_item *prev; + for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next); + new->next = prev->next; + prev->next = new; + } + } else { + new->next = *list; + *list = new; + } + + return new; +} + +/** + * Locally delete an item from the given item list. + * + * @param list A pointer to a pointer to the current list of items. + * @param del A pointer to the item you want to remove from the list. + * @return Return 0 if no errors, otherwise return the error number. + */ +static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del) +{ + if (!list || !(*list) || !del) + return -EINVAL; + + /* Remove the item from the list */ + if (*list == del) { + *list = (*list)->next; + } else { + struct aim_ssi_item *cur; + for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next); + if (cur->next) + cur->next = del->next; + } + + /* Free the removed item */ + free(del->name); + aim_tlvlist_free(&del->data); + free(del); + + return 0; +} + +/** + * Compare two items to see if they have the same data. + * + * @param cur1 A pointer to a pointer to the first item. + * @param cur2 A pointer to a pointer to the second item. + * @return Return 0 if no differences, or a number if there are differences. + */ +static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2) +{ + if (!cur1 || !cur2) + return 1; + + if (cur1->data && !cur2->data) + return 2; + + if (!cur1->data && cur2->data) + return 3; + + if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data))) + return 4; + + if (cur1->name && !cur2->name) + return 5; + + if (!cur1->name && cur2->name) + return 6; + + if (cur1->name && cur2->name && aim_sncmp(cur1->name, cur2->name)) + return 7; + + if (cur1->gid != cur2->gid) + return 8; + + if (cur1->bid != cur2->bid) + return 9; + + if (cur1->type != cur2->type) + return 10; + + return 0; +} + +faim_export int aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item) +{ + struct aim_ssi_item *cur; + for (cur=list; cur; cur=cur->next) + if (cur == item) + return 1; + return 0; +} + +/** + * Locally find an item given a group ID# and a buddy ID#. + * + * @param list A pointer to the current list of items. + * @param gid The group ID# of the desired item. + * @param bid The buddy ID# of the desired item. + * @return Return a pointer to the item if found, else return NULL; + */ +faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid) +{ + struct aim_ssi_item *cur; + for (cur=list; cur; cur=cur->next) + if ((cur->gid == gid) && (cur->bid == bid)) + return cur; + return NULL; +} + +/** + * Locally find an item given a group name, screen name, and type. If group name + * and screen name are null, then just return the first item of the given type. + * + * @param list A pointer to the current list of items. + * @param gn The group name of the desired item. + * @param bn The buddy name of the desired item. + * @param type The type of the desired item. + * @return Return a pointer to the item if found, else return NULL; + */ +faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, fu16_t type) +{ + struct aim_ssi_item *cur; + if (!list) + return NULL; + + if (gn && sn) { /* For finding buddies in groups */ + for (cur=list; cur; cur=cur->next) + if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) { + struct aim_ssi_item *curg; + for (curg=list; curg; curg=curg->next) + if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn))) + return cur; + } + + } else if (gn) { /* For finding groups */ + for (cur=list; cur; cur=cur->next) { + if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(aim_sncmp(cur->name, gn))) { + return cur; + } + } + + } else if (sn) { /* For finding permits, denies, and ignores */ + for (cur=list; cur; cur=cur->next) { + if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) { + return cur; + } + } + + /* For stuff without names--permit deny setting, visibility mask, etc. */ + } else for (cur=list; cur; cur=cur->next) { + if ((cur->type == type) && (!cur->name)) + return cur; + } + + return NULL; +} + +/** + * Check if the given buddy exists in any group in the buddy list. + * + * @param list A pointer to the current list of items. + * @param sn The group name of the desired item. + * @return Return a pointer to the name of the item if found, else return NULL; + */ +faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn) +{ + struct aim_ssi_item *cur; + if (!list || !sn) + return NULL; + for (cur=list; cur; cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && (!aim_sncmp(cur->name, sn))) + return cur; + return NULL; +} + +/** + * Locally find the parent item of the given buddy name. + * + * @param list A pointer to the current list of items. + * @param bn The buddy name of the desired item. + * @return Return a pointer to the name of the item if found, else return NULL; + */ +faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn) +{ + struct aim_ssi_item *cur, *curg; + if (!list || !sn) + return NULL; + if (!(cur = aim_ssi_itemlist_exists(list, sn))) + return NULL; + if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000))) + return NULL; + return curg->name; +} + +/** + * Locally find the permit/deny setting item, and return the setting. + * + * @param list A pointer to the current list of items. + * @return Return the current SSI permit deny setting, or 0 if no setting was found. + */ +faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO); + if (cur) { + aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00ca, 1); + if (tlv && tlv->value) + return aimutil_get8(tlv->value); + } + return 0; +} + +/** + * Locally find the presence flag item, and return the setting. The returned setting is a + * bitmask of the user flags that you are visible to. See the AIM_FLAG_* #defines + * in aim.h + * + * @param list A pointer to the current list of items. + * @return Return the current visibility mask. + */ +faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS); + if (cur) { + aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c9, 1); + if (tlv && tlv->length) + return aimutil_get32(tlv->value); + } + return 0xFFFFFFFF; +} + +/** + * Locally find the alias of the given buddy. + * + * @param list A pointer to the current list of items. + * @param gn The group of the buddy. + * @param sn The name of the buddy. + * @return A pointer to a NULL terminated string that is the buddy's + * alias, or NULL if the buddy has no alias. You should free + * this returned value! + */ +faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY); + if (cur) { + aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x0131, 1); + if (tlv && tlv->length) { + char *alias = (char *)malloc((tlv->length+1)*sizeof(char)); + strncpy(alias, tlv->value, tlv->length); + alias[tlv->length] = 0; + return alias; + } + } + return NULL; +} + +/** + * Locally find the comment of the given buddy. + * + * @param list A pointer to the current list of items. + * @param gn The group of the buddy. + * @param sn The name of the buddy. + * @return A pointer to a NULL terminated string that is the buddy's + * comment, or NULL if the buddy has no comment. You should free + * this returned value! + */ +faim_export char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY); + if (cur) { + aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1); + if (tlv && tlv->length) { + char *alias = (char *)malloc((tlv->length+1)*sizeof(char)); + strncpy(alias, tlv->value, tlv->length); + alias[tlv->length] = 0; + return alias; + } + } + return NULL; +} + +/** + * Locally find if you are waiting for authorization for a buddy. + * + * @param list A pointer to the current list of items. + * @param gn The group of the buddy. + * @param sn The name of the buddy. + * @return A pointer to a NULL terminated string that is the buddies + * alias, or NULL if the buddy has no alias. You should free + * this returned value! + */ +faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY); + if (cur) { + if (aim_tlv_gettlv(cur->data, 0x0066, 1)) + return 1; + } + return 0; +} + +/** + * If there are changes, then create temporary items and + * call addmoddel. + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +static int aim_ssi_sync(aim_session_t *sess) +{ + struct aim_ssi_item *cur1, *cur2; + struct aim_ssi_tmp *cur, *new; + + if (!sess) + return -EINVAL; + + /* If we're waiting for an ack, we shouldn't do anything else */ + if (sess->ssi.waiting_for_ack) + return 0; + + /* + * Compare the 2 lists and create an aim_ssi_tmp for each difference. + * We should only send either additions, modifications, or deletions + * before waiting for an acknowledgement. So first do deletions, then + * additions, then modifications. Also, both the official and the local + * list should be in ascending numerical order for the group ID#s and the + * buddy ID#s, which makes things more efficient. I think. + */ + + /* Additions */ + if (!sess->ssi.pending) { + for (cur1=sess->ssi.local; cur1; cur1=cur1->next) { + if (!aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid)) { + new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp)); + new->action = AIM_CB_SSI_ADD; + new->ack = 0xffff; + new->name = NULL; + new->item = cur1; + new->next = NULL; + if (sess->ssi.pending) { + for (cur=sess->ssi.pending; cur->next; cur=cur->next); + cur->next = new; + } else + sess->ssi.pending = new; + } + } + } + + /* Deletions */ + if (!sess->ssi.pending) { + for (cur1=sess->ssi.official; cur1; cur1=cur1->next) { + if (!aim_ssi_itemlist_find(sess->ssi.local, cur1->gid, cur1->bid)) { + new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp)); + new->action = AIM_CB_SSI_DEL; + new->ack = 0xffff; + new->name = NULL; + new->item = cur1; + new->next = NULL; + if (sess->ssi.pending) { + for (cur=sess->ssi.pending; cur->next; cur=cur->next); + cur->next = new; + } else + sess->ssi.pending = new; + } + } + } + + /* Modifications */ + if (!sess->ssi.pending) { + for (cur1=sess->ssi.local; cur1; cur1=cur1->next) { + cur2 = aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid); + if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) { + new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp)); + new->action = AIM_CB_SSI_MOD; + new->ack = 0xffff; + new->name = NULL; + new->item = cur1; + new->next = NULL; + if (sess->ssi.pending) { + for (cur=sess->ssi.pending; cur->next; cur=cur->next); + cur->next = new; + } else + sess->ssi.pending = new; + } + } + } + + /* We're out of stuff to do, so tell the AIM servers we're done and exit */ + if (!sess->ssi.pending) { + aim_ssi_modend(sess); + return 0; + } + + /* Make sure we don't send anything else between now + * and when we receive the ack for the following operation */ + sess->ssi.waiting_for_ack = 1; + + /* Now go mail off our data and wait 4 to 6 weeks */ + aim_ssi_addmoddel(sess); + + return 0; +} + +/** + * Free all SSI data. + * + * This doesn't remove it from the server, that's different. + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +static int aim_ssi_freelist(aim_session_t *sess) +{ + struct aim_ssi_item *cur, *del; + struct aim_ssi_tmp *curtmp, *deltmp; + + cur = sess->ssi.official; + while (cur) { + del = cur; + cur = cur->next; + free(del->name); + aim_tlvlist_free(&del->data); + free(del); + } + + cur = sess->ssi.local; + while (cur) { + del = cur; + cur = cur->next; + free(del->name); + aim_tlvlist_free(&del->data); + free(del); + } + + curtmp = sess->ssi.pending; + while (curtmp) { + deltmp = curtmp; + curtmp = curtmp->next; + free(deltmp); + } + + sess->ssi.numitems = 0; + sess->ssi.official = NULL; + sess->ssi.local = NULL; + sess->ssi.pending = NULL; + sess->ssi.timestamp = (time_t)0; + + return 0; +} + +/** + * Delete all SSI data. + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_deletelist(aim_session_t *sess) +{ + struct aim_ssi_item *cur, *del; + + if (!sess) + return -EINVAL; + + /* Free the local list */ + cur = sess->ssi.local; + while (cur) { + del = cur; + cur = cur->next; + free(del->name); + aim_tlvlist_free(&del->data); + free(del); + } + sess->ssi.local = NULL; + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * This "cleans" the ssi list. It does the following: + * 1) Makes sure all buddies, permits, and denies have names. + * 2) Makes sure that all buddies are in a group that exist. + * 3) Deletes any empty groups + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_cleanlist(aim_session_t *sess) +{ + struct aim_ssi_item *cur, *next; + + if (!sess) + return -EINVAL; + + /* Delete any buddies, permits, or denies with empty names. */ + /* If there are any buddies directly in the master group, add them to a real group. */ + /* DESTROY any buddies that are directly in the master group. */ + /* Do the same for buddies that are in a non-existant group. */ + /* This will kind of mess up if you hit the item limit, but this function isn't too critical */ + cur = sess->ssi.local; + while (cur) { + next = cur->next; + if (!cur->name) { + if (cur->type == AIM_SSI_TYPE_BUDDY) + aim_ssi_delbuddy(sess, NULL, NULL); + else if (cur->type == AIM_SSI_TYPE_PERMIT) + aim_ssi_delpermit(sess, NULL); + else if (cur->type == AIM_SSI_TYPE_DENY) + aim_ssi_deldeny(sess, NULL); + } else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(sess->ssi.local, cur->gid, 0x0000)))) { + aim_ssi_addbuddy(sess, cur->name, "orphans", NULL, NULL, NULL, 0); + aim_ssi_delbuddy(sess, cur->name, NULL); + } + cur = next; + } + + /* Check if there are empty groups and delete them */ + cur = sess->ssi.local; + while (cur) { + next = cur->next; + if (cur->type == AIM_SSI_TYPE_GROUP) { + aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c8, 1); + if (!tlv || !tlv->length) + aim_ssi_itemlist_del(&sess->ssi.local, cur); + } + cur = next; + } + + /* Check if the master group is empty */ + if ((cur = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!cur->data)) + aim_ssi_itemlist_del(&sess->ssi.local, cur); + + return 0; +} + +/** + * Add a buddy to the list. + * + * @param sess The oscar session. + * @param name The name of the item. + * @param group The group of the item. + * @param alias The alias/nickname of the item, or NULL. + * @param comment The buddy comment for the item, or NULL. + * @param smsnum The locally assigned SMS number, or NULL. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth) +{ + struct aim_ssi_item *parent; + aim_tlvlist_t *data = NULL; + + if (!sess || !name || !group) + return -EINVAL; + + /* Find the parent */ + if (!(parent = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) { + /* Find the parent's parent (the master group) */ + if (!(parent = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000))) + if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL))) + return -ENOMEM; + /* Add the parent */ + if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL))) + return -ENOMEM; + + /* Modify the parent's parent (the master group) */ + aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL); + } + + /* Create a TLV list for the new buddy */ + if (needauth) + aim_tlvlist_add_noval(&data, 0x0066); + if (alias) + aim_tlvlist_add_raw(&data, 0x0131, strlen(alias), alias); + if (smsnum) + aim_tlvlist_add_raw(&data, 0x013a, strlen(smsnum), smsnum); + if (comment) + aim_tlvlist_add_raw(&data, 0x013c, strlen(comment), comment); + + /* Add that bad boy */ + aim_ssi_itemlist_add(&sess->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data); + aim_tlvlist_free(&data); + + /* Modify the parent group */ + aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Add a permit buddy to the list. + * + * @param sess The oscar session. + * @param name The name of the item.. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name) +{ + + if (!sess || !name) + return -EINVAL; + + /* Add that bad boy */ + aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_PERMIT, NULL); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Add a deny buddy to the list. + * + * @param sess The oscar session. + * @param name The name of the item.. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name) +{ + + if (!sess || !name) + return -EINVAL; + + /* Add that bad boy */ + aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_DENY, NULL); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Deletes a buddy from the list. + * + * @param sess The oscar session. + * @param name The name of the item, or NULL. + * @param group The group of the item, or NULL. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group) +{ + struct aim_ssi_item *del; + + if (!sess) + return -EINVAL; + + /* Find the buddy */ + if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, group, name, AIM_SSI_TYPE_BUDDY))) + return -EINVAL; + + /* Remove the item from the list */ + aim_ssi_itemlist_del(&sess->ssi.local, del); + + /* Modify the parent group */ + aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group); + + /* Check if we should delete the parent group */ + if ((del = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)) && (!del->data)) { + aim_ssi_itemlist_del(&sess->ssi.local, del); + + /* Modify the parent group */ + aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL); + + /* Check if we should delete the parent's parent (the master group) */ + if ((del = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!del->data)) { + aim_ssi_itemlist_del(&sess->ssi.local, del); + } + } + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Deletes a permit buddy from the list. + * + * @param sess The oscar session. + * @param name The name of the item, or NULL. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name) +{ + struct aim_ssi_item *del; + + if (!sess) + return -EINVAL; + + /* Find the item */ + if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_PERMIT))) + return -EINVAL; + + /* Remove the item from the list */ + aim_ssi_itemlist_del(&sess->ssi.local, del); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Deletes a deny buddy from the list. + * + * @param sess The oscar session. + * @param name The name of the item, or NULL. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name) +{ + struct aim_ssi_item *del; + + if (!sess) + return -EINVAL; + + /* Find the item */ + if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_DENY))) + return -EINVAL; + + /* Remove the item from the list */ + aim_ssi_itemlist_del(&sess->ssi.local, del); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Move a buddy from one group to another group. This basically just deletes the + * buddy and re-adds it. + * + * @param sess The oscar session. + * @param oldgn The group that the buddy is currently in. + * @param newgn The group that the buddy should be moved in to. + * @param sn The name of the buddy to be moved. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn) +{ + aim_ssi_addbuddy(sess, sn, newgn, aim_ssi_getalias(sess->ssi.local, oldgn, sn), NULL, NULL, aim_ssi_waitingforauth(sess->ssi.local, oldgn, sn)); + aim_ssi_delbuddy(sess, sn, oldgn); + return 0; +} + +/** + * Change the alias stored on the server for a given buddy. + * + * @param sess The oscar session. + * @param gn The group that the buddy is currently in. + * @param sn The screen name of the buddy. + * @param alias The new alias for the buddy, or NULL if you want to remove + * a buddy's comment. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias) +{ + struct aim_ssi_item *tmp; + + if (!sess || !gn || !sn) + return -EINVAL; + + if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY))) + return -EINVAL; + + /* Either add or remove the 0x0131 TLV from the TLV chain */ + if ((alias != NULL) && (strlen(alias) > 0)) + aim_tlvlist_replace_raw(&tmp->data, 0x0131, strlen(alias), alias); + else + aim_tlvlist_remove(&tmp->data, 0x0131); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Change the comment stored on the server for a given buddy. + * + * @param sess The oscar session. + * @param gn The group that the buddy is currently in. + * @param sn The screen name of the buddy. + * @param alias The new comment for the buddy, or NULL if you want to remove + * a buddy's comment. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_editcomment(aim_session_t *sess, const char *gn, const char *sn, const char *comment) +{ + struct aim_ssi_item *tmp; + + if (!sess || !gn || !sn) + return -EINVAL; + + if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY))) + return -EINVAL; + + /* Either add or remove the 0x0131 TLV from the TLV chain */ + if ((comment != NULL) && (strlen(comment) > 0)) + aim_tlvlist_replace_raw(&tmp->data, 0x013c, strlen(comment), comment); + else + aim_tlvlist_remove(&tmp->data, 0x013c); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Rename a group. + * + * @param sess The oscar session. + * @param oldgn The old group name. + * @param newgn The new group name. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn) +{ + struct aim_ssi_item *group; + + if (!sess || !oldgn || !newgn) + return -EINVAL; + + if (!(group = aim_ssi_itemlist_finditem(sess->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP))) + return -EINVAL; + + free(group->name); + group->name = (char *)malloc((strlen(newgn)+1)*sizeof(char)); + strcpy(group->name, newgn); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Stores your permit/deny setting on the server, and starts using it. + * + * @param sess The oscar session. + * @param permdeny Your permit/deny setting. Can be one of the following: + * 1 - Allow all users + * 2 - Block all users + * 3 - Allow only the users below + * 4 - Block only the users below + * 5 - Allow only users on my buddy list + * @param vismask A bitmask of the class of users to whom you want to be + * visible. See the AIM_FLAG_BLEH #defines in aim.h + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask) +{ + struct aim_ssi_item *tmp; + + if (!sess) + return -EINVAL; + + /* Find the PDINFO item, or add it if it does not exist */ + if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) + tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL); + + /* Need to add the 0x00ca TLV to the TLV chain */ + aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny); + + /* Need to add the 0x00cb TLV to the TLV chain */ + aim_tlvlist_replace_32(&tmp->data, 0x00cb, vismask); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Set buddy icon information + * + * @param sess The oscar session. + * @param iconcsum The MD5 checksum of the icon you are using. + * @param iconcsumlen Length of the MD5 checksum given above. Should be 0x10 bytes. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen) +{ + struct aim_ssi_item *tmp; + fu8_t *csumdata; + + if (!sess || !iconsum || !iconsumlen) + return -EINVAL; + + /* Find the ICONINFO item, or add it if it does not exist */ + if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) { + tmp = aim_ssi_itemlist_add(&sess->ssi.local, "1", 0x0000, 0x51F4, AIM_SSI_TYPE_ICONINFO, NULL); + } + + /* Need to add the 0x00d5 TLV to the TLV chain */ + if (!(csumdata = (fu8_t *)malloc((iconsumlen+2)*sizeof(fu8_t)))) + return -ENOMEM; + csumdata[0] = 0x00; + csumdata[1] = 0x10; + memcpy(&csumdata[2], iconsum, iconsumlen); + aim_tlvlist_replace_raw(&tmp->data, 0x00d5, (iconsumlen+2) * sizeof(fu8_t), csumdata); + free(csumdata); + + /* Need to add the 0x0131 TLV to the TLV chain, used to cache the icon */ + aim_tlvlist_replace_noval(&tmp->data, 0x0131); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + return 0; +} + +/** + * Remove a reference to a server stored buddy icon. This will make your + * icon stop showing up to other people. + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_delicon(aim_session_t *sess) +{ + struct aim_ssi_item *tmp; + + if (!sess) + return -EINVAL; + + /* Find the ICONINFO item and delete it if it exists*/ + if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) + aim_ssi_itemlist_del(&sess->ssi.local, tmp); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + return 0; +} + +/** + * Stores your setting for whether you should show up as idle or not. + * + * @param sess The oscar session. + * @param presence I think it's a bitmask, but I only know what one of the bits is: + * 0x00000002 - Hide wireless? + * 0x00000400 - Allow others to see your idle time + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence) { + struct aim_ssi_item *tmp; + + if (!sess) + return -EINVAL; + + /* Find the PRESENCEPREFS item, or add it if it does not exist */ + if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) + tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL); + + /* Need to add the x00c9 TLV to the TLV chain */ + aim_tlvlist_replace_32(&tmp->data, 0x00c9, presence); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/* + * Subtype 0x0002 - Request SSI Rights. + */ +faim_export int aim_ssi_reqrights(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) + return -EINVAL; + + return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS); +} + +/* + * Subtype 0x0003 - SSI Rights Information. + */ +static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0, i; + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + aim_tlv_t *tlv; + aim_bstream_t bstream; + fu16_t *maxitems; + + /* This SNAC is made up of a bunch of TLVs */ + tlvlist = aim_tlvlist_read(bs); + + /* TLV 0x0004 contains the maximum number of each item */ + if (!(tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { + aim_tlvlist_free(&tlvlist); + return 0; + } + + aim_bstream_init(&bstream, tlv->value, tlv->length); + + if (!(maxitems = (fu16_t *)malloc((tlv->length/2)*sizeof(fu16_t)))) { + aim_tlvlist_free(&tlvlist); + return 0; + } + + for (i=0; i<(tlv->length/2); i++) + maxitems[i] = aimbs_get16(&bstream); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, tlv->length/2, maxitems); + + aim_tlvlist_free(&tlvlist); + free(maxitems); + + return ret; +} + +/* + * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and + * revision number. + * + */ +faim_export int aim_ssi_reqdata(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) + return -EINVAL; + + /* Free any current data, just in case */ + aim_ssi_freelist(sess); + + return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQDATA); +} + +/* + * Subtype 0x0005 - Request SSI Data when you have a timestamp and revision + * number. + * + * The data will only be sent if it is newer than the posted local + * timestamp and revision. + * + * Note that the client should never increment the revision, only the server. + * + */ +faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t timestamp, fu16_t numitems) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, snacid); + aimbs_put32(&fr->data, timestamp); + aimbs_put16(&fr->data, numitems); + + aim_tx_enqueue(sess, fr); + + /* Free any current data, just in case */ + aim_ssi_freelist(sess); + + return 0; +} + +/* + * Subtype 0x0006 - SSI Data. + */ +static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu8_t fmtver; /* guess */ + fu16_t namelen, gid, bid, type; + char *name; + aim_tlvlist_t *data; + + fmtver = aimbs_get8(bs); /* Version of ssi data. Should be 0x00 */ + sess->ssi.numitems += aimbs_get16(bs); /* # of items in this SSI SNAC */ + + /* Read in the list */ + while (aim_bstream_empty(bs) > 4) { /* last four bytes are timestamp */ + if ((namelen = aimbs_get16(bs))) + name = aimbs_getstr(bs, namelen); + else + name = NULL; + gid = aimbs_get16(bs); + bid = aimbs_get16(bs); + type = aimbs_get16(bs); + data = aim_tlvlist_readlen(bs, aimbs_get16(bs)); + aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data); + free(name); + aim_tlvlist_free(&data); + } + + /* Read in the timestamp */ + sess->ssi.timestamp = aimbs_get32(bs); + + if (!(snac->flags & 0x0001)) { + /* Make a copy of the list */ + struct aim_ssi_item *cur; + for (cur=sess->ssi.official; cur; cur=cur->next) + aim_ssi_itemlist_add(&sess->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data); + + sess->ssi.received_data = 1; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, fmtver, sess->ssi.numitems, sess->ssi.official, sess->ssi.timestamp); + } + + return ret; +} + +/* + * Subtype 0x0007 - SSI Activate Data. + * + * Should be sent after receiving 13/6 or 13/f to tell the server you + * are ready to begin using the list. It will promptly give you the + * presence information for everyone in your list and put your permit/deny + * settings into effect. + * + */ +faim_export int aim_ssi_enable(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) + return -EINVAL; + + return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007); +} + +/* + * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s). + * + * Sends the SNAC to add, modify, or delete an item from the server-stored + * information. These 3 SNACs all have an identical structure. The only + * difference is the subtype that is set for the SNAC. + * + */ +faim_export int aim_ssi_addmoddel(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int snaclen; + struct aim_ssi_tmp *cur; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sess->ssi.pending || !sess->ssi.pending->item) + return -EINVAL; + + /* Calculate total SNAC size */ + snaclen = 10; /* For family, subtype, flags, and SNAC ID */ + for (cur=sess->ssi.pending; cur; cur=cur->next) { + snaclen += 10; /* For length, GID, BID, type, and length */ + if (cur->item->name) + snaclen += strlen(cur->item->name); + if (cur->item->data) + snaclen += aim_tlvlist_size(&cur->item->data); + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, NULL, 0); + aim_putsnac(&fr->data, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, snacid); + + for (cur=sess->ssi.pending; cur; cur=cur->next) { + aimbs_put16(&fr->data, cur->item->name ? strlen(cur->item->name) : 0); + if (cur->item->name) + aimbs_putraw(&fr->data, cur->item->name, strlen(cur->item->name)); + aimbs_put16(&fr->data, cur->item->gid); + aimbs_put16(&fr->data, cur->item->bid); + aimbs_put16(&fr->data, cur->item->type); + aimbs_put16(&fr->data, cur->item->data ? aim_tlvlist_size(&cur->item->data) : 0); + if (cur->item->data) + aim_tlvlist_write(&fr->data, &cur->item->data); + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0008 - Incoming SSI add. + * + * XXX - It would probably be good for the client to actually do something when it gets this. + */ +static int parseadd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + char *name; + fu16_t len, gid, bid, type; + aim_tlvlist_t *data; + + while (aim_bstream_empty(bs)) { + if ((len = aimbs_get16(bs))) + name = aimbs_getstr(bs, len); + else + name = NULL; + gid = aimbs_get16(bs); + bid = aimbs_get16(bs); + type = aimbs_get16(bs); + if ((len = aimbs_get16(bs))) + data = aim_tlvlist_readlen(bs, len); + else + data = NULL; + + aim_ssi_itemlist_add(&sess->ssi.local, name, gid, bid, type, data); + aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data); + aim_tlvlist_free(&data); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + free(name); + } + + return ret; +} + +/* + * Subtype 0x0009 - Incoming SSI mod. + * + * XXX - It would probably be good for the client to actually do something when it gets this. + */ +static int parsemod(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + char *name; + fu16_t len, gid, bid, type; + aim_tlvlist_t *data; + struct aim_ssi_item *item; + + while (aim_bstream_empty(bs)) { + if ((len = aimbs_get16(bs))) + name = aimbs_getstr(bs, len); + else + name = NULL; + gid = aimbs_get16(bs); + bid = aimbs_get16(bs); + type = aimbs_get16(bs); + if ((len = aimbs_get16(bs))) + data = aim_tlvlist_readlen(bs, len); + else + data = NULL; + + /* Replace the 2 local items with the given one */ + if ((item = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) { + item->type = type; + free(item->name); + if (name) { + item->name = (char *)malloc((strlen(name)+1)*sizeof(char)); + strcpy(item->name, name); + } else + item->name = NULL; + aim_tlvlist_free(&item->data); + item->data = aim_tlvlist_copy(data); + } + + if ((item = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) { + item->type = type; + free(item->name); + if (name) { + item->name = (char *)malloc((strlen(name)+1)*sizeof(char)); + strcpy(item->name, name); + } else + item->name = NULL; + aim_tlvlist_free(&item->data); + item->data = aim_tlvlist_copy(data); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + free(name); + aim_tlvlist_free(&data); + } + + return ret; +} + +/* + * Subtype 0x000a - Incoming SSI del. + * + * XXX - It would probably be good for the client to actually do something when it gets this. + */ +static int parsedel(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t gid, bid; + struct aim_ssi_item *del; + + while (aim_bstream_empty(bs)) { + aim_bstream_advance(bs, aimbs_get16(bs)); + gid = aimbs_get16(bs); + bid = aimbs_get16(bs); + aimbs_get16(bs); + aim_bstream_advance(bs, aimbs_get16(bs)); + + if ((del = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) + aim_ssi_itemlist_del(&sess->ssi.local, del); + if ((del = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) + aim_ssi_itemlist_del(&sess->ssi.official, del); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + } + + return ret; +} + +/* + * Subtype 0x000e - SSI Add/Mod/Del Ack. + * + * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel). + * + */ +static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + struct aim_ssi_tmp *cur, *del; + + /* Read in the success/failure flags from the ack SNAC */ + cur = sess->ssi.pending; + while (cur && (aim_bstream_empty(bs)>0)) { + cur->ack = aimbs_get16(bs); + cur = cur->next; + } + + /* + * If outcome is 0, then add the item to the item list, or replace the other item, + * or remove the old item. If outcome is non-zero, then remove the item from the + * local list, or unmodify it, or add it. + */ + for (cur=sess->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) { + if (cur->item) { + if (cur->ack) { + /* Our action was unsuccessful, so change the local list back to how it was */ + if (cur->action == AIM_CB_SSI_ADD) { + /* Remove the item from the local list */ + /* Make sure cur->item is still valid memory */ + if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { + if (cur->item->name) { + cur->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char)); + strcpy(cur->name, cur->item->name); + } + aim_ssi_itemlist_del(&sess->ssi.local, cur->item); + } + cur->item = NULL; + + } else if (cur->action == AIM_CB_SSI_MOD) { + /* Replace the local item with the item from the official list */ + if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { + struct aim_ssi_item *cur1; + if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) { + free(cur->item->name); + if (cur1->name) { + cur->item->name = (char *)malloc((strlen(cur1->name)+1)*sizeof(char)); + strcpy(cur->item->name, cur1->name); + } else + cur->item->name = NULL; + aim_tlvlist_free(&cur->item->data); + cur->item->data = aim_tlvlist_copy(cur1->data); + } + } else + cur->item = NULL; + + } else if (cur->action == AIM_CB_SSI_DEL) { + /* Add the item back into the local list */ + if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) { + aim_ssi_itemlist_add(&sess->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data); + } else + cur->item = NULL; + } + + } else { + /* Do the exact opposite */ + if (cur->action == AIM_CB_SSI_ADD) { + /* Add the local item to the official list */ + if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { + aim_ssi_itemlist_add(&sess->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data); + } else + cur->item = NULL; + + } else if (cur->action == AIM_CB_SSI_MOD) { + /* Replace the official item with the item from the local list */ + if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { + struct aim_ssi_item *cur1; + if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) { + free(cur1->name); + if (cur->item->name) { + cur1->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char)); + strcpy(cur1->name, cur->item->name); + } else + cur1->name = NULL; + aim_tlvlist_free(&cur1->data); + cur1->data = aim_tlvlist_copy(cur->item->data); + } + } else + cur->item = NULL; + + } else if (cur->action == AIM_CB_SSI_DEL) { + /* Remove the item from the official list */ + if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) + aim_ssi_itemlist_del(&sess->ssi.official, cur->item); + cur->item = NULL; + } + + } + } /* End if (cur->item) */ + } /* End for loop */ + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sess->ssi.pending); + + /* Free all aim_ssi_tmp's with an outcome */ + cur = sess->ssi.pending; + while (cur && (cur->ack != 0xffff)) { + del = cur; + cur = cur->next; + free(del->name); + free(del); + } + sess->ssi.pending = cur; + + /* If we're not waiting for any more acks, then send more SNACs */ + if (!sess->ssi.pending) { + sess->ssi.pending = NULL; + sess->ssi.waiting_for_ack = 0; + aim_ssi_sync(sess); + } + + return ret; +} + +/* + * Subtype 0x000f - SSI Data Unchanged. + * + * Response to aim_ssi_reqifchanged() if the server-side data is not newer than + * posted local stamp/revision. + * + */ +static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + + sess->ssi.received_data = 1; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + return ret; +} + +/* + * Subtype 0x0011 - SSI Begin Data Modification. + * + * Tells the server you're going to start modifying data. + * + */ +faim_export int aim_ssi_modbegin(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) + return -EINVAL; + + return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART); +} + +/* + * Subtype 0x0012 - SSI End Data Modification. + * + * Tells the server you're finished modifying data. + * + */ +faim_export int aim_ssi_modend(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) + return -EINVAL; + + return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP); +} + +/* + * Subtype 0x0014 - Grant authorization + * + * Authorizes a contact so they can add you to their contact list. + * + */ +faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, NULL, 0); + aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, snacid); + + /* Screen name */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + /* Message (null terminated) */ + aimbs_put16(&fr->data, msg ? strlen(msg) : 0); + if (msg) { + aimbs_putraw(&fr->data, msg, strlen(msg)); + aimbs_put8(&fr->data, 0x00); + } + + /* Unknown */ + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0015 - Receive an authorization grant + */ +static int receiveauthgrant(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t tmp; + char *sn, *msg; + + /* Read screen name */ + if ((tmp = aimbs_get8(bs))) + sn = aimbs_getstr(bs, tmp); + else + sn = NULL; + + /* Read message (null terminated) */ + if ((tmp = aimbs_get16(bs))) + msg = aimbs_getstr(bs, tmp); + else + msg = NULL; + + /* Unknown */ + tmp = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sn, msg); + + free(sn); + free(msg); + + return ret; +} + +/* + * Subtype 0x0018 - Send authorization request + * + * Sends a request for authorization to the given contact. The request will either be + * granted, denied, or dropped. + * + */ +faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, char *msg) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0); + aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid); + + /* Screen name */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + /* Message (null terminated) */ + aimbs_put16(&fr->data, msg ? strlen(msg) : 0); + if (msg) { + aimbs_putraw(&fr->data, msg, strlen(msg)); + aimbs_put8(&fr->data, 0x00); + } + + /* Unknown */ + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0019 - Receive an authorization request + */ +static int receiveauthrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t tmp; + char *sn, *msg; + + /* Read screen name */ + if ((tmp = aimbs_get8(bs))) + sn = aimbs_getstr(bs, tmp); + else + sn = NULL; + + /* Read message (null terminated) */ + if ((tmp = aimbs_get16(bs))) + msg = aimbs_getstr(bs, tmp); + else + msg = NULL; + + /* Unknown */ + tmp = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sn, msg); + + free(sn); + free(msg); + + return ret; +} + +/* + * Subtype 0x001a - Send authorization reply + * + * Sends a reply to a request for authorization. The reply can either + * grant authorization or deny authorization. + * + * if reply=0x00 then deny + * if reply=0x01 then grant + * + */ +faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, fu8_t reply, char *msg) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 1 + 2+(msg ? strlen(msg)+1 : 0) + 2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0); + aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid); + + /* Screen name */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + /* Grant or deny */ + aimbs_put8(&fr->data, reply); + + /* Message (null terminated) */ + aimbs_put16(&fr->data, msg ? (strlen(msg)+1) : 0); + if (msg) { + aimbs_putraw(&fr->data, msg, strlen(msg)); + aimbs_put8(&fr->data, 0x00); + } + + /* Unknown */ + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x001b - Receive an authorization reply + * You get this bad boy when other people respond to the authorization + * request that you have previously sent them. + */ +static int receiveauthreply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t tmp; + fu8_t reply; + char *sn, *msg; + + /* Read screen name */ + if ((tmp = aimbs_get8(bs))) + sn = aimbs_getstr(bs, tmp); + else + sn = NULL; + + /* Read reply */ + reply = aimbs_get8(bs); + + /* Read message (null terminated) */ + if ((tmp = aimbs_get16(bs))) + msg = aimbs_getstr(bs, tmp); + else + msg = NULL; + + /* Unknown */ + tmp = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sn, reply, msg); + + free(sn); + free(msg); + + return ret; +} + +/* + * Subtype 0x001c - Receive a message telling you someone added you to their list. + */ +static int receiveadded(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t tmp; + char *sn; + + /* Read screen name */ + if ((tmp = aimbs_get8(bs))) + sn = aimbs_getstr(bs, tmp); + else + sn = NULL; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sn); + + free(sn); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == AIM_CB_SSI_RIGHTSINFO) + return parserights(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_LIST) + return parsedata(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_ADD) + return parseadd(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_MOD) + return parsemod(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_DEL) + return parsedel(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_SRVACK) + return parseack(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_NOLIST) + return parsedataunchanged(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_RECVAUTH) + return receiveauthgrant(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_RECVAUTHREQ) + return receiveauthrequest(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_RECVAUTHREP) + return receiveauthreply(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_ADDED) + return receiveadded(sess, mod, rx, snac, bs); + + return 0; +} + +static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + aim_ssi_freelist(sess); +} + +faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = AIM_CB_FAM_SSI; + mod->version = 0x0004; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "ssi", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = ssi_shutdown; + + return 0; +} diff --git a/libfaim/stats.c b/libfaim/stats.c new file mode 100644 index 0000000..a7f2cf4 --- /dev/null +++ b/libfaim/stats.c @@ -0,0 +1,44 @@ +/* + * Family 0x000b - Statistics. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +static int reportinterval(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + fu16_t interval; + + interval = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, interval); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0002) + return reportinterval(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000b; + mod->version = 0x0001; + mod->toolid = 0x0104; + mod->toolversion = 0x0001; + mod->flags = 0; + strncpy(mod->name, "stats", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/libfaim/tlv.c b/libfaim/tlv.c new file mode 100644 index 0000000..ccd99fe --- /dev/null +++ b/libfaim/tlv.c @@ -0,0 +1,823 @@ + +#define FAIM_INTERNAL +#include <aim.h> + +static aim_tlv_t *createtlv(fu16_t type, fu16_t length, fu8_t *value) +{ + aim_tlv_t *ret; + + if (!(ret = (aim_tlv_t *)malloc(sizeof(aim_tlv_t)))) + return NULL; + ret->type = type; + ret->length = length; + ret->value = value; + + return ret; +} + +static void freetlv(aim_tlv_t **oldtlv) +{ + + if (!oldtlv || !*oldtlv) + return; + + free((*oldtlv)->value); + free(*oldtlv); + *oldtlv = NULL; + + return; +} + +/** + * Read a TLV chain from a buffer. + * + * Reads and parses a series of TLV patterns from a data buffer; the + * returned structure is manipulatable with the rest of the TLV + * routines. When done with a TLV chain, aim_tlvlist_free() should + * be called to free the dynamic substructures. + * + * XXX There should be a flag setable here to have the tlvlist contain + * bstream references, so that at least the ->value portion of each + * element doesn't need to be malloc/memcpy'd. This could prove to be + * just as effecient as the in-place TLV parsing used in a couple places + * in libfaim. + * + * @param bs Input bstream + */ +faim_internal aim_tlvlist_t *aim_tlvlist_read(aim_bstream_t *bs) +{ + aim_tlvlist_t *list = NULL, *cur; + + while (aim_bstream_empty(bs) > 0) { + fu16_t type, length; + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + +#if 0 /* temporarily disabled until I know if they're still doing it or not */ + /* + * Okay, so now AOL has decided that any TLV of + * type 0x0013 can only be two bytes, despite + * what the actual given length is. So here + * we dump any invalid TLVs of that sort. Hopefully + * theres no special cases to this special case. + * - mid (30jun2000) + */ + if ((type == 0x0013) && (length != 0x0002)) + length = 0x0002; +#else + if (0) + ; +#endif + else { + + if (length > aim_bstream_empty(bs)) { + aim_tlvlist_free(&list); + return NULL; + } + + cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t)); + if (!cur) { + aim_tlvlist_free(&list); + return NULL; + } + + memset(cur, 0, sizeof(aim_tlvlist_t)); + + cur->tlv = createtlv(type, length, NULL); + if (!cur->tlv) { + free(cur); + aim_tlvlist_free(&list); + return NULL; + } + if (cur->tlv->length > 0) { + cur->tlv->value = aimbs_getraw(bs, length); + if (!cur->tlv->value) { + freetlv(&cur->tlv); + free(cur); + aim_tlvlist_free(&list); + return NULL; + } + } + + cur->next = list; + list = cur; + } + } + + return list; +} + +/** + * Read a TLV chain from a buffer. + * + * Reads and parses a series of TLV patterns from a data buffer; the + * returned structure is manipulatable with the rest of the TLV + * routines. When done with a TLV chain, aim_tlvlist_free() should + * be called to free the dynamic substructures. + * + * XXX There should be a flag setable here to have the tlvlist contain + * bstream references, so that at least the ->value portion of each + * element doesn't need to be malloc/memcpy'd. This could prove to be + * just as effecient as the in-place TLV parsing used in a couple places + * in libfaim. + * + * @param bs Input bstream + * @param num The max number of TLVs that will be read, or -1 if unlimited. + * There are a number of places where you want to read in a tlvchain, + * but the chain is not at the end of the SNAC, and the chain is + * preceeded by the number of TLVs. So you can limit that with this. + */ +faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, fu16_t num) +{ + aim_tlvlist_t *list = NULL, *cur; + + while ((aim_bstream_empty(bs) > 0) && (num != 0)) { + fu16_t type, length; + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + + if (length > aim_bstream_empty(bs)) { + aim_tlvlist_free(&list); + return NULL; + } + + cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t)); + if (!cur) { + aim_tlvlist_free(&list); + return NULL; + } + + memset(cur, 0, sizeof(aim_tlvlist_t)); + + cur->tlv = createtlv(type, length, NULL); + if (!cur->tlv) { + free(cur); + aim_tlvlist_free(&list); + return NULL; + } + if (cur->tlv->length > 0) { + cur->tlv->value = aimbs_getraw(bs, length); + if (!cur->tlv->value) { + freetlv(&cur->tlv); + free(cur); + aim_tlvlist_free(&list); + return NULL; + } + } + + if (num > 0) + num--; + cur->next = list; + list = cur; + } + + return list; +} + +/** + * Read a TLV chain from a buffer. + * + * Reads and parses a series of TLV patterns from a data buffer; the + * returned structure is manipulatable with the rest of the TLV + * routines. When done with a TLV chain, aim_tlvlist_free() should + * be called to free the dynamic substructures. + * + * XXX There should be a flag setable here to have the tlvlist contain + * bstream references, so that at least the ->value portion of each + * element doesn't need to be malloc/memcpy'd. This could prove to be + * just as effecient as the in-place TLV parsing used in a couple places + * in libfaim. + * + * @param bs Input bstream + * @param len The max length in bytes that will be read. + * There are a number of places where you want to read in a tlvchain, + * but the chain is not at the end of the SNAC, and the chain is + * preceeded by the length of the TLVs. So you can limit that with this. + */ +faim_internal aim_tlvlist_t *aim_tlvlist_readlen(aim_bstream_t *bs, fu16_t len) +{ + aim_tlvlist_t *list = NULL, *cur; + + while ((aim_bstream_empty(bs) > 0) && (len > 0)) { + fu16_t type, length; + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + + if (length > aim_bstream_empty(bs)) { + aim_tlvlist_free(&list); + return NULL; + } + + cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t)); + if (!cur) { + aim_tlvlist_free(&list); + return NULL; + } + + memset(cur, 0, sizeof(aim_tlvlist_t)); + + cur->tlv = createtlv(type, length, NULL); + if (!cur->tlv) { + free(cur); + aim_tlvlist_free(&list); + return NULL; + } + if (cur->tlv->length > 0) { + cur->tlv->value = aimbs_getraw(bs, length); + if (!cur->tlv->value) { + freetlv(&cur->tlv); + free(cur); + aim_tlvlist_free(&list); + return NULL; + } + } + + len -= aim_tlvlist_size(&cur); + cur->next = list; + list = cur; + } + + return list; +} + +/** + * Duplicate a TLV chain. + * This is pretty pelf exslanatory. + * + * @param orig The TLV chain you want to make a copy of. + * @return A newly allocated TLV chain. + */ +faim_internal aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig) +{ + aim_tlvlist_t *new = NULL; + + while (orig) { + aim_tlvlist_add_raw(&new, orig->tlv->type, orig->tlv->length, orig->tlv->value); + orig = orig->next; + } + + return new; +} + +/* + * Compare two TLV lists for equality. This probably is not the most + * efficient way to do this. + * + * @param one One of the TLV chains to compare. + * @param two The other TLV chain to compare. + * @preturn Retrun 0 if the lists are the same, return 1 if they are different. + */ +faim_internal int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two) +{ + aim_bstream_t bs1, bs2; + + if (aim_tlvlist_size(&one) != aim_tlvlist_size(&two)) + return 1; + + aim_bstream_init(&bs1, ((fu8_t *)malloc(aim_tlvlist_size(&one)*sizeof(fu8_t))), aim_tlvlist_size(&one)); + aim_bstream_init(&bs2, ((fu8_t *)malloc(aim_tlvlist_size(&two)*sizeof(fu8_t))), aim_tlvlist_size(&two)); + + aim_tlvlist_write(&bs1, &one); + aim_tlvlist_write(&bs2, &two); + + if (memcmp(bs1.data, bs2.data, bs1.len)) { + free(bs1.data); + free(bs2.data); + return 1; + } + + free(bs1.data); + free(bs2.data); + + return 0; +} + +/** + * Free a TLV chain structure + * @list: Chain to be freed + * + * Walks the list of TLVs in the passed TLV chain and + * frees each one. Note that any references to this data + * should be removed before calling this. + * + */ +faim_internal void aim_tlvlist_free(aim_tlvlist_t **list) +{ + aim_tlvlist_t *cur; + + if (!list || !*list) + return; + + for (cur = *list; cur; ) { + aim_tlvlist_t *tmp; + + freetlv(&cur->tlv); + + tmp = cur->next; + free(cur); + cur = tmp; + } + + list = NULL; + + return; +} + +/** + * Count the number of TLVs in a chain. + * + * @param list Chain to be counted. + * @return The number of TLVs stored in the passed chain. + */ +faim_internal int aim_tlvlist_count(aim_tlvlist_t **list) +{ + aim_tlvlist_t *cur; + int count; + + if (!list || !*list) + return 0; + + for (cur = *list, count = 0; cur; cur = cur->next) + count++; + + return count; +} + +/** + * Count the number of bytes in a TLV chain. + * + * @param list Chain to be sized + * @return The number of bytes that would be needed to + * write the passed TLV chain to a data buffer. + */ +faim_internal int aim_tlvlist_size(aim_tlvlist_t **list) +{ + aim_tlvlist_t *cur; + int size; + + if (!list || !*list) + return 0; + + for (cur = *list, size = 0; cur; cur = cur->next) + size += (4 + cur->tlv->length); + + return size; +} + +/** + * Adds the passed string as a TLV element of the passed type + * to the TLV chain. + * + * @param list Desination chain (%NULL pointer if empty). + * @param type TLV type. + * @length Length of string to add (not including %NULL). + * @value String to add. + * @retun The size of the value added. + */ +faim_internal int aim_tlvlist_add_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value) +{ + aim_tlvlist_t *newtlv, *cur; + + if (list == NULL) + return 0; + + if (!(newtlv = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t)))) + return 0; + memset(newtlv, 0x00, sizeof(aim_tlvlist_t)); + + if (!(newtlv->tlv = createtlv(type, length, NULL))) { + free(newtlv); + return 0; + } + if (newtlv->tlv->length > 0) { + newtlv->tlv->value = (fu8_t *)malloc(newtlv->tlv->length); + memcpy(newtlv->tlv->value, value, newtlv->tlv->length); + } + + if (!*list) + *list = newtlv; + else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtlv; + } + + return newtlv->tlv->length; +} + +/** + * Add a one byte integer to a TLV chain. + * + * @param list Destination chain. + * @param type TLV type to add. + * @param value Value to add. + * @retun The size of the value added. + */ +faim_internal int aim_tlvlist_add_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value) +{ + fu8_t v8[1]; + + aimutil_put8(v8, value); + + return aim_tlvlist_add_raw(list, type, 1, v8); +} + +/** + * Add a two byte integer to a TLV chain. + * + * @param list Destination chain. + * @param type TLV type to add. + * @param value Value to add. + * @retun The size of the value added. + */ +faim_internal int aim_tlvlist_add_16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value) +{ + fu8_t v16[2]; + + aimutil_put16(v16, value); + + return aim_tlvlist_add_raw(list, type, 2, v16); +} + +/** + * Add a four byte integer to a TLV chain. + * + * @param list Destination chain. + * @param type TLV type to add. + * @param value Value to add. + * @retun The size of the value added. + */ +faim_internal int aim_tlvlist_add_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value) +{ + fu8_t v32[4]; + + aimutil_put32(v32, value); + + return aim_tlvlist_add_raw(list, type, 4, v32); +} + +/** + * Adds a block of capability blocks to a TLV chain. The bitfield + * passed in should be a bitwise %OR of any of the %AIM_CAPS constants: + * + * %AIM_CAPS_BUDDYICON Supports Buddy Icons + * %AIM_CAPS_VOICE Supports Voice Chat + * %AIM_CAPS_IMIMAGE Supports DirectIM/IMImage + * %AIM_CAPS_CHAT Supports Chat + * %AIM_CAPS_GETFILE Supports Get File functions + * %AIM_CAPS_SENDFILE Supports Send File functions + * + * @param list Destination chain + * @param type TLV type to add + * @param caps Bitfield of capability flags to send + * @retun The size of the value added. + */ +faim_internal int aim_tlvlist_add_caps(aim_tlvlist_t **list, const fu16_t type, const fu32_t caps) +{ + fu8_t buf[16*16]; /* XXX icky fixed length buffer */ + aim_bstream_t bs; + + if (!caps) + return 0; /* nothing there anyway */ + + aim_bstream_init(&bs, buf, sizeof(buf)); + + aim_putcap(&bs, caps); + + return aim_tlvlist_add_raw(list, type, aim_bstream_curpos(&bs), buf); +} + +/** + * Adds the given userinfo struct to a TLV chain. + * + * @param list Destination chain. + * @param type TLV type to add. + * @retun The size of the value added. + */ +faim_internal int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, fu16_t type, aim_userinfo_t *userinfo) +{ + fu8_t buf[1024]; /* bleh */ + aim_bstream_t bs; + + aim_bstream_init(&bs, buf, sizeof(buf)); + + aim_putuserinfo(&bs, userinfo); + + return aim_tlvlist_add_raw(list, type, aim_bstream_curpos(&bs), buf); +} + +/** + * Adds a TLV with a zero length to a TLV chain. + * + * @param list Destination chain. + * @param type TLV type to add. + * @retun The size of the value added. + */ +faim_internal int aim_tlvlist_add_noval(aim_tlvlist_t **list, const fu16_t type) +{ + return aim_tlvlist_add_raw(list, type, 0, NULL); +} + +/* + * Note that the inner TLV chain will not be modifiable as a tlvchain once + * it is written using this. Or rather, it can be, but updates won't be + * made to this. + * + * XXX should probably support sublists for real. + * + * This is so neat. + * + */ +faim_internal int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, fu16_t type, aim_tlvlist_t **tl) +{ + fu8_t *buf; + int buflen; + aim_bstream_t bs; + + buflen = aim_tlvlist_size(tl); + + if (buflen <= 0) + return 0; + + if (!(buf = malloc(buflen))) + return 0; + + aim_bstream_init(&bs, buf, buflen); + + aim_tlvlist_write(&bs, tl); + + aim_tlvlist_add_raw(list, type, aim_bstream_curpos(&bs), buf); + + free(buf); + + return buflen; +} + +/** + * Substitute a TLV of a given type with a new TLV of the same type. If + * you attempt to replace a TLV that does not exist, this function will + * just add a new TLV as if you called aim_tlvlist_add_raw(). + * + * @param list Desination chain (%NULL pointer if empty). + * @param type TLV type. + * @param length Length of string to add (not including %NULL). + * @param value String to add. + * @return The length of the TLV. + */ +faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value) +{ + aim_tlvlist_t *cur; + + if (list == NULL) + return 0; + + for (cur = *list; ((cur != NULL) && (cur->tlv->type != type)); cur = cur->next); + if (cur == NULL) + return aim_tlvlist_add_raw(list, type, length, value); + + free(cur->tlv->value); + cur->tlv->length = length; + if (cur->tlv->length > 0) { + cur->tlv->value = (fu8_t *)malloc(cur->tlv->length); + memcpy(cur->tlv->value, value, cur->tlv->length); + } else + cur->tlv->value = NULL; + + return cur->tlv->length; +} + +/** + * Substitute a TLV of a given type with a new TLV of the same type. If + * you attempt to replace a TLV that does not exist, this function will + * just add a new TLV as if you called aim_tlvlist_add_raw(). + * + * @param list Desination chain (%NULL pointer if empty). + * @param type TLV type. + * @return The length of the TLV. + */ +faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const fu16_t type) +{ + return aim_tlvlist_replace_raw(list, type, 0, NULL); +} + +/** + * Substitute a TLV of a given type with a new TLV of the same type. If + * you attempt to replace a TLV that does not exist, this function will + * just add a new TLV as if you called aim_tlvlist_add_raw(). + * + * @param list Desination chain (%NULL pointer if empty). + * @param type TLV type. + * @param value 8 bit value to add. + * @return The length of the TLV. + */ +faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value) +{ + fu8_t v8[1]; + + aimutil_put8(v8, value); + + return aim_tlvlist_replace_raw(list, type, 1, v8); +} + +/** + * Substitute a TLV of a given type with a new TLV of the same type. If + * you attempt to replace a TLV that does not exist, this function will + * just add a new TLV as if you called aim_tlvlist_add_raw(). + * + * @param list Desination chain (%NULL pointer if empty). + * @param type TLV type. + * @param value 32 bit value to add. + * @return The length of the TLV. + */ +faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value) +{ + fu8_t v32[4]; + + aimutil_put32(v32, value); + + return aim_tlvlist_replace_raw(list, type, 4, v32); +} + +/** + * Remove a TLV of a given type. If you attempt to remove a TLV that + * does not exist, nothing happens. + * + * @param list Desination chain (%NULL pointer if empty). + * @param type TLV type. + */ +faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const fu16_t type) +{ + aim_tlvlist_t *del; + + if (!list || !(*list)) + return; + + /* Remove the item from the list */ + if ((*list)->tlv->type == type) { + del = *list; + *list = (*list)->next; + } else { + aim_tlvlist_t *cur; + for (cur=*list; (cur->next && (cur->next->tlv->type!=type)); cur=cur->next); + if (!cur->next) + return; + del = cur->next; + cur->next = del->next; + } + + /* Free the removed item */ + free(del->tlv->value); + free(del->tlv); + free(del); +} + +/** + * aim_tlvlist_write - Write a TLV chain into a data buffer. + * @buf: Destination buffer + * @buflen: Maximum number of bytes that will be written to buffer + * @list: Source TLV chain + * + * Copies a TLV chain into a raw data buffer, writing only the number + * of bytes specified. This operation does not free the chain; + * aim_tlvlist_free() must still be called to free up the memory used + * by the chain structures. + * + * XXX clean this up, make better use of bstreams + */ +faim_internal int aim_tlvlist_write(aim_bstream_t *bs, aim_tlvlist_t **list) +{ + int goodbuflen; + aim_tlvlist_t *cur; + + /* do an initial run to test total length */ + goodbuflen = aim_tlvlist_size(list); + + if (goodbuflen > aim_bstream_empty(bs)) + return 0; /* not enough buffer */ + + /* do the real write-out */ + for (cur = *list; cur; cur = cur->next) { + aimbs_put16(bs, cur->tlv->type); + aimbs_put16(bs, cur->tlv->length); + if (cur->tlv->length) + aimbs_putraw(bs, cur->tlv->value, cur->tlv->length); + } + + return 1; /* XXX this is a nonsensical return */ +} + + +/** + * Grab the Nth TLV of type type in the TLV list list. + * + * Returns a pointer to an aim_tlv_t of the specified type; + * %NULL on error. The @nth parameter is specified starting at %1. + * In most cases, there will be no more than one TLV of any type + * in a chain. + * + * @param list Source chain. + * @param type Requested TLV type. + * @param nth Index of TLV of type to get. + * @return The TLV you were looking for, or NULL if one could not be found. + */ +faim_internal aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, const fu16_t type, const int nth) +{ + aim_tlvlist_t *cur; + int i; + + for (cur = list, i = 0; cur; cur = cur->next) { + if (cur && cur->tlv) { + if (cur->tlv->type == type) + i++; + if (i >= nth) + return cur->tlv; + } + } + + return NULL; +} + +/** + * Retrieve the data from the nth TLV in the given TLV chain as a string. + * + * @param list Source TLV chain. + * @param type TLV type to search for. + * @param nth Index of TLV to return. + * @return The value of the TLV you were looking for, or NULL if one could + * not be found. This is a dynamic buffer and must be freed by the + * caller. + */ +faim_internal char *aim_tlv_getstr(aim_tlvlist_t *list, const fu16_t type, const int nth) +{ + aim_tlv_t *tlv; + char *newstr; + + if (!(tlv = aim_tlv_gettlv(list, type, nth))) + return NULL; + + newstr = (char *) malloc(tlv->length + 1); + memcpy(newstr, tlv->value, tlv->length); + newstr[tlv->length] = '\0'; + + return newstr; +} + +/** + * Retrieve the data from the nth TLV in the given TLV chain as an 8bit + * integer. + * + * @param list Source TLV chain. + * @param type TLV type to search for. + * @param nth Index of TLV to return. + * @return The value the TLV you were looking for, or 0 if one could + * not be found. + */ +faim_internal fu8_t aim_tlv_get8(aim_tlvlist_t *list, const fu16_t type, const int nth) +{ + aim_tlv_t *tlv; + + if (!(tlv = aim_tlv_gettlv(list, type, nth))) + return 0; /* erm */ + return aimutil_get8(tlv->value); +} + +/** + * Retrieve the data from the nth TLV in the given TLV chain as a 16bit + * integer. + * + * @param list Source TLV chain. + * @param type TLV type to search for. + * @param nth Index of TLV to return. + * @return The value the TLV you were looking for, or 0 if one could + * not be found. + */ +faim_internal fu16_t aim_tlv_get16(aim_tlvlist_t *list, const fu16_t type, const int nth) +{ + aim_tlv_t *tlv; + + if (!(tlv = aim_tlv_gettlv(list, type, nth))) + return 0; /* erm */ + return aimutil_get16(tlv->value); +} + +/** + * Retrieve the data from the nth TLV in the given TLV chain as a 32bit + * integer. + * + * @param list Source TLV chain. + * @param type TLV type to search for. + * @param nth Index of TLV to return. + * @return The value the TLV you were looking for, or 0 if one could + * not be found. + */ +faim_internal fu32_t aim_tlv_get32(aim_tlvlist_t *list, const fu16_t type, const int nth) +{ + aim_tlv_t *tlv; + + if (!(tlv = aim_tlv_gettlv(list, type, nth))) + return 0; /* erm */ + return aimutil_get32(tlv->value); +} diff --git a/libfaim/translate.c b/libfaim/translate.c new file mode 100644 index 0000000..9b63ec9 --- /dev/null +++ b/libfaim/translate.c @@ -0,0 +1,27 @@ +/* + * Family 0x000c - Translation. + * + * I have no idea why this group was issued. I have never seen anything + * that uses it. From what I remember, the last time I tried to poke at + * the server with this group, it whined about not supporting it. + * + * But we advertise it anyway, because its fun. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +faim_internal int translate_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000c; + mod->version = 0x0001; + mod->toolid = 0x0104; + mod->toolversion = 0x0001; + mod->flags = 0; + strncpy(mod->name, "translate", sizeof(mod->name)); + mod->snachandler = NULL; + + return 0; +} diff --git a/libfaim/txqueue.c b/libfaim/txqueue.c new file mode 100644 index 0000000..ecf2e86 --- /dev/null +++ b/libfaim/txqueue.c @@ -0,0 +1,439 @@ +/* + * txqueue.c + * + * Herein lies all the mangement routines for the transmit (Tx) queue. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#ifndef _WIN32 +#include <sys/socket.h> +#else +#include "win32dep.h" +#endif + +/* + * Allocate a new tx frame. + * + * This is more for looks than anything else. + * + * Right now, that is. If/when we implement a pool of transmit + * frames, this will become the request-an-unused-frame part. + * + * framing = AIM_FRAMETYPE_OFT/FLAP + * chan = channel for FLAP, hdrtype for OFT + * + */ +faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, fu8_t framing, fu16_t chan, int datalen) +{ + aim_frame_t *fr; + + if (!conn) { + faimdprintf(sess, 0, "aim_tx_new: ERROR: no connection specified\n"); + return NULL; + } + + /* For sanity... */ + if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) || (conn->type == AIM_CONN_TYPE_LISTENER)) { + if (framing != AIM_FRAMETYPE_OFT) { + faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for rendezvous connection\n"); + return NULL; + } + } else { + if (framing != AIM_FRAMETYPE_FLAP) { + faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for FLAP connection\n"); + return NULL; + } + } + + if (!(fr = (aim_frame_t *)malloc(sizeof(aim_frame_t)))) + return NULL; + memset(fr, 0, sizeof(aim_frame_t)); + + fr->conn = conn; + + fr->hdrtype = framing; + + if (fr->hdrtype == AIM_FRAMETYPE_FLAP) { + + fr->hdr.flap.type = chan; + + } else if (fr->hdrtype == AIM_FRAMETYPE_OFT) { + + fr->hdr.rend.type = chan; + + } else + faimdprintf(sess, 0, "tx_new: unknown framing\n"); + + if (datalen > 0) { + fu8_t *data; + + if (!(data = (unsigned char *)malloc(datalen))) { + aim_frame_destroy(fr); + return NULL; + } + + aim_bstream_init(&fr->data, data, datalen); + } + + return fr; +} + +/* + * aim_tx_enqeue__queuebased() + * + * The overall purpose here is to enqueue the passed in command struct + * into the outgoing (tx) queue. Basically... + * 1) Make a scope-irrelevent copy of the struct + * 3) Mark as not-sent-yet + * 4) Enqueue the struct into the list + * 6) Return + * + * Note that this is only used when doing queue-based transmitting; + * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased. + * + */ +static int aim_tx_enqueue__queuebased(aim_session_t *sess, aim_frame_t *fr) +{ + + if (!fr->conn) { + faimdprintf(sess, 1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n"); + fr->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS); + } + + if (fr->hdrtype == AIM_FRAMETYPE_FLAP) { + /* assign seqnum -- XXX should really not assign until hardxmit */ + fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn); + } + + fr->handled = 0; /* not sent yet */ + + /* see overhead note in aim_rxqueue counterpart */ + if (!sess->queue_outgoing) + sess->queue_outgoing = fr; + else { + aim_frame_t *cur; + + for (cur = sess->queue_outgoing; cur->next; cur = cur->next) + ; + cur->next = fr; + } + + return 0; +} + +/* + * aim_tx_enqueue__immediate() + * + * Parallel to aim_tx_enqueue__queuebased, however, this bypasses + * the whole queue mess when you want immediate writes to happen. + * + * Basically the same as its __queuebased couterpart, however + * instead of doing a list append, it just calls aim_tx_sendframe() + * right here. + * + */ +static int aim_tx_enqueue__immediate(aim_session_t *sess, aim_frame_t *fr) +{ + + if (!fr->conn) { + faimdprintf(sess, 1, "aim_tx_enqueue: ERROR: packet has no connection\n"); + aim_frame_destroy(fr); + return 0; + } + + if (fr->hdrtype == AIM_FRAMETYPE_FLAP) + fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn); + + fr->handled = 0; /* not sent yet */ + + aim_tx_sendframe(sess, fr); + + aim_frame_destroy(fr); + + return 0; +} + +faim_export int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *)) +{ + + if (what == AIM_TX_QUEUED) + sess->tx_enqueue = &aim_tx_enqueue__queuebased; + else if (what == AIM_TX_IMMEDIATE) + sess->tx_enqueue = &aim_tx_enqueue__immediate; + else if (what == AIM_TX_USER) { + if (!func) + return -EINVAL; + sess->tx_enqueue = func; + } else + return -EINVAL; /* unknown action */ + + return 0; +} + +faim_internal int aim_tx_enqueue(aim_session_t *sess, aim_frame_t *fr) +{ + + /* + * If we want to send a connection thats inprogress, we have to force + * them to use the queue based version. Otherwise, use whatever they + * want. + */ + if (fr && fr->conn && + (fr->conn->status & AIM_CONN_STATUS_INPROGRESS)) { + return aim_tx_enqueue__queuebased(sess, fr); + } + + return (*sess->tx_enqueue)(sess, fr); +} + +/* + * aim_get_next_txseqnum() + * + * This increments the tx command count, and returns the seqnum + * that should be stamped on the next FLAP packet sent. This is + * normally called during the final step of packet preparation + * before enqueuement (in aim_tx_enqueue()). + * + */ +faim_internal flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *conn) +{ + flap_seqnum_t ret; + + ret = ++conn->seqnum; + + return ret; +} + +static int aim_send(int fd, const void *buf, size_t count) +{ + int left, cur; + + for (cur = 0, left = count; left; ) { + int ret; + + ret = send(fd, ((unsigned char *)buf)+cur, left, 0); + if (ret == -1) + return -1; + else if (ret == 0) + return cur; + + cur += ret; + left -= ret; + } + + return cur; +} + +static int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count) +{ + int wrote = 0; + if (!bs || !conn || (count < 0)) + return -EINVAL; + + if (count > aim_bstream_empty(bs)) + count = aim_bstream_empty(bs); /* truncate to remaining space */ + + if (count) { + if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) && + (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) { + /* I strongly suspect that this is a horrible thing to do + * and I feel really guilty doing it. */ + const char *sn = aim_odc_getsn(conn); + aim_rxcallback_t userfunc; + while (count - wrote > 1024) { + wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, 1024); + if ((userfunc=aim_callhandler(conn->sessv, conn, + AIM_CB_FAM_SPECIAL, + AIM_CB_SPECIAL_IMAGETRANSFER))) + userfunc(conn->sessv, NULL, sn, + count-wrote>1024 ? ((double)wrote / count) : 1); + } + } + if (count - wrote) { + wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, count - wrote); + } + + } + + if (((aim_session_t *)conn->sessv)->debug >= 2) { + int i; + aim_session_t *sess = (aim_session_t *)conn->sessv; + + faimdprintf(sess, 2, "\nOutgoing data: (%d bytes)", wrote); + for (i = 0; i < wrote; i++) { + if (!(i % 8)) + faimdprintf(sess, 2, "\n\t"); + faimdprintf(sess, 2, "0x%02x ", *(bs->data + bs->offset + i)); + } + faimdprintf(sess, 2, "\n"); + } + + bs->offset += wrote; + + return wrote; +} + +static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr) +{ + aim_bstream_t obs; + fu8_t *obs_raw; + int payloadlen, err = 0, obslen; + + payloadlen = aim_bstream_curpos(&fr->data); + + if (!(obs_raw = malloc(6 + payloadlen))) + return -ENOMEM; + + aim_bstream_init(&obs, obs_raw, 6 + payloadlen); + + /* FLAP header */ + aimbs_put8(&obs, 0x2a); + aimbs_put8(&obs, fr->hdr.flap.type); + aimbs_put16(&obs, fr->hdr.flap.seqnum); + aimbs_put16(&obs, payloadlen); + + /* payload */ + aim_bstream_rewind(&fr->data); + aimbs_putbs(&obs, &fr->data, payloadlen); + + obslen = aim_bstream_curpos(&obs); + aim_bstream_rewind(&obs); + if (aim_bstream_send(&obs, fr->conn, obslen) != obslen) + err = -errno; + + free(obs_raw); /* XXX aim_bstream_free */ + + fr->handled = 1; + fr->conn->lastactivity = time(NULL); + + return err; +} + +static int sendframe_rendezvous(aim_session_t *sess, aim_frame_t *fr) +{ + aim_bstream_t bs; + fu8_t *bs_raw; + int err = 0; + int totlen = 8 + aim_bstream_curpos(&fr->data); + + if (!(bs_raw = malloc(totlen))) + return -1; + + aim_bstream_init(&bs, bs_raw, totlen); + + aimbs_putraw(&bs, fr->hdr.rend.magic, 4); + aimbs_put16(&bs, 8 + fr->hdr.rend.hdrlen); + aimbs_put16(&bs, fr->hdr.rend.type); + + /* payload */ + aim_bstream_rewind(&fr->data); + aimbs_putbs(&bs, &fr->data, totlen - 8); + + aim_bstream_rewind(&bs); + + if (aim_bstream_send(&bs, fr->conn, totlen) != totlen) + err = -errno; + + free(bs_raw); /* XXX aim_bstream_free */ + + fr->handled = 1; + fr->conn->lastactivity = time(NULL); + + return err; +} + +faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *fr) +{ + if (fr->hdrtype == AIM_FRAMETYPE_FLAP) + return sendframe_flap(sess, fr); + else if (fr->hdrtype == AIM_FRAMETYPE_OFT) + return sendframe_rendezvous(sess, fr); + return -1; +} + +faim_export int aim_tx_flushqueue(aim_session_t *sess) +{ + aim_frame_t *cur; + + for (cur = sess->queue_outgoing; cur; cur = cur->next) { + + if (cur->handled) + continue; /* already been sent */ + + if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS)) + continue; + + /* + * And now for the meager attempt to force transmit + * latency and avoid missed messages. + */ + if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) { + /* + * XXX should be a break! we dont want to block the + * upper layers + * + * XXX or better, just do this right. + * + */ + sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL)); + } + + /* XXX this should call the custom "queuing" function!! */ + aim_tx_sendframe(sess, cur); + } + + /* purge sent commands from queue */ + aim_tx_purgequeue(sess); + + return 0; +} + +/* + * aim_tx_purgequeue() + * + * This is responsable for removing sent commands from the transmit + * queue. This is not a required operation, but it of course helps + * reduce memory footprint at run time! + * + */ +faim_export void aim_tx_purgequeue(aim_session_t *sess) +{ + aim_frame_t *cur, **prev; + + for (prev = &sess->queue_outgoing; (cur = *prev); ) { + + if (cur->handled) { + *prev = cur->next; + + aim_frame_destroy(cur); + + } else + prev = &cur->next; + } + + return; +} + +/** + * aim_tx_cleanqueue - get rid of packets waiting for tx on a dying conn + * @sess: session + * @conn: connection that's dying + * + * for now this simply marks all packets as sent and lets them + * disappear without warning. + * + */ +faim_internal void aim_tx_cleanqueue(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *cur; + + for (cur = sess->queue_outgoing; cur; cur = cur->next) { + if (cur->conn == conn) + cur->handled = 1; + } + + return; +} diff --git a/libfaim/util.c b/libfaim/util.c new file mode 100644 index 0000000..5054876 --- /dev/null +++ b/libfaim/util.c @@ -0,0 +1,210 @@ +/* + * A little bit of this + * A little bit of that + * It started with a kiss + * Now we're up to bat + */ + +#define FAIM_INTERNAL +#include <aim.h> +#include <ctype.h> + +#ifdef _WIN32 +#include "win32dep.h" +#endif + +faim_internal void faimdprintf(aim_session_t *sess, int dlevel, const char *format, ...) +{ + if (!sess) { + fprintf(stderr, "faimdprintf: no session! boo! (%d, %s)\n", dlevel, format); + return; + } + if ((dlevel <= sess->debug) && sess->debugcb) { + va_list ap; + va_start(ap, format); + sess->debugcb(sess, dlevel, format, ap); + va_end(ap); + } + + return; +} + +faim_export int aimutil_putstr(char *dest, const char *src, int len) +{ + memcpy(dest, src, len); + return len; +} + +/* + * Tokenizing functions. Used to portably replace strtok/sep. + * -- DMP. + * + */ +faim_export int aimutil_tokslen(char *toSearch, int theindex, char dl) +{ + int curCount = 1; + char *next; + char *last; + int toReturn; + + last = toSearch; + next = strchr(toSearch, dl); + + while(curCount < theindex && next != NULL) { + curCount++; + last = next + 1; + next = strchr(last, dl); + } + + if ((curCount < theindex) || (next == NULL)) + toReturn = strlen(toSearch) - (curCount - 1); + else + toReturn = next - toSearch - (curCount - 1); + + return toReturn; +} + +faim_export int aimutil_itemcnt(char *toSearch, char dl) +{ + int curCount; + char *next; + + curCount = 1; + + next = strchr(toSearch, dl); + + while(next != NULL) { + curCount++; + next = strchr(next + 1, dl); + } + + return curCount; +} + +faim_export char *aimutil_itemindex(char *toSearch, int theindex, char dl) +{ + int curCount; + char *next; + char *last; + char *toReturn; + + curCount = 0; + + last = toSearch; + next = strchr(toSearch, dl); + + while (curCount < theindex && next != NULL) { + curCount++; + last = next + 1; + next = strchr(last, dl); + } + + if (curCount < theindex) { + toReturn = malloc(sizeof(char)); + *toReturn = '\0'; + } + next = strchr(last, dl); + + if (curCount < theindex) { + toReturn = malloc(sizeof(char)); + *toReturn = '\0'; + } else { + if (next == NULL) { + toReturn = malloc((strlen(last) + 1) * sizeof(char)); + strcpy(toReturn, last); + } else { + toReturn = malloc((next - last + 1) * sizeof(char)); + memcpy(toReturn, last, (next - last)); + toReturn[next - last] = '\0'; + } + } + return toReturn; +} + +/** + * Calculate the checksum of a given icon. + * + */ +faim_export fu16_t aimutil_iconsum(const fu8_t *buf, int buflen) +{ + fu32_t sum; + int i; + + for (i=0, sum=0; i+1<buflen; i+=2) + sum += (buf[i+1] << 8) + buf[i]; + if (i < buflen) + sum += buf[i]; + sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff); + + return sum; +} + +faim_export int aim_util_getlocalip(fu8_t *ip) +{ + struct hostent *hptr; + char localhost[129]; + + /* XXX if available, use getaddrinfo() */ + /* XXX allow client to specify which IP to use for multihomed boxes */ + + if (gethostname(localhost, 128) < 0) + return -1; + + if (!(hptr = gethostbyname(localhost))) + return -1; + memcpy(ip, hptr->h_addr_list[0], 4); + + return 0; +} + +/* +* int snlen(const char *) +* +* This takes a screen name and returns its length without +* spaces. If there are no spaces in the SN, then the +* return is equal to that of strlen(). +* +*/ +faim_export int aim_snlen(const char *sn) +{ + int i = 0; + + if (!sn) + return 0; + + while (*sn != '\0') { + if (*sn != ' ') + i++; + sn++; + } + + return i; +} + +/* +* int sncmp(const char *, const char *) +* +* This takes two screen names and compares them using the rules +* on screen names for AIM/AOL. Mainly, this means case and space +* insensitivity (all case differences and spacing differences are +* ignored, with the exception that screen names can not start with +* a space). +* +* Return: 0 if equal +* non-0 if different +* +*/ +faim_export int aim_sncmp(const char *sn1, const char *sn2) +{ + + do { + while (*sn2 == ' ') + sn2++; + while (*sn1 == ' ') + sn1++; + if (toupper(*sn1) != toupper(*sn2)) + return 1; + } while ((*sn1 != '\0') && sn1++ && sn2++); + + return 0; +} @@ -0,0 +1,124 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" +#include <stdlib.h> + +static const char fileIdent[] = "$Id: list.c,v 1.6 2009/03/29 12:38:21 kretch Exp $"; + +#define INITSIZE 30 +#define GROWAT 2 +#define GROWBY 1.5 + +int owl_list_create(owl_list *l) +{ + l->size=0; + l->list=(void **)owl_malloc(INITSIZE*sizeof(void *)); + l->avail=INITSIZE; + if (l->list==NULL) return(-1); + return(0); +} + +int owl_list_get_size(owl_list *l) +{ + return(l->size); +} + +void *owl_list_get_element(owl_list *l, int n) +{ + if (n>l->size-1) return(NULL); + return(l->list[n]); +} + +int owl_list_append_element(owl_list *l, void *element) +{ + void *ptr; + + if ((l->size+1) > (l->avail/GROWAT)) { + ptr=owl_realloc(l->list, l->avail*GROWBY*sizeof(void *)); + if (ptr==NULL) return(-1); + l->list=ptr; + l->avail=l->avail*GROWBY; + } + + l->list[l->size]=element; + l->size++; + return(0); +} + +int owl_list_prepend_element(owl_list *l, void *element) +{ + void *ptr; + int i; + + if ((l->size+1) > (l->avail/GROWAT)) { + ptr=owl_realloc(l->list, l->avail*GROWBY*sizeof(void *)); + if (ptr==NULL) return(-1); + l->list=ptr; + l->avail=l->avail*GROWBY; + } + + for (i=l->size; i>0; i--) { + l->list[i]=l->list[i-1]; + } + l->list[0]=element; + l->size++; + return(0); +} + +int owl_list_remove_element(owl_list *l, int n) +{ + int i; + + if (n>l->size-1) return(-1); + for (i=n; i<l->size-1; i++) { + l->list[i]=l->list[i+1]; + } + l->size--; + return(0); +} + +/* todo: might leak memory */ +int owl_list_replace_element(owl_list *l, int n, void *element) +{ + if (n>l->size-1) return(-1); + + l->list[n]=element; + return(0); +} + +void owl_list_free_all(owl_list *l, void (*elefree)(void *)) +{ + int i; + + for (i=0; i<l->size; i++) { + (elefree)(l->list[i]); + } + owl_free(l->list); +} + +void owl_list_free_simple(owl_list *l) +{ + if (l->list) owl_free(l->list); +} diff --git a/logging.c b/logging.c new file mode 100644 index 0000000..15414b8 --- /dev/null +++ b/logging.c @@ -0,0 +1,450 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <sys/param.h> + +static const char fileIdent[] = "$Id: logging.c,v 1.27 2009/03/29 12:38:21 kretch Exp $"; + +/* This is now the one function that should be called to log a + * message. It will do all the work necessary by calling the other + * functions in this file as necessary. + */ +void owl_log_message(owl_message *m) { + owl_function_debugmsg("owl_log_message: entering"); + + /* should we be logging this message? */ + if (!owl_log_shouldlog_message(m)) { + owl_function_debugmsg("owl_log_message: not logging message"); + return; + } + + /* handle incmoing messages */ + if (owl_message_is_direction_in(m)) { + owl_log_incoming(m); + owl_function_debugmsg("owl_log_message: leaving"); + return; + } + + /* handle outgoing messages */ + if (owl_message_is_type_aim(m)) { + owl_log_outgoing_aim(m); + } else if (owl_message_is_type_zephyr(m)) { + owl_log_outgoing_zephyr(m); + } else if (owl_message_is_type_loopback(m)) { + owl_log_outgoing_loopback(m); + } else { + owl_function_error("Unknown message type for logging"); + } + owl_function_debugmsg("owl_log_message: leaving"); +} + +/* Return 1 if we should log the given message, otherwise return 0 */ +int owl_log_shouldlog_message(owl_message *m) { + owl_filter *f; + + /* If there's a logfilter and this message matches it, log */ + f=owl_global_get_filter(&g, owl_global_get_logfilter(&g)); + if (f && owl_filter_message_match(f, m)) return(1); + + /* otherwise we do things based on the logging variables */ + + /* skip login/logout messages if appropriate */ + if (!owl_global_is_loglogins(&g) && owl_message_is_loginout(m)) return(0); + + /* check for nolog */ + if (!strcasecmp(owl_message_get_opcode(m), "nolog") || !strcasecmp(owl_message_get_instance(m), "nolog")) return(0); + + /* check direction */ + if ((owl_global_get_loggingdirection(&g)==OWL_LOGGING_DIRECTION_IN) && owl_message_is_direction_out(m)) { + return(0); + } + if ((owl_global_get_loggingdirection(&g)==OWL_LOGGING_DIRECTION_OUT) && owl_message_is_direction_in(m)) { + return(0); + } + + if (owl_message_is_type_zephyr(m)) { + if (owl_message_is_personal(m) && !owl_global_is_logging(&g)) return(0); + if (!owl_message_is_personal(m) && !owl_global_is_classlogging(&g)) return(0); + } else { + if (owl_message_is_private(m) || owl_message_is_loginout(m)) { + if (!owl_global_is_logging(&g)) return(0); + } else { + if (!owl_global_is_classlogging(&g)) return(0); + } + } + return(1); +} + +void owl_log_outgoing_zephyr(owl_message *m) +{ + FILE *file; + char filename[MAXPATHLEN], *logpath; + char *to, *text; + + /* strip local realm */ + to=short_zuser(owl_message_get_recipient(m)); + + /* expand ~ in path names */ + logpath=owl_text_substitute(owl_global_get_logpath(&g), "~", owl_global_get_homedir(&g)); + + text=owl_message_get_body(m); + + snprintf(filename, MAXPATHLEN, "%s/%s", logpath, to); + file=fopen(filename, "a"); + if (!file) { + owl_function_error("Unable to open file for outgoing logging"); + owl_free(logpath); + owl_free(to); + return; + } + fprintf(file, "OUTGOING (owl): %s\n%s\n", to, text); + if (text[strlen(text)-1]!='\n') { + fprintf(file, "\n"); + } + fclose(file); + + snprintf(filename, MAXPATHLEN, "%s/all", logpath); + owl_free(logpath); + file=fopen(filename, "a"); + if (!file) { + owl_function_error("Unable to open file for outgoing logging"); + owl_free(to); + return; + } + fprintf(file, "OUTGOING (owl): %s\n%s\n", to, text); + if (text[strlen(text)-1]!='\n') { + fprintf(file, "\n"); + } + fclose(file); + + owl_free(to); +} + +void owl_log_outgoing_zephyr_error(char *to, char *text) +{ + FILE *file; + char filename[MAXPATHLEN], *logpath; + char *tobuff, *zwriteline; + owl_message *m; + + /* create a present message so we can pass it to + * owl_log_shouldlog_message() + */ + zwriteline=owl_sprintf("zwrite %s", to); + m=owl_function_make_outgoing_zephyr(text, zwriteline, ""); + owl_free(zwriteline); + if (!owl_log_shouldlog_message(m)) { + owl_message_free(m); + return; + } + owl_message_free(m); + + /* chop off a local realm */ + tobuff=short_zuser(to); + + /* expand ~ in path names */ + logpath = owl_text_substitute(owl_global_get_logpath(&g), "~", owl_global_get_homedir(&g)); + + snprintf(filename, MAXPATHLEN, "%s/%s", logpath, tobuff); + file=fopen(filename, "a"); + if (!file) { + owl_function_error("Unable to open file for outgoing logging"); + owl_free(logpath); + owl_free(tobuff); + return; + } + fprintf(file, "ERROR (owl): %s\n%s\n", tobuff, text); + if (text[strlen(text)-1]!='\n') { + fprintf(file, "\n"); + } + fclose(file); + + snprintf(filename, MAXPATHLEN, "%s/all", logpath); + owl_free(logpath); + file=fopen(filename, "a"); + if (!file) { + owl_function_error("Unable to open file for outgoing logging"); + owl_free(tobuff); + return; + } + fprintf(file, "ERROR (owl): %s\n%s\n", tobuff, text); + if (text[strlen(text)-1]!='\n') { + fprintf(file, "\n"); + } + fclose(file); + + owl_free(tobuff); +} + +void owl_log_outgoing_aim(owl_message *m) +{ + FILE *file; + char filename[MAXPATHLEN], *logpath; + char *tobuff, *normalto, *text; + + owl_function_debugmsg("owl_log_outgoing_aim: entering"); + + /* normalize and downcase the screenname */ + normalto=owl_aim_normalize_screenname(owl_message_get_recipient(m)); + downstr(normalto); + tobuff=owl_sprintf("aim:%s", normalto); + owl_free(normalto); + + /* expand ~ in path names */ + logpath = owl_text_substitute(owl_global_get_logpath(&g), "~", owl_global_get_homedir(&g)); + + text=owl_message_get_body(m); + + snprintf(filename, MAXPATHLEN, "%s/%s", logpath, tobuff); + file=fopen(filename, "a"); + if (!file) { + owl_function_error("Unable to open file for outgoing logging"); + owl_free(logpath); + owl_free(tobuff); + return; + } + fprintf(file, "OUTGOING (owl): %s\n%s\n", tobuff, text); + if (text[strlen(text)-1]!='\n') { + fprintf(file, "\n"); + } + fclose(file); + + snprintf(filename, MAXPATHLEN, "%s/all", logpath); + owl_free(logpath); + file=fopen(filename, "a"); + if (!file) { + owl_function_error("Unable to open file for outgoing logging"); + owl_free(tobuff); + return; + } + fprintf(file, "OUTGOING (owl): %s\n%s\n", tobuff, text); + if (text[strlen(text)-1]!='\n') { + fprintf(file, "\n"); + } + fclose(file); + + owl_free(tobuff); +} + +void owl_log_outgoing_loopback(owl_message *m) +{ + FILE *file; + char filename[MAXPATHLEN], *logpath; + char *tobuff, *text; + + tobuff=owl_sprintf("loopback"); + + /* expand ~ in path names */ + logpath = owl_text_substitute(owl_global_get_logpath(&g), "~", owl_global_get_homedir(&g)); + + text=owl_message_get_body(m); + + snprintf(filename, MAXPATHLEN, "%s/%s", logpath, tobuff); + file=fopen(filename, "a"); + if (!file) { + owl_function_error("Unable to open file for outgoing logging"); + owl_free(logpath); + owl_free(tobuff); + return; + } + fprintf(file, "OUTGOING (owl): %s\n%s\n", tobuff, text); + if (text[strlen(text)-1]!='\n') { + fprintf(file, "\n"); + } + fclose(file); + + snprintf(filename, MAXPATHLEN, "%s/all", logpath); + owl_free(logpath); + file=fopen(filename, "a"); + if (!file) { + owl_function_error("Unable to open file for outgoing logging"); + owl_free(tobuff); + return; + } + fprintf(file, "OUTGOING (owl): %s\n%s\n", tobuff, text); + if (text[strlen(text)-1]!='\n') { + fprintf(file, "\n"); + } + fclose(file); + + owl_free(tobuff); +} + +void owl_log_incoming(owl_message *m) +{ + FILE *file, *allfile; + char filename[MAXPATHLEN], allfilename[MAXPATHLEN], *logpath; + char *frombuff=NULL, *from=NULL, *zsig=NULL; + int len, ch, i, personal; + + /* figure out if it's a "personal" message or not */ + if (owl_message_is_type_zephyr(m)) { + if (owl_message_is_personal(m)) { + personal=1; + } else { + personal=0; + } + } else { + if (owl_message_is_private(m) || owl_message_is_loginout(m)) { + personal=1; + } else { + personal=0; + } + } + + if (owl_message_is_type_zephyr(m)) { + if (personal) { + if (owl_message_is_type_zephyr(m)) { + from=frombuff=short_zuser(owl_message_get_sender(m)); + } + } else { + from=frombuff=owl_strdup(owl_message_get_class(m)); + } + } else if (owl_message_is_type_aim(m)) { + /* we do not yet handle chat rooms */ + char *normalto; + normalto=owl_aim_normalize_screenname(owl_message_get_sender(m)); + downstr(normalto); + from=frombuff=owl_sprintf("aim:%s", normalto); + owl_free(normalto); + } else if (owl_message_is_type_loopback(m)) { + from=frombuff=owl_strdup("loopback"); + } else { + from=frombuff=owl_strdup("unknown"); + } + + /* check for malicious sender formats */ + len=strlen(frombuff); + if (len<1 || len>35) from="weird"; + if (strchr(frombuff, '/')) from="weird"; + + ch=frombuff[0]; + if (!isalnum(ch)) from="weird"; + + for (i=0; i<len; i++) { + if (frombuff[i]<'!' || frombuff[i]>='~') from="weird"; + } + + if (!strcmp(frombuff, ".") || !strcasecmp(frombuff, "..")) from="weird"; + + if (!personal) { + if (strcmp(from, "weird")) downstr(from); + } + + /* create the filename (expanding ~ in path names) */ + if (personal) { + logpath = owl_text_substitute(owl_global_get_logpath(&g), "~", owl_global_get_homedir(&g)); + snprintf(filename, MAXPATHLEN, "%s/%s", logpath, from); + snprintf(allfilename, MAXPATHLEN, "%s/all", logpath); + + } else { + logpath = owl_text_substitute(owl_global_get_classlogpath(&g), "~", owl_global_get_homedir(&g)); + snprintf(filename, MAXPATHLEN, "%s/%s", logpath, from); + } + owl_free(logpath); + + file=fopen(filename, "a"); + if (!file) { + owl_function_error("Unable to open file for incoming logging"); + owl_free(frombuff); + return; + } + + allfile=NULL; + if (personal) { + allfile=fopen(allfilename, "a"); + if (!allfile) { + owl_function_error("Unable to open file for incoming logging"); + owl_free(frombuff); + fclose(file); + return; + } + } + + /* write to the main file */ + if (owl_message_is_type_zephyr(m)) { + char *tmp; + + tmp=short_zuser(owl_message_get_sender(m)); + fprintf(file, "Class: %s Instance: %s", owl_message_get_class(m), owl_message_get_instance(m)); + if (strcmp(owl_message_get_opcode(m), "")) fprintf(file, " Opcode: %s", owl_message_get_opcode(m)); + fprintf(file, "\n"); + fprintf(file, "Time: %s Host: %s\n", owl_message_get_timestr(m), owl_message_get_hostname(m)); + zsig=owl_message_get_zsig(m); + fprintf(file, "From: %s <%s>\n\n", zsig, tmp); + fprintf(file, "%s\n\n", owl_message_get_body(m)); + owl_free(tmp); + } else if (owl_message_is_type_aim(m) && !owl_message_is_loginout(m)) { + fprintf(file, "From: <%s> To: <%s>\n", owl_message_get_sender(m), owl_message_get_recipient(m)); + fprintf(file, "Time: %s\n\n", owl_message_get_timestr(m)); + fprintf(file, "%s\n\n", owl_message_get_body(m)); + } else if (owl_message_is_type_aim(m) && owl_message_is_loginout(m)) { + fprintf(file, "From: <%s> To: <%s>\n", owl_message_get_sender(m), owl_message_get_recipient(m)); + fprintf(file, "Time: %s\n\n", owl_message_get_timestr(m)); + if (owl_message_is_login(m)) fprintf(file, "LOGIN\n\n"); + if (owl_message_is_logout(m)) fprintf(file, "LOGOUT\n\n"); + } else { + fprintf(file, "From: <%s> To: <%s>\n", owl_message_get_sender(m), owl_message_get_recipient(m)); + fprintf(file, "Time: %s\n\n", owl_message_get_timestr(m)); + fprintf(file, "%s\n\n", owl_message_get_body(m)); + } + + fclose(file); + + /* if it's a personal message, also write to the 'all' file */ + if (personal) { + if (owl_message_is_type_zephyr(m)) { + char *tmp; + + tmp=short_zuser(owl_message_get_sender(m)); + fprintf(allfile, "Class: %s Instance: %s", owl_message_get_class(m), owl_message_get_instance(m)); + if (strcmp(owl_message_get_opcode(m), "")) fprintf(allfile, " Opcode: %s", owl_message_get_opcode(m)); + fprintf(allfile, "\n"); + fprintf(allfile, "Time: %s Host: %s\n", owl_message_get_timestr(m), owl_message_get_hostname(m)); + fprintf(allfile, "From: %s <%s>\n\n", zsig, tmp); + fprintf(allfile, "%s\n\n", owl_message_get_body(m)); + owl_free(tmp); + } else if (owl_message_is_type_aim(m) && !owl_message_is_loginout(m)) { + fprintf(allfile, "From: <%s> To: <%s>\n", owl_message_get_sender(m), owl_message_get_recipient(m)); + fprintf(allfile, "Time: %s\n\n", owl_message_get_timestr(m)); + fprintf(allfile, "%s\n\n", owl_message_get_body(m)); + } else if (owl_message_is_type_aim(m) && owl_message_is_loginout(m)) { + fprintf(allfile, "From: <%s> To: <%s>\n", owl_message_get_sender(m), owl_message_get_recipient(m)); + fprintf(allfile, "Time: %s\n\n", owl_message_get_timestr(m)); + if (owl_message_is_login(m)) fprintf(allfile, "LOGIN\n\n"); + if (owl_message_is_logout(m)) fprintf(allfile, "LOGOUT\n\n"); + } else { + fprintf(allfile, "From: <%s> To: <%s>\n", owl_message_get_sender(m), owl_message_get_recipient(m)); + fprintf(allfile, "Time: %s\n\n", owl_message_get_timestr(m)); + fprintf(allfile, "%s\n\n", owl_message_get_body(m)); + } + fclose(allfile); + } + + owl_free(frombuff); +} diff --git a/mainwin.c b/mainwin.c new file mode 100644 index 0000000..74af840 --- /dev/null +++ b/mainwin.c @@ -0,0 +1,180 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id: mainwin.c,v 1.8 2009/03/29 12:38:21 kretch Exp $"; + +void owl_mainwin_init(owl_mainwin *mw) +{ + mw->curtruncated=0; + mw->lastdisplayed=-1; +} + +void owl_mainwin_redisplay(owl_mainwin *mw) +{ + owl_message *m; + int i, p, q, lines, isfull, viewsize; + int x, y, savey, recwinlines, start; + int topmsg, curmsg, color; + WINDOW *recwin; + owl_view *v; + owl_list *filtlist; + owl_filter *f; + + recwin=owl_global_get_curs_recwin(&g); + topmsg=owl_global_get_topmsg(&g); + curmsg=owl_global_get_curmsg(&g); + v=owl_global_get_current_view(&g); + + if (v==NULL) { + owl_function_debugmsg("Hit a null window in owl_mainwin_redisplay."); + return; + } + + werase(recwin); + + recwinlines=owl_global_get_recwin_lines(&g); + topmsg=owl_global_get_topmsg(&g); + viewsize=owl_view_get_size(v); + + /* if there are no messages or if topmsg is past the end of the messages, + * just draw a blank screen */ + if (viewsize==0 || topmsg>=viewsize) { + if (viewsize==0) { + owl_global_set_topmsg(&g, 0); + } + mw->curtruncated=0; + mw->lastdisplayed=-1; + wnoutrefresh(recwin); + owl_global_set_needrefresh(&g); + return; + } + + /* write the messages out */ + isfull=0; + mw->curtruncated=0; + mw->lasttruncated=0; + + for (i=topmsg; i<viewsize; i++) { + if (isfull) break; + m=owl_view_get_element(v, i); + + /* hold on to y in case this is the current message or deleted */ + getyx(recwin, y, x); + savey=y; + + /* if it's the current message, account for a vert_offset */ + if (i==owl_global_get_curmsg(&g)) { + start=owl_global_get_curmsg_vert_offset(&g); + lines=owl_message_get_numlines(m)-start; + } else { + start=0; + lines=owl_message_get_numlines(m); + } + + /* if we match filters set the color */ + color=OWL_COLOR_DEFAULT; + filtlist=owl_global_get_filterlist(&g); + q=owl_list_get_size(filtlist); + for (p=0; p<q; p++) { + f=owl_list_get_element(filtlist, p); + if (owl_filter_message_match(f, m)) { + if (owl_filter_get_color(f)!=OWL_COLOR_DEFAULT) color=owl_filter_get_color(f); + } + } + + /* if we'll fill the screen print a partial message */ + if ((y+lines > recwinlines) && (i==owl_global_get_curmsg(&g))) mw->curtruncated=1; + if (y+lines > recwinlines) mw->lasttruncated=1; + if (y+lines > recwinlines-1) { + isfull=1; + owl_message_curs_waddstr(m, owl_global_get_curs_recwin(&g), + start, + start+recwinlines-y, + owl_global_get_rightshift(&g), + owl_global_get_cols(&g)+owl_global_get_rightshift(&g)-1, + color); + } else { + /* otherwise print the whole thing */ + owl_message_curs_waddstr(m, owl_global_get_curs_recwin(&g), + start, + start+lines, + owl_global_get_rightshift(&g), + owl_global_get_cols(&g)+owl_global_get_rightshift(&g)-1, + color); + } + + + /* is it the current message and/or deleted? */ + getyx(recwin, y, x); + wattrset(recwin, A_NORMAL); + if (owl_global_get_rightshift(&g)==0) { /* this lame and should be fixed */ + if (m==owl_view_get_element(v, curmsg)) { + wmove(recwin, savey, 0); + wattron(recwin, A_BOLD); + if (owl_global_get_curmsg_vert_offset(&g)>0) { + waddstr(recwin, "+"); + } else { + waddstr(recwin, "-"); + } + if (!owl_message_is_delete(m)) { + waddstr(recwin, ">"); + } else { + waddstr(recwin, "D"); + } + wmove(recwin, y, x); + wattroff(recwin, A_BOLD); + } else if (owl_message_is_delete(m)) { + wmove(recwin, savey, 0); + waddstr(recwin, " D"); + wmove(recwin, y, x); + } + } + wattroff(recwin, A_BOLD); + } + mw->lastdisplayed=i-1; + + wnoutrefresh(recwin); + owl_global_set_needrefresh(&g); +} + + +int owl_mainwin_is_curmsg_truncated(owl_mainwin *mw) +{ + if (mw->curtruncated) return(1); + return(0); +} + +int owl_mainwin_is_last_msg_truncated(owl_mainwin *mw) +{ + if (mw->lasttruncated) return(1); + return(0); +} + +int owl_mainwin_get_last_msg(owl_mainwin *mw) +{ + /* return the number of the last message displayed. -1 if none */ + return(mw->lastdisplayed); +} diff --git a/message.c b/message.c new file mode 100644 index 0000000..3c2da2a --- /dev/null +++ b/message.c @@ -0,0 +1,987 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/socket.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <time.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: message.c,v 1.53 2009/04/05 23:36:54 kretch Exp $"; + +void owl_message_init(owl_message *m) +{ + m->id=owl_global_get_nextmsgid(&g); + m->type=OWL_MESSAGE_TYPE_GENERIC; + owl_message_set_direction_none(m); + m->delete=0; + m->hostname=owl_strdup(""); + m->zwriteline=NULL; + m->invalid_format=1; + + owl_list_create(&(m->attributes)); + + /* save the time */ + m->time=time(NULL); + m->timestr=owl_strdup(ctime(&(m->time))); + m->timestr[strlen(m->timestr)-1]='\0'; + + /* initialize the fmtext */ + owl_fmtext_init_null(&(m->fmtext)); +} + +/* add the named attribute to the message. If an attribute with the + * name already exists, replace the old value with the new value + */ +void owl_message_set_attribute(owl_message *m, char *attrname, char *attrvalue) +{ + int i, j; + owl_pair *p; + + /* look for an existing pair with this key, and nuke the entry if + found */ + j=owl_list_get_size(&(m->attributes)); + for (i=0; i<j; i++) { + p=owl_list_get_element(&(m->attributes), i); + if (!strcmp(owl_pair_get_key(p), attrname)) { + owl_free(owl_pair_get_key(p)); + owl_free(owl_pair_get_value(p)); + owl_free(p); + owl_list_remove_element(&(m->attributes), i); + break; + } + } + + p=owl_malloc(sizeof(owl_pair)); + owl_pair_create(p, owl_strdup(attrname), owl_strdup(attrvalue)); + owl_list_append_element(&(m->attributes), p); +} + +/* return the value associated with the named attribute, or NULL if + * the attribute does not exist + */ +char *owl_message_get_attribute_value(owl_message *m, char *attrname) +{ + int i, j; + owl_pair *p; + + j=owl_list_get_size(&(m->attributes)); + for (i=0; i<j; i++) { + p=owl_list_get_element(&(m->attributes), i); + if (!strcmp(owl_pair_get_key(p), attrname)) { + return(owl_pair_get_value(p)); + } + } + + /* + owl_function_debugmsg("No attribute %s found for message %i", + attrname, + owl_message_get_id(m)); + */ + return(NULL); +} + +/* We cheat and indent it for now, since we really want this for + * the 'info' function. Later there should just be a generic + * function to indent fmtext. + */ +void owl_message_attributes_tofmtext(owl_message *m, owl_fmtext *fm) { + int i, j; + owl_pair *p; + char *buff; + + owl_fmtext_init_null(fm); + + j=owl_list_get_size(&(m->attributes)); + for (i=0; i<j; i++) { + p=owl_list_get_element(&(m->attributes), i); + buff=owl_sprintf(" %-15.15s: %-35.35s\n", owl_pair_get_key(p), owl_pair_get_value(p)); + owl_fmtext_append_normal(fm, buff); + owl_free(buff); + } +} + +void owl_message_invalidate_format(owl_message *m) +{ + m->invalid_format=1; +} + +owl_fmtext *owl_message_get_fmtext(owl_message *m) +{ + owl_message_format(m); + return(&(m->fmtext)); +} + +void owl_message_format(owl_message *m) +{ + owl_style *s; + owl_view *v; + + if (m->invalid_format) { + /* for now we assume there's jsut the one view and use that style */ + v=owl_global_get_current_view(&g); + s=owl_view_get_style(v); + + owl_fmtext_free(&(m->fmtext)); + owl_fmtext_init_null(&(m->fmtext)); + owl_style_get_formattext(s, &(m->fmtext), m); + m->invalid_format=0; + } +} + +void owl_message_set_class(owl_message *m, char *class) +{ + owl_message_set_attribute(m, "class", class); +} + +char *owl_message_get_class(owl_message *m) +{ + char *class; + + class=owl_message_get_attribute_value(m, "class"); + if (!class) return(""); + return(class); +} + +void owl_message_set_instance(owl_message *m, char *inst) +{ + owl_message_set_attribute(m, "instance", inst); +} + +char *owl_message_get_instance(owl_message *m) +{ + char *instance; + + instance=owl_message_get_attribute_value(m, "instance"); + if (!instance) return(""); + return(instance); +} + +void owl_message_set_sender(owl_message *m, char *sender) +{ + owl_message_set_attribute(m, "sender", sender); +} + +char *owl_message_get_sender(owl_message *m) +{ + char *sender; + + sender=owl_message_get_attribute_value(m, "sender"); + if (!sender) return(""); + return(sender); +} + +void owl_message_set_zsig(owl_message *m, char *zsig) +{ + owl_message_set_attribute(m, "zsig", zsig); +} + +char *owl_message_get_zsig(owl_message *m) +{ + char *zsig; + + zsig=owl_message_get_attribute_value(m, "zsig"); + if (!zsig) return(""); + return(zsig); +} + +void owl_message_set_recipient(owl_message *m, char *recip) +{ + owl_message_set_attribute(m, "recipient", recip); +} + +char *owl_message_get_recipient(owl_message *m) +{ + /* this is stupid for outgoing messages, we need to fix it. */ + + char *recip; + + recip=owl_message_get_attribute_value(m, "recipient"); + if (!recip) return(""); + return(recip); +} + +void owl_message_set_realm(owl_message *m, char *realm) +{ + owl_message_set_attribute(m, "realm", realm); +} + +char *owl_message_get_realm(owl_message *m) +{ + char *realm; + + realm=owl_message_get_attribute_value(m, "realm"); + if (!realm) return(""); + return(realm); +} + +void owl_message_set_body(owl_message *m, char *body) +{ + owl_message_set_attribute(m, "body", body); +} + +char *owl_message_get_body(owl_message *m) +{ + char *body; + + body=owl_message_get_attribute_value(m, "body"); + if (!body) return(""); + return(body); +} + + +void owl_message_set_opcode(owl_message *m, char *opcode) +{ + owl_message_set_attribute(m, "opcode", opcode); +} + +char *owl_message_get_opcode(owl_message *m) +{ + char *opcode; + + opcode=owl_message_get_attribute_value(m, "opcode"); + if (!opcode) return(""); + return(opcode); +} + + +void owl_message_set_islogin(owl_message *m) +{ + owl_message_set_attribute(m, "loginout", "login"); +} + + +void owl_message_set_islogout(owl_message *m) +{ + owl_message_set_attribute(m, "loginout", "logout"); +} + +int owl_message_is_loginout(owl_message *m) +{ + char *res; + + res=owl_message_get_attribute_value(m, "loginout"); + if (!res) return(0); + return(1); +} + +int owl_message_is_login(owl_message *m) +{ + char *res; + + res=owl_message_get_attribute_value(m, "loginout"); + if (!res) return(0); + if (!strcmp(res, "login")) return(1); + return(0); +} + + +int owl_message_is_logout(owl_message *m) +{ + char *res; + + res=owl_message_get_attribute_value(m, "loginout"); + if (!res) return(0); + if (!strcmp(res, "logout")) return(1); + return(0); +} + +void owl_message_set_isprivate(owl_message *m) +{ + owl_message_set_attribute(m, "isprivate", ""); +} + +int owl_message_is_private(owl_message *m) +{ + char *res; + + res=owl_message_get_attribute_value(m, "isprivate"); + if (!res) return(0); + return(1); +} + +char *owl_message_get_timestr(owl_message *m) +{ + if (m->timestr) return(m->timestr); + return(""); +} + +/* caller must free the return */ +char *owl_message_get_shorttimestr(owl_message *m) +{ + struct tm *tmstruct; + char *out; + + tmstruct=localtime(&(m->time)); + out=owl_sprintf("%2.2i:%2.2i", tmstruct->tm_hour, tmstruct->tm_min); + if (out) return(out); + return("??:??"); +} + +void owl_message_set_type_admin(owl_message *m) +{ + m->type=OWL_MESSAGE_TYPE_ADMIN; +} + +void owl_message_set_type_loopback(owl_message *m) +{ + m->type=OWL_MESSAGE_TYPE_LOOPBACK; +} + +void owl_message_set_type_zephyr(owl_message *m) +{ + m->type=OWL_MESSAGE_TYPE_ZEPHYR; +} + +void owl_message_set_type_aim(owl_message *m) +{ + m->type=OWL_MESSAGE_TYPE_AIM; +} + +int owl_message_is_type_admin(owl_message *m) +{ + if (m->type==OWL_MESSAGE_TYPE_ADMIN) return(1); + return(0); +} + +int owl_message_is_type_loopback(owl_message *m) +{ + if (m->type==OWL_MESSAGE_TYPE_LOOPBACK) return(1); + return(0); +} + +int owl_message_is_type_zephyr(owl_message *m) +{ + if (m->type==OWL_MESSAGE_TYPE_ZEPHYR) return(1); + return(0); +} + +int owl_message_is_type_aim(owl_message *m) +{ + if (m->type==OWL_MESSAGE_TYPE_AIM) return(1); + return(0); +} + +int owl_message_is_pseudo(owl_message *m) +{ + if (owl_message_get_attribute_value(m, "pseudo")) return(1); + return(0); +} + +int owl_message_is_type_generic(owl_message *m) +{ + if (m->type==OWL_MESSAGE_TYPE_GENERIC) return(1); + return(0); +} + +char *owl_message_get_text(owl_message *m) +{ + return(owl_fmtext_get_text(&(m->fmtext))); +} + +void owl_message_set_direction_in(owl_message *m) +{ + m->direction=OWL_MESSAGE_DIRECTION_IN; +} + +void owl_message_set_direction_out(owl_message *m) +{ + m->direction=OWL_MESSAGE_DIRECTION_OUT; +} + +void owl_message_set_direction_none(owl_message *m) +{ + m->direction=OWL_MESSAGE_DIRECTION_NONE; +} + +int owl_message_is_direction_in(owl_message *m) +{ + if (m->direction==OWL_MESSAGE_DIRECTION_IN) return(1); + return(0); +} + +int owl_message_is_direction_out(owl_message *m) +{ + if (m->direction==OWL_MESSAGE_DIRECTION_OUT) return(1); + return(0); +} + +int owl_message_is_direction_none(owl_message *m) +{ + if (m->direction==OWL_MESSAGE_DIRECTION_NONE) return(1); + return(0); +} + +int owl_message_get_numlines(owl_message *m) +{ + if (m == NULL) return(0); + owl_message_format(m); + return(owl_fmtext_num_lines(&(m->fmtext))); +} + +void owl_message_mark_delete(owl_message *m) +{ + if (m == NULL) return; + m->delete=1; +} + +void owl_message_unmark_delete(owl_message *m) +{ + if (m == NULL) return; + m->delete=0; +} + +char *owl_message_get_zwriteline(owl_message *m) +{ + if(!m->zwriteline) + return ""; + return(m->zwriteline); +} + +void owl_message_set_zwriteline(owl_message *m, char *line) +{ + if(m->zwriteline) owl_free(m->zwriteline); + m->zwriteline=strdup(line); +} + +int owl_message_is_delete(owl_message *m) +{ + if (m == NULL) return(0); + if (m->delete==1) return(1); + return(0); +} + +#ifdef HAVE_LIBZEPHYR +ZNotice_t *owl_message_get_notice(owl_message *m) +{ + return(&(m->notice)); +} +#else +void *owl_message_get_notice(owl_message *m) +{ + return(NULL); +} +#endif + +void owl_message_set_hostname(owl_message *m, char *hostname) +{ + if (m==NULL) return; + if (m->hostname!=NULL) { + owl_free(m->hostname); + } + m->hostname=owl_strdup(hostname); +} + +char *owl_message_get_hostname(owl_message *m) +{ + return(m->hostname); +} + +void owl_message_curs_waddstr(owl_message *m, WINDOW *win, int aline, int bline, int acol, int bcol, int color) +{ + owl_fmtext a, b; + + /* this will ensure that our cached copy is up to date */ + owl_message_format(m); + + owl_fmtext_init_null(&a); + owl_fmtext_init_null(&b); + + owl_fmtext_truncate_lines(&(m->fmtext), aline, bline-aline+1, &a); + owl_fmtext_truncate_cols(&a, acol, bcol, &b); + if (color!=OWL_COLOR_DEFAULT) { + owl_fmtext_colorize(&b, color); + } + + if (owl_global_is_search_active(&g)) { + owl_fmtext_search_and_highlight(&b, owl_global_get_search_string(&g)); + } + + owl_fmtext_curs_waddstr(&b, win); + + owl_fmtext_free(&a); + owl_fmtext_free(&b); +} + +int owl_message_is_personal(owl_message *m) +{ + if (owl_message_is_type_zephyr(m)) { + if (strcasecmp(owl_message_get_class(m), "message")) return(0); + if (strcasecmp(owl_message_get_instance(m), "personal")) return(0); + if (!strcasecmp(owl_message_get_recipient(m), owl_zephyr_get_sender()) || + !strcasecmp(owl_message_get_sender(m), owl_zephyr_get_sender())) { + return(1); + } + } + return(0); +} + +int owl_message_is_from_me(owl_message *m) +{ + if (owl_message_is_type_zephyr(m)) { + if (!strcasecmp(owl_message_get_sender(m), owl_zephyr_get_sender())) { + return(1); + } else { + return(0); + } + } else if (owl_message_is_type_aim(m)) { + if (!strcasecmp(owl_message_get_sender(m), owl_global_get_aim_screenname(&g))) { + return(1); + } else { + return(0); + } + } else if (owl_message_is_type_admin(m)) { + return(0); + } + return(0); +} + +int owl_message_is_mail(owl_message *m) +{ + if (owl_message_is_type_zephyr(m)) { + if (!strcasecmp(owl_message_get_class(m), "mail") && owl_message_is_private(m)) { + return(1); + } else { + return(0); + } + } + return(0); +} + +int owl_message_is_ping(owl_message *m) +{ + if (owl_message_is_type_zephyr(m)) { + if (!strcasecmp(owl_message_get_opcode(m), "ping")) { + return(1); + } else { + return(0); + } + } + return(0); +} + +int owl_message_is_burningears(owl_message *m) +{ + char *name; + int ret; + + /* if the message is from us or to us, it doesn't count */ + if (owl_message_is_from_me(m) || owl_message_is_private(m)) return(0); + + if (owl_message_is_type_zephyr(m)) { + name=short_zuser(owl_zephyr_get_sender()); + } else if (owl_message_is_type_aim(m)) { + name=owl_strdup(owl_global_get_aim_screenname(&g)); + } else { + return(0); + } + + if (stristr(owl_message_get_body(m), name)) { + ret=1; + } else { + ret=0; + } + + owl_free(name); + return(ret); +} + +/* caller must free return value. */ +char *owl_message_get_cc(owl_message *m) +{ + char *cur, *out, *end; + + cur = owl_message_get_body(m); + while (*cur && *cur==' ') cur++; + if (strncasecmp(cur, "cc:", 3)) return(NULL); + cur+=3; + while (*cur && *cur==' ') cur++; + out = owl_strdup(cur); + end = strchr(out, '\n'); + if (end) end[0] = '\0'; + return(out); +} + +int owl_message_get_id(owl_message *m) +{ + return(m->id); +} + +char *owl_message_get_type(owl_message *m) { + switch (m->type) { + case OWL_MESSAGE_TYPE_ADMIN: + return("admin"); + case OWL_MESSAGE_TYPE_ZEPHYR: + return("zephyr"); + case OWL_MESSAGE_TYPE_GENERIC: + return("generic"); + case OWL_MESSAGE_TYPE_AIM: + return("aim"); + case OWL_MESSAGE_TYPE_JABBER: + return("jabber"); + case OWL_MESSAGE_TYPE_ICQ: + return("icq"); + case OWL_MESSAGE_TYPE_YAHOO: + return("yahoo"); + case OWL_MESSAGE_TYPE_MSN: + return("msn"); + case OWL_MESSAGE_TYPE_LOOPBACK: + return("loopback"); + default: + return("unknown"); + } +} + +char *owl_message_get_direction(owl_message *m) { + switch (m->direction) { + case OWL_MESSAGE_DIRECTION_IN: + return("in"); + case OWL_MESSAGE_DIRECTION_OUT: + return("out"); + case OWL_MESSAGE_DIRECTION_NONE: + return("none"); + default: + return("unknown"); + } +} + +char *owl_message_get_login(owl_message *m) { + if (owl_message_is_login(m)) { + return "login"; + } else if (owl_message_is_logout(m)) { + return "logout"; + } else { + return "none"; + } +} + +char *owl_message_get_header(owl_message *m) { + return owl_message_get_attribute_value(m, "adminheader"); +} + +/* return 1 if the message contains "string", 0 otherwise. This is + * case insensitive because the functions it uses are + */ +int owl_message_search(owl_message *m, char *string) +{ + + owl_message_format(m); /* is this necessary? */ + + return (owl_fmtext_search(&(m->fmtext), string)); +} + + +/* if loginout == -1 it's a logout message + * 0 it's not a login/logout message + * 1 it's a login message + */ +void owl_message_create_aim(owl_message *m, char *sender, char *recipient, char *text, int direction, int loginout) +{ + owl_message_init(m); + owl_message_set_body(m, text); + owl_message_set_sender(m, sender); + owl_message_set_recipient(m, recipient); + owl_message_set_type_aim(m); + + if (direction==OWL_MESSAGE_DIRECTION_IN) { + owl_message_set_direction_in(m); + } else if (direction==OWL_MESSAGE_DIRECTION_OUT) { + owl_message_set_direction_out(m); + } + + /* for now all messages that aren't loginout are private */ + if (!loginout) { + owl_message_set_isprivate(m); + } + + if (loginout==-1) { + owl_message_set_islogout(m); + } else if (loginout==1) { + owl_message_set_islogin(m); + } +} + +void owl_message_create_admin(owl_message *m, char *header, char *text) +{ + owl_message_init(m); + owl_message_set_type_admin(m); + owl_message_set_body(m, text); + owl_message_set_attribute(m, "adminheader", header); /* just a hack for now */ +} + +/* caller should set the direction */ +void owl_message_create_loopback(owl_message *m, char *text) +{ + owl_message_init(m); + owl_message_set_type_loopback(m); + owl_message_set_body(m, text); + owl_message_set_sender(m, "loopsender"); + owl_message_set_recipient(m, "looprecip"); + owl_message_set_isprivate(m); +} + +#ifdef HAVE_LIBZEPHYR +void owl_message_create_from_znotice(owl_message *m, ZNotice_t *n) +{ + struct hostent *hent; + char *ptr, *tmp, *tmp2; + int len; + + owl_message_init(m); + + owl_message_set_type_zephyr(m); + owl_message_set_direction_in(m); + + /* first save the full notice */ + memcpy(&(m->notice), n, sizeof(ZNotice_t)); + + /* a little gross, we'll replace \r's with ' ' for now */ + owl_zephyr_hackaway_cr(&(m->notice)); + + /* save the time, we need to nuke the string saved by message_init */ + if (m->timestr) owl_free(m->timestr); + m->time=n->z_time.tv_sec; + m->timestr=owl_strdup(ctime(&(m->time))); + m->timestr[strlen(m->timestr)-1]='\0'; + + /* set other info */ + owl_message_set_sender(m, n->z_sender); + owl_message_set_class(m, n->z_class); + owl_message_set_instance(m, n->z_class_inst); + owl_message_set_recipient(m, n->z_recipient); + if (n->z_opcode) { + owl_message_set_opcode(m, n->z_opcode); + } else { + owl_message_set_opcode(m, ""); + } + owl_message_set_zsig(m, owl_zephyr_get_zsig(n, &len)); + + if ((ptr=strchr(n->z_recipient, '@'))!=NULL) { + owl_message_set_realm(m, ptr+1); + } else { + owl_message_set_realm(m, owl_zephyr_get_realm()); + } + + /* Set the "isloginout" attribute if it's a login message */ + if (!strcasecmp(n->z_class, "login") || !strcasecmp(n->z_class, OWL_WEBZEPHYR_CLASS)) { + if (!strcasecmp(n->z_opcode, "user_login") || !strcasecmp(n->z_opcode, "user_logout")) { + tmp=owl_zephyr_get_field(n, 1); + owl_message_set_attribute(m, "loginhost", tmp); + owl_free(tmp); + + tmp=owl_zephyr_get_field(n, 3); + owl_message_set_attribute(m, "logintty", tmp); + owl_free(tmp); + } + + if (!strcasecmp(n->z_opcode, "user_login")) { + owl_message_set_islogin(m); + } else if (!strcasecmp(n->z_opcode, "user_logout")) { + owl_message_set_islogout(m); + } + } + + + /* set the "isprivate" attribute if it's a private zephyr */ + if (!strcasecmp(n->z_recipient, owl_zephyr_get_sender())) { + owl_message_set_isprivate(m); + } + + /* set the "isauto" attribute if it's an autoreply */ + if (!strcasecmp(n->z_message, "Automated reply:") || + !strcasecmp(n->z_opcode, "auto")) { + owl_message_set_attribute(m, "isauto", ""); + } + + m->zwriteline=strdup(""); + + /* set the body */ + tmp=owl_zephyr_get_message(n); + if (owl_global_is_newlinestrip(&g)) { + tmp2=owl_util_stripnewlines(tmp); + owl_message_set_body(m, tmp2); + owl_free(tmp2); + } else { + owl_message_set_body(m, tmp); + } + owl_free(tmp); + +#ifdef OWL_ENABLE_ZCRYPT + /* if zcrypt is enabled try to decrypt the message */ + if (owl_global_is_zcrypt(&g) && !strcasecmp(n->z_opcode, "crypt")) { + char *out; + int ret; + + out=owl_malloc(strlen(owl_message_get_body(m))*16+20); + ret=owl_zcrypt_decrypt(out, owl_message_get_body(m), owl_message_get_class(m), owl_message_get_instance(m)); + if (ret==0) { + owl_message_set_body(m, out); + } else { + owl_free(out); + } + } +#endif + + /* save the hostname */ + owl_function_debugmsg("About to do gethostbyaddr"); + hent=gethostbyaddr((char *) &(n->z_uid.zuid_addr), sizeof(n->z_uid.zuid_addr), AF_INET); + if (hent && hent->h_name) { + owl_message_set_hostname(m, hent->h_name); + } else { + owl_message_set_hostname(m, inet_ntoa(n->z_sender_addr)); + } +} +#else +void owl_message_create_from_znotice(owl_message *m, void *n) +{ +} +#endif + +/* If 'direction' is '0' it is a login message, '1' is a logout message. */ +void owl_message_create_pseudo_zlogin(owl_message *m, int direction, char *user, char *host, char *time, char *tty) +{ + char *longuser, *ptr; + +#ifdef HAVE_LIBZEPHYR + memset(&(m->notice), 0, sizeof(ZNotice_t)); +#endif + + longuser=long_zuser(user); + + owl_message_init(m); + + owl_message_set_type_zephyr(m); + owl_message_set_direction_in(m); + + owl_message_set_attribute(m, "pseudo", ""); + owl_message_set_attribute(m, "loginhost", host ? host : ""); + owl_message_set_attribute(m, "logintty", tty ? tty : ""); + + owl_message_set_sender(m, longuser); + owl_message_set_class(m, "LOGIN"); + owl_message_set_instance(m, longuser); + owl_message_set_recipient(m, ""); + if (direction==0) { + owl_message_set_opcode(m, "USER_LOGIN"); + owl_message_set_islogin(m); + } else if (direction==1) { + owl_message_set_opcode(m, "USER_LOGOUT"); + owl_message_set_islogout(m); + } + + if ((ptr=strchr(longuser, '@'))!=NULL) { + owl_message_set_realm(m, ptr+1); + } else { + owl_message_set_realm(m, owl_zephyr_get_realm()); + } + + m->zwriteline=strdup(""); + + owl_message_set_body(m, "<uninitialized>"); + + /* save the hostname */ + owl_function_debugmsg("create_pseudo_login: host is %s", host ? host : ""); + owl_message_set_hostname(m, host ? host : ""); + owl_free(longuser); +} + +void owl_message_create_from_zwriteline(owl_message *m, char *line, char *body, char *zsig) +{ + owl_zwrite z; + int ret; + char hostbuff[5000]; + + owl_message_init(m); + + /* create a zwrite for the purpose of filling in other message fields */ + owl_zwrite_create_from_line(&z, line); + + /* set things */ + owl_message_set_direction_out(m); + owl_message_set_type_zephyr(m); + owl_message_set_sender(m, owl_zephyr_get_sender()); + owl_message_set_class(m, owl_zwrite_get_class(&z)); + owl_message_set_instance(m, owl_zwrite_get_instance(&z)); + if (owl_zwrite_get_numrecips(&z)>0) { + owl_message_set_recipient(m, + long_zuser(owl_zwrite_get_recip_n(&z, 0))); /* only gets the first user, must fix */ + } + owl_message_set_opcode(m, owl_zwrite_get_opcode(&z)); + owl_message_set_realm(m, owl_zwrite_get_realm(&z)); /* also a hack, but not here */ + m->zwriteline=owl_strdup(line); + owl_message_set_body(m, body); + owl_message_set_zsig(m, zsig); + + /* save the hostname */ + ret=gethostname(hostbuff, MAXHOSTNAMELEN); + hostbuff[MAXHOSTNAMELEN]='\0'; + if (ret) { + owl_message_set_hostname(m, "localhost"); + } else { + owl_message_set_hostname(m, hostbuff); + } + owl_zwrite_free(&z); +} + +void owl_message_pretty_zsig(owl_message *m, char *buff) +{ + /* stick a one line version of the zsig in buff */ + char *ptr; + + strcpy(buff, owl_message_get_zsig(m)); + ptr=strchr(buff, '\n'); + if (ptr) ptr[0]='\0'; +} + +void owl_message_free(owl_message *m) +{ + int i, j; + owl_pair *p; +#ifdef HAVE_LIBZEPHYR + if (owl_message_is_type_zephyr(m) && owl_message_is_direction_in(m)) { + ZFreeNotice(&(m->notice)); + } +#endif + if (m->timestr) owl_free(m->timestr); + if (m->zwriteline) owl_free(m->zwriteline); + + /* free all the attributes */ + j=owl_list_get_size(&(m->attributes)); + for (i=0; i<j; i++) { + p=owl_list_get_element(&(m->attributes), i); + owl_free(owl_pair_get_key(p)); + owl_free(owl_pair_get_value(p)); + owl_free(p); + } + + owl_list_free_simple(&(m->attributes)); + + owl_fmtext_free(&(m->fmtext)); +} diff --git a/messagelist.c b/messagelist.c new file mode 100644 index 0000000..50bad5b --- /dev/null +++ b/messagelist.c @@ -0,0 +1,128 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" +#include <stdlib.h> +#include <string.h> + +static const char fileIdent[] = "$Id: messagelist.c,v 1.5 2009/03/29 12:38:21 kretch Exp $"; + +int owl_messagelist_create(owl_messagelist *ml) +{ + owl_list_create(&(ml->list)); + return(0); +} + +int owl_messagelist_get_size(owl_messagelist *ml) +{ + return(owl_list_get_size(&(ml->list))); +} + +void *owl_messagelist_get_element(owl_messagelist *ml, int n) +{ + return(owl_list_get_element(&(ml->list), n)); +} + +owl_message *owl_messagelist_get_by_id(owl_messagelist *ml, int target_id) +{ + /* return the message with id == 'id'. If it doesn't exist return NULL. */ + int first, last, mid, msg_id; + owl_message *m; + + first = 0; + last = owl_list_get_size(&(ml->list)) - 1; + while (first <= last) { + mid = (first + last) / 2; + m = owl_list_get_element(&(ml->list), mid); + msg_id = owl_message_get_id(m); + if (msg_id == target_id) { + return(m); + } else if (msg_id < target_id) { + first = mid + 1; + } else { + last = mid - 1; + } + } + return(NULL); +} + +int owl_messagelist_append_element(owl_messagelist *ml, void *element) +{ + return(owl_list_append_element(&(ml->list), element)); +} + +/* do we really still want this? */ +int owl_messagelist_delete_element(owl_messagelist *ml, int n) +{ + /* mark a message as deleted */ + owl_message_mark_delete(owl_list_get_element(&(ml->list), n)); + return(0); +} + +int owl_messagelist_undelete_element(owl_messagelist *ml, int n) +{ + /* mark a message as deleted */ + owl_message_unmark_delete(owl_list_get_element(&(ml->list), n)); + return(0); +} + +int owl_messagelist_expunge(owl_messagelist *ml) +{ + /* expunge deleted messages */ + int i, j; + owl_list newlist; + owl_message *m; + + owl_list_create(&newlist); + /*create a new list without messages marked as deleted */ + j=owl_list_get_size(&(ml->list)); + for (i=0; i<j; i++) { + m=owl_list_get_element(&(ml->list), i); + if (owl_message_is_delete(m)) { + owl_message_free(m); + } else { + owl_list_append_element(&newlist, m); + } + } + + /* free the old list */ + owl_list_free_simple(&(ml->list)); + + /* copy the new list to the old list */ + memcpy(&(ml->list), &newlist, sizeof(owl_list)); + + return(0); +} + +void owl_messagelist_invalidate_formats(owl_messagelist *ml) +{ + int i, j; + owl_message *m; + + j=owl_list_get_size(&(ml->list)); + for (i=0; i<j; i++) { + m=owl_list_get_element(&(ml->list), i); + owl_message_invalidate_format(m); + } +} diff --git a/mkinstalldirs b/mkinstalldirs new file mode 100755 index 0000000..9f45ed1 --- /dev/null +++ b/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1993-05-16 +# Public domain + +# $Id: mkinstalldirs,v 1.1 2003/12/26 20:47:57 kretch Exp $ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here @@ -0,0 +1,760 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <time.h> +#include <sys/param.h> +#include <sys/types.h> +#include <termios.h> +#include <sys/stat.h> +#include "owl.h" + +#if OWL_STDERR_REDIR +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_FILIO_H +#include <sys/filio.h> +#endif +int stderr_replace(void); +#endif + +#define STDIN 0 + +static const char fileIdent[] = "$Id: owl.c,v 1.97 2009/04/06 00:09:13 kretch Exp $"; + +owl_global g; + +int main(int argc, char **argv, char **env) +{ + WINDOW *recwin, *sepwin, *typwin, *msgwin; + owl_editwin *tw; + owl_popwin *pw; + int ret, initialsubs, debug, argcsave, followlast; + int newmsgs, nexttimediff; + struct sigaction sigact; + char *configfile, *tty, *perlout, *perlerr, **argvsave; + owl_filter *f; + owl_style *s; + time_t nexttime, now; + struct tm *today; + char *dir; + struct termios tio; + + argcsave=argc; + argvsave=argv; + configfile=NULL; + tty=NULL; + debug=0; + initialsubs=1; + if (argc>0) { + argv++; + argc--; + } + while (argc>0) { + if (!strcmp(argv[0], "-n")) { + initialsubs=0; + argv++; + argc--; + } else if (!strcmp(argv[0], "-c")) { + if (argc<2) { + fprintf(stderr, "Too few arguments to -c\n"); + usage(); + exit(1); + } + configfile=argv[1]; + argv+=2; + argc-=2; + } else if (!strcmp(argv[0], "-t")) { + if (argc<2) { + fprintf(stderr, "Too few arguments to -t\n"); + usage(); + exit(1); + } + tty=argv[1]; + argv+=2; + argc-=2; + } else if (!strcmp(argv[0], "-d")) { + debug=1; + argv++; + argc--; + } else if (!strcmp(argv[0], "-D")) { + debug=1; + unlink(OWL_DEBUG_FILE); + argv++; + argc--; + } else if (!strcmp(argv[0], "-v")) { + printf("This is owl version %s\n", OWL_VERSION_STRING); + exit(0); + } else { + fprintf(stderr, "Uknown argument\n"); + usage(); + exit(1); + } + } + + owl_function_debugmsg("startup: Finished parsing arguments"); + +#ifdef HAVE_LIBZEPHYR + /* zephyr init */ + ret=owl_zephyr_initialize(); + if (ret) { + exit(1); + } +#endif + + /* signal handler */ + /*sigact.sa_handler=sig_handler;*/ + sigact.sa_sigaction=sig_handler; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags=SA_SIGINFO; + sigaction(SIGWINCH, &sigact, NULL); + sigaction(SIGALRM, &sigact, NULL); + sigaction(SIGPIPE, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + sigaction(SIGHUP, &sigact, NULL); + + /* save initial terminal settings */ + tcgetattr(0, owl_global_get_startup_tio(&g)); + + /* turn ISTRIP off */ + tcgetattr(0, &tio); + tio.c_iflag &= ~ISTRIP; + tcsetattr(0, TCSAFLUSH, &tio); + + /* screen init */ + if (!getenv("TERMINFO")) { + putenv(owl_sprintf("TERMINFO=%s", TERMINFO)); + owl_function_debugmsg("startup: setting TERMINFO to %s", TERMINFO); + } else { + owl_function_debugmsg("startup: leaving TERMINFO as %s from envrionment", getenv("TERMINFO")); + } + + initscr(); + start_color(); +#ifdef HAVE_USE_DEFAULT_COLORS + use_default_colors(); +#endif + raw(); + noecho(); + + /* define simple color pairs */ + if (has_colors() && COLOR_PAIRS>=8) { + init_pair(OWL_COLOR_BLACK, COLOR_BLACK, -1); + init_pair(OWL_COLOR_RED, COLOR_RED, -1); + init_pair(OWL_COLOR_GREEN, COLOR_GREEN, -1); + init_pair(OWL_COLOR_YELLOW, COLOR_YELLOW, -1); + init_pair(OWL_COLOR_BLUE, COLOR_BLUE, -1); + init_pair(OWL_COLOR_MAGENTA, COLOR_MAGENTA, -1); + init_pair(OWL_COLOR_CYAN, COLOR_CYAN, -1); + init_pair(OWL_COLOR_WHITE, COLOR_WHITE, -1); + } + + /* owl global init */ + owl_global_init(&g); + if (debug) owl_global_set_debug_on(&g); + owl_function_debugmsg("startup: first available debugging message"); + owl_global_set_startupargs(&g, argcsave, argvsave); + owl_global_set_haveaim(&g); + + /* prepare stdin dispatch */ + { + owl_dispatch *d = owl_malloc(sizeof(owl_dispatch)); + owl_function_debugmsg("startup: creating input selector"); + d->fd = STDIN; + d->cfunc = &owl_process_input; + d->destroy = NULL; + owl_select_add_dispatch(d); + } + +#ifdef HAVE_LIBZEPHYR + owl_global_set_havezephyr(&g); + if (!ret) { + owl_dispatch *d = owl_malloc(sizeof(owl_dispatch)); + owl_function_debugmsg("startup: creating zephyr selector"); + d->fd = ZGetFD(); + d->cfunc = &owl_zephyr_process_events; + d->destroy = NULL; + owl_select_add_dispatch(d); + owl_global_set_havezephyr(&g); + } +#endif + +#if OWL_STDERR_REDIR + /* Do this only after we've started curses up... */ + { + owl_dispatch *d = owl_malloc(sizeof(owl_dispatch)); + owl_function_debugmsg("startup: doing stderr redirection"); + d->fd = stderr_replace(); + d->cfunc = stderr_redirect_handler; + d->destroy = NULL; + owl_function_debugmsg("testing: 1"); + owl_select_add_dispatch(d); + owl_function_debugmsg("testing: 2"); + } + +#endif + + /* create the owl directory, in case it does not exist */ + owl_function_debugmsg("startup: creating owl directory, if not present"); + dir=owl_sprintf("%s/%s", owl_global_get_homedir(&g), OWL_CONFIG_DIR); + mkdir(dir, S_IRWXU); + owl_free(dir); + + /* set the tty, either from the command line, or by figuring it out */ + owl_function_debugmsg("startup: setting tty name"); + if (tty) { + owl_global_set_tty(&g, tty); + } else { + owl_global_set_tty(&g, owl_util_get_default_tty()); + } + + /* setup the built-in styles */ + owl_function_debugmsg("startup: creating built-in styles"); + s=owl_malloc(sizeof(owl_style)); + owl_style_create_internal(s, "default", &owl_stylefunc_default, "Default message formatting"); + owl_global_add_style(&g, s); + + s=owl_malloc(sizeof(owl_style)); + owl_style_create_internal(s, "basic", &owl_stylefunc_basic, "Basic message formatting."); + owl_global_add_style(&g, s); +#if 0 + s=owl_malloc(sizeof(owl_style)); + owl_style_create_internal(s, "vt", &owl_stylefunc_vt, "VT message formatting."); + owl_global_add_style(&g, s); +#endif + s=owl_malloc(sizeof(owl_style)); + owl_style_create_internal(s, "oneline", &owl_stylefunc_oneline, "Formats for one-line-per-message"); + owl_global_add_style(&g, s); + + /* setup the default filters */ + /* the personal filter will need to change again when AIM chat's are + * included. Also, there should be an %aimme% */ + owl_function_debugmsg("startup: creating default filters"); + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "personal", "( type ^zephyr$ " + "and class ^message$ and instance ^personal$ " + "and ( recipient ^%me%$ or sender ^%me%$ ) ) " + "or ( type ^aim$ and login ^none$ )"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "trash", "class ^mail$ or opcode ^ping$ or type ^admin$ or ( not login ^none$ )"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "ping", "opcode ^ping$"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "auto", "opcode ^auto$"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "login", "not login ^none$"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "reply-lockout", "class ^noc or class ^mail$"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "out", "direction ^out$"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "aim", "type ^aim$"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "zephyr", "type ^zephyr$"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "none", "false"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + f=owl_malloc(sizeof(owl_filter)); + owl_filter_init_fromstring(f, "all", "true"); + owl_list_append_element(owl_global_get_filterlist(&g), f); + + /* set the current view */ + owl_function_debugmsg("startup: setting the current view"); + owl_view_create(owl_global_get_current_view(&g), "main", f, owl_global_get_style_by_name(&g, "default")); + + /* AIM init */ + owl_function_debugmsg("startup: doing AIM initialization"); + owl_aim_init(); + + /* process the startup file */ + owl_function_debugmsg("startup: processing startup file"); + owl_function_source(NULL); + + /* read the config file */ + owl_function_debugmsg("startup: processing config file"); + owl_context_set_readconfig(owl_global_get_context(&g)); + perlerr=owl_perlconfig_readconfig(configfile); + if (perlerr) { + endwin(); + owl_function_error("Error parsing configfile: %s\n", perlerr); + fprintf(stderr, "\nError parsing configfile: %s\n", perlerr); + fflush(stderr); + printf("\nError parsing configfile: %s\n", perlerr); + fflush(stdout); + exit(1); + } + + /* if the config defines a formatting function, add 'perl' as a style */ + if (owl_global_is_config_format(&g)) { + owl_function_debugmsg("Found perl formatting"); + s=owl_malloc(sizeof(owl_style)); + owl_style_create_perl(s, "perl", "owl::_format_msg_legacy_wrap", + "User-defined perl style that calls owl::format_msg" + "with legacy global variable support"); + owl_global_add_style(&g, s); + owl_global_set_default_style(&g, "perl"); + } + + /* execute the startup function in the configfile */ + owl_function_debugmsg("startup: executing perl startup, if applicable"); + perlout = owl_perlconfig_execute("owl::startup();"); + if (perlout) owl_free(perlout); + + /* hold on to the window names for convenience */ + msgwin=owl_global_get_curs_msgwin(&g); + recwin=owl_global_get_curs_recwin(&g); + sepwin=owl_global_get_curs_sepwin(&g); + typwin=owl_global_get_curs_typwin(&g); + tw=owl_global_get_typwin(&g); + + /* welcome message */ + owl_function_debugmsg("startup: creating splash message"); + owl_function_adminmsg("", + "-----------------------------------------------------------------------\n" + "Welcome to Owl version " OWL_VERSION_STRING ". Press 'h' for on-line help. \n" + " \n" + "If you would like to receive release announcements about Owl you can \n" + "join the owl-users mailing list at http://www.ktools.org/ \n" + " ^ ^ \n" + " OvO \n" + "Please report any bugs or suggestions to bug-owl@ktools.org ( ) \n" + "-----------------------------------------------------------------m-m---\n" + ); + sepbar(NULL); + + wrefresh(sepwin); + + /* load zephyr subs */ + if (initialsubs) { + int ret2; + owl_function_debugmsg("startup: loading initial zephyr subs"); + + /* load default subscriptions */ + ret=owl_zephyr_loaddefaultsubs(); + + /* load subscriptions from subs file */ + ret2=owl_zephyr_loadsubs(NULL, 0); + + if (ret || ret2) { + owl_function_adminmsg("", "Error loading zephyr subscriptions"); + } else if (ret2!=-1) { + owl_global_add_userclue(&g, OWL_USERCLUE_CLASSES); + } + + /* load login subscriptions */ + if (owl_global_is_loginsubs(&g)) { + owl_function_debugmsg("startup: loading login subs"); + owl_function_loadloginsubs(NULL); + } + } + + /* First buddy check to sync the list without notifications */ + owl_function_debugmsg("startup: doing initial zephyr buddy check"); + /* owl_function_zephyr_buddy_check(0); */ + + /* set the startup and default style, based on userclue and presence of a + * formatting function */ + owl_function_debugmsg("startup: setting startup and default style"); + if (0 != strcmp(owl_global_get_default_style(&g), "__unspecified__")) { + /* the style was set by the user: leave it alone */ + } else if (owl_global_is_config_format(&g)) { + owl_global_set_default_style(&g, "perl"); + } else if (owl_global_is_userclue(&g, OWL_USERCLUE_CLASSES)) { + owl_global_set_default_style(&g, "default"); + } else { + owl_global_set_default_style(&g, "basic"); + } + + /* zlog in if we need to */ + if (owl_global_is_startuplogin(&g)) { + owl_function_debugmsg("startup: doing zlog in"); + owl_zephyr_zlog_in(); + } + + owl_function_debugmsg("startup: set style for the view"); + owl_view_set_style(owl_global_get_current_view(&g), + owl_global_get_style_by_name(&g, owl_global_get_default_style(&g))); + + owl_function_debugmsg("startup: setting context interactive"); + owl_context_set_interactive(owl_global_get_context(&g)); + + nexttimediff=10; + nexttime=time(NULL); + + /* owl_zephyr_process_events(NULL); */ + owl_function_debugmsg("startup: entering main loop"); + /* main loop */ + while (1) { + + /* if a resize has been scheduled, deal with it */ + owl_global_resize(&g, 0, 0); + + /* these are here in case a resize changes the windows */ + msgwin=owl_global_get_curs_msgwin(&g); + recwin=owl_global_get_curs_recwin(&g); + sepwin=owl_global_get_curs_sepwin(&g); + typwin=owl_global_get_curs_typwin(&g); + + followlast=owl_global_should_followlast(&g); + + /* Do AIM stuff */ + if (owl_global_is_doaimevents(&g)) { + /* owl_aim_process_events(); */ + + if (owl_global_is_aimloggedin(&g)) { + if (owl_timer_is_expired(owl_global_get_aim_buddyinfo_timer(&g))) { + /* owl_buddylist_request_idletimes(owl_global_get_buddylist(&g)); */ + owl_timer_reset(owl_global_get_aim_buddyinfo_timer(&g)); + } + } + } + + /* little hack */ + now=time(NULL); + today=localtime(&now); + if (today->tm_mon==9 && today->tm_mday==31 && owl_global_get_runtime(&g)<600) { + if (time(NULL)>nexttime) { + if (nexttimediff==1) { + nexttimediff=10; + } else { + nexttimediff=1; + } + nexttime+=nexttimediff; + owl_hack_animate(); + } + } + + /* Grab incoming messages. */ + newmsgs=0; + while(owl_global_messagequeue_pending(&g)) { + owl_message *m=NULL; + owl_filter *f; + + m=owl_global_messageuque_popmsg(&g); + + /* if this message it on the puntlist, nuke it and continue */ + if (owl_global_message_is_puntable(&g, m)) { + owl_message_free(m); + continue; + } + + /* login or logout that should be ignored? */ + if (owl_global_is_ignorelogins(&g) && owl_message_is_loginout(m)) { + owl_message_free(m); + continue; + } + + /* otherwise add it to the global list */ + owl_messagelist_append_element(owl_global_get_msglist(&g), m); + newmsgs=1; + + /* let the config know the new message has been received */ + owl_perlconfig_getmsg(m, 0, NULL); + + /* add it to any necessary views; right now there's only the current view */ + owl_view_consider_message(owl_global_get_current_view(&g), m); + + /* do we need to autoreply? */ + if (owl_global_is_zaway(&g) && !owl_message_get_attribute_value(m, "isauto")) { + if (owl_message_is_type_zephyr(m)) { + owl_zephyr_zaway(m); + } else if (owl_message_is_type_aim(m)) { + if (owl_message_is_private(m)) { + owl_function_send_aimawymsg(owl_message_get_sender(m), owl_global_get_zaway_msg(&g)); + } + } + } + + /* ring the bell if it's a personal */ + if (!strcmp(owl_global_get_personalbell(&g), "on")) { + if (!owl_message_is_loginout(m) && + !owl_message_is_mail(m) && + owl_message_is_private(m)) { + owl_function_beep(); + } + } else if (!strcmp(owl_global_get_personalbell(&g), "off")) { + /* do nothing */ + } else { + f=owl_global_get_filter(&g, owl_global_get_personalbell(&g)); + if (f && owl_filter_message_match(f, m)) { + owl_function_beep(); + } + } + + + /* if it matches the alert filter, do the alert action */ + f=owl_global_get_filter(&g, owl_global_get_alert_filter(&g)); + if (f && owl_filter_message_match(f, m)) { + owl_function_command(owl_global_get_alert_action(&g)); + } + + /* if it's a zephyr login or logout, update the zbuddylist */ + if (owl_message_is_type_zephyr(m) && owl_message_is_loginout(m)) { + if (owl_message_is_login(m)) { + owl_zbuddylist_adduser(owl_global_get_zephyr_buddylist(&g), owl_message_get_sender(m)); + } else if (owl_message_is_logout(m)) { + owl_zbuddylist_deluser(owl_global_get_zephyr_buddylist(&g), owl_message_get_sender(m)); + } else { + owl_function_error("Internal error: received login notice that is neither login nor logout"); + } + } + + /* check for burning ears message */ + /* this is an unsupported feature */ + if (owl_global_is_burningears(&g) && owl_message_is_burningears(m)) { + char *buff; + buff = owl_sprintf("@i(Burning ears message on class %s)", owl_message_get_class(m)); + owl_function_adminmsg(buff, ""); + owl_free(buff); + owl_function_beep(); + } + + /* log the message if we need to */ + owl_log_message(m); + } + + /* is it time to check zbuddies? */ + if (owl_global_is_pseudologins(&g)) { + if (owl_timer_is_expired(owl_global_get_zephyr_buddycheck_timer(&g))) { + owl_function_debugmsg("Doing zephyr buddy check"); + owl_function_zephyr_buddy_check(1); + owl_timer_reset(owl_global_get_zephyr_buddycheck_timer(&g)); + } + } + + /* follow the last message if we're supposed to */ + if (newmsgs && followlast) { + owl_function_lastmsg_noredisplay(); + } + + /* do the newmsgproc thing */ + if (newmsgs) { + owl_function_do_newmsgproc(); + } + + /* redisplay if necessary */ + /* this should be optimized to not run if the new messages won't be displayed */ + if (newmsgs) { + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + sepbar(NULL); + if (owl_popwin_is_active(owl_global_get_popwin(&g))) { + owl_popwin_refresh(owl_global_get_popwin(&g)); + /* TODO: this is a broken kludge */ + if (owl_global_get_viewwin(&g)) { + owl_viewwin_redisplay(owl_global_get_viewwin(&g), 0); + } + } + owl_global_set_needrefresh(&g); + } + + /* if a popwin just came up, refresh it */ + pw=owl_global_get_popwin(&g); + if (owl_popwin_is_active(pw) && owl_popwin_needs_first_refresh(pw)) { + owl_popwin_refresh(pw); + owl_popwin_no_needs_first_refresh(pw); + owl_global_set_needrefresh(&g); + /* TODO: this is a broken kludge */ + if (owl_global_get_viewwin(&g)) { + owl_viewwin_redisplay(owl_global_get_viewwin(&g), 0); + } + } + + /* update the terminal if we need to */ + if (owl_global_is_needrefresh(&g)) { + /* leave the cursor in the appropriate window */ + if (owl_global_is_typwin_active(&g)) { + owl_function_set_cursor(typwin); + } else { + owl_function_set_cursor(sepwin); + } + doupdate(); + owl_global_set_noneedrefresh(&g); + } + + /* select on FDs we know about. */ + + /* Some calls into libzephyr call Z_WaitForNotice(), which has its + * own select loop and may leave zephyrs on the queue. Check for + * them now, and process any we find. */ + /* owl_zephyr_process_events(NULL); */ + owl_select(); + /* owl_zephyr_process_events(NULL); */ + + /* Log any error signals */ + { + siginfo_t si; + int signum; + if ((signum = owl_global_get_errsignal_and_clear(&g, &si)) > 0) { + owl_function_error("Got unexpected signal: %d %s (code: %d band: %d errno: %d)", + signum, signum==SIGPIPE?"SIGPIPE":"SIG????", + si.si_code, si.si_band, si.si_errno); + } + } + + } +} + +void owl_process_input(owl_dispatch *d) +{ + int ret, j; + owl_popwin *pw; + owl_editwin *tw; + WINDOW *typwin; + + typwin = owl_global_get_curs_typwin(&g); + while (1) { + j=wgetch(typwin); + if (j==ERR) return; + + pw=owl_global_get_popwin(&g); + tw=owl_global_get_typwin(&g); + + /* find and activate the current keymap. + * TODO: this should really get fixed by activating + * keymaps as we switch between windows... + */ + if (pw && owl_popwin_is_active(pw) && owl_global_get_viewwin(&g)) { + owl_context_set_popless(owl_global_get_context(&g), + owl_global_get_viewwin(&g)); + owl_function_activate_keymap("popless"); + } else if (owl_global_is_typwin_active(&g) + && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_ONELINE) { + /* + owl_context_set_editline(owl_global_get_context(&g), tw); + owl_function_activate_keymap("editline"); + */ + } else if (owl_global_is_typwin_active(&g) + && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_MULTILINE) { + owl_context_set_editmulti(owl_global_get_context(&g), tw); + owl_function_activate_keymap("editmulti"); + } else { + owl_context_set_recv(owl_global_get_context(&g)); + owl_function_activate_keymap("recv"); + } + /* now actually handle the keypress */ + ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j); + if (ret!=0 && ret!=1) { + owl_function_makemsg("Unable to handle keypress"); + } + } +} + +void sig_handler(int sig, siginfo_t *si, void *data) +{ + if (sig==SIGWINCH) { + /* we can't inturrupt a malloc here, so it just sets a flag + * schedulding a resize for later + */ + owl_function_resize(); + } else if (sig==SIGPIPE || sig==SIGCHLD) { + /* Set a flag and some info that we got the sigpipe + * so we can record that we got it and why... */ + owl_global_set_errsignal(&g, sig, si); + } else if (sig==SIGTERM || sig==SIGHUP) { + owl_function_quit(); + } + +} + +void usage() +{ + fprintf(stderr, "Owl version %s\n", OWL_VERSION_STRING); + fprintf(stderr, "Usage: owl [-n] [-d] [-D] [-v] [-h] [-c <configfile>] [-t <ttyname>]\n"); + fprintf(stderr, " -n don't load zephyr subscriptions\n"); + fprintf(stderr, " -d enable debugging\n"); + fprintf(stderr, " -D enable debugging and delete previous debug file\n"); + fprintf(stderr, " -v print the Owl version number and exit\n"); + fprintf(stderr, " -h print this help message\n"); + fprintf(stderr, " -c specify an alternate config file\n"); + fprintf(stderr, " -t set the tty name\n"); +} + +#if OWL_STDERR_REDIR + +/* Replaces stderr with a pipe so that we can read from it. + * Returns the fd of the pipe from which stderr can be read. */ +int stderr_replace(void) +{ + int pipefds[2]; + if (0 != pipe(pipefds)) { + perror("pipe"); + owl_function_debugmsg("stderr_replace: pipe FAILED\n"); + return -1; + } + owl_function_debugmsg("stderr_replace: pipe: %d,%d", pipefds[0], pipefds[1]); + if (-1 == dup2(pipefds[1], 2 /*stderr*/)) { + owl_function_debugmsg("stderr_replace: dup2 FAILED (%s)", strerror(errno)); + perror("dup2"); + return -1; + } + return pipefds[0]; +} + +/* Sends stderr (read from rfd) messages to the error console */ +void stderr_redirect_handler(owl_dispatch *d) +{ + int navail, bread; + char buf[4096]; + int rfd = d->fd; + if (rfd<0) return; + if (-1 == ioctl(rfd, FIONREAD, (void*)&navail)) { + return; + } + /*owl_function_debugmsg("stderr_redirect: navail = %d\n", navail);*/ + if (navail <= 0) return; + if (navail > sizeof(buf)-1) { + navail = sizeof(buf)-1; + } + bread = read(rfd, buf, navail); + if (buf[navail-1] != '\0') { + buf[navail] = '\0'; + } + owl_function_error("[stderr]\n%s", buf); +} + +#endif /* OWL_STDERR_REDIR */ @@ -0,0 +1,562 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#ifndef INC_OWL_H +#define INC_OWL_H + +#include <curses.h> +#include <sys/param.h> +#include <EXTERN.h> +#include <netdb.h> +#include <regex.h> +#include <time.h> +#include <signal.h> +#include <termios.h> +#include <glib.h> +#include <libfaim/aim.h> +#include "config.h" +#ifdef HAVE_LIBZEPHYR +#include <zephyr/zephyr.h> +#endif +#ifdef HAVE_COM_ERR_H +#include <com_err.h> +#endif + +static const char owl_h_fileIdent[] = "$Id: owl.h,v 1.128 2009/04/07 05:00:29 kretch Exp $"; + +#define OWL_VERSION 2.2.2 +#define OWL_VERSION_STRING "2.2.2" + +/* Feature that is being tested to redirect stderr through a pipe. + * There may still be some portability problems with this. */ +#define OWL_STDERR_REDIR 1 + +#define OWL_DEBUG 0 +#define OWL_DEBUG_FILE "/var/tmp/owldebug" + +#define OWL_CONFIG_DIR "/.owl" /* this is relative to the user's home directory */ +#define OWL_STARTUP_FILE "/.owl/startup" /* this is relative to the user's home directory */ + +#define OWL_FMTEXT_ATTR_NONE 0 +#define OWL_FMTEXT_ATTR_BOLD 1 +#define OWL_FMTEXT_ATTR_REVERSE 2 +#define OWL_FMTEXT_ATTR_UNDERLINE 4 + +#define OWL_COLOR_BLACK 0 +#define OWL_COLOR_RED 1 +#define OWL_COLOR_GREEN 2 +#define OWL_COLOR_YELLOW 3 +#define OWL_COLOR_BLUE 4 +#define OWL_COLOR_MAGENTA 5 +#define OWL_COLOR_CYAN 6 +#define OWL_COLOR_WHITE 7 +#define OWL_COLOR_DEFAULT 8 + +#define OWL_EDITWIN_STYLE_MULTILINE 0 +#define OWL_EDITWIN_STYLE_ONELINE 1 + +#define OWL_PROTOCOL_ZEPHYR 0 +#define OWL_PROTOCOL_AIM 1 +#define OWL_PROTOCOL_JABBER 2 +#define OWL_PROTOCOL_ICQ 3 +#define OWL_PROTOCOL_YAHOO 4 +#define OWL_PROTOCOL_MSN 5 + +#define OWL_MESSAGE_TYPE_ADMIN 0 +#define OWL_MESSAGE_TYPE_GENERIC 1 +#define OWL_MESSAGE_TYPE_ZEPHYR 2 +#define OWL_MESSAGE_TYPE_AIM 3 +#define OWL_MESSAGE_TYPE_JABBER 4 +#define OWL_MESSAGE_TYPE_ICQ 5 +#define OWL_MESSAGE_TYPE_YAHOO 6 +#define OWL_MESSAGE_TYPE_MSN 7 +#define OWL_MESSAGE_TYPE_LOOPBACK 8 + +#define OWL_MESSAGE_DIRECTION_NONE 0 +#define OWL_MESSAGE_DIRECTION_IN 1 +#define OWL_MESSAGE_DIRECTION_OUT 2 + +#define OWL_MUX_READ 1 +#define OWL_MUX_WRITE 2 +#define OWL_MUX_EXCEPT 4 + +#define OWL_DIRECTION_NONE 0 +#define OWL_DIRECTION_DOWNWARDS 1 +#define OWL_DIRECTION_UPWARDS 2 + +#define OWL_LOGGING_DIRECTION_BOTH 0 +#define OWL_LOGGING_DIRECTION_IN 1 +#define OWL_LOGGING_DIRECTION_OUT 2 + +#define OWL_SCROLLMODE_NORMAL 0 +#define OWL_SCROLLMODE_TOP 1 +#define OWL_SCROLLMODE_NEARTOP 2 +#define OWL_SCROLLMODE_CENTER 3 +#define OWL_SCROLLMODE_PAGED 4 +#define OWL_SCROLLMODE_PAGEDCENTER 5 + +#define OWL_STYLE_TYPE_INTERNAL 0 +#define OWL_STYLE_TYPE_PERL 1 + +#define OWL_TAB 3 /* This *HAS* to be the size of TABSTR below */ +#define OWL_TABSTR " " +#define OWL_MSGTAB 7 +#define OWL_TYPWIN_SIZE 8 +#define OWL_HISTORYSIZE 50 + +/* Indicate current state, as well as what is allowed */ +#define OWL_CTX_ANY 0xffff +/* Only one of these may be active at a time... */ +#define OWL_CTX_MODE_BITS 0x000f +#define OWL_CTX_STARTUP 0x0001 +#define OWL_CTX_READCONFIG 0x0002 +#define OWL_CTX_INTERACTIVE 0x0004 +/* Only one of these may be active at a time... */ +#define OWL_CTX_ACTIVE_BITS 0xfff0 +#define OWL_CTX_POPWIN 0x00f0 +#define OWL_CTX_POPLESS 0x0010 +#define OWL_CTX_RECWIN 0x0f00 +#define OWL_CTX_RECV 0x0100 +#define OWL_CTX_TYPWIN 0xf000 +#define OWL_CTX_EDIT 0x7000 +#define OWL_CTX_EDITLINE 0x1000 +#define OWL_CTX_EDITMULTI 0x2000 +#define OWL_CTX_EDITRESPONSE 0x4000 + +#define OWL_USERCLUE_NONE 0 +#define OWL_USERCLUE_CLASSES 1 +#define OWL_USERCLUE_FOOBAR 2 +#define OWL_USERCLUE_BAZ 4 + +#define OWL_WEBBROWSER_NONE 0 +#define OWL_WEBBROWSER_NETSCAPE 1 +#define OWL_WEBBROWSER_GALEON 2 +#define OWL_WEBBROWSER_OPERA 3 + +#define OWL_VARIABLE_OTHER 0 +#define OWL_VARIABLE_INT 1 +#define OWL_VARIABLE_BOOL 2 +#define OWL_VARIABLE_STRING 3 + +#define OWL_FILTER_MAX_DEPTH 300 + +#define OWL_KEYMAP_MAXSTACK 20 + +#define OWL_KEYBINDING_COMMAND 1 /* command string */ +#define OWL_KEYBINDING_FUNCTION 2 /* function taking no args */ + +#define OWL_DEFAULT_ZAWAYMSG "I'm sorry, but I am currently away from the terminal and am\nnot able to receive your message.\n" +#define OWL_DEFAULT_AAWAYMSG "I'm sorry, but I am currently away from the terminal and am\nnot able to receive your message.\n" + +#define OWL_INCLUDE_REG_TESTS 1 /* whether to build in regression tests */ + +#define OWL_CMD_ALIAS_SUMMARY_PREFIX "command alias to: " + +#define OWL_WEBZEPHYR_PRINCIPAL "daemon.webzephyr" +#define OWL_WEBZEPHYR_CLASS "webzephyr" +#define OWL_WEBZEPHYR_OPCODE "webzephyr" + +#define OWL_REGEX_QUOTECHARS "+*.?[]^\\${}()" +#define OWL_REGEX_QUOTEWITH "\\" + +#if defined(HAVE_DES_STRING_TO_KEY) && defined(HAVE_DES_KEY_SCHED) && defined(HAVE_DES_ECB_ENCRYPT) +#define OWL_ENABLE_ZCRYPT 1 +#endif + +#define OWL_META(key) ((key)|0200) +/* OWL_CTRL is definied in kepress.c */ + +#define LINE 2048 + +typedef struct _owl_variable { + char *name; + int type; /* OWL_VARIABLE_* */ + void *pval_default; /* for types other and string */ + int ival_default; /* for types int and bool */ + char *validsettings; /* documentation of valid settings */ + char *summary; /* summary of usage */ + char *description; /* detailed description */ + void *val; /* current value */ + int (*validate_fn)(struct _owl_variable *v, void *newval); + /* returns 1 if newval is valid */ + int (*set_fn)(struct _owl_variable *v, void *newval); + /* sets the variable to a value + * of the appropriate type. + * unless documented, this + * should make a copy. + * returns 0 on success. */ + int (*set_fromstring_fn)(struct _owl_variable *v, char *newval); + /* sets the variable to a value + * of the appropriate type. + * unless documented, this + * should make a copy. + * returns 0 on success. */ + void *(*get_fn)(struct _owl_variable *v); + /* returns a reference to the current value. + * WARNING: this approach is hard to make + * thread-safe... */ + int (*get_tostring_fn)(struct _owl_variable *v, + char *buf, int bufsize, void *val); + /* converts val to a string + * and puts into buf */ + void (*free_fn)(struct _owl_variable *v); + /* frees val as needed */ +} owl_variable; + +typedef struct _owl_fmtext { + int textlen; + char *textbuff; + char *fmbuff; + char *colorbuff; +} owl_fmtext; + +typedef struct _owl_list { + int size; + int avail; + void **list; +} owl_list; + +typedef struct _owl_dict_el { + char *k; /* key */ + void *v; /* value */ +} owl_dict_el; + +typedef struct _owl_dict { + int size; + int avail; + owl_dict_el *els; /* invariant: sorted by k */ +} owl_dict; +typedef owl_dict owl_vardict; /* dict of variables */ +typedef owl_dict owl_cmddict; /* dict of commands */ + +typedef struct _owl_context { + int mode; + void *data; /* determined by mode */ +} owl_context; + +typedef struct _owl_cmd { /* command */ + char *name; + + char *summary; /* one line summary of command */ + char *usage; /* usage synopsis */ + char *description; /* long description of command */ + + int validctx; /* bitmask of valid contexts */ + + /* we should probably have a type here that says which of + * the following is valid, and maybe make the below into a union... */ + + /* Only one of these may be non-NULL ... */ + + char *cmd_aliased_to; /* what this command is aliased to... */ + + /* These don't take any context */ + char *(*cmd_args_fn)(int argc, char **argv, char *buff); + /* takes argv and the full command as buff. + * caller must free return value if !NULL */ + void (*cmd_v_fn)(void); /* takes no args */ + void (*cmd_i_fn)(int i); /* takes an int as an arg */ + + /* The following also take the active context if it's valid */ + char *(*cmd_ctxargs_fn)(void *ctx, int argc, char **argv, char *buff); + /* takes argv and the full command as buff. + * caller must free return value if !NULL */ + void (*cmd_ctxv_fn)(void *ctx); /* takes no args */ + void (*cmd_ctxi_fn)(void *ctx, int i); /* takes an int as an arg */ +} owl_cmd; + + +typedef struct _owl_zwrite { + char *class; + char *inst; + char *realm; + char *opcode; + char *zsig; + char *message; + owl_list recips; + int cc; + int noping; +} owl_zwrite; + +typedef struct _owl_pair { + void *key; + void *value; +} owl_pair; + +typedef struct _owl_message { + int id; + int type; + int direction; +#ifdef HAVE_LIBZEPHYR + ZNotice_t notice; +#endif + owl_fmtext fmtext; /* this is now only a CACHED copy */ + int invalid_format; /* indicates whether fmtext needs to be regenerated */ + int delete; + char *hostname; + owl_list attributes; /* this is a list of pairs */ + char *timestr; + time_t time; + char *zwriteline; +} owl_message; + +typedef struct _owl_style { + char *name; + char *description; + int type; + char *perlfuncname; + void (*formatfunc) (owl_fmtext *fm, owl_message *m); +} owl_style; + +typedef struct _owl_mainwin { + int curtruncated; + int lasttruncated; + int lastdisplayed; +} owl_mainwin; + +typedef struct _owl_viewwin { + owl_fmtext fmtext; + int textlines; + int topline; + int rightshift; + int winlines, wincols; + WINDOW *curswin; + void (*onclose_hook) (struct _owl_viewwin *vwin, void *data); + void *onclose_hook_data; +} owl_viewwin; + +typedef struct _owl_popwin { + WINDOW *borderwin; + WINDOW *popwin; + int lines; + int cols; + int active; + int needsfirstrefresh; + void (*handler) (int ch); +} owl_popwin; + +typedef struct _owl_dispatch { + int fd; /* FD to watch for dispatch. */ + int needs_gc; + void (*cfunc)(struct _owl_dispatch*); /* C function to dispatch to. */ + void (*destroy)(struct _owl_dispatch*); /* Destructor */ + void *data; +} owl_dispatch; + +typedef struct _owl_popexec { + int refcount; + owl_viewwin *vwin; + int winactive; + int pid; /* or 0 if it has terminated */ + owl_dispatch dispatch; +} owl_popexec; + +typedef struct _owl_messagelist { + owl_list list; +} owl_messagelist; + +typedef struct _owl_regex { + int negate; + char *string; + regex_t re; +} owl_regex; + +typedef struct _owl_filterelement { + int type; + char *field; + owl_regex re; + char *filtername; /* for maching on another filter */ +} owl_filterelement; + +typedef struct _owl_filter { + char *name; + int polarity; + owl_list fes; /* filterelements */ + int color; + int cachedmsgid; /* cached msgid: should move into view eventually */ +} owl_filter; + +typedef struct _owl_view { + char *name; + owl_filter *filter; + owl_messagelist ml; + owl_style *style; +} owl_view; + +typedef struct _owl_history { + owl_list hist; + int cur; + int touched; + int partial; + int repeats; +} owl_history; + +typedef struct _owl_editwin { + char *buff; + owl_history *hist; + int bufflen; + int allocated; + int buffx, buffy; + int topline; + int winlines, wincols, fillcol, wrapcol; + WINDOW *curswin; + int style; + int lock; + int dotsend; + int echochar; +} owl_editwin; + +typedef struct _owl_keybinding { + int *j; /* keypress stack (0-terminated) */ + int type; /* command or function? */ + char *desc; /* description (or "*user*") */ + char *command; /* command, if of type command */ + void (*function_fn)(void); /* function ptr, if of type function */ +} owl_keybinding; + +typedef struct _owl_keymap { + char *name; /* name of keymap */ + char *desc; /* description */ + owl_list bindings; /* key bindings */ + struct _owl_keymap *submap; /* submap */ + void (*default_fn)(int j); /* default action (takes a keypress) */ + void (*prealways_fn)(int j); /* always called before a keypress is received */ + void (*postalways_fn)(int j); /* always called after keypress is processed */ +} owl_keymap; + +typedef struct _owl_keyhandler { + owl_dict keymaps; /* dictionary of keymaps */ + owl_keymap *active; /* currently active keymap */ + int in_esc; /* escape pressed? */ + int kpstack[OWL_KEYMAP_MAXSTACK+1]; /* current stack of keypresses */ + int kpstackpos; /* location in stack (-1 = none) */ +} owl_keyhandler; + +typedef struct _owl_buddy { + int proto; + char *name; + int isidle; + int idlesince; +} owl_buddy; + +typedef struct _owl_buddylist { + owl_list buddies; +} owl_buddylist; + +typedef struct _owl_zbuddylist { + owl_list zusers; +} owl_zbuddylist; + +typedef struct _owl_timer { + int direction; + time_t starttime; + int start; +} owl_timer; + +typedef struct _owl_errqueue { + owl_list errlist; +} owl_errqueue; + +typedef struct _owl_global { + owl_mainwin mw; + owl_popwin pw; + owl_history cmdhist; /* command history */ + owl_history msghist; /* outgoing message history */ + owl_keyhandler kh; + owl_list filterlist; + owl_list puntlist; + owl_vardict vars; + owl_cmddict cmds; + owl_context ctx; + owl_errqueue errqueue; + int lines, cols; + int curmsg, topmsg; + int curmsg_vert_offset; + owl_view current_view; + owl_messagelist msglist; + WINDOW *recwin, *sepwin, *msgwin, *typwin; + int needrefresh; + int rightshift; + int resizepending; + int recwinlines; + int typwinactive; + char *thishost; + char *homedir; + int direction; + int zaway; + char *cur_zaway_msg; + int haveconfig; + int config_format; + char *buffercommand; + owl_editwin tw; + owl_viewwin vw; + void *perl; + int debug; + int starttime; + char *startupargs; + int userclue; + int nextmsgid; + int hascolors; + int colorpairs; + int searchactive; + int newmsgproc_pid; + int malloced, freed; + char *searchstring; + owl_filterelement fe_true; + owl_filterelement fe_false; + owl_filterelement fe_null; + aim_session_t aimsess; + aim_conn_t bosconn; + owl_timer aim_noop_timer; + owl_timer aim_ignorelogin_timer; + owl_timer aim_buddyinfo_timer; + int aim_loggedin; /* true if currently logged into AIM */ + int aim_doprocessing; /* true if we should process AIM events (like pending login) */ + char *aim_screenname; /* currently logged in AIM screen name */ + owl_buddylist buddylist; /* list of logged in AIM buddies */ + owl_list messagequeue; /* for queueing up aim and other messages */ + owl_dict styledict; /* global dictionary of available styles */ + char *response; /* response to the last question asked */ + int havezephyr; + int haveaim; + int got_err_signal; /* 1 if we got an unexpected signal */ + siginfo_t err_signal_info; + owl_zbuddylist zbuddies; + owl_timer zephyr_buddycheck_timer; + struct termios startup_tio; + owl_list dispatchlist; +} owl_global; + +/* globals */ +extern owl_global g; + +#include "owl_prototypes.h" + +/* these are missing from the zephyr includes for some reason */ +#ifdef HAVE_LIBZEPHYR +int ZGetSubscriptions(ZSubscription_t *, int *); +int ZGetLocations(ZLocations_t *,int *); +#endif + +#endif /* INC_OWL_H */ diff --git a/owl_prototypes.h b/owl_prototypes.h new file mode 100644 index 0000000..2c26c06 --- /dev/null +++ b/owl_prototypes.h @@ -0,0 +1,1409 @@ +/* -------------------------------- aim.c -------------------------------- */ +extern void owl_aim_init(void); +extern int owl_aim_login(char *screenname, char *password); +extern void owl_aim_successful_login(char *screenname); +extern void owl_aim_logout(void); +extern void owl_aim_logged_out(); +extern void owl_aim_login_error(char *message); +extern int owl_aim_send_im(char *to, char *msg); +extern int owl_aim_send_awaymsg(char *to, char *msg); +extern void owl_aim_addbuddy(char *name); +extern void owl_aim_delbuddy(char *name); +extern void owl_aim_search(char *email); +extern int owl_aim_set_awaymsg(char *msg); +extern void owl_aim_chat_join(char *name, int exchange); +extern void owl_aim_chat_leave(char *chatroom); +extern int owl_aim_chat_sendmsg(char *chatroom, char *msg); +extern char *owl_aim_normalize_screenname(char *in); +extern int owl_aim_process_events(); +extern int faimtest_flapversion(aim_session_t *sess, aim_frame_t *fr, ...); +#if 0 +#endif +extern int faimtest_conncomplete(aim_session_t *sess, aim_frame_t *fr, ...); +extern void addcb_bos(aim_session_t *sess, aim_conn_t *bosconn); +extern int logout(aim_session_t *sess); +#if 0 +#endif +extern int faimtest_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...); +extern void chatnav_redirect(aim_session_t *sess, struct aim_redirect_data *redir); +extern void chat_redirect(aim_session_t *sess, struct aim_redirect_data *redir); +extern void owl_process_aim(); +#if 0 +#endif + +/* -------------------------------- buddy.c -------------------------------- */ +extern void owl_buddy_create(owl_buddy *b, int proto, char *name); +extern char *owl_buddy_get_name(owl_buddy *b); +extern int owl_buddy_is_idle(owl_buddy *b); +extern void owl_buddy_set_idle(owl_buddy *b); +extern void owl_buddy_set_unidle(owl_buddy *b); +extern int owl_buddy_get_proto(owl_buddy *b); +extern int owl_buddy_is_proto_aim(owl_buddy *b); +extern void owl_buddy_set_idle_since(owl_buddy *b, int diff); +extern int owl_buddy_get_idle_time(owl_buddy *b); +extern void owl_buddy_free(owl_buddy *b); + +/* -------------------------------- buddylist.c -------------------------------- */ +extern void owl_buddylist_init(owl_buddylist *bl); +extern void owl_buddylist_add_aim_buddy(owl_buddylist *bl, char *screenname); +extern int owl_buddylist_remove_aim_buddy(owl_buddylist *bl, char *name); +extern void owl_buddylist_oncoming(owl_buddylist *bl, char *screenname); +extern void owl_buddylist_offgoing(owl_buddylist *bl, char *screenname); +extern int owl_buddylist_get_size(owl_buddylist *bl); +extern owl_buddy *owl_buddylist_get_buddy_n(owl_buddylist *bl, int index); +extern owl_buddy *owl_buddylist_get_aim_buddy(owl_buddylist *bl, char *name); +extern int owl_buddylist_is_aim_buddy_loggedin(owl_buddylist *bl, char *screenname); +extern void owl_buddylist_clear(owl_buddylist *bl); +extern void owl_buddylist_free(owl_buddylist *bl); + +/* -------------------------------- cmd.c -------------------------------- */ +extern int owl_cmddict_setup(owl_cmddict *cd); +extern int owl_cmddict_init(owl_cmddict *cd); +extern int owl_cmddict_add_from_list(owl_cmddict *cd, owl_cmd *cmds); +extern void owl_cmddict_get_names(owl_cmddict *d, owl_list *l); +extern owl_cmd *owl_cmddict_find(owl_cmddict *d, char *name); +extern void owl_cmddict_namelist_free(owl_list *l); +extern int owl_cmddict_add_alias(owl_cmddict *cd, char *alias_from, char *alias_to); +extern char *owl_cmddict_execute(owl_cmddict *cd, owl_context *ctx, char *cmdbuff); +extern int owl_cmd_create_from_template(owl_cmd *cmd, owl_cmd *templ); +extern int owl_cmd_create_alias(owl_cmd *cmd, char *name, char *aliased_to); +extern void owl_cmd_free(owl_cmd *cmd); +extern int owl_cmd_is_context_valid(owl_cmd *cmd, owl_context *ctx); +extern char *owl_cmd_execute(owl_cmd *cmd, owl_cmddict *cd, owl_context *ctx, int argc, char **argv, char *cmdbuff); +extern char *owl_cmd_get_summary(owl_cmd *cmd); +extern char *owl_cmd_describe(owl_cmd *cmd); +extern void owl_cmd_get_help(owl_cmddict *d, char *name, owl_fmtext *fm); + +/* -------------------------------- commands.c -------------------------------- */ +extern void owl_command_info(); +extern void owl_command_nop(); +extern char *owl_command_help(int argc, char **argv, char *buff); +extern char *owl_command_zlist(int argc, char **argv, char *buff); +extern char *owl_command_alist(); +extern char *owl_command_blist(); +extern char *owl_command_toggleoneline(); +extern void owl_command_about(); +extern void owl_command_version(); +extern char *owl_command_aim(int argc, char **argv, char *buff); +extern char *owl_command_addbuddy(int argc, char **argv, char *buff); +extern char *owl_command_delbuddy(int argc, char **argv, char *buff); +extern char *owl_command_join(int argc, char **argv, char *buff); +extern char *owl_command_startup(int argc, char **argv, char *buff); +extern char *owl_command_unstartup(int argc, char **argv, char *buff); +extern char *owl_command_dump(int argc, char **argv, char *buff); +extern char *owl_command_source(int argc, char **argv, char *buff); +extern char *owl_command_next(int argc, char **argv, char *buff); +extern char *owl_command_prev(int argc, char **argv, char *buff); +extern char *owl_command_smartnarrow(int argc, char **argv, char *buff); +extern char *owl_command_smartfilter(int argc, char **argv, char *buff); +extern void owl_command_expunge(); +extern void owl_command_first(); +extern void owl_command_last(); +extern void owl_command_resize(); +extern void owl_command_redisplay(); +extern void owl_command_shift_right(); +extern void owl_command_shift_left(); +extern void owl_command_unsuball(); +extern char *owl_command_loadsubs(int argc, char **argv, char *buff); +extern char *owl_command_loadloginsubs(int argc, char **argv, char *buff); +extern void owl_command_suspend(); +extern char *owl_command_start_command(int argc, char **argv, char *buff); +extern char *owl_command_start_question(int argc, char **argv, char *buff); +extern char *owl_command_start_password(int argc, char **argv, char *buff); +extern char *owl_command_zaway(int argc, char **argv, char *buff); +extern char *owl_command_aaway(int argc, char **argv, char *buff); +extern char *owl_command_away(int argc, char **argv, char *buff); +extern char *owl_command_set(int argc, char **argv, char *buff); +extern char *owl_command_unset(int argc, char **argv, char *buff); +extern char *owl_command_print(int argc, char **argv, char *buff); +extern char *owl_command_exec(int argc, char **argv, char *buff); +extern char *owl_command_pexec(int argc, char **argv, char *buff); +extern char *owl_command_aexec(int argc, char **argv, char *buff); +extern char *owl_command_perl(int argc, char **argv, char *buff); +extern char *owl_command_pperl(int argc, char **argv, char *buff); +extern char *owl_command_aperl(int argc, char **argv, char *buff); +extern char *owl_command_multi(int argc, char **argv, char *buff); +extern char *owl_command_alias(int argc, char **argv, char *buff); +extern char *owl_command_bindkey(int argc, char **argv, char *buff); +extern char *owl_command_style(int argc, char **argv, char *buff); +extern void owl_command_quit(); +extern char *owl_command_debug(int argc, char **argv, char *buff); +extern char *owl_command_term(int argc, char **argv, char *buff); +extern char *owl_command_zlog(int argc, char **argv, char *buff); +extern void owl_command_zlog_out(void); +extern char *owl_command_subscribe(int argc, char **argv, char *buff); +extern char *owl_command_unsubscribe(int argc, char **argv, char *buff); +extern char *owl_command_echo(int argc, char **argv, char *buff); +extern void owl_command_getsubs(void); +extern void owl_command_status(void); +extern char *owl_command_zwrite(int argc, char **argv, char *buff); +extern char *owl_command_aimwrite(int argc, char **argv, char *buff); +extern char *owl_command_loopwrite(int argc, char **argv, char *buff); +extern char *owl_command_zcrypt(int argc, char **argv, char *buff); +#ifdef OWL_ENABLE_ZCRYPT +#else +#endif +extern char *owl_command_reply(int argc, char **argv, char *buff); +extern char *owl_command_filter(int argc, char **argv, char *buff); +extern char *owl_command_zlocate(int argc, char **argv, char *buff); +extern char *owl_command_view(int argc, char **argv, char *buff); +extern char *owl_command_show(int argc, char **argv, char *buff); +extern char *owl_command_viewclass(int argc, char **argv, char *buff); +extern char *owl_command_viewuser(int argc, char **argv, char *buff); +extern void owl_command_pop_message(void); +extern char *owl_command_delete(int argc, char **argv, char *buff); +extern char *owl_command_undelete(int argc, char **argv, char *buff); +extern void owl_command_beep(); +extern char *owl_command_colorview(int argc, char **argv, char *buff); +extern char *owl_command_colorclass(int argc, char **argv, char *buff); +extern char *owl_command_zpunt(int argc, char **argv, char *buff); +extern char *owl_command_zunpunt(int argc, char **argv, char *buff); +extern void owl_command_zpunt_and_zunpunt(int argc, char **argv, int type); +extern char *owl_command_smartzpunt(int argc, char **argv, char *buff); +extern char *owl_command_getview(int argc, char **argv, char *buff); +extern char *owl_command_getvar(int argc, char **argv, char *buff); +extern char *owl_command_search(int argc, char **argv, char *buff); +extern char *owl_command_aimlogin(int argc, char **argv, char *buff); +extern char *owl_command_aimlogout(int argc, char **argv, char *buff); +extern char *owl_command_getstyle(int argc, char **argv, char *buff); +extern void owl_command_edit_cancel(owl_editwin *e); +extern void owl_command_edit_history_prev(owl_editwin *e); +extern void owl_command_edit_history_next(owl_editwin *e); +extern char *owl_command_edit_insert_text(owl_editwin *e, int argc, char **argv, char *buff); +extern void owl_command_editline_done(owl_editwin *e); +extern void owl_command_editresponse_done(owl_editwin *e); +extern void owl_command_editmulti_done(owl_editwin *e); +extern void owl_command_editmulti_done_or_delete(owl_editwin *e); +extern void owl_command_popless_quit(owl_viewwin *vw); + +/* -------------------------------- context.c -------------------------------- */ +extern int owl_context_init(owl_context *ctx); +extern int owl_context_matches(owl_context *ctx, int test); +extern void *owl_context_get_data(owl_context *ctx); +extern int owl_context_get_mode(owl_context *ctx); +extern int owl_context_get_active(owl_context *ctx); +extern int owl_context_is_startup(owl_context *ctx); +extern int owl_context_is_interactive(owl_context *ctx); +extern void owl_context_set_startup(owl_context *ctx); +extern void owl_context_set_readconfig(owl_context *ctx); +extern void owl_context_set_interactive(owl_context *ctx); +extern void owl_context_set_popless(owl_context *ctx, owl_viewwin *vw); +extern void owl_context_set_recv(owl_context *ctx); +extern void owl_context_set_editmulti(owl_context *ctx, owl_editwin *ew); +extern void owl_context_set_editline(owl_context *ctx, owl_editwin *ew); +extern void owl_context_set_editresponse(owl_context *ctx, owl_editwin *ew); + +/* -------------------------------- dict.c -------------------------------- */ +extern int owl_dict_create(owl_dict *d); +extern int owl_dict_get_size(owl_dict *d); +extern int _owl_dict_find_pos(owl_dict *d, char *k, int *pos); +extern void *owl_dict_find_element(owl_dict *d, char *k); +extern int owl_dict_get_keys(owl_dict *d, owl_list *l); +extern void owl_dict_noop_free(void *x); +extern int owl_dict_insert_element(owl_dict *d, char *k, void *v, void (*free_on_replace)(void *old)); +extern void *owl_dict_remove_element(owl_dict *d, char *k); +extern void owl_dict_free_all(owl_dict *d, void (*elefree)(void *)); +extern void owl_dict_free_simple(owl_dict *d); +#ifdef OWL_INCLUDE_REG_TESTS +extern int owl_dict_regtest(void); +#endif /* OWL_INCLUDE_REG_TESTS */ + +/* -------------------------------- editwin.c -------------------------------- */ +extern void owl_editwin_init(owl_editwin *e, WINDOW *win, int winlines, int wincols, int style, owl_history *hist); +extern void owl_editwin_set_curswin(owl_editwin *e, WINDOW *w, int winlines, int wincols); +extern void owl_editwin_set_echochar(owl_editwin *e, int ch); +extern WINDOW *owl_editwin_get_curswin(owl_editwin *e); +extern void owl_editwin_set_history(owl_editwin *e, owl_history *h); +extern owl_history *owl_editwin_get_history(owl_editwin *e); +extern void owl_editwin_set_dotsend(owl_editwin *e); +extern int owl_editwin_limit_maxcols(int v, int maxv); +extern void owl_editwin_set_locktext(owl_editwin *e, char *text); +extern int owl_editwin_get_style(owl_editwin *e); +extern void owl_editwin_new_style(owl_editwin *e, int newstyle, owl_history *h); +extern void owl_editwin_fullclear(owl_editwin *e); +extern void owl_editwin_clear(owl_editwin *e); +extern void _owl_editwin_addspace(owl_editwin *e); +extern void owl_editwin_recenter(owl_editwin *e); +extern void owl_editwin_redisplay(owl_editwin *e, int update); +extern int _owl_editwin_linewrap_word(owl_editwin *e); +extern void owl_editwin_insert_char(owl_editwin *e, char c); +extern void owl_editwin_overwrite_char(owl_editwin *e, char c); +extern void owl_editwin_delete_char(owl_editwin *e); +extern void owl_editwin_transpose_chars(owl_editwin *e); +extern void owl_editwin_insert_string(owl_editwin *e, char *string); +extern void owl_editwin_overwrite_string(owl_editwin *e, char *string); +extern int _owl_editwin_get_index_from_xy(owl_editwin *e); +extern void _owl_editwin_set_xy_by_index(owl_editwin *e, int index); +extern void owl_editwin_adjust_for_locktext(owl_editwin *e); +extern void owl_editwin_backspace(owl_editwin *e); +extern void owl_editwin_key_up(owl_editwin *e); +extern void owl_editwin_key_down(owl_editwin *e); +extern void owl_editwin_key_left(owl_editwin *e); +extern void owl_editwin_key_right(owl_editwin *e); +extern void owl_editwin_move_to_nextword(owl_editwin *e); +extern void owl_editwin_move_to_previousword(owl_editwin *e); +extern void owl_editwin_delete_nextword(owl_editwin *e); +extern void owl_editwin_delete_previousword(owl_editwin *e); +extern void owl_editwin_delete_to_endofline(owl_editwin *e); +extern void owl_editwin_move_to_line_end(owl_editwin *e); +extern void owl_editwin_move_to_line_start(owl_editwin *e); +extern void owl_editwin_move_to_end(owl_editwin *e); +extern void owl_editwin_move_to_top(owl_editwin *e); +extern void owl_editwin_fill_paragraph(owl_editwin *e); +extern int owl_editwin_is_at_end(owl_editwin *e); +extern int owl_editwin_check_dotsend(owl_editwin *e); +extern void owl_editwin_post_process_char(owl_editwin *e, int j); +extern void owl_editwin_process_char(owl_editwin *e, int j); +extern char *owl_editwin_get_text(owl_editwin *e); +extern int owl_editwin_get_numchars_on_line(owl_editwin *e, int line); +extern int owl_editwin_get_numlines(owl_editwin *e); + +/* -------------------------------- errqueue.c -------------------------------- */ +extern void owl_errqueue_init(owl_errqueue *eq); +extern void owl_errqueue_append_err(owl_errqueue *eq, char *msg); +extern void owl_errqueue_to_fmtext(owl_errqueue *eq, owl_fmtext *fm); + +/* -------------------------------- filter.c -------------------------------- */ +extern int owl_filter_init_fromstring(owl_filter *f, char *name, char *string); +extern int owl_filter_init(owl_filter *f, char *name, int argc, char **argv); +extern char *owl_filter_get_name(owl_filter *f); +extern void owl_filter_set_polarity_match(owl_filter *f); +extern void owl_filter_set_polarity_unmatch(owl_filter *f); +extern void owl_filter_set_color(owl_filter *f, int color); +extern int owl_filter_get_color(owl_filter *f); +extern void owl_filter_set_cachedmsgid(owl_filter *f, int cachedmsgid); +extern int owl_filter_get_cachedmsgid(owl_filter *f); +extern int owl_filter_message_match(owl_filter *f, owl_message *m); +extern int _owl_filter_message_match_recurse(owl_filter *f, owl_message *m, owl_list *fes, int start, int end); +extern char *owl_filter_print(owl_filter *f); +extern int owl_filter_equiv(owl_filter *a, owl_filter *b); +extern void _owl_filter_get_subfilter_names(owl_filter *f, owl_list *list); +extern int owl_filter_is_toodeep(owl_filter *f); +extern void owl_filter_free(owl_filter *f); + +/* -------------------------------- filterelement.c -------------------------------- */ +extern void owl_filterelement_create_null(owl_filterelement *fe); +extern void owl_filterelement_create_openbrace(owl_filterelement *fe); +extern void owl_filterelement_create_closebrace(owl_filterelement *fe); +extern void owl_filterelement_create_and(owl_filterelement *fe); +extern void owl_filterelement_create_or(owl_filterelement *fe); +extern void owl_filterelement_create_not(owl_filterelement *fe); +extern void owl_filterelement_create_true(owl_filterelement *fe); +extern void owl_filterelement_create_false(owl_filterelement *fe); +extern void owl_filterelement_create_re(owl_filterelement *fe, char *field, char *re); +extern void owl_filterelement_create_filter(owl_filterelement *fe, char *name); +extern void owl_filterelement_create_perl(owl_filterelement *fe, char *name); +extern void owl_filterelement_free(owl_filterelement *fe); +extern int owl_filterelement_is_null(owl_filterelement *fe); +extern int owl_filterelement_is_openbrace(owl_filterelement *fe); +extern int owl_filterelement_is_closebrace(owl_filterelement *fe); +extern int owl_filterelement_is_and(owl_filterelement *fe); +extern int owl_filterelement_is_or(owl_filterelement *fe); +extern int owl_filterelement_is_not(owl_filterelement *fe); +extern int owl_filterelement_is_true(owl_filterelement *fe); +extern int owl_filterelement_is_false(owl_filterelement *fe); +extern int owl_filterelement_is_re(owl_filterelement *fe); +extern int owl_filterelement_is_perl(owl_filterelement *fe); +extern owl_regex *owl_filterelement_get_re(owl_filterelement *fe); +extern int owl_filterelement_is_filter(owl_filterelement *fe); +extern char *owl_filterelement_get_field(owl_filterelement *fe); +extern char *owl_filterelement_get_filtername(owl_filterelement *fe); +extern int owl_filterelement_is_value(owl_filterelement *fe); +extern char *owl_filterelement_to_string(owl_filterelement *fe); + +/* -------------------------------- fmtext.c -------------------------------- */ +extern void owl_fmtext_init_null(owl_fmtext *f); +extern void _owl_fmtext_set_attr(owl_fmtext *f, int attr, int first, int last); +extern void _owl_fmtext_add_attr(owl_fmtext *f, int attr, int first, int last); +extern void _owl_fmtext_set_color(owl_fmtext *f, int color, int first, int last); +extern void owl_fmtext_append_attr(owl_fmtext *f, char *text, int attr, int color); +extern void owl_fmtext_append_normal(owl_fmtext *f, char *text); +extern void owl_fmtext_append_normal_color(owl_fmtext *f, char *text, int color); +extern void owl_fmtext_append_bold(owl_fmtext *f, char *text); +extern void owl_fmtext_append_reverse(owl_fmtext *f, char *text); +extern void owl_fmtext_append_reversebold(owl_fmtext *f, char *text); +extern void owl_fmtext_addattr(owl_fmtext *f, int attr); +extern void owl_fmtext_colorize(owl_fmtext *f, int color); +extern void _owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in, int start, int stop); +extern void owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in); +extern void owl_fmtext_append_spaces(owl_fmtext *f, int nspaces); +extern char *owl_fmtext_print_plain(owl_fmtext *f); +extern void owl_fmtext_curs_waddstr(owl_fmtext *f, WINDOW *w); +extern int owl_fmtext_truncate_lines(owl_fmtext *in, int aline, int lines, owl_fmtext *out); +extern void owl_fmtext_truncate_cols(owl_fmtext *in, int acol, int bcol, owl_fmtext *out); +extern int owl_fmtext_num_lines(owl_fmtext *f); +extern char *owl_fmtext_get_text(owl_fmtext *f); +extern void owl_fmtext_set_char(owl_fmtext *f, int index, int ch); +extern void owl_fmtext_copy(owl_fmtext *dst, owl_fmtext *src); +extern int owl_fmtext_search_and_highlight(owl_fmtext *f, char *string); +extern int owl_fmtext_search(owl_fmtext *f, char *string); +extern void owl_fmtext_append_ztext(owl_fmtext *f, char *text); +extern void owl_fmtext_append_list(owl_fmtext *f, owl_list *l, char *join_with, char *(format_fn)(void*)); +extern void owl_fmtext_free(owl_fmtext *f); + +/* -------------------------------- functions.c -------------------------------- */ +extern void owl_function_noop(void); +extern char *owl_function_command(char *cmdbuff); +extern void owl_function_command_norv(char *cmdbuff); +extern void owl_function_command_alias(char *alias_from, char *alias_to); +extern owl_cmd *owl_function_get_cmd(char *name); +extern void owl_function_show_commands(); +extern void owl_function_show_view(char *viewname); +extern void owl_function_show_styles(); +extern char *owl_function_style_describe(void *name); +extern char *owl_function_cmd_describe(void *name); +extern void owl_function_show_command(char *name); +extern void owl_function_show_license(); +extern int owl_function_add_message(owl_message *m); +extern void owl_function_adminmsg(char *header, char *body); +extern owl_message *owl_function_make_outgoing_zephyr(char *body, char *zwriteline, char *zsig); +extern owl_message *owl_function_make_outgoing_aim(char *body, char *to); +extern owl_message *owl_function_make_outgoing_loopback(char *body); +extern void owl_function_zwrite_setup(char *line); +extern void owl_function_aimwrite_setup(char *line); +extern void owl_function_loopwrite_setup(); +extern void owl_function_zwrite(char *line, char *msg); +extern void owl_function_zcrypt(char *line, char *msg); +#ifdef OWL_ENABLE_ZCRYPT +#endif +#ifdef OWL_ENABLE_ZCRYPT +#else +#endif +extern void owl_function_aimwrite(char *to); +extern void owl_function_send_aimawymsg(char *to, char *msg); +extern void owl_function_loopwrite(); +extern void owl_function_nextmsg_full(char *filter, int skip_deleted, int last_if_none); +extern void owl_function_prevmsg_full(char *filter, int skip_deleted, int first_if_none); +extern void owl_function_nextmsg(); +extern void owl_function_prevmsg(); +extern void owl_function_nextmsg_notdeleted(); +extern void owl_function_prevmsg_notdeleted(); +extern void owl_function_nextmsg_personal(); +extern void owl_function_prevmsg_personal(); +extern void owl_function_deletecur(int move_after); +extern void owl_function_undeletecur(int move_after); +extern void owl_function_expunge(); +extern void owl_function_firstmsg(); +extern void owl_function_lastmsg_noredisplay(); +extern void owl_function_lastmsg(); +extern void owl_function_shift_right(); +extern void owl_function_shift_left(); +extern void owl_function_unsuball(); +extern void owl_function_loadsubs(char *file); +extern void owl_function_loadloginsubs(char *file); +extern void owl_function_suspend(); +extern void owl_function_zaway_toggle(); +extern void owl_function_zaway_on(); +extern void owl_function_zaway_off(); +extern void owl_function_aaway_toggle(); +extern void owl_function_aaway_on(); +extern void owl_function_aaway_off(); +extern void owl_function_quit(); +extern void owl_function_calculate_topmsg(int direction); +extern int owl_function_calculate_topmsg_top(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines); +extern int owl_function_calculate_topmsg_neartop(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines); +extern int owl_function_calculate_topmsg_center(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines); +extern int owl_function_calculate_topmsg_paged(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page); +extern int owl_function_calculate_topmsg_normal(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines); +extern void owl_function_resize(); +extern void owl_function_run_buffercommand(); +extern void owl_function_debugmsg(char *fmt, ...); +extern void owl_function_refresh(); +extern void owl_function_beep(); +extern void owl_function_subscribe(char *class, char *inst, char *recip); +extern void owl_function_unsubscribe(char *class, char *inst, char *recip); +extern void owl_function_set_cursor(WINDOW *win); +extern void owl_function_full_redisplay(); +extern void owl_function_popless_text(char *text); +extern void owl_function_popless_fmtext(owl_fmtext *fm); +extern void owl_function_popless_file(char *filename); +extern void owl_function_about(); +extern void owl_function_info(); +#ifdef HAVE_LIBZEPHYR +#endif +#ifdef HAVE_LIBZEPHYR +#endif +extern void owl_function_curmsg_to_popwin(); +extern void owl_function_page_curmsg(int step); +extern void owl_function_resize_typwin(int newsize); +extern void owl_function_typwin_grow(); +extern void owl_function_typwin_shrink(); +extern void owl_function_mainwin_pagedown(); +extern void owl_function_mainwin_pageup(); +extern void owl_function_getsubs(); +extern void owl_function_printallvars(); +extern void owl_function_show_variables(); +extern void owl_function_show_variable(char *name); +extern void owl_function_delete_by_id(int id, int flag); +extern void owl_function_delete_automsgs(); +extern void owl_function_status(); +#if OWL_STDERR_REDIR +#else +#endif +extern void owl_function_show_term(); +extern void owl_function_reply(int type, int enter); +extern void owl_function_zlocate(int argc, char **argv, int auth); +extern void owl_function_start_command(char *line); +extern void owl_function_start_question(char *line); +extern void owl_function_start_password(char *line); +extern char *owl_function_exec(int argc, char **argv, char *buff, int type); +#if OWL_STDERR_REDIR +#endif +extern char *owl_function_perl(int argc, char **argv, char *buff, int type); +extern void owl_function_change_currentview_filter(char *filtname); +extern void owl_function_create_filter(int argc, char **argv); +extern char *owl_function_create_negative_filter(char *filtername); +extern void owl_function_show_filters(); +extern void owl_function_show_filter(char *name); +extern void owl_function_show_zpunts(); +extern char *owl_function_classinstfilt(char *c, char *i); +extern char *owl_function_zuserfilt(char *user); +extern char *owl_function_aimuserfilt(char *user); +extern char *owl_function_typefilt(char *type); +extern void owl_function_delete_curview_msgs(int flag); +extern char *owl_function_smartfilter(int type); +extern void owl_function_smartzpunt(int type); +extern void owl_function_color_current_filter(char *color); +extern int owl_function_color_filter(char *filtname, char *color); +extern void owl_function_show_colors(); +extern void owl_function_zpunt(char *class, char *inst, char *recip, int direction); +extern void owl_function_punt(char *filter, int direction); +extern void owl_function_activate_keymap(char *keymap); +extern void owl_function_show_keymaps(); +extern char *owl_function_keymap_summary(void *name); +extern void owl_function_show_keymap(char *name); +extern void owl_function_help_for_command(char *cmdname); +extern void owl_function_search_start(char *string, int direction); +extern void owl_function_search_continue(int direction); +extern void owl_function_search_helper(int mode, int direction); +extern char *owl_function_ztext_stylestrip(char *zt); +extern void owl_function_buddylist(int aim, int zephyr, char *filename); +#ifdef HAVE_LIBZEPHYR +#endif +#ifdef HAVE_LIBZEPHYR +#endif +extern void owl_function_dump(char *filename); +extern void owl_function_do_newmsgproc(void); +extern void owl_function_xterm_raise(void); +extern void owl_function_xterm_deiconify(void); +extern void owl_function_addstartup(char *buff); +extern void owl_function_delstartup(char *buff); +extern void owl_function_source(char *filename); +extern void owl_function_change_style(owl_view *v, char *stylename); +extern void owl_function_toggleoneline(); +extern void owl_function_error(char *fmt, ...); +extern void owl_function_showerrs(); +extern void owl_function_makemsg(char *fmt, ...); +extern void owl_function_zephyr_buddy_check(int notify); +#ifdef HAVE_LIBZEPHYR +#endif +extern void owl_function_aimsearch_results(char *email, owl_list *namelist); + +/* -------------------------------- global.c -------------------------------- */ +#ifndef MAXHOSTNAMELEN +#endif +extern void owl_global_init(owl_global *g); +extern void _owl_global_setup_windows(owl_global *g); +extern owl_context *owl_global_get_context(owl_global *g); +extern int owl_global_get_lines(owl_global *g); +extern int owl_global_get_cols(owl_global *g); +extern int owl_global_get_recwin_lines(owl_global *g); +extern int owl_global_get_curmsg(owl_global *g); +extern void owl_global_set_curmsg(owl_global *g, int i); +extern int owl_global_get_topmsg(owl_global *g); +extern void owl_global_set_topmsg(owl_global *g, int i); +extern owl_mainwin *owl_global_get_mainwin(owl_global *g); +extern owl_popwin *owl_global_get_popwin(owl_global *g); +extern owl_messagelist *owl_global_get_msglist(owl_global *g); +extern owl_keyhandler *owl_global_get_keyhandler(owl_global *g); +extern WINDOW *owl_global_get_curs_recwin(owl_global *g); +extern WINDOW *owl_global_get_curs_sepwin(owl_global *g); +extern WINDOW *owl_global_get_curs_msgwin(owl_global *g); +extern WINDOW *owl_global_get_curs_typwin(owl_global *g); +extern owl_editwin *owl_global_get_typwin(owl_global *g); +extern void owl_global_set_buffercommand(owl_global *g, char *command); +extern char *owl_global_get_buffercommand(owl_global *g); +extern int owl_global_is_needrefresh(owl_global *g); +extern void owl_global_set_needrefresh(owl_global *g); +extern void owl_global_set_noneedrefresh(owl_global *g); +extern owl_vardict *owl_global_get_vardict(owl_global *g); +extern owl_cmddict *owl_global_get_cmddict(owl_global *g); +extern void owl_global_set_rightshift(owl_global *g, int i); +extern int owl_global_get_rightshift(owl_global *g); +extern int owl_global_is_typwin_active(owl_global *g); +extern void owl_global_set_typwin_active(owl_global *g); +extern void owl_global_set_typwin_inactive(owl_global *g); +extern void owl_global_set_resize_pending(owl_global *g); +extern char *owl_global_get_homedir(owl_global *g); +extern int owl_global_get_direction(owl_global *g); +extern void owl_global_set_direction_downwards(owl_global *g); +extern void owl_global_set_direction_upwards(owl_global *g); +extern void owl_global_set_perlinterp(owl_global *g, void *p); +extern void *owl_global_get_perlinterp(owl_global *g); +extern int owl_global_is_config_format(owl_global *g); +extern void owl_global_set_config_format(owl_global *g, int state); +extern void owl_global_set_have_config(owl_global *g); +extern void owl_global_set_no_have_config(owl_global *g); +extern int owl_global_have_config(owl_global *g); +extern void owl_global_resize(owl_global *g, int x, int y); +extern int owl_global_is_debug_fast(owl_global *g); +extern time_t owl_global_get_starttime(owl_global *g); +extern time_t owl_global_get_runtime(owl_global *g); +extern char *owl_global_get_runtime_string(owl_global *g); +extern char *owl_global_get_hostname(owl_global *g); +extern void owl_global_set_userclue(owl_global *g, int clue); +extern void owl_global_add_userclue(owl_global *g, int clue); +extern int owl_global_get_userclue(owl_global *g); +extern int owl_global_is_userclue(owl_global *g, int clue); +extern owl_viewwin *owl_global_get_viewwin(owl_global *g); +extern int owl_global_get_curmsg_vert_offset(owl_global *g); +extern void owl_global_set_curmsg_vert_offset(owl_global *g, int i); +extern void owl_global_set_startupargs(owl_global *g, int argc, char **argv); +extern char *owl_global_get_startupargs(owl_global *g); +extern owl_history *owl_global_get_msg_history(owl_global *g); +extern owl_history *owl_global_get_cmd_history(owl_global *g); +extern owl_list *owl_global_get_filterlist(owl_global *g); +extern owl_filter *owl_global_get_filter(owl_global *g, char *name); +extern void owl_global_add_filter(owl_global *g, owl_filter *f); +extern void owl_global_remove_filter(owl_global *g, char *name); +extern int owl_global_get_nextmsgid(owl_global *g); +extern owl_view *owl_global_get_current_view(owl_global *g); +extern owl_filterelement *owl_global_get_filterelement_true(owl_global *g); +extern owl_filterelement *owl_global_get_filterelement_false(owl_global *g); +extern owl_filterelement *owl_global_get_filterelement_null(owl_global *g); +extern int owl_global_get_hascolors(owl_global *g); +extern int owl_global_get_colorpairs(owl_global *g); +extern owl_list *owl_global_get_puntlist(owl_global *g); +extern int owl_global_message_is_puntable(owl_global *g, owl_message *m); +extern int owl_global_should_followlast(owl_global *g); +extern int owl_global_is_search_active(owl_global *g); +extern void owl_global_set_search_active(owl_global *g, char *string); +extern void owl_global_set_search_inactive(owl_global *g); +extern char *owl_global_get_search_string(owl_global *g); +extern void owl_global_set_newmsgproc_pid(owl_global *g, int i); +extern int owl_global_get_newmsgproc_pid(owl_global *g); +extern void owl_global_add_to_malloced(owl_global *g, int i); +extern void owl_global_add_to_freed(owl_global *g, int i); +extern int owl_global_get_malloced(owl_global *g); +extern int owl_global_get_freed(owl_global *g); +extern int owl_global_get_meminuse(owl_global *g); +extern int owl_global_is_aimloggedin(owl_global *g); +extern char *owl_global_get_aim_screenname(owl_global *g); +extern void owl_global_set_aimloggedin(owl_global *g, char *screenname); +extern void owl_global_set_aimnologgedin(owl_global *g); +extern int owl_global_is_doaimevents(owl_global *g); +extern void owl_global_set_doaimevents(owl_global *g); +extern void owl_global_set_no_doaimevents(owl_global *g); +extern aim_session_t *owl_global_get_aimsess(owl_global *g); +extern aim_conn_t *owl_global_get_bosconn(owl_global *g); +extern void owl_global_set_bossconn(owl_global *g, aim_conn_t *conn); +extern int owl_global_is_aimnop_time(owl_global *g); +extern void owl_global_aimnop_sent(owl_global *g); +extern owl_timer *owl_global_get_aim_login_timer(owl_global *g); +extern void owl_global_messagequeue_addmsg(owl_global *g, owl_message *m); +extern owl_message *owl_global_messageuque_popmsg(owl_global *g); +extern int owl_global_messagequeue_pending(owl_global *g); +extern owl_buddylist *owl_global_get_buddylist(owl_global *g); +extern owl_style *owl_global_get_style_by_name(owl_global *g, char *name); +extern int owl_global_get_style_names(owl_global *g, owl_list *l); +extern void owl_global_add_style(owl_global *g, owl_style *s); +extern char *owl_global_get_response(owl_global *g); +extern void owl_global_set_response(owl_global *g, char *resp); +extern void owl_global_set_haveaim(owl_global *g); +extern int owl_global_is_haveaim(owl_global *g); +extern void owl_global_set_havezephyr(owl_global *g); +extern int owl_global_is_havezephyr(owl_global *g); +extern owl_timer *owl_global_get_aim_buddyinfo_timer(owl_global *g); +extern owl_errqueue *owl_global_get_errqueue(owl_global *g); +extern void owl_global_set_errsignal(owl_global *g, int signum, siginfo_t *siginfo); +extern int owl_global_get_errsignal_and_clear(owl_global *g, siginfo_t *siginfo); +extern owl_timer *owl_global_get_zephyr_buddycheck_timer(owl_global *g); +extern owl_zbuddylist *owl_global_get_zephyr_buddylist(owl_global *g); +extern struct termios *owl_global_get_startup_tio(owl_global *g); +extern owl_list *owl_global_get_dispatchlist(owl_global *g); + +/* -------------------------------- help.c -------------------------------- */ +extern void owl_help(); + +/* -------------------------------- history.c -------------------------------- */ +extern void owl_history_init(owl_history *h); +extern void owl_history_set_norepeats(owl_history *h); +extern char *owl_history_get_prev(owl_history *h); +extern char *owl_history_get_next(owl_history *h); +extern void owl_history_store(owl_history *h, char *line); +extern void owl_history_set_partial(owl_history *h); +extern void owl_history_reset(owl_history *h); +extern int owl_history_is_touched(owl_history *h); + +/* -------------------------------- keybinding.c -------------------------------- */ +extern int owl_keybinding_init(owl_keybinding *kb, char *keyseq, char *command, void (*function_fn)(void), char *desc); +extern void owl_keybinding_free(owl_keybinding *kb); +extern void owl_keybinding_free_all(owl_keybinding *kb); +extern void owl_keybinding_execute(owl_keybinding *kb, int j); +extern int owl_keybinding_stack_tostring(int *j, char *buff, int bufflen); +extern int owl_keybinding_tostring(owl_keybinding *kb, char *buff, int bufflen); +extern char *owl_keybinding_get_desc(owl_keybinding *kb); +extern int owl_keybinding_match(owl_keybinding *kb, int *kpstack); +extern int owl_keybinding_equal(owl_keybinding *kb1, owl_keybinding *kb2); + +/* -------------------------------- keymap.c -------------------------------- */ +extern int owl_keymap_init(owl_keymap *km, char *name, char *desc, void (*default_fn)(int), void (*prealways_fn)(int), void (*postalways_fn)(int)); +extern void owl_keymap_free(owl_keymap *km); +extern void owl_keymap_set_submap(owl_keymap *km, owl_keymap *submap); +extern int owl_keymap_create_binding(owl_keymap *km, char *keyseq, char *command, void (*function_fn)(void), char *desc); +extern char *owl_keymap_summary(owl_keymap *km); +extern void owl_keymap_get_details(owl_keymap *km, owl_fmtext *fm); +extern int owl_keyhandler_init(owl_keyhandler *kh); +extern void owl_keyhandler_add_keymap(owl_keyhandler *kh, owl_keymap *km); +extern owl_keymap *owl_keyhandler_create_and_add_keymap(owl_keyhandler *kh, char *name, char *desc, void (*default_fn)(int), void (*prealways_fn)(int), void (*postalways_fn)(int)); +extern void owl_keyhandler_reset(owl_keyhandler *kh); +extern owl_keymap *owl_keyhandler_get_keymap(owl_keyhandler *kh, char *mapname); +extern void owl_keyhandler_get_keymap_names(owl_keyhandler *kh, owl_list *l); +extern void owl_keyhandler_keymap_namelist_free(owl_list *l); +extern owl_keymap *owl_keyhandler_activate(owl_keyhandler *kh, char *mapname); +extern int owl_keyhandler_process(owl_keyhandler *kh, int j); +extern void owl_keyhandler_invalidkey(owl_keyhandler *kh); + +/* -------------------------------- keypress.c -------------------------------- */ +#ifdef KEY_CODE_YES +#endif +#ifdef KEY_RESIZE +#endif +extern int owl_keypress_tostring(int j, int esc, char *buff, int bufflen); +extern int owl_keypress_fromstring(char *kb); + +/* -------------------------------- keys.c -------------------------------- */ +extern void owl_keys_setup_keymaps(owl_keyhandler *kh); +extern void owl_keys_recwin_prealways(int j); +extern void owl_keys_editwin_default(int j); +extern void owl_keys_editwin_postalways(int j); +extern void owl_keys_popless_postalways(int j); +extern void owl_keys_default_invalid(int j); + +/* -------------------------------- list.c -------------------------------- */ +extern int owl_list_create(owl_list *l); +extern int owl_list_get_size(owl_list *l); +extern void *owl_list_get_element(owl_list *l, int n); +extern int owl_list_append_element(owl_list *l, void *element); +extern int owl_list_prepend_element(owl_list *l, void *element); +extern int owl_list_remove_element(owl_list *l, int n); +extern int owl_list_replace_element(owl_list *l, int n, void *element); +extern void owl_list_free_all(owl_list *l, void (*elefree)(void *)); +extern void owl_list_free_simple(owl_list *l); + +/* -------------------------------- logging.c -------------------------------- */ +extern void owl_log_message(owl_message *m); +extern int owl_log_shouldlog_message(owl_message *m); +extern void owl_log_outgoing_zephyr(owl_message *m); +extern void owl_log_outgoing_zephyr_error(char *to, char *text); +extern void owl_log_outgoing_aim(owl_message *m); +extern void owl_log_outgoing_loopback(owl_message *m); +extern void owl_log_incoming(owl_message *m); + +/* -------------------------------- mainwin.c -------------------------------- */ +extern void owl_mainwin_init(owl_mainwin *mw); +extern void owl_mainwin_redisplay(owl_mainwin *mw); +extern int owl_mainwin_is_curmsg_truncated(owl_mainwin *mw); +extern int owl_mainwin_is_last_msg_truncated(owl_mainwin *mw); +extern int owl_mainwin_get_last_msg(owl_mainwin *mw); + +/* -------------------------------- message.c -------------------------------- */ +extern void owl_message_init(owl_message *m); +extern void owl_message_set_attribute(owl_message *m, char *attrname, char *attrvalue); +extern char *owl_message_get_attribute_value(owl_message *m, char *attrname); +extern void owl_message_attributes_tofmtext(owl_message *m, owl_fmtext *fm); +extern void owl_message_invalidate_format(owl_message *m); +extern owl_fmtext *owl_message_get_fmtext(owl_message *m); +extern void owl_message_format(owl_message *m); +extern void owl_message_set_class(owl_message *m, char *class); +extern char *owl_message_get_class(owl_message *m); +extern void owl_message_set_instance(owl_message *m, char *inst); +extern char *owl_message_get_instance(owl_message *m); +extern void owl_message_set_sender(owl_message *m, char *sender); +extern char *owl_message_get_sender(owl_message *m); +extern void owl_message_set_zsig(owl_message *m, char *zsig); +extern char *owl_message_get_zsig(owl_message *m); +extern void owl_message_set_recipient(owl_message *m, char *recip); +extern char *owl_message_get_recipient(owl_message *m); +extern void owl_message_set_realm(owl_message *m, char *realm); +extern char *owl_message_get_realm(owl_message *m); +extern void owl_message_set_body(owl_message *m, char *body); +extern char *owl_message_get_body(owl_message *m); +extern void owl_message_set_opcode(owl_message *m, char *opcode); +extern char *owl_message_get_opcode(owl_message *m); +extern void owl_message_set_islogin(owl_message *m); +extern void owl_message_set_islogout(owl_message *m); +extern int owl_message_is_loginout(owl_message *m); +extern int owl_message_is_login(owl_message *m); +extern int owl_message_is_logout(owl_message *m); +extern void owl_message_set_isprivate(owl_message *m); +extern int owl_message_is_private(owl_message *m); +extern char *owl_message_get_timestr(owl_message *m); +extern char *owl_message_get_shorttimestr(owl_message *m); +extern void owl_message_set_type_admin(owl_message *m); +extern void owl_message_set_type_loopback(owl_message *m); +extern void owl_message_set_type_zephyr(owl_message *m); +extern void owl_message_set_type_aim(owl_message *m); +extern int owl_message_is_type_admin(owl_message *m); +extern int owl_message_is_type_loopback(owl_message *m); +extern int owl_message_is_type_zephyr(owl_message *m); +extern int owl_message_is_type_aim(owl_message *m); +extern int owl_message_is_pseudo(owl_message *m); +extern int owl_message_is_type_generic(owl_message *m); +extern char *owl_message_get_text(owl_message *m); +extern void owl_message_set_direction_in(owl_message *m); +extern void owl_message_set_direction_out(owl_message *m); +extern void owl_message_set_direction_none(owl_message *m); +extern int owl_message_is_direction_in(owl_message *m); +extern int owl_message_is_direction_out(owl_message *m); +extern int owl_message_is_direction_none(owl_message *m); +extern int owl_message_get_numlines(owl_message *m); +extern void owl_message_mark_delete(owl_message *m); +extern void owl_message_unmark_delete(owl_message *m); +extern char *owl_message_get_zwriteline(owl_message *m); +extern void owl_message_set_zwriteline(owl_message *m, char *line); +extern int owl_message_is_delete(owl_message *m); +#ifdef HAVE_LIBZEPHYR +extern ZNotice_t *owl_message_get_notice(owl_message *m); +#else +extern void *owl_message_get_notice(owl_message *m); +#endif +extern void owl_message_set_hostname(owl_message *m, char *hostname); +extern char *owl_message_get_hostname(owl_message *m); +extern void owl_message_curs_waddstr(owl_message *m, WINDOW *win, int aline, int bline, int acol, int bcol, int color); +extern int owl_message_is_personal(owl_message *m); +extern int owl_message_is_from_me(owl_message *m); +extern int owl_message_is_mail(owl_message *m); +extern int owl_message_is_ping(owl_message *m); +extern int owl_message_is_burningears(owl_message *m); +extern char *owl_message_get_cc(owl_message *m); +extern int owl_message_get_id(owl_message *m); +extern char *owl_message_get_type(owl_message *m); +extern char *owl_message_get_direction(owl_message *m); +extern char *owl_message_get_login(owl_message *m); +extern char *owl_message_get_header(owl_message *m); +extern int owl_message_search(owl_message *m, char *string); +extern void owl_message_create_aim(owl_message *m, char *sender, char *recipient, char *text, int direction, int loginout); +extern void owl_message_create_admin(owl_message *m, char *header, char *text); +extern void owl_message_create_loopback(owl_message *m, char *text); +#ifdef HAVE_LIBZEPHYR +extern void owl_message_create_from_znotice(owl_message *m, ZNotice_t *n); +#ifdef OWL_ENABLE_ZCRYPT +#endif +#else +extern void owl_message_create_from_znotice(owl_message *m, void *n); +#endif +extern void owl_message_create_pseudo_zlogin(owl_message *m, int direction, char *user, char *host, char *time, char *tty); +#ifdef HAVE_LIBZEPHYR +#endif +extern void owl_message_create_from_zwriteline(owl_message *m, char *line, char *body, char *zsig); +extern void owl_message_pretty_zsig(owl_message *m, char *buff); +extern void owl_message_free(owl_message *m); +#ifdef HAVE_LIBZEPHYR +#endif + +/* -------------------------------- messagelist.c -------------------------------- */ +extern int owl_messagelist_create(owl_messagelist *ml); +extern int owl_messagelist_get_size(owl_messagelist *ml); +extern void *owl_messagelist_get_element(owl_messagelist *ml, int n); +extern owl_message *owl_messagelist_get_by_id(owl_messagelist *ml, int target_id); +extern int owl_messagelist_append_element(owl_messagelist *ml, void *element); +extern int owl_messagelist_delete_element(owl_messagelist *ml, int n); +extern int owl_messagelist_undelete_element(owl_messagelist *ml, int n); +extern int owl_messagelist_expunge(owl_messagelist *ml); +extern void owl_messagelist_invalidate_formats(owl_messagelist *ml); + +/* -------------------------------- owl.c -------------------------------- */ +#if OWL_STDERR_REDIR +#ifdef HAVE_SYS_IOCTL_H +#endif +#ifdef HAVE_SYS_FILIO_H +#endif +#endif +extern int main(int argc, char **argv, char **env); +#ifdef HAVE_LIBZEPHYR +#endif +#ifdef HAVE_USE_DEFAULT_COLORS +#endif +#ifdef HAVE_LIBZEPHYR +#endif +#if OWL_STDERR_REDIR +#endif +#if 0 +#endif +extern void owl_process_input(owl_dispatch *d); +extern void sig_handler(int sig, siginfo_t *si, void *data); +extern void usage(); +#if OWL_STDERR_REDIR +extern int stderr_replace(void); +extern void stderr_redirect_handler(owl_dispatch *d); +#endif /* OWL_STDERR_REDIR */ + +/* -------------------------------- pair.c -------------------------------- */ +extern void owl_pair_create(owl_pair *p, void *key, void *value); +extern void owl_pair_set_key(owl_pair *p, void *key); +extern void owl_pair_set_value(owl_pair *p, void *value); +extern void *owl_pair_get_key(owl_pair *p); +extern void *owl_pair_get_value(owl_pair *p); + +/* -------------------------------- perlconfig.c -------------------------------- */ +extern char *owl_perlconfig_call_with_message(char *subname, owl_message *m); +extern char *owl_perlconfig_readconfig(char *file); +extern int owl_perlconfig_is_function(char *fn); +extern int owl_perlconfig_get_hashkeys(char *hashname, owl_list *l); +extern char *owl_perlconfig_execute(char *line); +extern char *owl_perlconfig_getmsg(owl_message *m, int mode, char *subname); + +/* -------------------------------- perlglue.c -------------------------------- */ +#ifdef HAVE_LIBZEPHYR +#endif +#ifdef __cplusplus +#endif + +/* -------------------------------- perlwrap.c -------------------------------- */ + +/* -------------------------------- popexec.c -------------------------------- */ +#ifdef HAVE_SYS_IOCTL_H +#endif +#ifdef HAVE_SYS_FILIO_H +#endif +extern owl_popexec *owl_popexec_new(char *command); +extern void owl_popexec_inputhandler(owl_dispatch *d); +extern void owl_popexec_free_dispatch(owl_dispatch *d); +extern void owl_popexec_viewwin_onclose(owl_viewwin *vwin, void *data); +extern void owl_popexec_unref(owl_popexec *pe); + +/* -------------------------------- popwin.c -------------------------------- */ +extern int owl_popwin_init(owl_popwin *pw); +extern int owl_popwin_up(owl_popwin *pw); +extern int owl_popwin_display_text(owl_popwin *pw, char *text); +extern int owl_popwin_close(owl_popwin *pw); +extern int owl_popwin_is_active(owl_popwin *pw); +extern int owl_popwin_refresh(owl_popwin *pw); +extern void owl_popwin_set_handler(owl_popwin *pw, void (*func)(int ch)); +extern void owl_popwin_unset_handler(owl_popwin *pw); +extern WINDOW *owl_popwin_get_curswin(owl_popwin *pw); +extern int owl_popwin_get_lines(owl_popwin *pw); +extern int owl_popwin_get_cols(owl_popwin *pw); +extern int owl_popwin_needs_first_refresh(owl_popwin *pw); +extern void owl_popwin_no_needs_first_refresh(owl_popwin *pw); + +/* -------------------------------- regex.c -------------------------------- */ +extern void owl_regex_init(owl_regex *re); +extern int owl_regex_create(owl_regex *re, char *string); +extern int owl_regex_create_quoted(owl_regex *re, char *string); +extern int owl_regex_compare(owl_regex *re, char *string); +extern int owl_regex_is_set(owl_regex *re); +extern char *owl_regex_get_string(owl_regex *re); +extern void owl_regex_copy(owl_regex *a, owl_regex *b); +extern void owl_regex_free(owl_regex *re); + +/* -------------------------------- select.c -------------------------------- */ +extern int owl_select_find_dispatch(int fd); +extern void owl_select_add_dispatch(owl_dispatch *d); +extern void owl_select_remove_dispatch(int fd); +extern int owl_select_dispatch_count(); +extern int owl_select_dispatch_prepare_fd_sets(fd_set *r, fd_set *e); +extern void owl_select_gc(); +extern void owl_select_dispatch(fd_set *fds, int max_fd); +extern int owl_select_aim_hack(fd_set *rfds, fd_set *wfds); +extern void owl_select(); + +/* -------------------------------- style.c -------------------------------- */ +extern void owl_style_create_internal(owl_style *s, char *name, void (*formatfunc) (owl_fmtext *fm, owl_message *m), char *description); +extern void owl_style_create_perl(owl_style *s, char *name, char *perlfuncname, char *description); +extern int owl_style_matches_name(owl_style *s, char *name); +extern char *owl_style_get_name(owl_style *s); +extern char *owl_style_get_description(owl_style *s); +extern void owl_style_get_formattext(owl_style *s, owl_fmtext *fm, owl_message *m); +extern int owl_style_validate(owl_style *s); +extern void owl_style_free(owl_style *s); + +/* -------------------------------- stylefunc.c -------------------------------- */ +extern void owl_stylefunc_basic(owl_fmtext *fm, owl_message *m); +#ifdef HAVE_LIBZEPHYR +#endif +#ifdef HAVE_LIBZEPHYR +#endif +extern void owl_stylefunc_default(owl_fmtext *fm, owl_message *m); +#ifdef HAVE_LIBZEPHYR +#endif +#ifdef HAVE_LIBZEPHYR +#endif +extern void owl_stylefunc_oneline(owl_fmtext *fm, owl_message *m); +#ifdef HAVE_LIBZEPHYR +#endif +#ifdef HAVE_LIBZEPHYR +#endif +extern void owl_stylefunc_vt(owl_fmtext *fm, owl_message *m); +#ifdef HAVE_LIBZEPHYR +#endif +#ifdef HAVE_LIBZEPHYR +#endif + +/* -------------------------------- tester.c -------------------------------- */ +extern void screeninit(); +extern void test1(); +extern void test2(char *in); +extern void test3(); +extern void colorinfo(); +extern void test4(); +extern void test_keypress(); +extern int main(int argc, char **argv, char **env); + +/* -------------------------------- text.c -------------------------------- */ +extern int owl_text_truncate_lines(char *out, char *in, int aline, int lines); +extern void owl_text_truncate_cols(char *out, char *in, int acol, int bcol); +extern void owl_text_indent(char *out, char *in, int n); +extern int owl_text_num_lines(char *in); +extern char *owl_text_htmlstrip(char *in); +extern char *owl_text_wordwrap(char *in, int col); +extern void owl_text_wordunwrap(char *in); +extern char *stristr(char *a, char *b); +extern int only_whitespace(char *s); +extern char *owl_getquoting(char *line); +extern char *owl_text_substitute(char *in, char *from, char *to); +extern void owl_text_tr(char *buff, char a, char b); +extern char *owl_text_quote(char *in, char *toquote, char *quotestr); + +/* -------------------------------- timer.c -------------------------------- */ +extern void owl_timer_create_countup(owl_timer *t); +extern void owl_timer_create_countdown(owl_timer *t, int start); +extern void owl_timer_reset(owl_timer *t); +extern void owl_timer_reset_newstart(owl_timer *t, int start); +extern int owl_timer_get_time(owl_timer *t); +extern int owl_timer_is_expired(owl_timer *t); + +/* -------------------------------- util.c -------------------------------- */ +extern void sepbar(char *in); +extern void pophandler_quit(int ch); +extern char **atokenize(char *buffer, char *sep, int *i); +extern char *skiptokens(char *buff, int n); +extern char *owl_util_makepath(char *in); +extern void atokenize_free(char **tok, int nels); +extern void owl_parsefree(char **argv, int argc); +extern char **owl_parseline(char *line, int *argc); +extern char *owl_util_minutes_to_timestr(int in); +extern int owl_util_find_trans(char *in, int len); +extern void downstr(char *foo); +extern char *owl_util_uniq(char *A, char *B, char *prohibit); +extern void *owl_malloc(size_t size); +extern void owl_free(void *ptr); +extern char *owl_strdup(const char *s1); +extern void *owl_realloc(void *ptr, size_t size); +extern char *owl_sprintf(const char *fmt, ...); +extern int owl_util_string_to_color(char *color); +extern char *owl_util_color_to_string(int color); +extern char *owl_util_get_default_tty(); +extern void owl_hack_animate(); +extern char *owl_util_stripnewlines(char *in); +extern void owl_util_file_deleteline(char *filename, char *line, int backup); +extern void owl_util_list_add_unique_string(owl_list *list, char *str); +extern int owl_util_common_strings_in_lists(owl_list *a, owl_list *b); +extern int owl_util_max(int a, int b); +extern int owl_util_min(int a, int b); +extern char * owl_util_baseclass(char * class); +#ifdef OWL_INCLUDE_REG_TESTS +extern int owl_util_regtest(void); +#endif /* OWL_INCLUDE_REG_TESTS */ + +/* -------------------------------- variable.c -------------------------------- */ +extern int owl_variable_int_validate_gt0(owl_variable *v, void *newval); +extern int owl_variable_int_validate_positive(owl_variable *v, void *newval); +extern int owl_variable_typewinsize_set(owl_variable *v, void *newval); +extern int owl_variable_debug_set(owl_variable *v, void *newval); +extern int owl_variable_aaway_set(owl_variable *v, void *newval); +extern int owl_variable_pseudologins_set(owl_variable *v, void *newval); +extern int owl_variable_disable_ctrl_d_set(owl_variable *v, void *newval); +extern int owl_variable_tty_set(owl_variable *v, void *newval); +extern int owl_variable_dict_setup(owl_vardict *vd); +extern void owl_variable_dict_free(owl_vardict *d); +extern void owl_variable_dict_get_names(owl_vardict *d, owl_list *l); +extern void owl_variable_dict_namelist_free(owl_list *l); +extern void owl_variable_free(owl_variable *v); +extern char *owl_variable_get_description(owl_variable *v); +extern char *owl_variable_get_summary(owl_variable *v); +extern char *owl_variable_get_validsettings(owl_variable *v); +extern int owl_variable_set_fromstring(owl_vardict *d, char *name, char *value, int msg, int requirebool); +extern int owl_variable_set_string(owl_vardict *d, char *name, char *newval); +extern int owl_variable_set_int(owl_vardict *d, char *name, int newval); +extern int owl_variable_set_bool_on(owl_vardict *d, char *name); +extern int owl_variable_set_bool_off(owl_vardict *d, char *name); +extern int owl_variable_get_tostring(owl_vardict *d, char *name, char *buf, int bufsize); +extern int owl_variable_get_default_tostring(owl_vardict *d, char *name, char *buf, int bufsize); +extern void *owl_variable_get(owl_vardict *d, char *name, int require_type); +extern char *owl_variable_get_string(owl_vardict *d, char *name); +extern void *owl_variable_get_other(owl_vardict *d, char *name); +extern int owl_variable_get_int(owl_vardict *d, char *name); +extern int owl_variable_get_bool(owl_vardict *d, char *name); +extern void owl_variable_describe(owl_vardict *d, char *name, owl_fmtext *fm); +extern void owl_variable_get_help(owl_vardict *d, char *name, owl_fmtext *fm); +extern void *owl_variable_get_default(owl_variable *v); +extern void owl_variable_free_default(owl_variable *v); +extern int owl_variable_bool_validate_default(owl_variable *v, void *newval); +extern int owl_variable_bool_set_default(owl_variable *v, void *newval); +extern int owl_variable_bool_set_fromstring_default(owl_variable *v, char *newval); +extern int owl_variable_bool_get_tostring_default(owl_variable *v, char* buf, int bufsize, void *val); +extern int owl_variable_int_validate_default(owl_variable *v, void *newval); +extern int owl_variable_int_set_default(owl_variable *v, void *newval); +extern int owl_variable_int_set_fromstring_default(owl_variable *v, char *newval); +extern int owl_variable_int_get_tostring_default(owl_variable *v, char* buf, int bufsize, void *val); +extern int owl_variable_enum_validate(owl_variable *v, void *newval); +extern int owl_variable_enum_set_fromstring(owl_variable *v, char *newval); +extern int owl_variable_enum_get_tostring(owl_variable *v, char* buf, int bufsize, void *val); +extern int owl_variable_string_validate_default(struct _owl_variable *v, void *newval); +extern int owl_variable_string_set_default(owl_variable *v, void *newval); +extern int owl_variable_string_set_fromstring_default(owl_variable *v, char *newval); +extern int owl_variable_string_get_tostring_default(owl_variable *v, char* buf, int bufsize, void *val); +#ifdef OWL_INCLUDE_REG_TESTS +extern int owl_variable_regtest(void); +#endif /* OWL_INCLUDE_REG_TESTS */ + +/* -------------------------------- varstubs.c -------------------------------- */ +extern void owl_global_set_personalbell(owl_global *g, char *text); +extern char *owl_global_get_personalbell(owl_global *g); +extern void owl_global_set_bell_on(owl_global *g); +extern void owl_global_set_bell_off(owl_global *g); +extern int owl_global_is_bell(owl_global *g); +extern void owl_global_set_debug_on(owl_global *g); +extern void owl_global_set_debug_off(owl_global *g); +extern int owl_global_is_debug(owl_global *g); +extern void owl_global_set_startuplogin_on(owl_global *g); +extern void owl_global_set_startuplogin_off(owl_global *g); +extern int owl_global_is_startuplogin(owl_global *g); +extern void owl_global_set_shutdownlogout_on(owl_global *g); +extern void owl_global_set_shutdownlogout_off(owl_global *g); +extern int owl_global_is_shutdownlogout(owl_global *g); +extern void owl_global_set_rxping_on(owl_global *g); +extern void owl_global_set_rxping_off(owl_global *g); +extern int owl_global_is_rxping(owl_global *g); +extern void owl_global_set_txping_on(owl_global *g); +extern void owl_global_set_txping_off(owl_global *g); +extern int owl_global_is_txping(owl_global *g); +extern void owl_global_set_sepbar_disable_on(owl_global *g); +extern void owl_global_set_sepbar_disable_off(owl_global *g); +extern int owl_global_is_sepbar_disable(owl_global *g); +extern void owl_global_set_smartstrip_on(owl_global *g); +extern void owl_global_set_smartstrip_off(owl_global *g); +extern int owl_global_is_smartstrip(owl_global *g); +extern void owl_global_set_newlinestrip_on(owl_global *g); +extern void owl_global_set_newlinestrip_off(owl_global *g); +extern int owl_global_is_newlinestrip(owl_global *g); +extern void owl_global_set_displayoutgoing_on(owl_global *g); +extern void owl_global_set_displayoutgoing_off(owl_global *g); +extern int owl_global_is_displayoutgoing(owl_global *g); +extern void owl_global_set_loginsubs_on(owl_global *g); +extern void owl_global_set_loginsubs_off(owl_global *g); +extern int owl_global_is_loginsubs(owl_global *g); +extern void owl_global_set_logging_on(owl_global *g); +extern void owl_global_set_logging_off(owl_global *g); +extern int owl_global_is_logging(owl_global *g); +extern void owl_global_set_classlogging_on(owl_global *g); +extern void owl_global_set_classlogging_off(owl_global *g); +extern int owl_global_is_classlogging(owl_global *g); +extern void owl_global_set_loggingdirection(owl_global *g, int n); +extern int owl_global_get_loggingdirection(owl_global *g); +extern void owl_global_set_colorztext_on(owl_global *g); +extern void owl_global_set_colorztext_off(owl_global *g); +extern int owl_global_is_colorztext(owl_global *g); +extern void owl_global_set_fancylines_on(owl_global *g); +extern void owl_global_set_fancylines_off(owl_global *g); +extern int owl_global_is_fancylines(owl_global *g); +extern void owl_global_set_zcrypt_on(owl_global *g); +extern void owl_global_set_zcrypt_off(owl_global *g); +extern int owl_global_is_zcrypt(owl_global *g); +extern void owl_global_set_pseudologins_on(owl_global *g); +extern void owl_global_set_pseudologins_off(owl_global *g); +extern int owl_global_is_pseudologins(owl_global *g); +extern void owl_global_set_ignorelogins_on(owl_global *g); +extern void owl_global_set_ignorelogins_off(owl_global *g); +extern int owl_global_is_ignorelogins(owl_global *g); +extern void owl_global_set_logfilter(owl_global *g, char *text); +extern char *owl_global_get_logfilter(owl_global *g); +extern void owl_global_set_loglogins_on(owl_global *g); +extern void owl_global_set_loglogins_off(owl_global *g); +extern int owl_global_is_loglogins(owl_global *g); +extern void owl_global_set_lockout_ctrld(owl_global *g, int n); +extern int owl_global_get_lockout_ctrld(owl_global *g); +extern void owl_global_set_burningears_on(owl_global *g); +extern void owl_global_set_burningears_off(owl_global *g); +extern int owl_global_is_burningears(owl_global *g); +extern void owl_global_set_summarymode_on(owl_global *g); +extern void owl_global_set_summarymode_off(owl_global *g); +extern int owl_global_is_summarymode(owl_global *g); +extern void owl_global_set_logpath(owl_global *g, char *text); +extern char *owl_global_get_logpath(owl_global *g); +extern void owl_global_set_classlogpath(owl_global *g, char *text); +extern char *owl_global_get_classlogpath(owl_global *g); +extern void owl_global_set_debug_file(owl_global *g, char *text); +extern char *owl_global_get_debug_file(owl_global *g); +extern void owl_global_set_zsigproc(owl_global *g, char *text); +extern char *owl_global_get_zsigproc(owl_global *g); +extern void owl_global_set_newmsgproc(owl_global *g, char *text); +extern char *owl_global_get_newmsgproc(owl_global *g); +extern void owl_global_set_zsig(owl_global *g, char *text); +extern char *owl_global_get_zsig(owl_global *g); +extern void owl_global_set_appendtosepbar(owl_global *g, char *text); +extern char *owl_global_get_appendtosepbar(owl_global *g); +extern void owl_global_set_zaway_on(owl_global *g); +extern void owl_global_set_zaway_off(owl_global *g); +extern int owl_global_is_zaway(owl_global *g); +extern void owl_global_set_zaway_msg(owl_global *g, char *text); +extern char *owl_global_get_zaway_msg(owl_global *g); +extern void owl_global_set_zaway_msg_default(owl_global *g, char *text); +extern char *owl_global_get_zaway_msg_default(owl_global *g); +extern void owl_global_set_aaway_on(owl_global *g); +extern void owl_global_set_aaway_off(owl_global *g); +extern int owl_global_is_aaway(owl_global *g); +extern void owl_global_set_aaway_msg(owl_global *g, char *text); +extern char *owl_global_get_aaway_msg(owl_global *g); +extern void owl_global_set_aaway_msg_default(owl_global *g, char *text); +extern char *owl_global_get_aaway_msg_default(owl_global *g); +extern void owl_global_set_view_home(owl_global *g, char *text); +extern char *owl_global_get_view_home(owl_global *g); +extern void owl_global_set_alert_filter(owl_global *g, char *text); +extern char *owl_global_get_alert_filter(owl_global *g); +extern void owl_global_set_alert_action(owl_global *g, char *text); +extern char *owl_global_get_alert_action(owl_global *g); +extern void owl_global_set_tty(owl_global *g, char *text); +extern char *owl_global_get_tty(owl_global *g); +extern void owl_global_set_default_style(owl_global *g, char *text); +extern char *owl_global_get_default_style(owl_global *g); +extern void owl_global_set_edit_maxfillcols(owl_global *g, int n); +extern int owl_global_get_edit_maxfillcols(owl_global *g); +extern void owl_global_set_edit_maxwrapcols(owl_global *g, int n); +extern int owl_global_get_edit_maxwrapcols(owl_global *g); +extern void owl_global_set_aim_ignorelogin_timer(owl_global *g, int n); +extern int owl_global_get_aim_ignorelogin_timer(owl_global *g); +extern void owl_global_set_typwin_lines(owl_global *g, int n); +extern int owl_global_get_typwin_lines(owl_global *g); +extern void owl_global_set_scrollmode(owl_global *g, int n); +extern int owl_global_get_scrollmode(owl_global *g); +extern void owl_global_set__followlast_on(owl_global *g); +extern void owl_global_set__followlast_off(owl_global *g); +extern int owl_global_is__followlast(owl_global *g); + +/* -------------------------------- view.c -------------------------------- */ +extern void owl_view_create(owl_view *v, char *name, owl_filter *f, owl_style *s); +extern char *owl_view_get_name(owl_view *v); +extern void owl_view_consider_message(owl_view *v, owl_message *m); +extern void owl_view_recalculate(owl_view *v); +extern void owl_view_new_filter(owl_view *v, owl_filter *f); +extern void owl_view_set_style(owl_view *v, owl_style *s); +extern owl_style *owl_view_get_style(owl_view *v); +extern char *owl_view_get_style_name(owl_view *v); +extern owl_message *owl_view_get_element(owl_view *v, int index); +extern void owl_view_delete_element(owl_view *v, int index); +extern void owl_view_undelete_element(owl_view *v, int index); +extern int owl_view_get_size(owl_view *v); +extern int owl_view_get_nearest_to_msgid(owl_view *v, int targetid); +extern int owl_view_get_nearest_to_saved(owl_view *v); +extern void owl_view_save_curmsgid(owl_view *v, int curid); +extern void owl_view_to_fmtext(owl_view *v, owl_fmtext *fm); +extern char *owl_view_get_filtname(owl_view *v); +extern void owl_view_free(owl_view *v); + +/* -------------------------------- viewwin.c -------------------------------- */ +extern void owl_viewwin_init_text(owl_viewwin *v, WINDOW *win, int winlines, int wincols, char *text); +extern void owl_viewwin_append_text(owl_viewwin *v, char *text); +extern void owl_viewwin_init_fmtext(owl_viewwin *v, WINDOW *win, int winlines, int wincols, owl_fmtext *fmtext); +extern void owl_viewwin_set_curswin(owl_viewwin *v, WINDOW *w, int winlines, int wincols); +extern void owl_viewwin_set_onclose_hook(owl_viewwin *v, void (*onclose_hook) (owl_viewwin *vwin, void *data), void *onclose_hook_data); +extern void owl_viewwin_redisplay(owl_viewwin *v, int update); +extern void owl_viewwin_pagedown(owl_viewwin *v); +extern void owl_viewwin_linedown(owl_viewwin *v); +extern void owl_viewwin_pageup(owl_viewwin *v); +extern void owl_viewwin_lineup(owl_viewwin *v); +extern void owl_viewwin_right(owl_viewwin *v, int n); +extern void owl_viewwin_left(owl_viewwin *v, int n); +extern void owl_viewwin_top(owl_viewwin *v); +extern void owl_viewwin_bottom(owl_viewwin *v); +extern void owl_viewwin_free(owl_viewwin *v); + +/* -------------------------------- zbuddylist.c -------------------------------- */ +extern void owl_zbuddylist_create(owl_zbuddylist *zb); +extern int owl_zbuddylist_adduser(owl_zbuddylist *zb, char *name); +extern int owl_zbuddylist_deluser(owl_zbuddylist *zb, char *name); +extern int owl_zbuddylist_contains_user(owl_zbuddylist *zb, char *name); + +/* -------------------------------- zcrypt.c -------------------------------- */ +#ifdef OWL_ENABLE_ZCRYPT +#ifndef TRUE +#endif +#ifndef FALSE +#endif +#ifndef HAVE_DES_ECB_ENCRYPT_PROTO +#endif +extern int zcrypt(int argc, char *argv[]); +extern int owl_zcrypt_decrypt(char *out, char *in, char *class, char *instance); +extern int owl_zcrypt_encrypt(char *out, char *in, char *class, char *instance); +extern char *BuildArgString(char **argv, int start, int end); +extern char *GetZephyrVarKeyFile(char *whoami, char *class, char *instance); +extern void CloseZephyrPipe(FILE *pipe); +extern void block_to_ascii(char *output, FILE *outfile); +extern int read_ascii_nybble(); +extern int read_ascii_byte(); +extern int read_ascii_block(char *input); +extern int do_decrypt(char *keystring); +#endif + +/* -------------------------------- zephyr.c -------------------------------- */ +#ifdef HAVE_LIBZEPHYR +#endif +extern int owl_zephyr_initialize(); +#ifdef HAVE_LIBZEPHYR +#endif +extern int owl_zephyr_shutdown(); +#ifdef HAVE_LIBZEPHYR +#endif +extern int owl_zephyr_zpending(); +#ifdef HAVE_LIBZEPHYR +#else +#endif +extern char *owl_zephyr_get_realm(); +#ifdef HAVE_LIBZEPHYR +#else +#endif +extern char *owl_zephyr_get_sender(); +#ifdef HAVE_LIBZEPHYR +#else +#endif +#ifdef HAVE_LIBZEPHYR +extern int owl_zephyr_loadsubs_helper(ZSubscription_t subs[], int count); +#endif +extern int owl_zephyr_loadsubs(char *filename, int error_on_nofile); +#ifdef HAVE_LIBZEPHYR +#else +#endif +extern int owl_zephyr_loaddefaultsubs(); +#ifdef HAVE_LIBZEPHYR +#else +#endif +extern int owl_zephyr_loadloginsubs(char *filename); +#ifdef HAVE_LIBZEPHYR +#else +#endif +extern void unsuball(); +#if HAVE_LIBZEPHYR +#endif +extern int owl_zephyr_sub(char *class, char *inst, char *recip); +#ifdef HAVE_LIBZEPHYR +#else +#endif +extern int owl_zephyr_unsub(char *class, char *inst, char *recip); +#ifdef HAVE_LIBZEPHYR +#else +#endif +#ifdef HAVE_LIBZEPHYR +extern char *owl_zephyr_get_field(ZNotice_t *n, int j); +#else +extern char *owl_zephyr_get_field(void *n, int j); +#endif +#ifdef HAVE_LIBZEPHYR +extern int owl_zephyr_get_num_fields(ZNotice_t *n); +#else +extern int owl_zephyr_get_num_fields(void *n); +#endif +#ifdef HAVE_LIBZEPHYR +extern char *owl_zephyr_get_message(ZNotice_t *n); +#endif +#ifdef HAVE_LIBZEPHYR +extern char *owl_zephyr_get_zsig(ZNotice_t *n, int *k); +#else +extern char *owl_zephyr_get_zsig(void *n, int *k); +#endif +extern int send_zephyr(char *opcode, char *zsig, char *class, char *instance, char *recipient, char *message); +#ifdef HAVE_LIBZEPHYR +#else +#endif +#ifdef HAVE_LIBZEPHYR +extern Code_t send_zephyr_helper(ZNotice_t *notice, char *buf, int len, int wait); +#endif +extern void send_ping(char *to); +#ifdef HAVE_LIBZEPHYR +#endif +#ifdef HAVE_LIBZEPHYR +extern void owl_zephyr_handle_ack(ZNotice_t *retnotice); +#else +extern void owl_zephyr_handle_ack(void *retnotice); +#endif +#ifdef HAVE_LIBZEPHYR +extern int owl_zephyr_notice_is_ack(ZNotice_t *n); +#else +extern int owl_zephyr_notice_is_ack(void *n); +#endif +extern void owl_zephyr_zaway(owl_message *m); +#ifdef HAVE_LIBZEPHYR +#endif +#ifdef HAVE_LIBZEPHYR +extern void owl_zephyr_hackaway_cr(ZNotice_t *n); +#endif +extern char *owl_zephyr_zlocate(char *user, int auth); +#ifdef HAVE_LIBZEPHYR +#else +#endif +extern void owl_zephyr_addsub(char *filename, char *class, char *inst, char *recip); +#ifdef HAVE_LIBZEPHYR +#endif +extern void owl_zephyr_delsub(char *filename, char *class, char *inst, char *recip); +#ifdef HAVE_LIBZEPHYR +#endif +extern char *owl_zephyr_makesubline(char *class, char *inst, char *recip); +extern void owl_zephyr_zlog_in(void); +#ifdef HAVE_LIBZEPHYR +#endif +extern void owl_zephyr_zlog_out(void); +#ifdef HAVE_LIBZEPHYR +#endif +extern void owl_zephyr_addbuddy(char *name); +extern void owl_zephyr_delbuddy(char *name); +#ifdef HAVE_LIBZEPHYR +extern char *owl_zephyr_get_authstr(ZNotice_t *n); +#else +extern char *owl_zephyr_get_authstr(void *n); +#endif +extern char *owl_zephyr_getsubs(); +#ifdef HAVE_LIBZEPHYR +#else +#endif +extern char *owl_zephyr_get_variable(char *var); +#ifdef HAVE_LIBZEPHYR +#else +#endif +extern void owl_zephyr_set_locationinfo(char *host, char *val); +#ifdef HAVE_LIBZEPHYR +#endif +extern char *short_zuser(char *in); +extern char *long_zuser(char *in); +extern char *owl_zephyr_smartstripped_user(char *in); +extern int owl_zephyr_get_anyone_list(owl_list *in, char *filename); +#ifdef HAVE_LIBZEPHYR +#else +#endif +#ifdef HAVE_LIBZEPHYR +extern void owl_zephyr_process_events(owl_dispatch *d); +#else +extern void owl_zephyr_process_events(owl_dispatch *d); +#endif + +/* -------------------------------- zwrite.c -------------------------------- */ +extern int owl_zwrite_create_from_line(owl_zwrite *z, char *line); +#if !(OWL_STDERR_REDIR) +#endif +extern void owl_zwrite_send_ping(owl_zwrite *z); +extern void owl_zwrite_set_message(owl_zwrite *z, char *msg); +extern char *owl_zwrite_get_message(owl_zwrite *z); +extern int owl_zwrite_is_message_set(owl_zwrite *z); +extern int owl_zwrite_send_message(owl_zwrite *z); +extern int owl_zwrite_create_and_send_from_line(char *cmd, char *msg); +extern char *owl_zwrite_get_class(owl_zwrite *z); +extern char *owl_zwrite_get_instance(owl_zwrite *z); +extern char *owl_zwrite_get_opcode(owl_zwrite *z); +extern void owl_zwrite_set_opcode(owl_zwrite *z, char *opcode); +extern char *owl_zwrite_get_realm(owl_zwrite *z); +extern char *owl_zwrite_get_zsig(owl_zwrite *z); +extern int owl_zwrite_get_numrecips(owl_zwrite *z); +extern char *owl_zwrite_get_recip_n(owl_zwrite *z, int n); +extern int owl_zwrite_is_personal(owl_zwrite *z); +extern void owl_zwrite_free(owl_zwrite *z); + @@ -0,0 +1,46 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +void owl_pair_create(owl_pair *p, void *key, void *value) { + p->key=key; + p->value=value; +} + +void owl_pair_set_key(owl_pair *p, void *key) { + p->key=key; +} + +void owl_pair_set_value(owl_pair *p, void *value) { + p->value=value; +} + +void *owl_pair_get_key(owl_pair *p) { + return(p->key); +} + +void *owl_pair_get_value(owl_pair *p) { + return(p->value); +} diff --git a/perlconfig.c b/perlconfig.c new file mode 100644 index 0000000..98f013f --- /dev/null +++ b/perlconfig.c @@ -0,0 +1,362 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include "owl.h" +#include <perl.h> +#include "XSUB.h" + +static const char fileIdent[] = "$Id: perlconfig.c,v 1.8 2009/04/05 23:36:54 kretch Exp $"; + +extern char *owl_perlwrap_codebuff; + +extern XS(boot_owl); + +static void owl_perl_xs_init(pTHX) +{ + char *file = __FILE__; + dXSUB_SYS; + { + newXS("owl::bootstrap", boot_owl, file); + } +} + +SV *owl_perlconfig_message2hashref(owl_message *m) /*noproto*/ +{ + HV *h; + SV *hr; + char *ptr, *blessas; + int i, j; + + if (!m) return &PL_sv_undef; + h = newHV(); + +#define MSG2H(h,field) hv_store(h, #field, strlen(#field), \ + newSVpv(owl_message_get_##field(m),0), 0) + + if (owl_message_is_type_zephyr(m) + && owl_message_is_direction_in(m)) { + /* Handle zephyr-specific fields... */ + AV *av_zfields; + + av_zfields = newAV(); + j=owl_zephyr_get_num_fields(owl_message_get_notice(m)); + for (i=0; i<j; i++) { + ptr=owl_zephyr_get_field(owl_message_get_notice(m), i+1); + av_push(av_zfields, newSVpvn(ptr, strlen(ptr))); + owl_free(ptr); + } + hv_store(h, "fields", strlen("fields"), newRV_noinc((SV*)av_zfields), 0); + + hv_store(h, "auth", strlen("auth"), + newSVpv(owl_zephyr_get_authstr(owl_message_get_notice(m)),0),0); + } + + MSG2H(h, type); + MSG2H(h, direction); + MSG2H(h, class); + MSG2H(h, instance); + MSG2H(h, sender); + MSG2H(h, realm); + MSG2H(h, recipient); + MSG2H(h, opcode); + MSG2H(h, hostname); + MSG2H(h, body); + MSG2H(h, login); + MSG2H(h, zsig); + MSG2H(h, zwriteline); + if (owl_message_get_header(m)) { + MSG2H(h, header); + } + hv_store(h, "time", strlen("time"), newSVpv(owl_message_get_timestr(m),0),0); + hv_store(h, "id", strlen("id"), newSViv(owl_message_get_id(m)),0); + hv_store(h, "deleted", strlen("deleted"), newSViv(owl_message_is_delete(m)),0); + hv_store(h, "private", strlen("private"), newSViv(owl_message_is_private(m)),0); + + if (owl_message_is_type_zephyr(m)) blessas = "owl::Message::Zephyr"; + else if (owl_message_is_type_aim(m)) blessas = "owl::Message::AIM"; + else if (owl_message_is_type_admin(m)) blessas = "owl::Message::Admin"; + else if (owl_message_is_type_generic(m)) blessas = "owl::Message::Generic"; + else blessas = "owl::Message"; + + hr = sv_2mortal(newRV_noinc((SV*)h)); + return sv_bless(hr, gv_stashpv(blessas,0)); +} + + +SV *owl_perlconfig_curmessage2hashref(void) /*noproto*/ +{ + int curmsg; + owl_view *v; + v=owl_global_get_current_view(&g); + if (owl_view_get_size(v) < 1) { + return &PL_sv_undef; + } + curmsg=owl_global_get_curmsg(&g); + return owl_perlconfig_message2hashref(owl_view_get_element(v, curmsg)); +} + + +/* Calls in a scalar context, passing it a hash reference. + If return value is non-null, caller must free. */ +char *owl_perlconfig_call_with_message(char *subname, owl_message *m) +{ + dSP ; + int count, len; + SV *msgref, *srv; + char *out, *preout; + + ENTER ; + SAVETMPS; + + PUSHMARK(SP) ; + msgref = owl_perlconfig_message2hashref(m); + XPUSHs(msgref); + PUTBACK ; + + count = call_pv(subname, G_SCALAR|G_EVAL|G_KEEPERR); + + SPAGAIN ; + + if (SvTRUE(ERRSV)) { + STRLEN n_a; + owl_function_error("Perl Error: '%s'", SvPV(ERRSV, n_a)); + /* and clear the error */ + sv_setsv (ERRSV, &PL_sv_undef); + } + + if (count != 1) { + fprintf(stderr, "bad perl! no biscuit! returned wrong count!\n"); + abort(); + } + + srv = POPs; + + if (srv) { + preout=SvPV(srv, len); + out = owl_malloc(strlen(preout)+1); + strncpy(out, preout, len); + out[len] = '\0'; + } else { + out = NULL; + } + + PUTBACK ; + FREETMPS ; + LEAVE ; + + return out; +} + +char *owl_perlconfig_readconfig(char *file) +{ + int ret, fd; + PerlInterpreter *p; + char filename[MAXPATHLEN]; + int embedding_argc; + char *embedding[5]; + char *err; + struct stat statbuff; + + if (file==NULL) { + sprintf(filename, "%s/%s", getenv("HOME"), ".owlconf"); + } else { + strcpy(filename, file); + } + embedding[0]=""; + embedding[1]=filename; + embedding[2]=0; + embedding_argc = 2; + + /* create and initialize interpreter */ + PERL_SYS_INIT3(&embedding_argc, &embedding, &environ); + /* we never perl_free so we don't PERL_TERM either */ + p=perl_alloc(); + owl_global_set_perlinterp(&g, (void*)p); + perl_construct(p); + + owl_global_set_no_have_config(&g); + + /* Before we let perl have at it, we'll do our own checks on the the + * file to see if it's present, readnable etc. + */ + + /* Not present, start without it */ + ret=stat(filename, &statbuff); + if (ret) { + return(NULL); + } + + /* present, but stat thinks it's unreadable */ + if (! (statbuff.st_mode & S_IREAD)) { + return(owl_sprintf("%s present but not readable", filename)); + } + + /* can we open it? */ + fd=open(filename, O_RDONLY); + if (fd==-1) { + return(owl_sprintf("could not open %s for reading", filename)); + } + close(fd); + + ret=perl_parse(p, owl_perl_xs_init, 2, embedding, NULL); + if (ret || SvTRUE(ERRSV)) { + STRLEN n_a; + err=owl_strdup(SvPV(ERRSV, n_a)); + sv_setsv(ERRSV, &PL_sv_undef); /* and clear the error */ + return(err); + } + + ret=perl_run(p); + if (ret || SvTRUE(ERRSV)) { + STRLEN n_a; + err=owl_strdup(SvPV(ERRSV, n_a)); + sv_setsv(ERRSV, &PL_sv_undef); /* and clear the error */ + return(err); + } + + owl_global_set_have_config(&g); + + /* create legacy variables */ + perl_get_sv("owl::id", TRUE); + perl_get_sv("owl::class", TRUE); + perl_get_sv("owl::instance", TRUE); + perl_get_sv("owl::recipient", TRUE); + perl_get_sv("owl::sender", TRUE); + perl_get_sv("owl::realm", TRUE); + perl_get_sv("owl::opcode", TRUE); + perl_get_sv("owl::zsig", TRUE); + perl_get_sv("owl::msg", TRUE); + perl_get_sv("owl::time", TRUE); + perl_get_sv("owl::host", TRUE); + perl_get_av("owl::fields", TRUE); + + perl_eval_pv(owl_perlwrap_codebuff, FALSE); + + if (SvTRUE(ERRSV)) { + STRLEN n_a; + err=owl_strdup(SvPV(ERRSV, n_a)); + sv_setsv (ERRSV, &PL_sv_undef); /* and clear the error */ + return(err); + } + + /* check if we have the formatting function */ + if (owl_perlconfig_is_function("owl::format_msg")) { + owl_global_set_config_format(&g, 1); + } + + return(NULL); +} + +/* returns whether or not a function exists */ +int owl_perlconfig_is_function(char *fn) { + if (perl_get_cv(fn, FALSE)) return(1); + else return(0); +} + +/* returns 0 on success */ +int owl_perlconfig_get_hashkeys(char *hashname, owl_list *l) +{ + HV *hv; + HE *he; + char *key; + I32 i; + + if (owl_list_create(l)) return(-1); + hv = get_hv(hashname, FALSE); + if (!hv) return(-1); + i = hv_iterinit(hv); + while ((he = hv_iternext(hv))) { + key = hv_iterkey(he, &i); + if (key) { + owl_list_append_element(l, owl_strdup(key)); + } + } + return(0); +} + +/* caller is responsible for freeing returned string */ +char *owl_perlconfig_execute(char *line) +{ + STRLEN len; + SV *response; + char *out, *preout; + + if (!owl_global_have_config(&g)) return NULL; + + /* execute the subroutine */ + response = perl_eval_pv(line, FALSE); + + if (SvTRUE(ERRSV)) { + STRLEN n_a; + owl_function_error("Perl Error: '%s'", SvPV(ERRSV, n_a)); + sv_setsv (ERRSV, &PL_sv_undef); /* and clear the error */ + } + + preout=SvPV(response, len); + /* leave enough space in case we have to add a newline */ + out = owl_malloc(strlen(preout)+2); + strncpy(out, preout, len); + out[len] = '\0'; + if (!strlen(out) || out[strlen(out)-1]!='\n') { + strcat(out, "\n"); + } + + return(out); +} + +char *owl_perlconfig_getmsg(owl_message *m, int mode, char *subname) +{ + /* if mode==1 we are doing message formatting. The returned + * formatted message needs to be freed by the caller. + * + * if mode==0 we are just doing the message-has-been-received + * thing. + */ + if (!owl_global_have_config(&g)) return(NULL); + + /* run the procedure corresponding to the mode */ + if (mode==1) { + char *ret = NULL; + ret = owl_perlconfig_call_with_message(subname?subname + :"owl::_format_msg_legacy_wrap", m); + if (!ret) { + ret = owl_sprintf("@b([Perl Message Formatting Failed!])\n"); + } + return ret; + } else { + char *ptr = NULL; + if (owl_perlconfig_is_function("owl::receive_msg")) { + ptr = owl_perlconfig_call_with_message(subname?subname + :"owl::_receive_msg_legacy_wrap", m); + } + if (ptr) owl_free(ptr); + return(NULL); + } +} diff --git a/perlglue.c b/perlglue.c new file mode 100644 index 0000000..2efadee --- /dev/null +++ b/perlglue.c @@ -0,0 +1,186 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of perlglue.xs. Do not edit this file, edit perlglue.xs instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + +#line 1 "perlglue.xs" +static const char fileIdent[] = "$Id: perlglue.xs,v 1.8 2009/03/28 21:00:42 kretch Exp $"; + +#ifdef HAVE_LIBZEPHYR +#include <zephyr/zephyr.h> +#endif +#include <EXTERN.h> +#include <perl.h> +#include <XSUB.h> + +/* Yeah, we should just include owl.h, but curses and perl don't play nice. */ +extern char *owl_function_command(char *cmd); +extern void owl_free(void *x); +extern SV *owl_perlconfig_curmessage2hashref(); +extern int owl_zwrite_create_and_send_from_line(char *, char *); +extern char *owl_function_ztext_stylestrip(char *); +extern void g; +extern int owl_global_get_cols(void*); +extern char *owl_zephyr_get_realm(); +extern char *owl_zephyr_get_sender(); + +#line 31 "perlglue.c" +XS(XS_owl_command); /* prototype to pass -Wmissing-prototypes */ +XS(XS_owl_command) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: owl::command(cmd)"); + { + char * cmd = (char *)SvPV_nolen(ST(0)); +#line 27 "perlglue.xs" + char *rv = NULL; +#line 42 "perlglue.c" + char * RETVAL; + dXSTARG; +#line 29 "perlglue.xs" + rv = owl_function_command(cmd); + RETVAL = rv; +#line 48 "perlglue.c" + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; +#line 34 "perlglue.xs" + if (rv) owl_free(rv); +#line 52 "perlglue.c" + } + XSRETURN(1); +} + +XS(XS_owl_getcurmsg); /* prototype to pass -Wmissing-prototypes */ +XS(XS_owl_getcurmsg) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: owl::getcurmsg()"); + { + SV * RETVAL; +#line 39 "perlglue.xs" + ST(0) = owl_perlconfig_curmessage2hashref(); +#line 67 "perlglue.c" + } + XSRETURN(1); +} + +XS(XS_owl_getnumcols); /* prototype to pass -Wmissing-prototypes */ +XS(XS_owl_getnumcols) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: owl::getnumcols()"); + { + int RETVAL; + dXSTARG; +#line 44 "perlglue.xs" + RETVAL = owl_global_get_cols(&g); +#line 83 "perlglue.c" + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_owl_zephyr_getrealm); /* prototype to pass -Wmissing-prototypes */ +XS(XS_owl_zephyr_getrealm) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: owl::zephyr_getrealm()"); + { + char * RETVAL; + dXSTARG; +#line 51 "perlglue.xs" + RETVAL = owl_zephyr_get_realm(); +#line 100 "perlglue.c" + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_owl_zephyr_getsender); /* prototype to pass -Wmissing-prototypes */ +XS(XS_owl_zephyr_getsender) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: owl::zephyr_getsender()"); + { + char * RETVAL; + dXSTARG; +#line 58 "perlglue.xs" + RETVAL = owl_zephyr_get_sender(); +#line 117 "perlglue.c" + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_owl_zephyr_zwrite); /* prototype to pass -Wmissing-prototypes */ +XS(XS_owl_zephyr_zwrite) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: owl::zephyr_zwrite(cmd, msg)"); + { + char * cmd = (char *)SvPV_nolen(ST(0)); + char * msg = (char *)SvPV_nolen(ST(1)); +#line 67 "perlglue.xs" + int i; +#line 134 "perlglue.c" +#line 69 "perlglue.xs" + i = owl_zwrite_create_and_send_from_line(cmd, msg); +#line 137 "perlglue.c" + } + XSRETURN_EMPTY; +} + +XS(XS_owl_ztext_stylestrip); /* prototype to pass -Wmissing-prototypes */ +XS(XS_owl_ztext_stylestrip) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: owl::ztext_stylestrip(ztext)"); + { + char * ztext = (char *)SvPV_nolen(ST(0)); +#line 75 "perlglue.xs" + char *rv = NULL; +#line 152 "perlglue.c" + char * RETVAL; + dXSTARG; +#line 77 "perlglue.xs" + rv = owl_function_ztext_stylestrip(ztext); + RETVAL = rv; +#line 158 "perlglue.c" + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; +#line 82 "perlglue.xs" + if (rv) owl_free(rv); +#line 162 "perlglue.c" + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_owl); /* prototype to pass -Wmissing-prototypes */ +XS(boot_owl) +{ + dXSARGS; + char* file = __FILE__; + + XS_VERSION_BOOTCHECK ; + + newXSproto("owl::command", XS_owl_command, file, "$"); + newXSproto("owl::getcurmsg", XS_owl_getcurmsg, file, ""); + newXSproto("owl::getnumcols", XS_owl_getnumcols, file, ""); + newXSproto("owl::zephyr_getrealm", XS_owl_zephyr_getrealm, file, ""); + newXSproto("owl::zephyr_getsender", XS_owl_zephyr_getsender, file, ""); + newXSproto("owl::zephyr_zwrite", XS_owl_zephyr_zwrite, file, "$$"); + newXSproto("owl::ztext_stylestrip", XS_owl_ztext_stylestrip, file, "$"); + XSRETURN_YES; +} + diff --git a/perlglue.xs b/perlglue.xs new file mode 100644 index 0000000..201db6c --- /dev/null +++ b/perlglue.xs @@ -0,0 +1,83 @@ +static const char fileIdent[] = "$Id: perlglue.xs,v 1.8 2009/03/28 21:00:42 kretch Exp $"; + +#ifdef HAVE_LIBZEPHYR +#include <zephyr/zephyr.h> +#endif +#include <EXTERN.h> +#include <perl.h> +#include <XSUB.h> + +/* Yeah, we should just include owl.h, but curses and perl don't play nice. */ +extern char *owl_function_command(char *cmd); +extern void owl_free(void *x); +extern SV *owl_perlconfig_curmessage2hashref(); +extern int owl_zwrite_create_and_send_from_line(char *, char *); +extern char *owl_function_ztext_stylestrip(char *); +extern void g; +extern int owl_global_get_cols(void*); +extern char *owl_zephyr_get_realm(); +extern char *owl_zephyr_get_sender(); + +MODULE = owl PACKAGE = owl + +char * +command(cmd) + char *cmd + PREINIT: + char *rv = NULL; + CODE: + rv = owl_function_command(cmd); + RETVAL = rv; + OUTPUT: + RETVAL + CLEANUP: + if (rv) owl_free(rv); + +SV * +getcurmsg() + CODE: + ST(0) = owl_perlconfig_curmessage2hashref(); + +int +getnumcols() + CODE: + RETVAL = owl_global_get_cols(&g); + OUTPUT: + RETVAL + +char * +zephyr_getrealm() + CODE: + RETVAL = owl_zephyr_get_realm(); + OUTPUT: + RETVAL + +char * +zephyr_getsender() + CODE: + RETVAL = owl_zephyr_get_sender(); + OUTPUT: + RETVAL + +void +zephyr_zwrite(cmd,msg) + char *cmd + char *msg + PREINIT: + int i; + CODE: + i = owl_zwrite_create_and_send_from_line(cmd, msg); + +char * +ztext_stylestrip(ztext) + char *ztext + PREINIT: + char *rv = NULL; + CODE: + rv = owl_function_ztext_stylestrip(ztext); + RETVAL = rv; + OUTPUT: + RETVAL + CLEANUP: + if (rv) owl_free(rv); + diff --git a/perlwrap.c b/perlwrap.c new file mode 100644 index 0000000..b4213ba --- /dev/null +++ b/perlwrap.c @@ -0,0 +1,235 @@ +/* This file was autogenerated from perlwrap.pm. DO NOT EDIT !!!! */ + +char *owl_perlwrap_codebuff = + "# $Id: perlwrap.pm,v 1.4 2009/03/28 21:00:42 kretch Exp $\n" + "#\n" + "# This is all linked into the binary and evaluated when perl starts up...\n" + "#\n" + "#####################################################################\n" + "#####################################################################\n" + "\n" + "package owl;\n" + "\n" + "# bootstrap in C bindings and glue\n" + "bootstrap owl 1.2;\n" + "\n" + "# populate global variable space for legacy owlconf files \n" + "sub _format_msg_legacy_wrap {\n" + " my ($m) = @_;\n" + " $m->legacy_populate_global();\n" + " return &owl::format_msg($m);\n" + "}\n" + "\n" + "# populate global variable space for legacy owlconf files \n" + "sub _receive_msg_legacy_wrap {\n" + " my ($m) = @_;\n" + " $m->legacy_populate_global();\n" + " return &owl::receive_msg($m);\n" + "}\n" + "\n" + "# make owl::<command>(\"foo\") be aliases to owl::command(\"<command> foo\");\n" + "sub AUTOLOAD {\n" + " my $called = $AUTOLOAD;\n" + " $called =~ s/.*:://;\n" + " return &owl::command(\"$called \".join(\" \",@_));\n" + "}\n" + "\n" + "#####################################################################\n" + "#####################################################################\n" + "\n" + "package owl::Message;\n" + "\n" + "sub type { return shift->{\"type\"}; }\n" + "sub direction { return shift->{\"direction\"}; }\n" + "sub time { return shift->{\"time\"}; }\n" + "sub id { return shift->{\"id\"}; }\n" + "sub body { return shift->{\"body\"}; }\n" + "sub sender { return shift->{\"sender\"}; }\n" + "sub recipient { return shift->{\"recipient\"}; }\n" + "sub login { return shift->{\"login\"}; }\n" + "sub is_private { return shift->{\"private\"}; }\n" + "\n" + "sub is_login { return shift->login eq \"login\"; }\n" + "sub is_logout { return shift->login eq \"logout\"; }\n" + "sub is_loginout { my $m=shift; return ($m->is_login or $m->is_logout); }\n" + "sub is_incoming { return (shift->{\"direction\"} eq \"in\"); }\n" + "sub is_outgoing { return (shift->{\"direction\"} eq \"out\"); }\n" + "\n" + "sub is_deleted { return shift->{\"deleted\"}; }\n" + "\n" + "sub is_zephyr { return (shift->{\"type\"} eq \"zephyr\"); }\n" + "sub is_aim { return (shift->{\"type\"} eq \"aim\"); }\n" + "sub is_admin { return (shift->{\"type\"} eq \"admin\"); }\n" + "sub is_generic { return (shift->{\"type\"} eq \"generic\"); }\n" + "\n" + "# These are overridden by appropriate message types\n" + "sub is_ping { return 0; }\n" + "sub is_mail { return 0; }\n" + "sub is_personal { return undef; }\n" + "sub class { return undef; }\n" + "sub instance { return undef; }\n" + "sub realm { return undef; }\n" + "sub opcode { return undef; }\n" + "sub header { return undef; }\n" + "sub host { return undef; }\n" + "sub hostname { return undef; }\n" + "sub auth { return undef; }\n" + "sub fields { return undef; }\n" + "sub zsig { return undef; }\n" + "sub zwriteline { return undef; }\n" + "sub login_host { return undef; }\n" + "sub login_tty { return undef; }\n" + "\n" + "sub pretty_sender { return shift->sender; }\n" + "\n" + "sub delete {\n" + " my ($m) = @_;\n" + " &owl::command(\"delete --id \".$m->id);\n" + "}\n" + "\n" + "sub undelete {\n" + " my ($m) = @_;\n" + " &owl::command(\"undelete --id \".$m->id);\n" + "}\n" + "\n" + "# Serializes the message into something similar to the zwgc->vt format\n" + "sub serialize {\n" + " my ($this) = @_;\n" + " my $s;\n" + " for my $f (keys %$this) {\n" + " my $val = $this->{$f};\n" + " if (ref($val) eq \"ARRAY\") {\n" + " for my $i (0..@$val-1) {\n" + " my $aval;\n" + " $aval = $val->[$i];\n" + " $aval =~ s/\\n/\\n$f.$i: /g;\n" + " $s .= \"$f.$i: $aval\\n\"; \n" + " }\n" + " } else {\n" + " $val =~ s/\\n/\\n$f: /g;\n" + " $s .= \"$f: $val\\n\";\n" + " }\n" + " }\n" + " return $s;\n" + "}\n" + "\n" + "# Populate the annoying legacy global variables\n" + "sub legacy_populate_global {\n" + " my ($m) = @_;\n" + " $owl::direction = $m->direction ;\n" + " $owl::type = $m->type ;\n" + " $owl::id = $m->id ;\n" + " $owl::class = $m->class ;\n" + " $owl::instance = $m->instance ;\n" + " $owl::recipient = $m->recipient ;\n" + " $owl::sender = $m->sender ;\n" + " $owl::realm = $m->realm ;\n" + " $owl::opcode = $m->opcode ;\n" + " $owl::zsig = $m->zsig ;\n" + " $owl::msg = $m->body ;\n" + " $owl::time = $m->time ;\n" + " $owl::host = $m->host ;\n" + " $owl::login = $m->login ;\n" + " $owl::auth = $m->auth ;\n" + " if ($m->fields) {\n" + " @owl::fields = @{$m->fields};\n" + " @main::fields = @{$m->fields};\n" + " } else {\n" + " @owl::fields = undef;\n" + " @main::fields = undef;\n" + " }\n" + "}\n" + "\n" + "#####################################################################\n" + "#####################################################################\n" + "\n" + "package owl::Message::Admin;\n" + "\n" + "@ISA = qw( owl::Message );\n" + "\n" + "sub header { return shift->{\"header\"}; }\n" + "\n" + "#####################################################################\n" + "#####################################################################\n" + "\n" + "package owl::Message::Generic;\n" + "\n" + "@ISA = qw( owl::Message );\n" + "\n" + "#####################################################################\n" + "#####################################################################\n" + "\n" + "package owl::Message::AIM;\n" + "\n" + "@ISA = qw( owl::Message );\n" + "\n" + "# all non-loginout AIM messages are personal for now...\n" + "sub is_personal { \n" + " return !(shift->is_loginout);\n" + "}\n" + "\n" + "#####################################################################\n" + "#####################################################################\n" + "\n" + "package owl::Message::Zephyr;\n" + "\n" + "@ISA = qw( owl::Message );\n" + "\n" + "sub login_tty { \n" + " my ($m) = @_;\n" + " return undef if (!$m->is_loginout);\n" + " return $m->fields->[2];\n" + "}\n" + "\n" + "sub login_host { \n" + " my ($m) = @_;\n" + " return undef if (!$m->is_loginout);\n" + " return $m->fields->[0];\n" + "}\n" + "\n" + "sub zwriteline { return shift->{\"zwriteline\"}; }\n" + "\n" + "sub zsig { return shift->{\"zsig\"}; }\n" + "\n" + "sub is_ping { return (lc(shift->opcode) eq \"ping\"); }\n" + "\n" + "sub is_personal { \n" + " my ($m) = @_;\n" + " return ((lc($m->class) eq \"message\")\n" + " && (lc($m->instance) eq \"personal\")\n" + " && $m->is_private);\n" + "}\n" + "\n" + "sub is_mail { \n" + " my ($m) = @_;\n" + " return ((lc($m->class) eq \"mail\") && $m->is_private);\n" + "}\n" + "\n" + "sub pretty_sender {\n" + " my ($m) = @_;\n" + " my $sender = $m->sender;\n" + " my $realm = owl::zephyr_getrealm();\n" + " $sender =~ s/\\@$realm$//;\n" + " return $sender;\n" + "}\n" + "\n" + "# These are arguably zephyr-specific\n" + "sub class { return shift->{\"class\"}; }\n" + "sub instance { return shift->{\"instance\"}; }\n" + "sub realm { return shift->{\"realm\"}; }\n" + "sub opcode { return shift->{\"opcode\"}; }\n" + "sub host { return shift->{\"hostname\"}; }\n" + "sub hostname { return shift->{\"hostname\"}; }\n" + "sub header { return shift->{\"header\"}; }\n" + "sub auth { return shift->{\"auth\"}; }\n" + "sub fields { return shift->{\"fields\"}; }\n" + "sub zsig { return shift->{\"zsig\"}; }\n" + "\n" + "#####################################################################\n" + "#####################################################################\n" + "\n" + "# switch to package main when we're done\n" + "package main;\n" + "\n" + "1;\n" + ; diff --git a/perlwrap.pm b/perlwrap.pm new file mode 100644 index 0000000..38815a5 --- /dev/null +++ b/perlwrap.pm @@ -0,0 +1,231 @@ +# $Id: perlwrap.pm,v 1.4 2009/03/28 21:00:42 kretch Exp $ +# +# This is all linked into the binary and evaluated when perl starts up... +# +##################################################################### +##################################################################### + +package owl; + +# bootstrap in C bindings and glue +bootstrap owl 1.2; + +# populate global variable space for legacy owlconf files +sub _format_msg_legacy_wrap { + my ($m) = @_; + $m->legacy_populate_global(); + return &owl::format_msg($m); +} + +# populate global variable space for legacy owlconf files +sub _receive_msg_legacy_wrap { + my ($m) = @_; + $m->legacy_populate_global(); + return &owl::receive_msg($m); +} + +# make owl::<command>("foo") be aliases to owl::command("<command> foo"); +sub AUTOLOAD { + my $called = $AUTOLOAD; + $called =~ s/.*:://; + return &owl::command("$called ".join(" ",@_)); +} + +##################################################################### +##################################################################### + +package owl::Message; + +sub type { return shift->{"type"}; } +sub direction { return shift->{"direction"}; } +sub time { return shift->{"time"}; } +sub id { return shift->{"id"}; } +sub body { return shift->{"body"}; } +sub sender { return shift->{"sender"}; } +sub recipient { return shift->{"recipient"}; } +sub login { return shift->{"login"}; } +sub is_private { return shift->{"private"}; } + +sub is_login { return shift->login eq "login"; } +sub is_logout { return shift->login eq "logout"; } +sub is_loginout { my $m=shift; return ($m->is_login or $m->is_logout); } +sub is_incoming { return (shift->{"direction"} eq "in"); } +sub is_outgoing { return (shift->{"direction"} eq "out"); } + +sub is_deleted { return shift->{"deleted"}; } + +sub is_zephyr { return (shift->{"type"} eq "zephyr"); } +sub is_aim { return (shift->{"type"} eq "aim"); } +sub is_admin { return (shift->{"type"} eq "admin"); } +sub is_generic { return (shift->{"type"} eq "generic"); } + +# These are overridden by appropriate message types +sub is_ping { return 0; } +sub is_mail { return 0; } +sub is_personal { return undef; } +sub class { return undef; } +sub instance { return undef; } +sub realm { return undef; } +sub opcode { return undef; } +sub header { return undef; } +sub host { return undef; } +sub hostname { return undef; } +sub auth { return undef; } +sub fields { return undef; } +sub zsig { return undef; } +sub zwriteline { return undef; } +sub login_host { return undef; } +sub login_tty { return undef; } + +sub pretty_sender { return shift->sender; } + +sub delete { + my ($m) = @_; + &owl::command("delete --id ".$m->id); +} + +sub undelete { + my ($m) = @_; + &owl::command("undelete --id ".$m->id); +} + +# Serializes the message into something similar to the zwgc->vt format +sub serialize { + my ($this) = @_; + my $s; + for my $f (keys %$this) { + my $val = $this->{$f}; + if (ref($val) eq "ARRAY") { + for my $i (0..@$val-1) { + my $aval; + $aval = $val->[$i]; + $aval =~ s/\n/\n$f.$i: /g; + $s .= "$f.$i: $aval\n"; + } + } else { + $val =~ s/\n/\n$f: /g; + $s .= "$f: $val\n"; + } + } + return $s; +} + +# Populate the annoying legacy global variables +sub legacy_populate_global { + my ($m) = @_; + $owl::direction = $m->direction ; + $owl::type = $m->type ; + $owl::id = $m->id ; + $owl::class = $m->class ; + $owl::instance = $m->instance ; + $owl::recipient = $m->recipient ; + $owl::sender = $m->sender ; + $owl::realm = $m->realm ; + $owl::opcode = $m->opcode ; + $owl::zsig = $m->zsig ; + $owl::msg = $m->body ; + $owl::time = $m->time ; + $owl::host = $m->host ; + $owl::login = $m->login ; + $owl::auth = $m->auth ; + if ($m->fields) { + @owl::fields = @{$m->fields}; + @main::fields = @{$m->fields}; + } else { + @owl::fields = undef; + @main::fields = undef; + } +} + +##################################################################### +##################################################################### + +package owl::Message::Admin; + +@ISA = qw( owl::Message ); + +sub header { return shift->{"header"}; } + +##################################################################### +##################################################################### + +package owl::Message::Generic; + +@ISA = qw( owl::Message ); + +##################################################################### +##################################################################### + +package owl::Message::AIM; + +@ISA = qw( owl::Message ); + +# all non-loginout AIM messages are personal for now... +sub is_personal { + return !(shift->is_loginout); +} + +##################################################################### +##################################################################### + +package owl::Message::Zephyr; + +@ISA = qw( owl::Message ); + +sub login_tty { + my ($m) = @_; + return undef if (!$m->is_loginout); + return $m->fields->[2]; +} + +sub login_host { + my ($m) = @_; + return undef if (!$m->is_loginout); + return $m->fields->[0]; +} + +sub zwriteline { return shift->{"zwriteline"}; } + +sub zsig { return shift->{"zsig"}; } + +sub is_ping { return (lc(shift->opcode) eq "ping"); } + +sub is_personal { + my ($m) = @_; + return ((lc($m->class) eq "message") + && (lc($m->instance) eq "personal") + && $m->is_private); +} + +sub is_mail { + my ($m) = @_; + return ((lc($m->class) eq "mail") && $m->is_private); +} + +sub pretty_sender { + my ($m) = @_; + my $sender = $m->sender; + my $realm = owl::zephyr_getrealm(); + $sender =~ s/\@$realm$//; + return $sender; +} + +# These are arguably zephyr-specific +sub class { return shift->{"class"}; } +sub instance { return shift->{"instance"}; } +sub realm { return shift->{"realm"}; } +sub opcode { return shift->{"opcode"}; } +sub host { return shift->{"hostname"}; } +sub hostname { return shift->{"hostname"}; } +sub header { return shift->{"header"}; } +sub auth { return shift->{"auth"}; } +sub fields { return shift->{"fields"}; } +sub zsig { return shift->{"zsig"}; } + +##################################################################### +##################################################################### + +# switch to package main when we're done +package main; + +1; diff --git a/popexec.c b/popexec.c new file mode 100644 index 0000000..ff1b8db --- /dev/null +++ b/popexec.c @@ -0,0 +1,215 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_FILIO_H +#include <sys/filio.h> +#endif +#include <sys/wait.h> + +static const char fileIdent[] = "$Id: popexec.c,v 1.5 2009/03/29 12:38:22 kretch Exp $"; + +/* starts up popexec in a new viewwin */ +owl_popexec *owl_popexec_new(char *command) +{ + owl_popexec *pe; + owl_popwin *pw; + owl_viewwin *v; + int pipefds[2], child_write_fd, parent_read_fd; + int pid; + + pe = owl_malloc(sizeof(owl_popexec)); + if (!pe) return NULL; + pe->winactive=0; + pe->pid=0; + pe->refcount=0; + + pw=owl_global_get_popwin(&g); + pe->vwin=v=owl_global_get_viewwin(&g); + + owl_popwin_up(pw); + owl_viewwin_init_text(v, owl_popwin_get_curswin(pw), + owl_popwin_get_lines(pw), owl_popwin_get_cols(pw), + ""); + owl_popwin_refresh(pw); + owl_viewwin_redisplay(v, 0); + owl_global_set_needrefresh(&g); + owl_viewwin_set_onclose_hook(v, owl_popexec_viewwin_onclose, pe); + pe->refcount++; + + if (0 != pipe(pipefds)) { + owl_function_error("owl_function_popless_exec: pipe failed\n"); + return NULL; + } + parent_read_fd = pipefds[0]; + child_write_fd = pipefds[1]; + pid = fork(); + if (pid == -1) { + close(pipefds[0]); + close(pipefds[1]); + owl_function_error("owl_function_popless_exec: fork failed\n"); + return NULL; + } else if (pid != 0) { + close(child_write_fd); + /* still in owl */ + pe->pid=pid; + pe->winactive=1; + pe->dispatch.fd = parent_read_fd; + pe->dispatch.cfunc = owl_popexec_inputhandler; + pe->dispatch.destroy = owl_popexec_free_dispatch; + pe->dispatch.data = pe; + owl_select_add_dispatch(&pe->dispatch); + pe->refcount++; + } else { + /* in the child process */ + char *argv[4]; + int i; + int fdlimit = sysconf(_SC_OPEN_MAX); + + for (i=0; i<fdlimit; i++) { + if (i!=child_write_fd) close(i); + } + dup2(child_write_fd, 1 /*stdout*/); + dup2(child_write_fd, 2 /*stderr*/); + close(child_write_fd); + + argv[0] = "sh"; + argv[1] = "-c"; + argv[2] = command; + argv[3] = 0; + execv("/bin/sh", argv); + _exit(127); + } + + return pe; +} + +void owl_popexec_inputhandler(owl_dispatch *d) +{ + owl_popexec *pe = d->data; + int navail, bread, rv_navail; + char *buf; + int status; + + if (!pe) return; + + /* If pe->winactive is 0 then the vwin has closed. + * If pe->pid is 0 then the child has already been reaped. + * if d->fd is -1 then the fd has been closed out. + * Under these cases we want to get to a state where: + * - data read until end if child running + * - child reaped + * - fd closed + * - callback removed + */ + + /* the viewwin has closed */ + if (!pe->pid && !pe->winactive) { + owl_select_remove_dispatch(d->fd); + return; + } + + if (0 != (rv_navail = ioctl(d->fd, FIONREAD, (void*)&navail))) { + owl_function_debugmsg("ioctl error"); + } + + /* check to see if the child has ended gracefully and no more data is + * ready to be read... */ + if (navail==0 && pe->pid>0 && waitpid(pe->pid, &status, WNOHANG) > 0) { + owl_function_debugmsg("waitpid got child status: <%d>\n", status); + pe->pid = 0; + if (pe->winactive) { + owl_viewwin_append_text(pe->vwin, "\n"); + owl_viewwin_redisplay(pe->vwin, 1); + } + owl_select_remove_dispatch(d->fd); + return; + } + + if (d->fd<0 || !pe->pid || !pe->winactive || rv_navail) { + owl_function_error("popexec should not have reached this point"); + return; + } + + if (navail<=0) return; + if (navail>1024) { navail = 1024; } + buf = owl_malloc(navail+1); + owl_function_debugmsg("about to read %d", navail); + bread = read(d->fd, buf, navail); + if (bread<0) { + perror("read"); + owl_function_debugmsg("read error"); + } + if (buf[navail-1] != '\0') { + buf[navail] = '\0'; + } + owl_function_debugmsg("got data: <%s>", buf); + if (pe->winactive) { + owl_viewwin_append_text(pe->vwin, buf); + owl_viewwin_redisplay(pe->vwin, 1); + } + owl_free(buf); + +} + +void owl_popexec_free_dispatch(owl_dispatch *d) +{ + owl_popexec *pe = d->data; + close(d->fd); + owl_popexec_unref(pe); +} + +void owl_popexec_viewwin_onclose(owl_viewwin *vwin, void *data) +{ + owl_popexec *pe = (owl_popexec*)data; + int status, rv; + + pe->winactive = 0; + if (pe->dispatch.fd>0) { + owl_select_remove_dispatch(pe->dispatch.fd); + } + if (pe->pid) { + /* TODO: we should handle the case where SIGTERM isn't good enough */ + rv = kill(pe->pid, SIGTERM); + owl_function_debugmsg("kill of pid %d returned %d", pe->pid, rv); + rv = waitpid(pe->pid, &status, 0); + owl_function_debugmsg("waidpid returned %d, status %d", rv, status); + pe->pid = 0; + } + owl_function_debugmsg("unref of %p from onclose", pe); + owl_popexec_unref(pe); +} + +void owl_popexec_unref(owl_popexec *pe) +{ + owl_function_debugmsg("unref of %p was %d", pe, pe->refcount); + pe->refcount--; + if (pe->refcount<=0) { + owl_function_debugmsg("doing free of %p", pe); + owl_free(pe); + } +} diff --git a/popwin.c b/popwin.c new file mode 100644 index 0000000..31c28ab --- /dev/null +++ b/popwin.c @@ -0,0 +1,156 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id: popwin.c,v 1.9 2009/03/29 12:38:22 kretch Exp $"; + +int owl_popwin_init(owl_popwin *pw) +{ + pw->active=0; + pw->needsfirstrefresh=0; + pw->lines=0; + pw->cols=0; + return(0); +} + +int owl_popwin_up(owl_popwin *pw) +{ + int glines, gcols, startcol, startline; + + /* calculate the size of the popwin */ + glines=owl_global_get_lines(&g); + gcols=owl_global_get_cols(&g); + + pw->lines = owl_util_min(glines,24)*3/4 + owl_util_max(glines-24,0)/2; + startline = (glines-pw->lines)/2; + + pw->cols = owl_util_min(gcols,90)*15/16 + owl_util_max(gcols-90,0)/2; + startcol = (gcols-pw->cols)/2; + + pw->borderwin=newwin(pw->lines, pw->cols, startline, startcol); + pw->popwin=newwin(pw->lines-2, pw->cols-2, startline+1, startcol+1); + pw->needsfirstrefresh=1; + + meta(pw->popwin,TRUE); + nodelay(pw->popwin, 1); + keypad(pw->popwin, TRUE); + + werase(pw->popwin); + werase(pw->borderwin); + if (owl_global_is_fancylines(&g)) { + box(pw->borderwin, 0, 0); + } else { + box(pw->borderwin, '|', '-'); + wmove(pw->borderwin, 0, 0); + waddch(pw->borderwin, '+'); + wmove(pw->borderwin, pw->lines-1, 0); + waddch(pw->borderwin, '+'); + wmove(pw->borderwin, pw->lines-1, pw->cols-1); + waddch(pw->borderwin, '+'); + wmove(pw->borderwin, 0, pw->cols-1); + waddch(pw->borderwin, '+'); + } + + wnoutrefresh(pw->popwin); + wnoutrefresh(pw->borderwin); + owl_global_set_needrefresh(&g); + pw->active=1; + return(0); +} + +int owl_popwin_display_text(owl_popwin *pw, char *text) +{ + waddstr(pw->popwin, text); + wnoutrefresh(pw->popwin); + touchwin(pw->borderwin); + wnoutrefresh(pw->borderwin); + owl_global_set_needrefresh(&g); + return(0); +} + +int owl_popwin_close(owl_popwin *pw) +{ + delwin(pw->popwin); + delwin(pw->borderwin); + pw->active=0; + owl_global_set_needrefresh(&g); + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + owl_function_full_redisplay(&g); + return(0); +} + +int owl_popwin_is_active(owl_popwin *pw) +{ + if (pw->active==1) return(1); + return(0); +} + +/* this will refresh the border as well as the text area */ +int owl_popwin_refresh(owl_popwin *pw) +{ + touchwin(pw->borderwin); + touchwin(pw->popwin); + + wnoutrefresh(pw->borderwin); + wnoutrefresh(pw->popwin); + owl_global_set_needrefresh(&g); + return(0); +} + +void owl_popwin_set_handler(owl_popwin *pw, void (*func)(int ch)) +{ + pw->handler=func; +} + +void owl_popwin_unset_handler(owl_popwin *pw) +{ + pw->handler=NULL; +} + +WINDOW *owl_popwin_get_curswin(owl_popwin *pw) +{ + return(pw->popwin); +} + +int owl_popwin_get_lines(owl_popwin *pw) +{ + return(pw->lines-2); +} + +int owl_popwin_get_cols(owl_popwin *pw) +{ + return(pw->cols-2); +} + +int owl_popwin_needs_first_refresh(owl_popwin *pw) +{ + if (pw->needsfirstrefresh) return(1); + return(0); +} + +void owl_popwin_no_needs_first_refresh(owl_popwin *pw) +{ + pw->needsfirstrefresh=0; +} @@ -0,0 +1,114 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <string.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: regex.c,v 1.8 2009/03/29 19:51:32 kretch Exp $"; + +void owl_regex_init(owl_regex *re) +{ + re->negate=0; + re->string=NULL; +} + +int owl_regex_create(owl_regex *re, char *string) +{ + int ret; + char buff1[LINE]; + char *ptr; + + re->string=owl_strdup(string); + + ptr=string; + re->negate=0; + if (string[0]=='!') { + ptr++; + re->negate=1; + } + + /* set the regex */ + ret=regcomp(&(re->re), ptr, REG_EXTENDED|REG_ICASE); + if (ret) { + regerror(ret, NULL, buff1, LINE); + owl_function_makemsg("Error in regular expression: %s", buff1); + owl_free(re->string); + re->string=NULL; + return(-1); + } + + return(0); +} + +int owl_regex_create_quoted(owl_regex *re, char *string) +{ + char *quoted; + + quoted=owl_text_quote(string, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH); + owl_regex_create(re, quoted); + owl_free(quoted); + return(0); +} + +int owl_regex_compare(owl_regex *re, char *string) +{ + int out, ret; + + /* if the regex is not set we match */ + if (!owl_regex_is_set(re)) { + return(0); + } + + ret=regexec(&(re->re), string, 0, NULL, 0); + out=ret; + if (re->negate) { + out=!out; + } + return(out); +} + +int owl_regex_is_set(owl_regex *re) +{ + if (re->string) return(1); + return(0); +} + +char *owl_regex_get_string(owl_regex *re) +{ + return(re->string); +} + +void owl_regex_copy(owl_regex *a, owl_regex *b) +{ + b->negate=a->negate; + b->string=owl_strdup(a->string); + memcpy(&(b->re), &(a->re), sizeof(regex_t)); +} + +void owl_regex_free(owl_regex *re) +{ + if (re->string) owl_free(re->string); + + /* do we need to free the regular expression? */ +} diff --git a/select.c b/select.c new file mode 100644 index 0000000..a4b16c0 --- /dev/null +++ b/select.c @@ -0,0 +1,269 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +/* This select loop implementation was contributed by developers of + * the branched BarnOwl project. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id $"; + +static int dispatch_active = 0; + +/* Returns the index of the dispatch for the file descriptor. */ +int owl_select_find_dispatch(int fd) +{ + int i, len; + owl_list *dl; + owl_dispatch *d; + + dl = owl_global_get_dispatchlist(&g); + len = owl_list_get_size(dl); + owl_function_debugmsg("test: len is %i", len); + for(i = 0; i < len; i++) { + d = (owl_dispatch*)owl_list_get_element(dl, i); + if (d->fd == fd) return i; + } + return -1; +} + +void owl_select_remove_dispatch_at(int elt) /* noproto */ +{ + owl_list *dl; + owl_dispatch *d; + + dl = owl_global_get_dispatchlist(&g); + d = (owl_dispatch*)owl_list_get_element(dl, elt); + owl_list_remove_element(dl, elt); + if (d->destroy) { + d->destroy(d); + } +} + +/* Adds a new owl_dispatch to the list, replacing existing ones if needed. */ +void owl_select_add_dispatch(owl_dispatch *d) +{ + int elt; + owl_list *dl; + + d->needs_gc = 0; + + elt = owl_select_find_dispatch(d->fd); + dl = owl_global_get_dispatchlist(&g); + + if (elt != -1) { /* If we have a dispatch for this FD */ + owl_dispatch *d_old; + owl_function_debugmsg("select: duplicate dispatch found"); + d_old = (owl_dispatch*)owl_list_get_element(dl, elt); + /* Ignore if we're adding the same dispatch again. Otherwise + replace the old dispatch. */ + if (d_old != d) { + owl_select_remove_dispatch_at(elt); + } + } + owl_list_append_element(dl, d); +} + +/* Removes an owl_dispatch to the list, based on it's file descriptor. */ +void owl_select_remove_dispatch(int fd) +{ + int elt; + owl_list *dl; + owl_dispatch *d; + + elt = owl_select_find_dispatch(fd); + if(elt == -1) { + return; + } else if(dispatch_active) { + /* Defer the removal until dispatch is done walking the list */ + dl = owl_global_get_dispatchlist(&g); + d = (owl_dispatch*)owl_list_get_element(dl, elt); + d->needs_gc = 1; + } else { + owl_select_remove_dispatch_at(elt); + } +} + +int owl_select_dispatch_count() +{ + return owl_list_get_size(owl_global_get_dispatchlist(&g)); +} + +int owl_select_dispatch_prepare_fd_sets(fd_set *r, fd_set *e) +{ + int i, len, max_fd; + owl_dispatch *d; + owl_list *dl; + + dl = owl_global_get_dispatchlist(&g); + FD_ZERO(r); + FD_ZERO(e); + max_fd = 0; + len = owl_select_dispatch_count(g); + for(i = 0; i < len; i++) { + d = (owl_dispatch*)owl_list_get_element(dl, i); + FD_SET(d->fd, r); + FD_SET(d->fd, e); + if (max_fd < d->fd) max_fd = d->fd; + } + return max_fd + 1; +} + +void owl_select_gc() +{ + int i; + owl_list *dl; + + dl = owl_global_get_dispatchlist(&g); + /* + * Count down so we aren't set off by removing items from the list + * during the iteration. + */ + for(i = owl_list_get_size(dl) - 1; i >= 0; i--) { + owl_dispatch *d = owl_list_get_element(dl, i); + if(d->needs_gc) { + owl_select_remove_dispatch_at(i); + } + } +} + +void owl_select_dispatch(fd_set *fds, int max_fd) +{ + int i, len; + owl_dispatch *d; + owl_list *dl; + + dl = owl_global_get_dispatchlist(&g); + len = owl_select_dispatch_count(); + + dispatch_active = 1; + + for(i = 0; i < len; i++) { + d = (owl_dispatch*)owl_list_get_element(dl, i); + /* While d shouldn't normally be null, the list may be altered by + * functions we dispatch to. */ + if (d != NULL && !d->needs_gc && FD_ISSET(d->fd, fds)) { + if (d->cfunc != NULL) { + d->cfunc(d); + } + } + } + + dispatch_active = 0; + owl_select_gc(); +} + +int owl_select_aim_hack(fd_set *rfds, fd_set *wfds) +{ + aim_conn_t *cur; + aim_session_t *sess; + int max_fd; + + FD_ZERO(rfds); + FD_ZERO(wfds); + max_fd = 0; + sess = owl_global_get_aimsess(&g); + for (cur = sess->connlist, max_fd = 0; cur; cur = cur->next) { + if (cur->fd != -1) { + FD_SET(cur->fd, rfds); + if (cur->status & AIM_CONN_STATUS_INPROGRESS) { + /* Yes, we're checking writable sockets here. Without it, AIM + login is really slow. */ + FD_SET(cur->fd, wfds); + } + + if (cur->fd > max_fd) + max_fd = cur->fd; + } + } + return max_fd; +} + +void owl_select() +{ + int i, max_fd, aim_max_fd, aim_done; + fd_set r; + fd_set e; + fd_set aim_rfds, aim_wfds; + struct timeval timeout; + + /* owl_select_process_timers(&timeout); */ + + /* settings to 5 seconds for the moment, we can raise this when the + * odd select behavior with zephyr is understood + */ + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + max_fd = owl_select_dispatch_prepare_fd_sets(&r, &e); + + /* AIM HACK: + * + * The problem - I'm not sure where to hook into the owl/faim + * interface to keep track of when the AIM socket(s) open and + * close. In particular, the bosconn thing throws me off. So, + * rather than register particular dispatchers for AIM, I look up + * the relevant FDs and add them to select's watch lists, then + * check for them individually before moving on to the other + * dispatchers. --asedeno + */ + aim_done = 1; + FD_ZERO(&aim_rfds); + FD_ZERO(&aim_wfds); + if (owl_global_is_doaimevents(&g)) { + aim_done = 0; + aim_max_fd = owl_select_aim_hack(&aim_rfds, &aim_wfds); + if (max_fd < aim_max_fd) max_fd = aim_max_fd; + for(i = 0; i <= aim_max_fd; i++) { + if (FD_ISSET(i, &aim_rfds)) { + FD_SET(i, &r); + FD_SET(i, &e); + } + } + } + /* END AIM HACK */ + + if ( select(max_fd+1, &r, &aim_wfds, &e, &timeout) ) { + /* Merge fd_sets and clear AIM FDs. */ + for(i = 0; i <= max_fd; i++) { + /* Merge all interesting FDs into one set, since we have a + single dispatch per FD. */ + if (FD_ISSET(i, &r) || FD_ISSET(i, &aim_wfds) || FD_ISSET(i, &e)) { + /* AIM HACK: no separate dispatch, just process here if + needed, and only once per run through. */ + if (!aim_done && (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds))) { + owl_process_aim(); + aim_done = 1; + } + else { + FD_SET(i, &r); + } + } + } + /* NOTE: the same dispatch function is called for both exceptional + and read ready FDs. */ + owl_select_dispatch(&r, max_fd); + } +} diff --git a/stubgen.pl b/stubgen.pl new file mode 100755 index 0000000..2e08e4d --- /dev/null +++ b/stubgen.pl @@ -0,0 +1,44 @@ + +# $Id: stubgen.pl,v 1.2 2002/06/28 06:18:47 nygren Exp $ + +if ($#ARGV eq -1) { + @ARGV=`ls *.c`; + chop(@ARGV); +} + +print qq(/* THIS FILE WAS AUTOGENERATED BY STUBGEN.PL --- DO NOT EDIT BY HAND!!! */\n\n); +print qq(#include "owl.h"); + +foreach $file (@ARGV) { + open(FILE, $file); + + print "/* -------------------------------- $file -------------------------------- */\n"; + while (<FILE>) { + if (m|^\s*OWLVAR_([A-Z_0-9]+)\s*\(\s*"([^"]+)"\s*/\*\s*%OwlVarStub:?([a-z0-9_]+)?\s*\*/|) { # " + my $vartype = $1; + my $varname = $2; + my $altvarname = $2; + $altvarname = $3 if ($3); + if ($vartype =~ /^BOOL/) { + print "void owl_global_set_${altvarname}_on(owl_global *g) {\n"; + print " owl_variable_set_bool_on(&g->vars, \"$varname\");\n}\n"; + print "void owl_global_set_${altvarname}_off(owl_global *g) {\n"; + print " owl_variable_set_bool_off(&g->vars, \"$varname\");\n}\n"; + print "int owl_global_is_$altvarname(owl_global *g) {\n"; + print " return owl_variable_get_bool(&g->vars, \"$varname\");\n}\n"; + } elsif ($vartype =~ /^PATH/ or $vartype =~ /^STRING/) { + print "void owl_global_set_$altvarname(owl_global *g, char *text) {\n"; + print " owl_variable_set_string(&g->vars, \"$varname\", text);\n}\n"; + print "char *owl_global_get_$altvarname(owl_global *g) {\n"; + print " return owl_variable_get_string(&g->vars, \"$varname\");\n}\n"; + } elsif ($vartype =~ /^INT/ or $vartype =~ /^ENUM/) { + print "void owl_global_set_$altvarname(owl_global *g, int n) {\n"; + print " owl_variable_set_int(&g->vars, \"$varname\", n);\n}\n"; + print "int owl_global_get_$altvarname(owl_global *g) {\n"; + print " return owl_variable_get_int(&g->vars, \"$varname\");\n}\n"; + } + } + } + close(FILE); + print "\n"; +} @@ -0,0 +1,128 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id: style.c,v 1.9 2009/03/29 12:38:22 kretch Exp $"; + +void owl_style_create_internal(owl_style *s, char *name, void (*formatfunc) (owl_fmtext *fm, owl_message *m), char *description) +{ + s->type=OWL_STYLE_TYPE_INTERNAL; + s->name=owl_strdup(name); + if (description) { + s->description=owl_strdup(description); + } else { + s->description=owl_sprintf("Owl internal style %s", name); + } + s->perlfuncname=NULL; + s->formatfunc=formatfunc; +} + +void owl_style_create_perl(owl_style *s, char *name, char *perlfuncname, char *description) +{ + s->type=OWL_STYLE_TYPE_PERL; + s->name=owl_strdup(name); + s->perlfuncname=owl_strdup(perlfuncname); + if (description) { + s->description=owl_strdup(description); + } else { + s->description=owl_sprintf("User-defined perl style that calls %s", + perlfuncname); + } + s->formatfunc=NULL; +} + +int owl_style_matches_name(owl_style *s, char *name) +{ + if (!strcmp(s->name, name)) return(1); + return(0); +} + +char *owl_style_get_name(owl_style *s) +{ + return(s->name); +} + +char *owl_style_get_description(owl_style *s) +{ + return(s->description); +} + +/* Use style 's' to format message 'm' into fmtext 'fm'. + * 'fm' should already be be initialzed + */ +void owl_style_get_formattext(owl_style *s, owl_fmtext *fm, owl_message *m) +{ + if (s->type==OWL_STYLE_TYPE_INTERNAL) { + (* s->formatfunc)(fm, m); + } else if (s->type==OWL_STYLE_TYPE_PERL) { + char *body, *indent; + int curlen; + + /* run the perl function */ + body=owl_perlconfig_getmsg(m, 1, s->perlfuncname); + if (!strcmp(body, "")) { + owl_free(body); + body=owl_strdup("<unformatted message>"); + } + + /* indent and ensure ends with a newline */ + indent=owl_malloc(strlen(body)+(owl_text_num_lines(body))*OWL_TAB+10); + owl_text_indent(indent, body, OWL_TAB); + curlen = strlen(indent); + if (curlen==0 || indent[curlen-1] != '\n') { + indent[curlen] = '\n'; + indent[curlen+1] = '\0'; + } + + /* fmtext_append. This needs to change */ + owl_fmtext_append_ztext(fm, indent); + + owl_free(indent); + owl_free(body); + } +} + +int owl_style_validate(owl_style *s) { + if (!s) { + return -1; + } else if (s->type==OWL_STYLE_TYPE_INTERNAL) { + return 0; + } else if (s->type==OWL_STYLE_TYPE_PERL + && s->perlfuncname + && owl_perlconfig_is_function(s->perlfuncname)) { + return 0; + } else { + return -1; + } +} + +void owl_style_free(owl_style *s) +{ + if (s->name) owl_free(s->name); + if (s->description) owl_free(s->description); + if (s->type==OWL_STYLE_TYPE_PERL && s->perlfuncname) { + owl_free(s->perlfuncname); + } +} diff --git a/stylefunc.c b/stylefunc.c new file mode 100644 index 0000000..d3280b9 --- /dev/null +++ b/stylefunc.c @@ -0,0 +1,794 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id: stylefunc.c,v 1.22 2009/04/05 23:36:54 kretch Exp $"; + +/* In all of these functions, 'fm' is expected to already be + * initialized. + */ + +void owl_stylefunc_basic(owl_fmtext *fm, owl_message *m) +{ +#ifdef HAVE_LIBZEPHYR + char *body, *indent, *ptr, *zsigbuff, *frombuff; + ZNotice_t *n; +#endif + + if (owl_message_is_type_zephyr(m) && owl_message_is_direction_in(m)) { +#ifdef HAVE_LIBZEPHYR + n=owl_message_get_notice(m); + + /* get the body */ + body=owl_strdup(owl_message_get_body(m)); + body=realloc(body, strlen(body)+30); + + /* add a newline if we need to */ + if (body[0]!='\0' && body[strlen(body)-1]!='\n') { + strcat(body, "\n"); + } + + /* do the indenting into indent */ + indent=owl_malloc(strlen(body)+owl_text_num_lines(body)*OWL_MSGTAB+10); + owl_text_indent(indent, body, OWL_MSGTAB); + + /* edit the from addr for printing */ + frombuff=short_zuser(owl_message_get_sender(m)); + + /* set the message for printing */ + owl_fmtext_append_normal(fm, OWL_TABSTR); + + if (owl_message_is_ping(m)) { + owl_fmtext_append_bold(fm, "PING"); + owl_fmtext_append_normal(fm, " from "); + owl_fmtext_append_bold(fm, frombuff); + owl_fmtext_append_normal(fm, "\n"); + } else if (owl_message_is_loginout(m)) { + char *host, *tty; + + host=owl_message_get_attribute_value(m, "loginhost"); + tty=owl_message_get_attribute_value(m, "logintty"); + + if (owl_message_is_login(m)) { + owl_fmtext_append_bold(fm, "LOGIN"); + } else if (owl_message_is_logout(m)) { + owl_fmtext_append_bold(fm, "LOGOUT"); + } + if (owl_message_is_pseudo(m)) { + owl_fmtext_append_bold(fm, " (PSEUDO)"); + } + owl_fmtext_append_normal(fm, " for "); + ptr=short_zuser(owl_message_get_instance(m)); + owl_fmtext_append_bold(fm, ptr); + owl_free(ptr); + owl_fmtext_append_normal(fm, " at "); + owl_fmtext_append_normal(fm, host ? host : ""); + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, tty ? tty : ""); + owl_fmtext_append_normal(fm, "\n"); + } else { + owl_fmtext_append_normal(fm, "From: "); + if (strcasecmp(owl_message_get_class(m), "message")) { + owl_fmtext_append_normal(fm, "Class "); + owl_fmtext_append_normal(fm, owl_message_get_class(m)); + owl_fmtext_append_normal(fm, " / Instance "); + owl_fmtext_append_normal(fm, owl_message_get_instance(m)); + owl_fmtext_append_normal(fm, " / "); + } + owl_fmtext_append_normal(fm, frombuff); + if (strcasecmp(owl_message_get_realm(m), owl_zephyr_get_realm())) { + owl_fmtext_append_normal(fm, " {"); + owl_fmtext_append_normal(fm, owl_message_get_realm(m)); + owl_fmtext_append_normal(fm, "} "); + } + + /* stick on the zsig */ + zsigbuff=owl_malloc(strlen(owl_message_get_zsig(m))+30); + owl_message_pretty_zsig(m, zsigbuff); + owl_fmtext_append_normal(fm, " ("); + owl_fmtext_append_ztext(fm, zsigbuff); + owl_fmtext_append_normal(fm, ")"); + owl_fmtext_append_normal(fm, "\n"); + owl_free(zsigbuff); + + /* then the indented message */ + owl_fmtext_append_ztext(fm, indent); + + /* make personal messages bold for smaat users */ + if (owl_global_is_userclue(&g, OWL_USERCLUE_CLASSES)) { + if (owl_message_is_personal(m)) { + owl_fmtext_addattr((&m->fmtext), OWL_FMTEXT_ATTR_BOLD); + } + } + } + + owl_free(body); + owl_free(indent); + owl_free(frombuff); +#endif + } else if (owl_message_is_type_zephyr(m) && owl_message_is_direction_out(m)) { + char *indent, *text, *zsigbuff, *foo; + + text=owl_message_get_body(m); + + indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10); + owl_text_indent(indent, text, OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, "To: "); + foo=short_zuser(owl_message_get_recipient(m)); + owl_fmtext_append_normal(fm, foo); + owl_free(foo); + owl_fmtext_append_normal(fm, " (Zsig: "); + + zsigbuff=owl_malloc(strlen(owl_message_get_zsig(m))+30); + owl_message_pretty_zsig(m, zsigbuff); + owl_fmtext_append_ztext(fm, zsigbuff); + owl_free(zsigbuff); + + owl_fmtext_append_normal(fm, ")"); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_ztext(fm, indent); + if (text[strlen(text)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + + owl_free(indent); + } else if (owl_message_is_type_aim(m)) { + char *indent; + + if (owl_message_is_loginout(m)) { + owl_fmtext_append_normal(fm, OWL_TABSTR); + if (owl_message_is_login(m)) { + owl_fmtext_append_bold(fm, "AIM LOGIN"); + } else { + owl_fmtext_append_bold(fm, "AIM LOGOUT"); + } + owl_fmtext_append_normal(fm, " for "); + owl_fmtext_append_normal(fm, owl_message_get_sender(m)); + owl_fmtext_append_normal(fm, "\n"); + } else if (owl_message_is_direction_in(m)) { + indent=owl_malloc(strlen(owl_message_get_body(m))+owl_text_num_lines(owl_message_get_body(m))*OWL_MSGTAB+10); + owl_text_indent(indent, owl_message_get_body(m), OWL_MSGTAB); + owl_fmtext_append_bold(fm, OWL_TABSTR); + owl_fmtext_append_bold(fm, "AIM from "); + owl_fmtext_append_bold(fm, owl_message_get_sender(m)); + owl_fmtext_append_bold(fm, "\n"); + owl_fmtext_append_bold(fm, indent); + if (indent[strlen(indent)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + owl_free(indent); + } else if (owl_message_is_direction_out(m)) { + indent=owl_malloc(strlen(owl_message_get_body(m))+owl_text_num_lines(owl_message_get_body(m))*OWL_MSGTAB+10); + owl_text_indent(indent, owl_message_get_body(m), OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, "AIM sent to "); + owl_fmtext_append_normal(fm, owl_message_get_recipient(m)); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_ztext(fm, indent); + if (indent[strlen(indent)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + owl_free(indent); + } + } else if (owl_message_is_type_admin(m)) { + char *text, *header, *indent; + + text=owl_message_get_body(m); + header=owl_message_get_attribute_value(m, "adminheader"); + + indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10); + owl_text_indent(indent, text, OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_bold(fm, "OWL ADMIN "); + owl_fmtext_append_ztext(fm, header); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_ztext(fm, indent); + if (text[strlen(text)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + + owl_free(indent); + } else { + char *text, *header, *indent; + + text=owl_message_get_body(m); + header=owl_sprintf("%s from: %s to: %s", + owl_message_get_type(m), + owl_message_get_sender(m), + owl_message_get_recipient(m)); + + indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10); + owl_text_indent(indent, text, OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, header); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_normal(fm, indent); + if (text[strlen(text)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + + owl_free(indent); + owl_free(header); + } +} + +void owl_stylefunc_default(owl_fmtext *fm, owl_message *m) +{ + char *shorttimestr; +#ifdef HAVE_LIBZEPHYR + char *body, *indent, *ptr, *zsigbuff, *frombuff; + ZNotice_t *n; +#endif + + shorttimestr=owl_message_get_shorttimestr(m); + + if (owl_message_is_type_zephyr(m) && owl_message_is_direction_in(m)) { +#ifdef HAVE_LIBZEPHYR + n=owl_message_get_notice(m); + + /* get the body */ + body=owl_malloc(strlen(owl_message_get_body(m))+30); + strcpy(body, owl_message_get_body(m)); + + /* add a newline if we need to */ + if (body[0]!='\0' && body[strlen(body)-1]!='\n') { + strcat(body, "\n"); + } + + /* do the indenting into indent */ + indent=owl_malloc(strlen(body)+owl_text_num_lines(body)*OWL_MSGTAB+10); + owl_text_indent(indent, body, OWL_MSGTAB); + + /* edit the from addr for printing */ + frombuff=short_zuser(owl_message_get_sender(m)); + + /* set the message for printing */ + owl_fmtext_append_normal(fm, OWL_TABSTR); + + if (owl_message_is_ping(m) && owl_message_is_private(m)) { + owl_fmtext_append_bold(fm, "PING"); + owl_fmtext_append_normal(fm, " from "); + owl_fmtext_append_bold(fm, frombuff); + owl_fmtext_append_normal(fm, "\n"); + } else if (owl_message_is_loginout(m)) { + char *host, *tty; + + host=owl_message_get_attribute_value(m, "loginhost"); + tty=owl_message_get_attribute_value(m, "logintty"); + + if (owl_message_is_login(m)) { + owl_fmtext_append_bold(fm, "LOGIN"); + } else if (owl_message_is_logout(m)) { + owl_fmtext_append_bold(fm, "LOGOUT"); + } + + if (owl_message_is_pseudo(m)) owl_fmtext_append_bold(fm, " (PSEUDO)"); + + owl_fmtext_append_normal(fm, " for "); + ptr=short_zuser(owl_message_get_instance(m)); + owl_fmtext_append_bold(fm, ptr); + owl_free(ptr); + owl_fmtext_append_normal(fm, " at "); + owl_fmtext_append_normal(fm, host ? host : ""); + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, tty ? tty : ""); + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, shorttimestr); + owl_fmtext_append_normal(fm, "\n"); + } else { + owl_fmtext_append_normal(fm, owl_message_get_class(m)); + owl_fmtext_append_normal(fm, " / "); + owl_fmtext_append_normal(fm, owl_message_get_instance(m)); + owl_fmtext_append_normal(fm, " / "); + owl_fmtext_append_bold(fm, frombuff); + if (strcasecmp(owl_message_get_realm(m), ZGetRealm())) { + owl_fmtext_append_normal(fm, " {"); + owl_fmtext_append_normal(fm, owl_message_get_realm(m)); + owl_fmtext_append_normal(fm, "}"); + } + if (strcmp(owl_message_get_opcode(m), "")) { + owl_fmtext_append_normal(fm, " ["); + owl_fmtext_append_normal(fm, owl_message_get_opcode(m)); + owl_fmtext_append_normal(fm, "]"); + } + + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, shorttimestr); + + /* stick on the zsig */ + zsigbuff=owl_malloc(strlen(owl_message_get_zsig(m))+30); + owl_message_pretty_zsig(m, zsigbuff); + owl_fmtext_append_normal(fm, " ("); + owl_fmtext_append_ztext(fm, zsigbuff); + owl_fmtext_append_normal(fm, ")"); + owl_fmtext_append_normal(fm, "\n"); + owl_free(zsigbuff); + + /* then the indented message */ + owl_fmtext_append_ztext(fm, indent); + + /* make private messages bold for smaat users */ + if (owl_global_is_userclue(&g, OWL_USERCLUE_CLASSES)) { + if (owl_message_is_personal(m)) { + owl_fmtext_addattr((&m->fmtext), OWL_FMTEXT_ATTR_BOLD); + } + } + } + + owl_free(body); + owl_free(indent); + owl_free(frombuff); +#endif + } else if (owl_message_is_type_zephyr(m) && owl_message_is_direction_out(m)) { + char *indent, *text, *zsigbuff, *foo; + + text=owl_message_get_body(m); + + indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10); + owl_text_indent(indent, text, OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, "Zephyr sent to "); + foo=short_zuser(owl_message_get_recipient(m)); + owl_fmtext_append_normal(fm, foo); + owl_free(foo); + + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, shorttimestr); + + owl_fmtext_append_normal(fm, " (Zsig: "); + + zsigbuff=owl_malloc(strlen(owl_message_get_zsig(m))+30); + owl_message_pretty_zsig(m, zsigbuff); + owl_fmtext_append_ztext(fm, zsigbuff); + owl_free(zsigbuff); + + owl_fmtext_append_normal(fm, ")"); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_ztext(fm, indent); + if (text[strlen(text)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + + owl_free(indent); + } else if (owl_message_is_type_aim(m)) { + char *indent; + + if (owl_message_is_loginout(m)) { + owl_fmtext_append_normal(fm, OWL_TABSTR); + if (owl_message_is_login(m)) { + owl_fmtext_append_bold(fm, "AIM LOGIN"); + } else { + owl_fmtext_append_bold(fm, "AIM LOGOUT"); + } + owl_fmtext_append_normal(fm, " for "); + owl_fmtext_append_normal(fm, owl_message_get_sender(m)); + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, shorttimestr); + owl_fmtext_append_normal(fm, "\n"); + } else if (owl_message_is_direction_in(m)) { + indent=owl_malloc(strlen(owl_message_get_body(m))+owl_text_num_lines(owl_message_get_body(m))*OWL_MSGTAB+10); + owl_text_indent(indent, owl_message_get_body(m), OWL_MSGTAB); + owl_fmtext_append_bold(fm, OWL_TABSTR); + owl_fmtext_append_bold(fm, "AIM from "); + owl_fmtext_append_bold(fm, owl_message_get_sender(m)); + + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, shorttimestr); + + owl_fmtext_append_bold(fm, "\n"); + owl_fmtext_append_bold(fm, indent); + if (indent[strlen(indent)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + owl_free(indent); + } else if (owl_message_is_direction_out(m)) { + indent=owl_malloc(strlen(owl_message_get_body(m))+owl_text_num_lines(owl_message_get_body(m))*OWL_MSGTAB+10); + owl_text_indent(indent, owl_message_get_body(m), OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, "AIM sent to "); + owl_fmtext_append_normal(fm, owl_message_get_recipient(m)); + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, shorttimestr); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_ztext(fm, indent); + if (indent[strlen(indent)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + owl_free(indent); + } + } else if (owl_message_is_type_admin(m)) { + char *text, *header, *indent; + + text=owl_message_get_body(m); + header=owl_message_get_attribute_value(m, "adminheader"); + + indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10); + owl_text_indent(indent, text, OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_bold(fm, "OWL ADMIN "); + owl_fmtext_append_ztext(fm, header); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_ztext(fm, indent); + if (text[strlen(text)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + + owl_free(indent); + } else { + char *text, *header, *indent; + + text=owl_message_get_body(m); + header=owl_sprintf("%s from: %s to: %s", + owl_message_get_type(m), + owl_message_get_sender(m), + owl_message_get_recipient(m)); + + indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10); + owl_text_indent(indent, text, OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, header); + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, shorttimestr); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_normal(fm, indent); + if (text[strlen(text)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + + owl_free(indent); + owl_free(header); + } + owl_free(shorttimestr); +} + +void owl_stylefunc_oneline(owl_fmtext *fm, owl_message *m) +{ + char *tmp; + char *baseformat="%s %-13.13s %-11.11s %-12.12s "; + char *sender, *recip; +#ifdef HAVE_LIBZEPHYR + ZNotice_t *n; +#endif + + sender=short_zuser(owl_message_get_sender(m)); + recip=short_zuser(owl_message_get_recipient(m)); + + if (owl_message_is_type_zephyr(m)) { +#ifdef HAVE_LIBZEPHYR + n=owl_message_get_notice(m); + + owl_fmtext_append_spaces(fm, OWL_TAB); + + if (owl_message_is_loginout(m)) { + char *host, *tty; + + host=owl_message_get_attribute_value(m, "loginhost"); + tty=owl_message_get_attribute_value(m, "logintty"); + + if (owl_message_is_login(m)) { + tmp=owl_sprintf(baseformat, "<", owl_message_is_pseudo(m)?"LOGIN-P":"LOGIN", "", sender); + owl_fmtext_append_normal(fm, tmp); + owl_free(tmp); + } else if (owl_message_is_logout(m)) { + tmp=owl_sprintf(baseformat, "<", owl_message_is_pseudo(m)?"LOGOUT-P":"LOGOUT", "", sender); + owl_fmtext_append_normal(fm, tmp); + owl_free(tmp); + } + + owl_fmtext_append_normal(fm, "at "); + owl_fmtext_append_normal(fm, host ? host : ""); + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, tty ? tty : ""); + owl_fmtext_append_normal(fm, "\n"); + + } else if (owl_message_is_ping(m)) { + tmp=owl_sprintf(baseformat, "<", "PING", "", sender); + owl_fmtext_append_normal(fm, tmp); + owl_fmtext_append_normal(fm, "\n"); + owl_free(tmp); + + } else { + if (owl_message_is_direction_in(m)) { + tmp=owl_sprintf(baseformat, "<", owl_message_get_class(m), owl_message_get_instance(m), sender); + } else if (owl_message_is_direction_out(m)) { + tmp=owl_sprintf(baseformat, ">", owl_message_get_class(m), owl_message_get_instance(m), recip); + } else { + tmp=owl_sprintf(baseformat, "-", owl_message_get_class(m), owl_message_get_instance(m), sender); + } + owl_fmtext_append_normal(fm, tmp); + if (tmp) owl_free(tmp); + + tmp=owl_strdup(owl_message_get_body(m)); + owl_text_tr(tmp, '\n', ' '); + owl_fmtext_append_ztext(fm, tmp); + owl_fmtext_append_normal(fm, "\n"); + if (tmp) owl_free(tmp); + } + + /* make personal messages bold for smaat users */ + if (owl_global_is_userclue(&g, OWL_USERCLUE_CLASSES) && + owl_message_is_personal(m) && + owl_message_is_direction_in(m)) { + owl_fmtext_addattr(fm, OWL_FMTEXT_ATTR_BOLD); + } + + owl_free(sender); + owl_free(recip); +#endif + } else if (owl_message_is_type_aim(m)) { + owl_fmtext_append_spaces(fm, OWL_TAB); + if (owl_message_is_login(m)) { + tmp=owl_sprintf(baseformat, "<", "AIM LOGIN", "", owl_message_get_sender(m)); + owl_fmtext_append_normal(fm, tmp); + owl_fmtext_append_normal(fm, "\n"); + if (tmp) owl_free(tmp); + } else if (owl_message_is_logout(m)) { + tmp=owl_sprintf(baseformat, "<", "AIM LOGOUT", "", owl_message_get_sender(m)); + owl_fmtext_append_normal(fm, tmp); + owl_fmtext_append_normal(fm, "\n"); + if (tmp) owl_free(tmp); + } else { + if (owl_message_is_direction_in(m)) { + tmp=owl_sprintf(baseformat, "<", "AIM", "", owl_message_get_sender(m)); + owl_fmtext_append_normal(fm, tmp); + if (tmp) owl_free(tmp); + } else if (owl_message_is_direction_out(m)) { + tmp=owl_sprintf(baseformat, ">", "AIM", "", owl_message_get_recipient(m)); + owl_fmtext_append_normal(fm, tmp); + if (tmp) owl_free(tmp); + } + + tmp=owl_strdup(owl_message_get_body(m)); + owl_text_tr(tmp, '\n', ' '); + owl_fmtext_append_normal(fm, tmp); + owl_fmtext_append_normal(fm, "\n"); + if (tmp) owl_free(tmp); + + /* make personal messages bold for smaat users */ + if (owl_global_is_userclue(&g, OWL_USERCLUE_CLASSES) && owl_message_is_direction_in(m)) { + owl_fmtext_addattr(fm, OWL_FMTEXT_ATTR_BOLD); + } + } + } else if (owl_message_is_type_admin(m)) { + owl_fmtext_append_spaces(fm, OWL_TAB); + owl_fmtext_append_normal(fm, "< ADMIN "); + + tmp=owl_strdup(owl_message_get_body(m)); + owl_text_tr(tmp, '\n', ' '); + owl_fmtext_append_normal(fm, tmp); + owl_fmtext_append_normal(fm, "\n"); + if (tmp) owl_free(tmp); + } else { + owl_fmtext_append_spaces(fm, OWL_TAB); + owl_fmtext_append_normal(fm, "< LOOPBACK "); + + tmp=owl_strdup(owl_message_get_body(m)); + owl_text_tr(tmp, '\n', ' '); + owl_fmtext_append_normal(fm, tmp); + owl_fmtext_append_normal(fm, "\n"); + if (tmp) owl_free(tmp); + } + +} + +void owl_stylefunc_vt(owl_fmtext *fm, owl_message *m) +{ +#ifdef HAVE_LIBZEPHYR + char *body, *indent, *ptr, *frombuff; + owl_fmtext fm_first, fm_other, fm_tmp; + ZNotice_t *n; +#endif + char *sender, *hostname, *timestr, *classinst1, *classinst2; + + if (owl_message_is_type_zephyr(m) && owl_message_is_direction_in(m)) { +#ifdef HAVE_LIBZEPHYR + n=owl_message_get_notice(m); + + /* get the body */ + body=owl_malloc(strlen(owl_message_get_body(m))+30); + strcpy(body, owl_message_get_body(m)); + + /* add a newline if we need to */ + if (body[0]!='\0' && body[strlen(body)-1]!='\n') { + strcat(body, "\n"); + } + + owl_fmtext_init_null(&fm_tmp); + owl_fmtext_append_ztext(&fm_tmp, body); + owl_fmtext_init_null(&fm_first); + owl_fmtext_truncate_lines(&fm_tmp, 0, 1, &fm_first); + + /* do the indenting into indent */ + indent=owl_malloc(strlen(body)+owl_text_num_lines(body)*OWL_MSGTAB+10); + owl_text_indent(indent, body, 31); + + owl_fmtext_free(&fm_tmp); + owl_fmtext_init_null(&fm_tmp); + owl_fmtext_append_ztext(&fm_tmp, indent); + owl_fmtext_init_null(&fm_other); + owl_fmtext_truncate_lines(&fm_tmp, 1, owl_fmtext_num_lines(&fm_tmp)-1, &fm_other); + owl_fmtext_free(&fm_tmp); + + /* edit the from addr for printing */ + frombuff=short_zuser(owl_message_get_sender(m)); + sender=owl_sprintf("%-9.9s", frombuff); + + hostname=owl_sprintf("%-9.9s", owl_message_get_hostname(m)); + timestr=owl_strdup("00:00"); + classinst1=owl_sprintf("<%s>[%s]", owl_message_get_class(m), owl_message_get_instance(m)); + classinst2=owl_sprintf("%-9.9s", classinst1); + + /* set the message for printing */ + owl_fmtext_append_normal(fm, OWL_TABSTR); + + if (owl_message_is_ping(m) && owl_message_is_private(m)) { + owl_fmtext_append_bold(fm, "PING"); + owl_fmtext_append_normal(fm, " from "); + owl_fmtext_append_bold(fm, frombuff); + owl_fmtext_append_normal(fm, "\n"); + } else if (owl_message_is_loginout(m)) { + char *host, *tty; + + host=owl_message_get_attribute_value(m, "loginhost"); + tty=owl_message_get_attribute_value(m, "logintty"); + + if (owl_message_is_login(m)) { + owl_fmtext_append_bold(fm, "LOGIN"); + } else if (owl_message_is_logout(m)) { + owl_fmtext_append_bold(fm, "LOGOUT"); + } + if (owl_message_is_pseudo(m)) owl_fmtext_append_bold(fm, " (PSEUDO)"); + + owl_fmtext_append_normal(fm, " for "); + ptr=short_zuser(owl_message_get_instance(m)); + owl_fmtext_append_bold(fm, ptr); + owl_free(ptr); + owl_fmtext_append_normal(fm, " at "); + owl_fmtext_append_normal(fm, host ? host : ""); + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, tty ? tty : ""); + owl_fmtext_append_normal(fm, "\n"); + } else { + owl_fmtext_append_normal(fm, sender); + owl_fmtext_append_normal(fm, "|"); + owl_fmtext_append_normal(fm, hostname); + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, timestr); + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_normal(fm, classinst2); + + owl_fmtext_append_normal(fm, " "); + owl_fmtext_append_fmtext(fm, &fm_first); + owl_fmtext_append_fmtext(fm, &fm_other); + + owl_fmtext_free(&fm_other); + owl_fmtext_free(&fm_first); + + /* make private messages bold for smaat users */ + if (owl_global_is_userclue(&g, OWL_USERCLUE_CLASSES)) { + if (owl_message_is_personal(m)) { + owl_fmtext_addattr((&m->fmtext), OWL_FMTEXT_ATTR_BOLD); + } + } + } + + owl_free(sender); + owl_free(frombuff); + owl_free(hostname); + owl_free(timestr); + owl_free(classinst1); + owl_free(classinst2); + + owl_free(body); + owl_free(indent); +#endif + } else if (owl_message_is_type_zephyr(m) && owl_message_is_direction_out(m)) { + char *indent, *text, *zsigbuff, *foo; + + text=owl_message_get_body(m); + + indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10); + owl_text_indent(indent, text, OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, "Zephyr sent to "); + foo=short_zuser(owl_message_get_recipient(m)); + owl_fmtext_append_normal(fm, foo); + owl_free(foo); + owl_fmtext_append_normal(fm, " (Zsig: "); + + zsigbuff=owl_malloc(strlen(owl_message_get_zsig(m))+30); + owl_message_pretty_zsig(m, zsigbuff); + owl_fmtext_append_ztext(fm, zsigbuff); + owl_free(zsigbuff); + + owl_fmtext_append_normal(fm, ")"); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_ztext(fm, indent); + if (text[strlen(text)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + + owl_free(indent); + } else if (owl_message_is_type_aim(m)) { + char *indent; + + if (owl_message_is_loginout(m)) { + owl_fmtext_append_normal(fm, OWL_TABSTR); + if (owl_message_is_login(m)) { + owl_fmtext_append_bold(fm, "AIM LOGIN"); + } else { + owl_fmtext_append_bold(fm, "AIM LOGOUT"); + } + owl_fmtext_append_normal(fm, " for "); + owl_fmtext_append_normal(fm, owl_message_get_sender(m)); + owl_fmtext_append_normal(fm, "\n"); + } else if (owl_message_is_direction_in(m)) { + indent=owl_malloc(strlen(owl_message_get_body(m))+owl_text_num_lines(owl_message_get_body(m))*OWL_MSGTAB+10); + owl_text_indent(indent, owl_message_get_body(m), OWL_MSGTAB); + owl_fmtext_append_bold(fm, OWL_TABSTR); + owl_fmtext_append_bold(fm, "AIM from "); + owl_fmtext_append_bold(fm, owl_message_get_sender(m)); + owl_fmtext_append_bold(fm, "\n"); + owl_fmtext_append_bold(fm, indent); + if (indent[strlen(indent)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + owl_free(indent); + } else if (owl_message_is_direction_out(m)) { + indent=owl_malloc(strlen(owl_message_get_body(m))+owl_text_num_lines(owl_message_get_body(m))*OWL_MSGTAB+10); + owl_text_indent(indent, owl_message_get_body(m), OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, "AIM sent to "); + owl_fmtext_append_normal(fm, owl_message_get_recipient(m)); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_ztext(fm, indent); + if (indent[strlen(indent)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + owl_free(indent); + } + } else if (owl_message_is_type_admin(m)) { + char *text, *header, *indent; + + text=owl_message_get_body(m); + header=owl_message_get_attribute_value(m, "adminheader"); + + indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10); + owl_text_indent(indent, text, OWL_MSGTAB); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_bold(fm, "OWL ADMIN "); + owl_fmtext_append_ztext(fm, header); + owl_fmtext_append_normal(fm, "\n"); + owl_fmtext_append_ztext(fm, indent); + if (text[strlen(text)-1]!='\n') { + owl_fmtext_append_normal(fm, "\n"); + } + + owl_free(indent); + } else { + + } +} diff --git a/tester.c b/tester.c new file mode 100644 index 0000000..7953be1 --- /dev/null +++ b/tester.c @@ -0,0 +1,192 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" +#include <unistd.h> +#include <stdlib.h> + +static const char fileIdent[] = "$Id: tester.c,v 1.7 2009/03/29 12:38:22 kretch Exp $"; + +owl_global g; + +void screeninit() +{ + char buff[1024]; + + sprintf(buff, "TERMINFO=%s", TERMINFO); + putenv(buff); + + initscr(); + start_color(); + /* cbreak(); */ + raw(); + noecho(); + intrflush(stdscr,FALSE); + keypad(stdscr,TRUE); + nodelay(stdscr,1); + clear(); + refresh(); + meta(stdscr, TRUE); +} + +void test1() +{ + int j; + owl_editwin e; + + screeninit(); + + owl_editwin_init(&e, stdscr, LINES, COLS, OWL_EDITWIN_STYLE_MULTILINE, NULL); + /* owl_editwin_set_locktext(&e, "Here is some locktext:\n");*/ + doupdate(); + while (1) { + usleep(50); + + j=getch(); + + if (j==ERR) continue; + + if (j==3) break; + + if (j==27) { + j=getch(); + if (j==ERR) continue; + owl_editwin_process_char(&e, j); + doupdate(); + } else { + owl_editwin_process_char(&e, j); + doupdate(); + } + } + endwin(); + printf("Had:\n%s", owl_editwin_get_text(&e)); +} + +void test2(char *in) +{ + owl_fmtext t; + + screeninit(); + + owl_fmtext_init_null(&t); + owl_fmtext_append_ztext(&t, in); + owl_fmtext_curs_waddstr(&t, stdscr); + wrefresh(stdscr); + sleep(5000); + endwin(); +} + +void test3() +{ + ZNotice_t *n; + + printf("%i\n", sizeof(n->z_uid.zuid_addr)); + /* gethostbyaddr((char *) &(n->z_uid.zuid_addr), sizeof(n->z_uid.zuid_addr), AF_INET); */ +} + +void colorinfo() +{ + char buff[1024]; + + screeninit(); + sprintf(buff, "Have %i COLOR_PAIRS\n", COLOR_PAIRS); + addstr(buff); + refresh(); + sleep(10); + endwin(); +} + +void test4() +{ + int j; + char buff[1024]; + + screeninit(); + + while (1) { + usleep(100); + + j=getch(); + + if (j==ERR) continue; + + if (j==3) break; + sprintf(buff, "%o\n", j); + addstr(buff); + } + endwin(); +} + +void test_keypress() +{ + int j, rev; + char buff[1024], buff2[64]; + + screeninit(); + + while (1) { + usleep(100); + + j=wgetch(stdscr); + + if (j==ERR) continue; + + if (j==3) break; + if (0 == owl_keypress_tostring(j, 0, buff2, 1000)) { + rev = owl_keypress_fromstring(buff2); + sprintf(buff, "%s : 0x%x 0%o %d %d %s\n", buff2, j, j, j, rev, + (j==rev?"matches":"*** WARNING: Does Not Reverse")); + } else { + sprintf(buff, "UNKNOWN : 0x%x 0%o %d\n", j, j, j); + } + addstr(buff); + } + endwin(); +} + + +int main(int argc, char **argv, char **env) +{ + int numfailures=0; + if (argc==2 && 0==strcmp(argv[1],"reg")) { + numfailures += owl_util_regtest(); + numfailures += owl_dict_regtest(); + numfailures += owl_variable_regtest(); + if (numfailures) { + fprintf(stderr, "*** WARNING: %d failures total\n", numfailures); + } + return(numfailures); + } else if (argc==2 && 0==strcmp(argv[1],"test1")) { + test1(); + } else if (argc==2 && 0==strcmp(argv[1],"colorinfo")) { + colorinfo(); + } else if (argc==2 && 0==strcmp(argv[1],"test4")) { + test4(); + } else if (argc==2 && 0==strcmp(argv[1],"keypress")) { + test_keypress(); + } else { + fprintf(stderr, "No test specified. Current options are: reg test1\n"); + } + return(0); +} @@ -0,0 +1,423 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: text.c,v 1.15 2009/04/05 23:36:54 kretch Exp $"; + +/* start with line aline (where the first line is 1) and print 'lines' + * lines + */ +int owl_text_truncate_lines(char *out, char *in, int aline, int lines) +{ + char *ptr1, *ptr2; + int i; + + strcpy(out, ""); + + if (aline==0) aline=1; /* really illegal use */ + + /* find the starting line */ + ptr1=in; + if (aline!=1) { + for (i=0; i<aline-1; i++) { + ptr1=strchr(ptr1, '\n'); + if (!ptr1) return(-1); + ptr1++; + } + } + /* ptr1 now holds the starting point */ + + /* copy in the next 'lines' lines */ + if (lines<1) return(-1); + + for (i=0; i<lines; i++) { + ptr2=strchr(ptr1, '\n'); + if (!ptr2) { + strcat(out, ptr1); + return(-1); + } + strncat(out, ptr1, ptr2-ptr1+1); + ptr1=ptr2+1; + } + return(0); +} + + +/* the first column is column 0. The message is expected to end in a + * new line for now */ +void owl_text_truncate_cols(char *out, char *in, int acol, int bcol) +{ + char *ptr1, *ptr2, *tmpbuff, *last; + int len; + + tmpbuff=owl_malloc(strlen(in)+20); + + strcpy(tmpbuff, ""); + last=in+strlen(in)-1; + ptr1=in; + while (ptr1<last) { + ptr2=strchr(ptr1, '\n'); + if (!ptr2) { + /* but this shouldn't happen if we end in a \n */ + break; + } + + if (ptr2==ptr1) { + strcat(tmpbuff, "\n"); + ptr1++; + continue; + } + + /* we need to check that we won't run over here */ + if ( (ptr2-ptr1) < (bcol-acol) ) { + len=ptr2-(ptr1+acol); + } else { + len=bcol-acol; + } + if ((ptr1+len)>=last) { + len-=last-(ptr1+len); + } + + strncat(tmpbuff, ptr1+acol, len); + strcat(tmpbuff, "\n"); + + ptr1=ptr2+1; + } + strcpy(out, tmpbuff); + owl_free(tmpbuff); +} + + +void owl_text_indent(char *out, char *in, int n) +{ + char *ptr1, *ptr2, *last; + int i; + + strcpy(out, ""); + + last=in+strlen(in)-1; + ptr1=in; + while (ptr1<=last) { + for (i=0; i<n; i++) { + strcat(out, " "); + } + ptr2=strchr(ptr1, '\n'); + if (!ptr2) { + strcat(out, ptr1); + break; + } else { + strncat(out, ptr1, ptr2-ptr1+1); + } + ptr1=ptr2+1; + } +} + +int owl_text_num_lines(char *in) +{ + int lines, i; + + lines=0; + for (i=0; in[i]!='\0'; i++) { + if (in[i]=='\n') lines++; + } + + /* if the last char wasn't a \n there's one more line */ + if (i>0 && in[i-1]!='\n') lines++; + + return(lines); +} + + +/* caller must free the return */ +char *owl_text_htmlstrip(char *in) +{ + char *ptr1, *end, *ptr2, *ptr3, *out, *out2; + + out=owl_malloc(strlen(in)+30); + strcpy(out, ""); + + ptr1=in; + end=in+strlen(in); + + while(ptr1<end) { + /* look for an open bracket */ + ptr2=strchr(ptr1, '<'); + + /* if none, copy in from here to end and exit */ + if (ptr2==NULL) { + strcat(out, ptr1); + break; + } + + /* otherwise copy in everything before the open bracket */ + if (ptr2>ptr1) { + strncat(out, ptr1, ptr2-ptr1); + } + + /* find the close bracket */ + ptr3=strchr(ptr2, '>'); + + /* if there is no close, copy as you are and exit */ + if (!ptr3) { + strcat(out, ptr2); + break; + } + + /* look for things we know */ + if (!strncasecmp(ptr2, "<BODY ", 6) || + !strncasecmp(ptr2, "<FONT", 5) || + !strncasecmp(ptr2, "<HTML", 5) || + !strncasecmp(ptr2, "</FONT", 6) || + !strncasecmp(ptr2, "</HTML", 6) || + !strncasecmp(ptr2, "</BODY", 6)) { + + /* advance to beyond the angle brakcet and go again */ + ptr1=ptr3+1; + continue; + } + if (!strncasecmp(ptr2, "<BR>", 4)) { + strcat(out, "\n"); + ptr1=ptr3+1; + continue; + } + + /* if it wasn't something we know, copy to the > and go again */ + strncat(out, ptr2, ptr3-ptr2+1); + ptr1=ptr3+1; + } + + out2=owl_text_substitute(out, "<", "<"); + owl_free(out); + out=owl_text_substitute(out2, ">", ">"); + owl_free(out2); + out2=owl_text_substitute(out, "&", "&"); + owl_free(out); + out=owl_text_substitute(out2, """, "\""); + owl_free(out2); + out2=owl_text_substitute(out, " ", " "); + owl_free(out); + out=owl_text_substitute(out2, " ", " "); + owl_free(out2); + out2=owl_text_substitute(out, " ", " "); + owl_free(out); + out=owl_text_substitute(out2, "&endash;", "--"); + owl_free(out2); + out2=owl_text_substitute(out, "&emdash;", "---"); + owl_free(out); + + return(out2); +} + +/* caller must free the return */ +char *owl_text_wordwrap(char *in, int col) +{ + char *out; + int cur, lastspace, len, lastnewline; + + out=owl_strdup(in); + len=strlen(in); + cur=0; + lastspace=-1; + lastnewline=-1; + + while (cur<(len-1)) { + if (out[cur]==' ') { + lastspace=cur; + cur++; + continue; + } else if (out[cur]=='\n') { + lastnewline=cur; + cur++; + continue; + } + + /* do we need to wrap? */ + if ( (cur-(lastnewline+1)) > col ) { + if (lastspace==-1 || + (lastnewline>0 && (lastspace<=lastnewline))) { + /* we can't help, sorry */ + cur++; + continue; + } + + /* turn the last space into a newline */ + out[lastspace]='\n'; + lastnewline=lastspace; + lastspace=-1; + cur++; + continue; + } + + cur++; + continue; + } + return(out); +} + +/* this modifies 'in' */ +void owl_text_wordunwrap(char *in) +{ + int i, j; + + j=strlen(in); + for (i=0; i<j; i++) { + if ( (in[i]=='\n') && + ((i>0) && (i<(j-1))) && + (in[i-1]!='\n') && + (in[i+1]!='\n') ) + in[i]=' '; + } +} + +/* exactly like strstr but case insensitive */ +char *stristr(char *a, char *b) +{ + char *x, *y, *ret; + + if ((x=owl_strdup(a))==NULL) return(NULL); + if ((y=owl_strdup(b))==NULL) return(NULL); + downstr(x); + downstr(y); + ret=strstr(x, y); + if (ret==NULL) { + owl_free(x); + owl_free(y); + return(NULL); + } + ret=ret-x+a; + owl_free(x); + owl_free(y); + return(ret); +} + +/* return 1 if a string is only whitespace, otherwise 0 */ +int only_whitespace(char *s) +{ + int i; + for (i=0; s[i]; i++) { + if (!isspace((int) s[i])) return(0); + } + return(1); +} + +char *owl_getquoting(char *line) +{ + if (line[0]=='\0') return("'"); + if (strchr(line, '\'')) return("\""); + if (strchr(line, '"')) return("'"); + if (strchr(line, ' ')) return("'"); + return(""); +} + +/* Return a string with any occurances of 'from' replaced with 'to'. + * Does not currently handle backslash quoting, but may in the future. + * Caller must free returned string. + */ +char *owl_text_substitute(char *in, char *from, char *to) +{ + + char *out; + int outlen, tolen, fromlen, inpos=0, outpos=0; + + if (!*from) return owl_strdup(in); + + outlen = strlen(in)+1; + tolen = strlen(to); + fromlen = strlen(from); + out = owl_malloc(outlen); + + while (in[inpos]) { + if (!strncmp(in+inpos, from, fromlen)) { + outlen += tolen; + out = owl_realloc(out, outlen); + strcpy(out+outpos, to); + inpos += fromlen; + outpos += tolen; + } else { + out[outpos] = in[inpos]; + inpos++; outpos++; + } + } + out[outpos] = '\0'; + return(out); +} + +/* replace all instances of character a in buff with the character + * b. buff must be null terminated. + */ +void owl_text_tr(char *buff, char a, char b) +{ + int i; + + owl_function_debugmsg("In: %s", buff); + for (i=0; buff[i]!='\0'; i++) { + if (buff[i]==a) buff[i]=b; + } + owl_function_debugmsg("Out: %s", buff); +} + +/* Return a string which is like 'in' except that every instance of + * any character in 'toquote' found in 'in' is preceeded by the string + * 'quotestr'. For example, owl_text_quote(in, "+*.", "\") would + * place a backslash before every '+', '*' or '.' in 'in'. It is + * permissable for a character in 'quotestr' to be in 'toquote'. + * On success returns the string, on error returns NULL. + */ +char *owl_text_quote(char *in, char *toquote, char *quotestr) +{ + int i, x, r, place; + int in_len, toquote_len, quotestr_len; + char *out; + + in_len=strlen(in); + toquote_len=strlen(toquote); + quotestr_len=strlen(quotestr); + out=owl_malloc((in_len*quotestr_len)+30); + place=0; + for (i=0; i<in_len; i++) { + + /* check if it's a character that needs quoting */ + for (x=0; x<toquote_len; x++) { + if (in[i]==toquote[x]) { + /* quote it */ + for (r=0; r<quotestr_len; r++) { + out[place+r]=quotestr[r]; + } + place+=quotestr_len; + break; + } + } + + /* either way, we now copy over the character */ + out[place]=in[i]; + place++; + } + out[place]='\0'; + return(out); +} @@ -0,0 +1,98 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +#define OWL_TIMER_DIRECTION_COUNTUP 1 +#define OWL_TIMER_DIRECTION_COUNTDOWN 2 + + +/* Create a "counting up" timer. The counter starts running as soon + * as this is called. Use owl_timer_reset() to reset it. + */ +void owl_timer_create_countup(owl_timer *t) +{ + t->direction=OWL_TIMER_DIRECTION_COUNTUP; + t->starttime=time(NULL); +} + +/* create a "counting down" timer, which counts down from 'start' + * seconds. The counter starts running as soon as this is called. + * Use owl_timer_reset to reset it. + */ +void owl_timer_create_countdown(owl_timer *t, int start) +{ + t->direction=OWL_TIMER_DIRECTION_COUNTDOWN; + t->start=start; + t->starttime=time(NULL); +} + +/* Reset the timer. For a "counting up" timer, it is reset to 0. For + * a "counting down" timer it is set to the value initially set with + * owl_timer_create_countdown() or the last value set with + * owl_timer_reset_newstart() */ +void owl_timer_reset(owl_timer *t) +{ + t->starttime=time(NULL); +} + +/* Only for a countdown timer. Rest the timer, but this time (and on + * future resets) start with 'start' seconds. + */ +void owl_timer_reset_newstart(owl_timer *t, int start) +{ + t->start=start; + t->starttime=time(NULL); +} + +/* Return the number of seconds elapsed or remaining. If using a + * countdown timer, a negative value is never reported. Once the + * timer gets to 0 it stays at 0 */ +int owl_timer_get_time(owl_timer *t) +{ + time_t now; + int rem; + + now=time(NULL); + + if (t->direction==OWL_TIMER_DIRECTION_COUNTUP) { + return(now-t->starttime); + } else if (t->direction==OWL_TIMER_DIRECTION_COUNTDOWN) { + rem=t->start-(now-t->starttime); + if (rem<0) return(0); + return(rem); + } + + /* never reached */ + return(0); +} + +/* Only for countdown timer. Return true if time has run out (the + * timer is at 0 seconds or less + */ +int owl_timer_is_expired(owl_timer *t) +{ + if (owl_timer_get_time(t)==0) return(1); + return(0); +} @@ -0,0 +1,830 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <pwd.h> + +static const char fileIdent[] = "$Id: util.c,v 1.49 2009/03/29 19:08:14 kretch Exp $"; + +void sepbar(char *in) +{ + char buff[1024]; + WINDOW *sepwin; + owl_messagelist *ml; + owl_view *v; + int x, y, i; + char *foo, *appendtosepbar; + + sepwin=owl_global_get_curs_sepwin(&g); + ml=owl_global_get_msglist(&g); + v=owl_global_get_current_view(&g); + + werase(sepwin); + wattron(sepwin, A_REVERSE); + if (owl_global_is_fancylines(&g)) { + whline(sepwin, ACS_HLINE, owl_global_get_cols(&g)); + } else { + whline(sepwin, '-', owl_global_get_cols(&g)); + } + + if (owl_global_is_sepbar_disable(&g)) { + getyx(sepwin, y, x); + wmove(sepwin, y, owl_global_get_cols(&g)-1); + return; + } + + wmove(sepwin, 0, 2); + + if (owl_messagelist_get_size(ml)==0) { + strcpy(buff, " (-/-) "); + } else { + snprintf(buff, 1024, " (%i/%i/%i) ", owl_global_get_curmsg(&g)+1, + owl_view_get_size(v), + owl_messagelist_get_size(ml)); + } + waddstr(sepwin, buff); + + foo=owl_view_get_filtname(v); + if (strcmp(foo, owl_global_get_view_home(&g))) wattroff(sepwin, A_REVERSE); + waddstr(sepwin, " "); + waddstr(sepwin, owl_view_get_filtname(v)); + waddstr(sepwin, " "); + if (strcmp(foo, owl_global_get_view_home(&g))) wattron(sepwin, A_REVERSE); + + if (owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) { + getyx(sepwin, y, x); + wmove(sepwin, y, x+2); + wattron(sepwin, A_BOLD); + waddstr(sepwin, " <truncated> "); + wattroff(sepwin, A_BOLD); + } + + i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g)); + if ((i != -1) && + (i < owl_view_get_size(v)-1)) { + getyx(sepwin, y, x); + wmove(sepwin, y, x+2); + wattron(sepwin, A_BOLD); + waddstr(sepwin, " <more> "); + wattroff(sepwin, A_BOLD); + } + + if (owl_global_get_rightshift(&g)>0) { + getyx(sepwin, y, x); + wmove(sepwin, y, x+2); + snprintf(buff, 1024, " right: %i ", owl_global_get_rightshift(&g)); + waddstr(sepwin, buff); + } + + if (owl_global_is_zaway(&g) || owl_global_is_aaway(&g)) { + getyx(sepwin, y, x); + wmove(sepwin, y, x+2); + wattron(sepwin, A_BOLD); + wattroff(sepwin, A_REVERSE); + if (owl_global_is_zaway(&g) && owl_global_is_aaway(&g)) { + waddstr(sepwin, " AWAY "); + } else if (owl_global_is_zaway(&g)) { + waddstr(sepwin, " Z-AWAY "); + } else if (owl_global_is_aaway(&g)) { + waddstr(sepwin, " A-AWAY "); + } + wattron(sepwin, A_REVERSE); + wattroff(sepwin, A_BOLD); + } + + if (owl_global_get_curmsg_vert_offset(&g)) { + getyx(sepwin, y, x); + wmove(sepwin, y, x+2); + wattron(sepwin, A_BOLD); + wattroff(sepwin, A_REVERSE); + waddstr(sepwin, " SCROLL "); + wattron(sepwin, A_REVERSE); + wattroff(sepwin, A_BOLD); + } + + if (in) { + getyx(sepwin, y, x); + wmove(sepwin, y, x+2); + waddstr(sepwin, in); + } + + appendtosepbar = owl_global_get_appendtosepbar(&g); + if (appendtosepbar && *appendtosepbar) { + getyx(sepwin, y, x); + wmove(sepwin, y, x+2); + waddstr(sepwin, " "); + waddstr(sepwin, owl_global_get_appendtosepbar(&g)); + waddstr(sepwin, " "); + } + + getyx(sepwin, y, x); + wmove(sepwin, y, owl_global_get_cols(&g)-1); + + wattroff(sepwin, A_BOLD); + wattroff(sepwin, A_REVERSE); + wnoutrefresh(sepwin); +} + + +void pophandler_quit(int ch) +{ + if (ch=='q') { + owl_popwin_close(owl_global_get_popwin(&g)); + } +} + +char **atokenize(char *buffer, char *sep, int *i) +{ + /* each element of return must be freed by user */ + char **args; + char *workbuff, *foo; + int done=0, first=1, count=0; + + workbuff=owl_malloc(strlen(buffer)+1); + memcpy(workbuff, buffer, strlen(buffer)+1); + + args=NULL; + while (!done) { + if (first) { + first=0; + foo=(char *)strtok(workbuff, sep); + } else { + foo=(char *)strtok(NULL, sep); + } + if (foo==NULL) { + done=1; + } else { + args=(char **)owl_realloc(args, sizeof(char *) * (count+1)); + args[count]=owl_malloc(strlen(foo)+1); + strcpy(args[count], foo); + count++; + } + } + *i=count; + owl_free(workbuff); + return(args); +} + +char *skiptokens(char *buff, int n) { + /* skips n tokens and returns where that would be. + * TODO: handle quotes more sanely. */ + + int inquotes=0; + while (*buff && n>0) { + while (*buff == ' ') buff++; + while (*buff && (inquotes || *buff != ' ')) { + if (*buff == '"' || *buff == '\'') inquotes=!inquotes; + buff++; + } + while (*buff == ' ') buff++; + n--; + } + return buff; +} + +/* Return a "nice" version of the path. Tilde expansion is done, and + * duplicate slashes are removed. Caller must free the return. + */ +char *owl_util_makepath(char *in) +{ + int i, j, x; + char *out, user[MAXPATHLEN]; + struct passwd *pw; + + out=owl_malloc(MAXPATHLEN+1); + out[0]='\0'; + j=strlen(in); + x=0; + for (i=0; i<j; i++) { + if (in[i]=='~') { + if ( (i==(j-1)) || /* last character */ + (in[i+1]=='/') ) { /* ~/ */ + /* use my homedir */ + pw=getpwuid(getuid()); + if (!pw) { + out[x]=in[i]; + } else { + out[x]='\0'; + strcat(out, pw->pw_dir); + x+=strlen(pw->pw_dir); + } + } else { + /* another user homedir */ + int a, b; + b=0; + for (a=i+1; i<j; a++) { + if (in[a]==' ' || in[a]=='/') { + break; + } else { + user[b]=in[a]; + i++; + b++; + } + } + user[b]='\0'; + pw=getpwnam(user); + if (!pw) { + out[x]=in[i]; + } else { + out[x]='\0'; + strcat(out, pw->pw_dir); + x+=strlen(pw->pw_dir); + } + } + } else if (in[i]=='/') { + /* check for a double / */ + if (i<(j-1) && (in[i+1]=='/')) { + /* do nothing */ + } else { + out[x]=in[i]; + x++; + } + } else { + out[x]=in[i]; + x++; + } + } + out[x]='\0'; + return(out); +} + +void atokenize_free(char **tok, int nels) +{ + int i; + for (i=0; i<nels; i++) { + owl_free(tok[i]); + } + owl_free(tok); +} + + +void owl_parsefree(char **argv, int argc) +{ + int i; + + if (!argv) return; + + for (i=0; i<argc; i++) { + if (argv[i]) owl_free(argv[i]); + } + owl_free(argv); +} + +char **owl_parseline(char *line, int *argc) +{ + /* break a command line up into argv, argc. The caller must free + the returned values. If there is an error argc will be set to + -1, argv will be NULL and the caller does not need to free + anything */ + + char **argv; + int i, len, between=1; + char *curarg; + char quote; + + argv=owl_malloc(sizeof(char *)); + len=strlen(line); + curarg=owl_malloc(len+10); + strcpy(curarg, ""); + quote='\0'; + *argc=0; + for (i=0; i<len+1; i++) { + /* find the first real character */ + if (between) { + if (line[i]==' ' || line[i]=='\t' || line[i]=='\0') { + continue; + } else { + between=0; + i--; + continue; + } + } + + /* deal with a quote character */ + if (line[i]=='"' || line[i]=="'"[0]) { + /* if this type of quote is open, close it */ + if (quote==line[i]) { + quote='\0'; + continue; + } + + /* if no quoting is open then open with this */ + if (quote=='\0') { + quote=line[i]; + continue; + } + + /* if another type of quote is open then treat this as a literal */ + curarg[strlen(curarg)+1]='\0'; + curarg[strlen(curarg)]=line[i]; + continue; + } + + /* if it's not a space or end of command, then use it */ + if (line[i]!=' ' && line[i]!='\t' && line[i]!='\n' && line[i]!='\0') { + curarg[strlen(curarg)+1]='\0'; + curarg[strlen(curarg)]=line[i]; + continue; + } + + /* otherwise, if we're not in quotes, add the whole argument */ + if (quote=='\0') { + /* add the argument */ + argv=owl_realloc(argv, sizeof(char *)*((*argc)+1)); + argv[*argc]=owl_malloc(strlen(curarg)+2); + strcpy(argv[*argc], curarg); + *argc=*argc+1; + strcpy(curarg, ""); + between=1; + continue; + } + + /* if it is a space and we're in quotes, then use it */ + curarg[strlen(curarg)+1]='\0'; + curarg[strlen(curarg)]=line[i]; + } + + owl_free(curarg); + + /* check for unbalanced quotes */ + if (quote!='\0') { + owl_parsefree(argv, *argc); + *argc=-1; + return(NULL); + } + + return(argv); +} + +/* caller must free the return */ +char *owl_util_minutes_to_timestr(int in) +{ + int days, hours; + long run; + char *out; + + run=in; + + days=run/1440; + run-=days*1440; + hours=run/60; + run-=hours*60; + + if (days>0) { + out=owl_sprintf("%i d %2.2i:%2.2i", days, hours, run); + } else { + out=owl_sprintf(" %2.2i:%2.2i", hours, run); + } + return(out); +} + +/* return the index of the last char before a change from the first one */ +int owl_util_find_trans(char *in, int len) +{ + int i; + for (i=1; i<len; i++) { + if (in[i] != in[0]) return(i-1); + } + return(i); +} + +/* downcase the string 'foo' */ +void downstr(char *foo) +{ + int i; + for (i=0; foo[i]!='\0'; i++) { + foo[i]=tolower(foo[i]); + } +} + +/* Caller must free response. + * Takes in strings which are space-separated lists of tokens + * and returns a single string containing no token more than once. + * If prohibit is non-null, no token may start with a character + * in prohibit. + */ +char *owl_util_uniq(char *A, char *B, char *prohibit) +{ + + char *cat, **tok; + int toklen, i, j, first=1; + cat = owl_malloc(strlen(A)+strlen(B)+3); + strcpy(cat, A); + strcat(cat, " "); + strcat(cat, B); + tok = atokenize(cat, " ", &toklen); + strcpy(cat, ""); + for (i=0; i<toklen; i++) { + int dup=0; + for (j=0; j<i; j++) { + if (!strcmp(tok[i], tok[j])) dup=1; + } + if (!dup && (!prohibit || !strchr(prohibit, tok[i][0]))) { + if (!first) { + strcat(cat, " "); + } + first=0; + strcat(cat, tok[i]); + } + } + atokenize_free(tok, toklen); + return(cat); +} + +/* hooks for doing memory allocation et. al. in owl */ + +void *owl_malloc(size_t size) +{ + return(malloc(size)); +} + +void owl_free(void *ptr) +{ + free(ptr); +} + +char *owl_strdup(const char *s1) +{ + return(strdup(s1)); +} + +void *owl_realloc(void *ptr, size_t size) +{ + return(realloc(ptr, size)); +} + +/* allocates memory and returns the string or null. + * caller must free the string. + * from Linux sprintf man page. + */ +char *owl_sprintf(const char *fmt, ...) +{ + int n, size = 100; + char *p; + va_list ap; + if ((p = owl_malloc (size)) == NULL) return (NULL); + while (1) { + /* Try to print in the allocated space. */ + va_start(ap, fmt); + n = vsnprintf (p, size, fmt, ap); + va_end(ap); + /* If that worked, return the string. */ + if (n > -1 && n < size) + return p; + /* Else try again with more space. */ + if (n > -1) /* glibc 2.1 */ + size = n+1; /* precisely what is needed */ + else /* glibc 2.0 */ + size *= 2; /* twice the old size */ + if ((p = owl_realloc (p, size)) == NULL) + return NULL; + } +} + +/* Return the owl color associated with the named color. Return -1 + * if the named color is not available + */ +int owl_util_string_to_color(char *color) +{ + if (!strcasecmp(color, "black")) { + return(OWL_COLOR_BLACK); + } else if (!strcasecmp(color, "red")) { + return(OWL_COLOR_RED); + } else if (!strcasecmp(color, "green")) { + return(OWL_COLOR_GREEN); + } else if (!strcasecmp(color, "yellow")) { + return(OWL_COLOR_YELLOW); + } else if (!strcasecmp(color, "blue")) { + return(OWL_COLOR_BLUE); + } else if (!strcasecmp(color, "magenta")) { + return(OWL_COLOR_MAGENTA); + } else if (!strcasecmp(color, "cyan")) { + return(OWL_COLOR_CYAN); + } else if (!strcasecmp(color, "white")) { + return(OWL_COLOR_WHITE); + } else if (!strcasecmp(color, "default")) { + return(OWL_COLOR_DEFAULT); + } + return(-1); +} + +/* Return a string name of the given owl color */ +char *owl_util_color_to_string(int color) +{ + if (color==OWL_COLOR_BLACK) return("black"); + if (color==OWL_COLOR_RED) return("red"); + if (color==OWL_COLOR_GREEN) return("green"); + if (color==OWL_COLOR_YELLOW) return("yellow"); + if (color==OWL_COLOR_BLUE) return("blue"); + if (color==OWL_COLOR_MAGENTA) return("magenta"); + if (color==OWL_COLOR_CYAN) return("cyan"); + if (color==OWL_COLOR_WHITE) return("white"); + if (color==OWL_COLOR_DEFAULT) return("default"); + return("Unknown color"); +} + +/* Get the default tty name. Caller must free the return */ +char *owl_util_get_default_tty() +{ + char *out, *tmp; + + if (getenv("DISPLAY")) { + out=owl_strdup(getenv("DISPLAY")); + } else if ((tmp=ttyname(fileno(stdout)))!=NULL) { + out=owl_strdup(tmp); + if (!strncmp(out, "/dev/", 5)) { + owl_free(out); + out=owl_strdup(tmp+5); + } + } else { + out=owl_strdup("unknown"); + } + return(out); +} + + +/* Animation hack */ +void owl_hack_animate() +{ + owl_messagelist *ml; + owl_message *m; + owl_fmtext *fm; + char *text, *ptr; + int place; + + /* grab the first message and make sure its id is 0 */ + ml=owl_global_get_msglist(&g); + m=owl_messagelist_get_element(ml, 0); + if (!m) return; + if (owl_message_get_id(m)!=0) return; + + fm=owl_message_get_fmtext(m); + text=owl_fmtext_get_text(fm); + + ptr=strstr(text, "OvO"); + if (ptr) { + place=ptr-text; + owl_fmtext_set_char(fm, place, '-'); + owl_fmtext_set_char(fm, place+2, '-'); + + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + if (owl_popwin_is_active(owl_global_get_popwin(&g))) { + owl_popwin_refresh(owl_global_get_popwin(&g)); + /* TODO: this is a broken kludge */ + if (owl_global_get_viewwin(&g)) { + owl_viewwin_redisplay(owl_global_get_viewwin(&g), 0); + } + } + owl_global_set_needrefresh(&g); + return; + } + + ptr=strstr(text, "-v-"); + if (ptr) { + place=ptr-text; + owl_fmtext_set_char(fm, place, 'O'); + owl_fmtext_set_char(fm, place+2, 'O'); + + owl_mainwin_redisplay(owl_global_get_mainwin(&g)); + if (owl_popwin_is_active(owl_global_get_popwin(&g))) { + owl_popwin_refresh(owl_global_get_popwin(&g)); + /* TODO: this is a broken kludge */ + if (owl_global_get_viewwin(&g)) { + owl_viewwin_redisplay(owl_global_get_viewwin(&g), 0); + } + } + owl_global_set_needrefresh(&g); + return; + } +} + +/* strip leading and trailing new lines. Caller must free the + * return. + */ +char *owl_util_stripnewlines(char *in) +{ + + char *tmp, *ptr1, *ptr2, *out; + + ptr1=tmp=owl_strdup(in); + while (ptr1[0]=='\n') { + ptr1++; + } + ptr2=ptr1+strlen(ptr1)-1; + while (ptr2>ptr1 && ptr2[0]=='\n') { + ptr2[0]='\0'; + ptr2--; + } + + out=owl_strdup(ptr1); + owl_free(tmp); + return(out); +} + +/* Delete the line matching "line" from the named file. If no such + * line is found the file is left intact. If backup==1 then create a + * backupfile containing the original contents. This is an + * inefficient impelementation which reads the entire file into + * memory. + */ +void owl_util_file_deleteline(char *filename, char *line, int backup) +{ + char buff[LINE], *text; + char *backupfilename=""; + FILE *file, *backupfile=NULL; + int size, newline; + + /* open the file for reading */ + file=fopen(filename, "r"); + if (!file) { + owl_function_error("Error opening file %s", filename); + return; + } + + /* open the backup file for writing */ + if (backup) { + backupfilename=owl_sprintf("%s.backup", filename); + backupfile=fopen(backupfilename, "w"); + if (!backupfile) { + owl_function_error("Error opening file %s for writing", backupfilename); + owl_free(backupfilename); + fclose(file); + return; + } + } + + /* we'll read the entire file into memory, minus the line we don't want and + * and at the same time create the backup file if necessary + */ + text=owl_malloc(LINE); + strcpy(text, ""); + size=LINE; + while (fgets(buff, LINE, file)!=NULL) { + /* strip the newline */ + newline=0; + if (buff[strlen(buff)-1]=='\n') { + buff[strlen(buff)-1]='\0'; + newline=1; + } + + /* if we don't match the line, add to saved text in memory */ + if (strcasecmp(buff, line)) { + size+=LINE; + text=owl_realloc(text, size); + strcat(text, buff); + if (newline) strcat(text, "\n"); + } + + /* write to backupfile if necessary */ + if (backup) { + fputs(buff, backupfile); + if (newline) fputs("\n", backupfile); + } + } + if (backup) fclose(backupfile); + fclose(file); + + /* now rewrite the original file from memory */ + file=fopen(filename, "w"); + if (!file) { + owl_function_error("WARNING: Error opening %s for writing. Use %s to restore.", filename, backupfilename); + owl_function_beep(); + } else { + fputs(text, file); + fclose(file); + } + + if (backup) + owl_free(backupfilename); + owl_free(text); +} + +/* add the string 'str' to the list 'list' of strings, only if it + * is not already present + */ +void owl_util_list_add_unique_string(owl_list *list, char *str) +{ + int i, j; + + j=owl_list_get_size(list); + for (i=0; i<j; i++) { + if (!strcmp(str, owl_list_get_element(list, i))) return; + } + owl_list_append_element(list, owl_strdup(str)); +} + +int owl_util_common_strings_in_lists(owl_list *a, owl_list *b) +{ + int i, j, x, y; + + j=owl_list_get_size(a); + for (i=0; i<j; i++) { + y=owl_list_get_size(b); + for (x=0; x<y; x++) { + if (!strcmp(owl_list_get_element(a, i), owl_list_get_element(b, x))) return(1); + } + } + return(0); +} + +int owl_util_max(int a, int b) +{ + if (a>b) return(a); + return(b); +} + +int owl_util_min(int a, int b) +{ + if (a<b) return(a); + return(b); +} + +/* Return the base class or instance from a zephyr class, by removing + leading `un' or trailing `.d'. + The caller is responsible for freeing the allocated string. +*/ +char * owl_util_baseclass(char * class) +{ + char *start, *end; + + start = class; + while(!strncmp(start, "un", 2)) { + start += 2; + } + + start = owl_strdup(start); + end = start + strlen(start) - 1; + while(end > start && *end == 'd' && *(end-1) == '.') { + end -= 2; + } + *(end + 1) = 0; + + return start; +} + +/**************************************************************************/ +/************************* REGRESSION TESTS *******************************/ +/**************************************************************************/ + +#ifdef OWL_INCLUDE_REG_TESTS + +#define FAIL_UNLESS(desc,pred) printf("\t%-4s: %s\n", (pred)?"ok":(numfailed++,"FAIL"), desc) + +int owl_util_regtest(void) +{ + int numfailed=0; + + printf("BEGIN testing owl_util\n"); + + FAIL_UNLESS("owl_util_substitute 1", + !strcmp("foo", owl_text_substitute("foo", "", "Y"))); + FAIL_UNLESS("owl_text_substitute 2", + !strcmp("fYZYZ", owl_text_substitute("foo", "o", "YZ"))); + FAIL_UNLESS("owl_text_substitute 3", + !strcmp("foo", owl_text_substitute("fYZYZ", "YZ", "o"))); + FAIL_UNLESS("owl_text_substitute 4", + !strcmp("/u/foo/meep", owl_text_substitute("~/meep", "~", "/u/foo"))); + + FAIL_UNLESS("skiptokens 1", + !strcmp("bar quux", skiptokens("foo bar quux", 1))); + FAIL_UNLESS("skiptokens 2", + !strcmp("meep", skiptokens("foo 'bar quux' meep", 2))); + + FAIL_UNLESS("owl_util_uniq 1", + !strcmp("foo bar x", owl_util_uniq("foo", "bar x", "-"))); + FAIL_UNLESS("owl_util_uniq 2", + !strcmp("foo bar x", owl_util_uniq("foo", "bar -y x", "-"))); + FAIL_UNLESS("owl_util_uniq 3", + !strcmp("meep foo bar", owl_util_uniq("meep foo", "bar foo meep", "-"))); + + if (numfailed) printf("*** WARNING: failures encountered with owl_util\n"); + printf("END testing owl_util (%d failures)\n", numfailed); + return(numfailed); +} + +#endif /* OWL_INCLUDE_REG_TESTS */ diff --git a/variable.c b/variable.c new file mode 100644 index 0000000..173376c --- /dev/null +++ b/variable.c @@ -0,0 +1,983 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: variable.c,v 1.43 2009/03/29 12:38:22 kretch Exp $"; + +static int in_regtest = 0; + +#define OWLVAR_BOOL(name,default,summary,description) \ + { name, OWL_VARIABLE_BOOL, NULL, default, "on,off", summary,description, NULL, \ + NULL, NULL, NULL, NULL, NULL } + +#define OWLVAR_BOOL_FULL(name,default,summary,description,validate,set,get) \ + { name, OWL_VARIABLE_BOOL, NULL, default, "on,off", summary,description, NULL, \ + validate, set, NULL, get, NULL } + +#define OWLVAR_INT(name,default,summary,description) \ + { name, OWL_VARIABLE_INT, NULL, default, "<int>", summary,description, NULL, \ + NULL, NULL, NULL, NULL, NULL, NULL } + +#define OWLVAR_INT_FULL(name,default,summary,description,validset,validate,set,get) \ + { name, OWL_VARIABLE_INT, NULL, default, validset, summary,description, NULL, \ + validate, set, NULL, get, NULL, NULL } + +#define OWLVAR_PATH(name,default,summary,description) \ + { name, OWL_VARIABLE_STRING, default, 0, "<path>", summary,description, NULL, \ + NULL, NULL, NULL, NULL, NULL, NULL } + +#define OWLVAR_STRING(name,default,summary,description) \ + { name, OWL_VARIABLE_STRING, default, 0, "<string>", summary,description, NULL, \ + NULL, NULL, NULL, NULL, NULL, NULL } + +#define OWLVAR_STRING_FULL(name,default,summary,description,validate,set,get) \ + { name, OWL_VARIABLE_STRING, default, 0, "<string>", summary,description, NULL, \ + validate, set, NULL, get, NULL, NULL } + +/* enums are really integers, but where validset is a comma-separated + * list of strings which can be specified. The tokens, starting at 0, + * correspond to the values that may be specified. */ +#define OWLVAR_ENUM(name,default,summary,description,validset) \ + { name, OWL_VARIABLE_INT, NULL, default, validset, summary,description, NULL, \ + owl_variable_enum_validate, \ + NULL, owl_variable_enum_set_fromstring, \ + NULL, owl_variable_enum_get_tostring, \ + NULL } + +#define OWLVAR_ENUM_FULL(name,default,summary,description,validset,validate, set, get) \ + { name, OWL_VARIABLE_INT, NULL, default, validset, summary,description, NULL, \ + validate, \ + set, owl_variable_enum_set_fromstring, \ + get, owl_variable_enum_get_tostring, \ + NULL } + +static owl_variable variables_to_init[] = { + + OWLVAR_STRING( "personalbell" /* %OwlVarStub */, "off", + "ring the terminal bell when personal messages are received", + "Can be set to 'on', 'off', or the name of a filter which\n" + "messages need to match in order to ring the bell"), + + OWLVAR_BOOL( "bell" /* %OwlVarStub */, 1, + "enable / disable the terminal bell", "" ), + + OWLVAR_BOOL_FULL( "debug" /* %OwlVarStub */, OWL_DEBUG, + "whether debugging is enabled", + "If set to 'on', debugging messages are logged to the\n" + "file specified by the debugfile variable.\n", + NULL, owl_variable_debug_set, NULL), + + OWLVAR_BOOL( "startuplogin" /* %OwlVarStub */, 1, + "send a login message when owl starts", "" ), + + OWLVAR_BOOL( "shutdownlogout" /* %OwlVarStub */, 1, + "send a logout message when owl exits", "" ), + + OWLVAR_BOOL( "rxping" /* %OwlVarStub */, 0, + "display received pings", "" ), + + OWLVAR_BOOL( "txping" /* %OwlVarStub */, 1, + "send pings", "" ), + + OWLVAR_BOOL( "sepbar_disable" /* %OwlVarStub */, 0, + "disable printing information in the seperator bar", "" ), + + OWLVAR_BOOL( "smartstrip" /* %OwlVarStub */, 1, + "strip kerberos instance for reply", ""), + + OWLVAR_BOOL( "newlinestrip" /* %OwlVarStub */, 1, + "strip leading and trailing newlines", ""), + + OWLVAR_BOOL( "displayoutgoing" /* %OwlVarStub */, 1, + "display outgoing messages", "" ), + + OWLVAR_BOOL( "loginsubs" /* %OwlVarStub */, 1, + "load logins from .anyone on startup", "" ), + + OWLVAR_BOOL( "logging" /* %OwlVarStub */, 0, + "turn personal logging on or off", + "If this is set to on, personal messages are\n" + "logged in the directory specified\n" + "by the 'logpath' variable. The filename in that\n" + "directory is derived from the sender of the message.\n" ), + + OWLVAR_BOOL( "classlogging" /* %OwlVarStub */, 0, + "turn class logging on or off", + "If this is set to on, class messages are\n" + "logged in the directory specified\n" + "by the 'classlogpath' variable.\n" + "The filename in that directory is derived from\n" + "the name of the class to which the message was sent.\n" ), + + OWLVAR_ENUM( "loggingdirection" /* %OwlVarStub */, OWL_LOGGING_DIRECTION_BOTH, + "specifices which kind of messages should be logged", + "Can be one of 'both', 'in', or 'out'. If 'in' is\n" + "selected, only incoming messages are logged, if 'out'\n" + "is selected only outgoing messages are logged. If 'both'\n" + "is selected both incoming and outgoing messages are\n" + "logged.", + "both,in,out"), + + OWLVAR_BOOL( "colorztext" /* %OwlVarStub */, 1, + "allow @color() in zephyrs to change color", + "Note that only messages received after this variable\n" + "is set will be affected." ), + + OWLVAR_BOOL( "fancylines" /* %OwlVarStub */, 1, + "Use 'nice' line drawing on the terminal.", + "If turned off, dashes, pipes and pluses will be used\n" + "to draw lines on the screen. Useful when the terminal\n" + "is causing problems" ), + + OWLVAR_BOOL( "zcrypt" /* %OwlVarStub */, 1, + "Do automatic zcrypt processing", + "" ), + + OWLVAR_BOOL_FULL( "pseudologins" /* %OwlVarStub */, 0, + "Enable zephyr pseudo logins", + "When this is enabled, Owl will periodically check the zephyr\n" + "location of users in your .anyone file. If a user is present\n" + "but sent no login message, or a user is not present that sent no\n" + "logout message a pseudo login or logout message wil be created\n", + NULL, owl_variable_pseudologins_set, NULL), + + OWLVAR_BOOL( "ignorelogins" /* %OwlVarStub */, 0, + "Enable printing of login notifications", + "When this is enabled, Owl will print login and logout notifications\n" + "for AIM, zephyr, or other protocols. If disabled Owl will not print\n" + "login or logout notifications.\n"), + + OWLVAR_STRING( "logfilter" /* %OwlVarStub */, "", + "name of a filter controlling which messages to log", + + "If non empty, any messages matching the given filter will be logged.\n" + "This is a completely separate mechanisim from the other logging\n" + "variables like logging, classlogging, loglogins, loggingdirection,\n" + "etc. If you want this variable to control all logging, make sure\n" + "all other logging variables are in their default state.\n"), + + OWLVAR_BOOL( "loglogins" /* %OwlVarStub */, 0, + "Enable logging of login notifications", + "When this is enabled, Owl will login login and logout notifications\n" + "for AIM, zephyr, or other protocols. If disabled Owl will not print\n" + "login or logout notifications.\n"), + + OWLVAR_ENUM_FULL( "disable-ctrl-d" /* %OwlVarStub:lockout_ctrld */, 1, + "don't send zephyrs on C-d", + "If set to 'off', C-d won't send a zephyr from the edit\n" + "window. If set to 'on', C-d will always send a zephyr\n" + "being composed in the edit window. If set to 'middle',\n" + "C-d will only ever send a zephyr if the cursor is at\n" + "the end of the message being composed.\n\n" + "Note that this works by changing the C-d keybinding\n" + "in the editmulti keymap.\n", + "off,middle,on", + NULL, owl_variable_disable_ctrl_d_set, NULL), + + OWLVAR_BOOL( "_burningears" /* %OwlVarStub:burningears */, 0, + "[NOT YET IMPLEMENTED] beep on messages matching patterns", "" ), + + OWLVAR_BOOL( "_summarymode" /* %OwlVarStub:summarymode */, 0, + "[NOT YET IMPLEMENTED]", "" ), + + OWLVAR_PATH( "logpath" /* %OwlVarStub */, "~/zlog/people", + "path for logging personal zephyrs", + "Specifies a directory which must exist.\n" + "Files will be created in the directory for each sender.\n"), + + OWLVAR_PATH( "classlogpath" /* %OwlVarStub:classlogpath */, "~/zlog/class", + "path for logging class zephyrs", + "Specifies a directory which must exist.\n" + "Files will be created in the directory for each class.\n"), + + OWLVAR_PATH( "debug_file" /* %OwlVarStub */, OWL_DEBUG_FILE, + "path for logging debug messages when debugging is enabled", + "This file will be logged to if 'debug' is set to 'on'.\n"), + + OWLVAR_PATH( "zsigproc" /* %OwlVarStub:zsigproc */, NULL, + "name of a program to run that will generate zsigs", + "This program should produce a zsig on stdout when run.\n" + "Note that it is important that this program not block.\n" ), + + OWLVAR_PATH( "newmsgproc" /* %OwlVarStub:newmsgproc */, NULL, + "name of a program to run when new messages are present", + "The named program will be run when owl recevies new.\n" + "messages. It will not be run again until the first\n" + "instance exits"), + + OWLVAR_STRING( "zsig" /* %OwlVarStub */, "", + "zephyr signature", + "If 'zsigproc' is not set, this string will be used\n" + "as a zsig. If this is also unset, the 'zwrite-signature'\n" + "zephyr variable will be used instead.\n"), + + OWLVAR_STRING( "appendtosepbar" /* %OwlVarStub */, "", + "string to append to the end of the sepbar", + "The sepbar is the bar separating the top and bottom\n" + "of the owl screen. Any string specified here will\n" + "be displayed on the right of the sepbar\n"), + + OWLVAR_BOOL( "zaway" /* %OwlVarStub */, 0, + "turn zaway on or off", "" ), + + OWLVAR_STRING( "zaway_msg" /* %OwlVarStub */, + OWL_DEFAULT_ZAWAYMSG, + "zaway msg for responding to zephyrs when away", "" ), + + OWLVAR_STRING( "zaway_msg_default" /* %OwlVarStub */, + OWL_DEFAULT_ZAWAYMSG, + "default zaway message", "" ), + + OWLVAR_BOOL_FULL( "aaway" /* %OwlVarStub */, 0, + "Set AIM away status", + "", + NULL, owl_variable_aaway_set, NULL), + + OWLVAR_STRING( "aaway_msg" /* %OwlVarStub */, + OWL_DEFAULT_AAWAYMSG, + "AIM away msg for responding when away", "" ), + + OWLVAR_STRING( "aaway_msg_default" /* %OwlVarStub */, + OWL_DEFAULT_AAWAYMSG, + "default AIM away message", "" ), + + OWLVAR_STRING( "view_home" /* %OwlVarStub */, "all", + "home view to switch to after 'X' and 'V'", + "SEE ALSO: view, filter\n" ), + + OWLVAR_STRING( "alert_filter" /* %OwlVarStub */, "none", + "filter on which to trigger alert actions", + "" ), + + OWLVAR_STRING( "alert_action" /* %OwlVarStub */, "nop", + "owl command to execute for alert actions", + "" ), + + OWLVAR_STRING_FULL( "tty" /* %OwlVarStub */, "", "tty name for zephyr location", "", + NULL, owl_variable_tty_set, NULL), + + OWLVAR_STRING( "default_style" /* %OwlVarStub */, "__unspecified__", + "name of the default formatting style", + "This sets the default message formatting style.\n" + "Styles may be created with the 'style' command.\n" + "Some built-in styles include:\n" + " default - the default owl formatting\n" + " basic - simple formatting\n" + " oneline - one line per-message\n" + " perl - legacy perl interface\n" + "\nSEE ALSO: style, show styles, view -s <style>\n" + ), + + + OWLVAR_INT( "edit:maxfillcols" /* %OwlVarStub:edit_maxfillcols */, 70, + "maximum number of columns for M-q to fill text to", + "This specifies the maximum number of columns for M-q\n" + "to fill text to. If set to 0, ther will be no maximum\n" + "limit. In all cases, the current width of the screen\n" + "will also be taken into account. It will be used instead\n" + "if it is narrower than the maximum, or if this\n" + "is set to 0.\n" ), + + OWLVAR_INT( "edit:maxwrapcols" /* %OwlVarStub:edit_maxwrapcols */, 0, + "maximum number of columns for line-wrapping", + "This specifies the maximum number of columns for\n" + "auto-line-wrapping. If set to 0, ther will be no maximum\n" + "limit. In all cases, the current width of the screen\n" + "will also be taken into account. It will be used instead\n" + "if it is narrower than the maximum, or if this\n" + "is set to 0.\n\n" + "It is recommended that outgoing messages be no wider\n" + "than 60 columns, as a courtesy to recipients.\n"), + + OWLVAR_INT( "aim_ignorelogin_timer" /* %OwlVarStub */, 15, + "number of seconds after AIM login to ignore login messages", + "This specifies the number of seconds to wait after an\n" + "AIM login before allowing the recipt of AIM login notifications.\n" + "By default this is set to 15. If you would like to view login\n" + "notifications of buddies as soon as you login, set it to 0 instead."), + + + OWLVAR_INT_FULL( "typewinsize" /* %OwlVarStub:typwin_lines */, + OWL_TYPWIN_SIZE, + "number of lines in the typing window", + "This specifies the height of the window at the\n" + "bottom of the screen where commands are entered\n" + "and where messages are composed.\n", + "int > 0", + owl_variable_int_validate_gt0, + owl_variable_typewinsize_set, + NULL /* use default for get */ + ), + + OWLVAR_ENUM( "scrollmode" /* %OwlVarStub */, OWL_SCROLLMODE_NORMAL, + "how to scroll up and down", + "This controls how the screen is scrolled as the\n" + "cursor moves between messages being displayed.\n" + "The following modes are supported:\n\n" + " normal - This is the owl default. Scrolling happens\n" + " when it needs to, and an attempt is made to\n" + " keep the current message roughly near\n" + " the middle of the screen.\n" + " top - The current message will always be the\n" + " the top message displayed.\n" + " neartop - The current message will be one down\n" + " from the top message displayed,\n" + " where possible.\n" + " center - An attempt is made to keep the current\n" + " message near the center of the screen.\n" + " paged - The top message displayed only changes\n" + " when user moves the cursor to the top\n" + " or bottom of the screen. When it moves,\n" + " the screen will be paged up or down and\n" + " the cursor will be near the top or\n" + " the bottom.\n" + " pagedcenter - The top message displayed only changes\n" + " when user moves the cursor to the top\n" + " or bottom of the screen. When it moves,\n" + " the screen will be paged up or down and\n" + " the cursor will be near the center.\n", + "normal,top,neartop,center,paged,pagedcenter" ), + + + OWLVAR_BOOL( "_followlast" /* %OwlVarStub */, 0, + "enable automatic following of the last zephyr", + "If the cursor is at the last message, it will\n" + "continue to follow the last message if this is set.\n" + "Note that this is currently risky as you might accidentally\n" + "delete a message right as it came in.\n" ), + + /* This MUST be last... */ + { NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL } + +}; + +/**************************************************************************/ +/*********************** SPECIFIC TO VARIABLES ****************************/ +/**************************************************************************/ + + +/* commonly useful */ + +int owl_variable_int_validate_gt0(owl_variable *v, void *newval) +{ + if (newval == NULL) return(0); + else if (*(int*)newval < 1) return(0); + else return (1); +} + +int owl_variable_int_validate_positive(owl_variable *v, void *newval) +{ + if (newval == NULL) return(0); + else if (*(int*)newval < 0) return(0); + else return (1); +} + +/* typewinsize */ +int owl_variable_typewinsize_set(owl_variable *v, void *newval) +{ + int rv; + rv = owl_variable_int_set_default(v, newval); + if (0 == rv) owl_function_resize(); + return(rv); +} + +/* debug (cache value in g->debug) */ +int owl_variable_debug_set(owl_variable *v, void *newval) +{ + if (newval && (*(int*)newval == 1 || *(int*)newval == 0)) { + g.debug = *(int*)newval; + } + return owl_variable_bool_set_default(v, newval); +} + +/* When 'aaway' is changed, need to notify the AIM server */ +int owl_variable_aaway_set(owl_variable *v, void *newval) +{ + if (newval) { + if (*(int*)newval == 1) { + owl_aim_set_awaymsg(owl_global_get_aaway_msg(&g)); + } else if (*(int*)newval == 0) { + owl_aim_set_awaymsg(""); + } + } + return owl_variable_bool_set_default(v, newval); +} + +int owl_variable_pseudologins_set(owl_variable *v, void *newval) +{ + if (newval) { + if (*(int*)newval == 1) { + owl_function_zephyr_buddy_check(0); + } + } + return owl_variable_bool_set_default(v, newval); +} + +/* note that changing the value of this will clobber + * any user setting of this */ +int owl_variable_disable_ctrl_d_set(owl_variable *v, void *newval) +{ + + if (in_regtest) return owl_variable_int_set_default(v, newval); + + if (newval && !owl_context_is_startup(owl_global_get_context(&g))) { + if (*(int*)newval == 2) { + owl_function_command_norv("bindkey editmulti C-d command edit:delete-next-char"); + } else if (*(int*)newval == 1) { + owl_function_command_norv("bindkey editmulti C-d command editmulti:done-or-delete"); + } else { + owl_function_command_norv("bindkey editmulti C-d command editmulti:done"); + } + } + return owl_variable_int_set_default(v, newval); +} + +int owl_variable_tty_set(owl_variable *v, void *newval) +{ + owl_zephyr_set_locationinfo(owl_global_get_hostname(&g), newval); + return(owl_variable_string_set_default(v, newval)); +} + + +/**************************************************************************/ +/****************************** GENERAL ***********************************/ +/**************************************************************************/ + +int owl_variable_dict_setup(owl_vardict *vd) { + owl_variable *cur; + if (owl_dict_create(vd)) return(-1); + for (cur = variables_to_init; cur->name != NULL; cur++) { + switch (cur->type) { + case OWL_VARIABLE_OTHER: + cur->set_fn(cur, cur->pval_default); + break; + case OWL_VARIABLE_STRING: + if (!cur->validate_fn) + cur->validate_fn = owl_variable_string_validate_default; + if (!cur->set_fn) + cur->set_fn = owl_variable_string_set_default; + if (!cur->set_fromstring_fn) + cur->set_fromstring_fn = owl_variable_string_set_fromstring_default; + if (!cur->get_fn) + cur->get_fn = owl_variable_get_default; + if (!cur->get_tostring_fn) + cur->get_tostring_fn = owl_variable_string_get_tostring_default; + if (!cur->free_fn) + cur->free_fn = owl_variable_free_default; + cur->set_fn(cur, cur->pval_default); + break; + case OWL_VARIABLE_BOOL: + if (!cur->validate_fn) + cur->validate_fn = owl_variable_bool_validate_default; + if (!cur->set_fn) + cur->set_fn = owl_variable_bool_set_default; + if (!cur->set_fromstring_fn) + cur->set_fromstring_fn = owl_variable_bool_set_fromstring_default; + if (!cur->get_fn) + cur->get_fn = owl_variable_get_default; + if (!cur->get_tostring_fn) + cur->get_tostring_fn = owl_variable_bool_get_tostring_default; + if (!cur->free_fn) + cur->free_fn = owl_variable_free_default; + cur->val = owl_malloc(sizeof(int)); + cur->set_fn(cur, &cur->ival_default); + break; + case OWL_VARIABLE_INT: + if (!cur->validate_fn) + cur->validate_fn = owl_variable_int_validate_default; + if (!cur->set_fn) + cur->set_fn = owl_variable_int_set_default; + if (!cur->set_fromstring_fn) + cur->set_fromstring_fn = owl_variable_int_set_fromstring_default; + if (!cur->get_fn) + cur->get_fn = owl_variable_get_default; + if (!cur->get_tostring_fn) + cur->get_tostring_fn = owl_variable_int_get_tostring_default; + if (!cur->free_fn) + cur->free_fn = owl_variable_free_default; + cur->val = owl_malloc(sizeof(int)); + cur->set_fn(cur, &cur->ival_default); + break; + default: + fprintf(stderr, "owl_variable_setup: invalid variable type\n"); + return(-2); + } + owl_dict_insert_element(vd, cur->name, (void*)cur, NULL); + } + return 0; +} + +void owl_variable_dict_free(owl_vardict *d) { + owl_dict_free_all(d, (void(*)(void*))owl_variable_free); +} + +/* free the list with owl_variable_dict_namelist_free */ +void owl_variable_dict_get_names(owl_vardict *d, owl_list *l) { + owl_dict_get_keys(d, l); +} + +void owl_variable_dict_namelist_free(owl_list *l) { + owl_list_free_all(l, owl_free); +} + +void owl_variable_free(owl_variable *v) { + if (v->free_fn) v->free_fn(v); +} + + +char *owl_variable_get_description(owl_variable *v) { + return v->description; +} + +char *owl_variable_get_summary(owl_variable *v) { + return v->summary; +} + +char *owl_variable_get_validsettings(owl_variable *v) { + if (v->validsettings) { + return v->validsettings; + } else { + return ""; + } +} + +/* functions for getting and setting variable values */ + +/* returns 0 on success, prints a status msg if msg is true */ +int owl_variable_set_fromstring(owl_vardict *d, char *name, char *value, int msg, int requirebool) { + owl_variable *v; + char buff2[1024]; + if (!name) return(-1); + v = owl_dict_find_element(d, name); + if (v == NULL) { + if (msg) owl_function_error("Unknown variable %s", name); + return -1; + } + if (!v->set_fromstring_fn) { + if (msg) owl_function_error("Variable %s is read-only", name); + return -1; + } + if (requirebool && v->type!=OWL_VARIABLE_BOOL) { + if (msg) owl_function_error("Variable %s is not a boolean", name); + return -1; + } + if (0 != v->set_fromstring_fn(v, value)) { + if (msg) owl_function_error("Unable to set %s (must be %s)", name, + owl_variable_get_validsettings(v)); + return -1; + } + if (msg && v->get_tostring_fn) { + v->get_tostring_fn(v, buff2, 1024, v->val); + owl_function_makemsg("%s = '%s'", name, buff2); + } + return 0; +} + +int owl_variable_set_string(owl_vardict *d, char *name, char *newval) { + owl_variable *v; + if (!name) return(-1); + v = owl_dict_find_element(d, name); + if (v == NULL || !v->set_fn) return(-1); + if (v->type!=OWL_VARIABLE_STRING) return(-1); + return v->set_fn(v, (void*)newval); +} + +int owl_variable_set_int(owl_vardict *d, char *name, int newval) { + owl_variable *v; + if (!name) return(-1); + v = owl_dict_find_element(d, name); + if (v == NULL || !v->set_fn) return(-1); + if (v->type!=OWL_VARIABLE_INT && v->type!=OWL_VARIABLE_BOOL) return(-1); + return v->set_fn(v, &newval); +} + +int owl_variable_set_bool_on(owl_vardict *d, char *name) { + return owl_variable_set_int(d,name,1); +} + +int owl_variable_set_bool_off(owl_vardict *d, char *name) { + return owl_variable_set_int(d,name,0); +} + +int owl_variable_get_tostring(owl_vardict *d, char *name, char *buf, int bufsize) { + owl_variable *v; + if (!name) return(-1); + v = owl_dict_find_element(d, name); + if (v == NULL || !v->get_tostring_fn) return(-1); + return v->get_tostring_fn(v, buf, bufsize, v->val); +} + +int owl_variable_get_default_tostring(owl_vardict *d, char *name, char *buf, int bufsize) { + owl_variable *v; + if (!name) return(-1); + v = owl_dict_find_element(d, name); + if (v == NULL || !v->get_tostring_fn) return(-1); + if (v->type == OWL_VARIABLE_INT || v->type == OWL_VARIABLE_BOOL) { + return v->get_tostring_fn(v, buf, bufsize, &(v->ival_default)); + } else { + return v->get_tostring_fn(v, buf, bufsize, v->pval_default); + } +} + +/* returns a reference */ +void *owl_variable_get(owl_vardict *d, char *name, int require_type) { + owl_variable *v; + if (!name) return(NULL); + v = owl_dict_find_element(d, name); + if (v == NULL || !v->get_fn || v->type != require_type) return(NULL); + return v->get_fn(v); +} + +/* returns a reference */ +char *owl_variable_get_string(owl_vardict *d, char *name) { + return (char*)owl_variable_get(d,name, OWL_VARIABLE_STRING); +} + +/* returns a reference */ +void *owl_variable_get_other(owl_vardict *d, char *name) { + return (char*)owl_variable_get(d,name, OWL_VARIABLE_OTHER); +} + +int owl_variable_get_int(owl_vardict *d, char *name) { + int *pi; + pi = (int*)owl_variable_get(d,name,OWL_VARIABLE_INT); + if (!pi) return(-1); + return(*pi); +} + +int owl_variable_get_bool(owl_vardict *d, char *name) { + int *pi; + pi = (int*)owl_variable_get(d,name,OWL_VARIABLE_BOOL); + if (!pi) return(-1); + return(*pi); +} + +void owl_variable_describe(owl_vardict *d, char *name, owl_fmtext *fm) { + char defaultbuf[50]; + char buf[1024]; + int buflen = 1023; + owl_variable *v; + + if (!name + || (v = owl_dict_find_element(d, name)) == NULL + || !v->get_fn) { + snprintf(buf, buflen, " No such variable '%s'\n", name); + owl_fmtext_append_normal(fm, buf); + return; + } + if (v->type == OWL_VARIABLE_INT || v->type == OWL_VARIABLE_BOOL) { + v->get_tostring_fn(v, defaultbuf, 50, &(v->ival_default)); + } else { + v->get_tostring_fn(v, defaultbuf, 50, v->pval_default); + } + snprintf(buf, buflen, OWL_TABSTR "%-20s - %s (default: '%s')\n", + v->name, + owl_variable_get_summary(v), defaultbuf); + owl_fmtext_append_normal(fm, buf); +} + +void owl_variable_get_help(owl_vardict *d, char *name, owl_fmtext *fm) { + char buff[1024]; + int bufflen = 1023; + owl_variable *v; + + if (!name + || (v = owl_dict_find_element(d, name)) == NULL + || !v->get_fn) { + owl_fmtext_append_normal(fm, "No such variable...\n"); + return; + } + + owl_fmtext_append_bold(fm, "OWL VARIABLE\n\n"); + owl_fmtext_append_normal(fm, OWL_TABSTR); + owl_fmtext_append_normal(fm, name); + owl_fmtext_append_normal(fm, " - "); + owl_fmtext_append_normal(fm, v->summary); + owl_fmtext_append_normal(fm, "\n\n"); + + owl_fmtext_append_normal(fm, "Current: "); + owl_variable_get_tostring(d, name, buff, bufflen); + owl_fmtext_append_normal(fm, buff); + owl_fmtext_append_normal(fm, "\n\n"); + + + if (v->type == OWL_VARIABLE_INT || v->type == OWL_VARIABLE_BOOL) { + v->get_tostring_fn(v, buff, bufflen, &(v->ival_default)); + } else { + v->get_tostring_fn(v, buff, bufflen, v->pval_default); + } + owl_fmtext_append_normal(fm, "Default: "); + owl_fmtext_append_normal(fm, buff); + owl_fmtext_append_normal(fm, "\n\n"); + + owl_fmtext_append_normal(fm, "Valid Settings: "); + owl_fmtext_append_normal(fm, owl_variable_get_validsettings(v)); + owl_fmtext_append_normal(fm, "\n\n"); + + if (v->description && *v->description) { + owl_fmtext_append_normal(fm, "Description:\n"); + owl_fmtext_append_normal(fm, owl_variable_get_description(v)); + owl_fmtext_append_normal(fm, "\n\n"); + } +} + + + + +/**************************************************************************/ +/*********************** GENERAL TYPE-SPECIFIC ****************************/ +/**************************************************************************/ + +/* default common functions */ + +void *owl_variable_get_default(owl_variable *v) { + return v->val; +} + +void owl_variable_free_default(owl_variable *v) { + if (v->val) owl_free(v->val); +} + +/* default functions for booleans */ + +int owl_variable_bool_validate_default(owl_variable *v, void *newval) { + if (newval == NULL) return(0); + else if (*(int*)newval==1 || *(int*)newval==0) return(1); + else return (0); +} + +int owl_variable_bool_set_default(owl_variable *v, void *newval) { + if (v->validate_fn) { + if (!v->validate_fn(v, newval)) return(-1); + } + *(int*)v->val = *(int*)newval; + return(0); +} + +int owl_variable_bool_set_fromstring_default(owl_variable *v, char *newval) { + int i; + if (!strcmp(newval, "on")) i=1; + else if (!strcmp(newval, "off")) i=0; + else return(-1); + return (v->set_fn(v, &i)); +} + +int owl_variable_bool_get_tostring_default(owl_variable *v, char* buf, int bufsize, void *val) { + if (val == NULL) { + snprintf(buf, bufsize, "<null>"); + return -1; + } else if (*(int*)val == 0) { + snprintf(buf, bufsize, "off"); + return 0; + } else if (*(int*)val == 1) { + snprintf(buf, bufsize, "on"); + return 0; + } else { + snprintf(buf, bufsize, "<invalid>"); + return -1; + } +} + +/* default functions for integers */ + +int owl_variable_int_validate_default(owl_variable *v, void *newval) { + if (newval == NULL) return(0); + else return (1); +} + +int owl_variable_int_set_default(owl_variable *v, void *newval) { + if (v->validate_fn) { + if (!v->validate_fn(v, newval)) return(-1); + } + *(int*)v->val = *(int*)newval; + return(0); +} + +int owl_variable_int_set_fromstring_default(owl_variable *v, char *newval) { + int i; + char *ep = "x"; + i = strtol(newval, &ep, 10); + if (*ep || ep==newval) return(-1); + return (v->set_fn(v, &i)); +} + +int owl_variable_int_get_tostring_default(owl_variable *v, char* buf, int bufsize, void *val) { + if (val == NULL) { + snprintf(buf, bufsize, "<null>"); + return -1; + } else { + snprintf(buf, bufsize, "%d", *(int*)val); + return 0; + } +} + +/* default functions for enums (a variant of integers) */ + +int owl_variable_enum_validate(owl_variable *v, void *newval) { + char **enums; + int nenums, val; + if (newval == NULL) return(0); + enums = atokenize(v->validsettings, ",", &nenums); + if (enums == NULL) return(0); + atokenize_free(enums, nenums); + val = *(int*)newval; + if (val < 0 || val >= nenums) { + return(0); + } + return(1); +} + +int owl_variable_enum_set_fromstring(owl_variable *v, char *newval) { + char **enums; + int nenums, i, val=-1; + if (newval == NULL) return(-1); + enums = atokenize(v->validsettings, ",", &nenums); + if (enums == NULL) return(-1); + for (i=0; i<nenums; i++) { + if (0==strcmp(newval, enums[i])) { + val = i; + } + } + atokenize_free(enums, nenums); + if (val == -1) return(-1); + return (v->set_fn(v, &val)); +} + +int owl_variable_enum_get_tostring(owl_variable *v, char* buf, int bufsize, void *val) { + char **enums; + int nenums, i; + + if (val == NULL) { + snprintf(buf, bufsize, "<null>"); + return -1; + } + enums = atokenize(v->validsettings, ",", &nenums); + i = *(int*)val; + if (i<0 || i>=nenums) { + snprintf(buf, bufsize, "<invalid:%d>",i); + atokenize_free(enums, nenums); + return(-1); + } + snprintf(buf, bufsize, "%s", enums[i]); + return 0; +} + +/* default functions for stringeans */ + +int owl_variable_string_validate_default(struct _owl_variable *v, void *newval) { + if (newval == NULL) return(0); + else return (1); +} + +int owl_variable_string_set_default(owl_variable *v, void *newval) { + if (v->validate_fn) { + if (!v->validate_fn(v, newval)) return(-1); + } + if (v->val) owl_free(v->val); + v->val = owl_strdup(newval); + return(0); +} + +int owl_variable_string_set_fromstring_default(owl_variable *v, char *newval) { + return (v->set_fn(v, newval)); +} + +int owl_variable_string_get_tostring_default(owl_variable *v, char* buf, int bufsize, void *val) { + if (val == NULL) { + snprintf(buf, bufsize, "<null>"); + return -1; + } else { + snprintf(buf, bufsize, "%s", (char*)val); + return 0; + } +} + + + +/**************************************************************************/ +/************************* REGRESSION TESTS *******************************/ +/**************************************************************************/ + +#ifdef OWL_INCLUDE_REG_TESTS + +#define FAIL_UNLESS(desc,pred) printf("\t%-4s: %s\n", (pred)?"ok":(numfailed++,"FAIL"), desc) + +int owl_variable_regtest(void) { + owl_vardict vd; + int numfailed=0; + char buf[1024]; + + in_regtest = 1; + + printf("BEGIN testing owl_variable\n"); + FAIL_UNLESS("setup", 0==owl_variable_dict_setup(&vd)); + + FAIL_UNLESS("get bool", 0==owl_variable_get_bool(&vd,"personalbell")); + FAIL_UNLESS("get bool (no such)", -1==owl_variable_get_bool(&vd,"mumble")); + FAIL_UNLESS("get bool as string 1", 0==owl_variable_get_tostring(&vd,"personalbell", buf, 1024)); + FAIL_UNLESS("get bool as string 2", 0==strcmp(buf,"off")); + FAIL_UNLESS("set bool 1", 0==owl_variable_set_bool_on(&vd,"personalbell")); + FAIL_UNLESS("get bool 2", 1==owl_variable_get_bool(&vd,"personalbell")); + FAIL_UNLESS("set bool 3", 0==owl_variable_set_fromstring(&vd,"personalbell","off",0,0)); + FAIL_UNLESS("get bool 4", 0==owl_variable_get_bool(&vd,"personalbell")); + FAIL_UNLESS("set bool 5", -1==owl_variable_set_fromstring(&vd,"personalbell","xxx",0,0)); + FAIL_UNLESS("get bool 6", 0==owl_variable_get_bool(&vd,"personalbell")); + + + FAIL_UNLESS("get string", 0==strcmp("~/zlog/people", owl_variable_get_string(&vd,"logpath"))); + FAIL_UNLESS("set string 7", 0==owl_variable_set_string(&vd,"logpath","whee")); + FAIL_UNLESS("get string", 0==strcmp("whee", owl_variable_get_string(&vd,"logpath"))); + + FAIL_UNLESS("get int", 8==owl_variable_get_int(&vd,"typewinsize")); + FAIL_UNLESS("get int (no such)", -1==owl_variable_get_int(&vd,"mmble")); + FAIL_UNLESS("get int as string 1", 0==owl_variable_get_tostring(&vd,"typewinsize", buf, 1024)); + FAIL_UNLESS("get int as string 2", 0==strcmp(buf,"8")); + FAIL_UNLESS("set int 1", 0==owl_variable_set_int(&vd,"typewinsize",12)); + FAIL_UNLESS("get int 2", 12==owl_variable_get_int(&vd,"typewinsize")); + FAIL_UNLESS("set int 1b", -1==owl_variable_set_int(&vd,"typewinsize",-3)); + FAIL_UNLESS("get int 2b", 12==owl_variable_get_int(&vd,"typewinsize")); + FAIL_UNLESS("set int 3", 0==owl_variable_set_fromstring(&vd,"typewinsize","9",0,0)); + FAIL_UNLESS("get int 4", 9==owl_variable_get_int(&vd,"typewinsize")); + FAIL_UNLESS("set int 5", -1==owl_variable_set_fromstring(&vd,"typewinsize","xxx",0,0)); + FAIL_UNLESS("set int 6", -1==owl_variable_set_fromstring(&vd,"typewinsize","",0,0)); + FAIL_UNLESS("get int 7", 9==owl_variable_get_int(&vd,"typewinsize")); + + + + owl_variable_dict_free(&vd); + + if (numfailed) printf("*** WARNING: failures encountered with owl_variable\n"); + printf("END testing owl_variable (%d failures)\n", numfailed); + return(numfailed); +} + + +#endif /* OWL_INCLUDE_REG_TESTS */ diff --git a/varstubs.c b/varstubs.c new file mode 100644 index 0000000..c2bbf30 --- /dev/null +++ b/varstubs.c @@ -0,0 +1,460 @@ +/* THIS FILE WAS AUTOGENERATED BY STUBGEN.PL --- DO NOT EDIT BY HAND!!! */ + +#include "owl.h"/* -------------------------------- aim.c -------------------------------- */ + +/* -------------------------------- buddy.c -------------------------------- */ + +/* -------------------------------- buddylist.c -------------------------------- */ + +/* -------------------------------- cmd.c -------------------------------- */ + +/* -------------------------------- commands.c -------------------------------- */ + +/* -------------------------------- context.c -------------------------------- */ + +/* -------------------------------- dict.c -------------------------------- */ + +/* -------------------------------- editwin.c -------------------------------- */ + +/* -------------------------------- errqueue.c -------------------------------- */ + +/* -------------------------------- filter.c -------------------------------- */ + +/* -------------------------------- filterelement.c -------------------------------- */ + +/* -------------------------------- fmtext.c -------------------------------- */ + +/* -------------------------------- functions.c -------------------------------- */ + +/* -------------------------------- global.c -------------------------------- */ + +/* -------------------------------- help.c -------------------------------- */ + +/* -------------------------------- history.c -------------------------------- */ + +/* -------------------------------- keybinding.c -------------------------------- */ + +/* -------------------------------- keymap.c -------------------------------- */ + +/* -------------------------------- keypress.c -------------------------------- */ + +/* -------------------------------- keys.c -------------------------------- */ + +/* -------------------------------- list.c -------------------------------- */ + +/* -------------------------------- logging.c -------------------------------- */ + +/* -------------------------------- mainwin.c -------------------------------- */ + +/* -------------------------------- message.c -------------------------------- */ + +/* -------------------------------- messagelist.c -------------------------------- */ + +/* -------------------------------- owl.c -------------------------------- */ + +/* -------------------------------- pair.c -------------------------------- */ + +/* -------------------------------- perlconfig.c -------------------------------- */ + +/* -------------------------------- popexec.c -------------------------------- */ + +/* -------------------------------- popwin.c -------------------------------- */ + +/* -------------------------------- regex.c -------------------------------- */ + +/* -------------------------------- select.c -------------------------------- */ + +/* -------------------------------- style.c -------------------------------- */ + +/* -------------------------------- stylefunc.c -------------------------------- */ + +/* -------------------------------- tester.c -------------------------------- */ + +/* -------------------------------- text.c -------------------------------- */ + +/* -------------------------------- timer.c -------------------------------- */ + +/* -------------------------------- util.c -------------------------------- */ + +/* -------------------------------- variable.c -------------------------------- */ +void owl_global_set_personalbell(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "personalbell", text); +} +char *owl_global_get_personalbell(owl_global *g) { + return owl_variable_get_string(&g->vars, "personalbell"); +} +void owl_global_set_bell_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "bell"); +} +void owl_global_set_bell_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "bell"); +} +int owl_global_is_bell(owl_global *g) { + return owl_variable_get_bool(&g->vars, "bell"); +} +void owl_global_set_debug_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "debug"); +} +void owl_global_set_debug_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "debug"); +} +int owl_global_is_debug(owl_global *g) { + return owl_variable_get_bool(&g->vars, "debug"); +} +void owl_global_set_startuplogin_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "startuplogin"); +} +void owl_global_set_startuplogin_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "startuplogin"); +} +int owl_global_is_startuplogin(owl_global *g) { + return owl_variable_get_bool(&g->vars, "startuplogin"); +} +void owl_global_set_shutdownlogout_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "shutdownlogout"); +} +void owl_global_set_shutdownlogout_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "shutdownlogout"); +} +int owl_global_is_shutdownlogout(owl_global *g) { + return owl_variable_get_bool(&g->vars, "shutdownlogout"); +} +void owl_global_set_rxping_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "rxping"); +} +void owl_global_set_rxping_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "rxping"); +} +int owl_global_is_rxping(owl_global *g) { + return owl_variable_get_bool(&g->vars, "rxping"); +} +void owl_global_set_txping_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "txping"); +} +void owl_global_set_txping_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "txping"); +} +int owl_global_is_txping(owl_global *g) { + return owl_variable_get_bool(&g->vars, "txping"); +} +void owl_global_set_sepbar_disable_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "sepbar_disable"); +} +void owl_global_set_sepbar_disable_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "sepbar_disable"); +} +int owl_global_is_sepbar_disable(owl_global *g) { + return owl_variable_get_bool(&g->vars, "sepbar_disable"); +} +void owl_global_set_smartstrip_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "smartstrip"); +} +void owl_global_set_smartstrip_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "smartstrip"); +} +int owl_global_is_smartstrip(owl_global *g) { + return owl_variable_get_bool(&g->vars, "smartstrip"); +} +void owl_global_set_newlinestrip_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "newlinestrip"); +} +void owl_global_set_newlinestrip_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "newlinestrip"); +} +int owl_global_is_newlinestrip(owl_global *g) { + return owl_variable_get_bool(&g->vars, "newlinestrip"); +} +void owl_global_set_displayoutgoing_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "displayoutgoing"); +} +void owl_global_set_displayoutgoing_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "displayoutgoing"); +} +int owl_global_is_displayoutgoing(owl_global *g) { + return owl_variable_get_bool(&g->vars, "displayoutgoing"); +} +void owl_global_set_loginsubs_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "loginsubs"); +} +void owl_global_set_loginsubs_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "loginsubs"); +} +int owl_global_is_loginsubs(owl_global *g) { + return owl_variable_get_bool(&g->vars, "loginsubs"); +} +void owl_global_set_logging_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "logging"); +} +void owl_global_set_logging_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "logging"); +} +int owl_global_is_logging(owl_global *g) { + return owl_variable_get_bool(&g->vars, "logging"); +} +void owl_global_set_classlogging_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "classlogging"); +} +void owl_global_set_classlogging_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "classlogging"); +} +int owl_global_is_classlogging(owl_global *g) { + return owl_variable_get_bool(&g->vars, "classlogging"); +} +void owl_global_set_loggingdirection(owl_global *g, int n) { + owl_variable_set_int(&g->vars, "loggingdirection", n); +} +int owl_global_get_loggingdirection(owl_global *g) { + return owl_variable_get_int(&g->vars, "loggingdirection"); +} +void owl_global_set_colorztext_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "colorztext"); +} +void owl_global_set_colorztext_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "colorztext"); +} +int owl_global_is_colorztext(owl_global *g) { + return owl_variable_get_bool(&g->vars, "colorztext"); +} +void owl_global_set_fancylines_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "fancylines"); +} +void owl_global_set_fancylines_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "fancylines"); +} +int owl_global_is_fancylines(owl_global *g) { + return owl_variable_get_bool(&g->vars, "fancylines"); +} +void owl_global_set_zcrypt_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "zcrypt"); +} +void owl_global_set_zcrypt_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "zcrypt"); +} +int owl_global_is_zcrypt(owl_global *g) { + return owl_variable_get_bool(&g->vars, "zcrypt"); +} +void owl_global_set_pseudologins_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "pseudologins"); +} +void owl_global_set_pseudologins_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "pseudologins"); +} +int owl_global_is_pseudologins(owl_global *g) { + return owl_variable_get_bool(&g->vars, "pseudologins"); +} +void owl_global_set_ignorelogins_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "ignorelogins"); +} +void owl_global_set_ignorelogins_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "ignorelogins"); +} +int owl_global_is_ignorelogins(owl_global *g) { + return owl_variable_get_bool(&g->vars, "ignorelogins"); +} +void owl_global_set_logfilter(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "logfilter", text); +} +char *owl_global_get_logfilter(owl_global *g) { + return owl_variable_get_string(&g->vars, "logfilter"); +} +void owl_global_set_loglogins_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "loglogins"); +} +void owl_global_set_loglogins_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "loglogins"); +} +int owl_global_is_loglogins(owl_global *g) { + return owl_variable_get_bool(&g->vars, "loglogins"); +} +void owl_global_set_lockout_ctrld(owl_global *g, int n) { + owl_variable_set_int(&g->vars, "disable-ctrl-d", n); +} +int owl_global_get_lockout_ctrld(owl_global *g) { + return owl_variable_get_int(&g->vars, "disable-ctrl-d"); +} +void owl_global_set_burningears_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "_burningears"); +} +void owl_global_set_burningears_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "_burningears"); +} +int owl_global_is_burningears(owl_global *g) { + return owl_variable_get_bool(&g->vars, "_burningears"); +} +void owl_global_set_summarymode_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "_summarymode"); +} +void owl_global_set_summarymode_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "_summarymode"); +} +int owl_global_is_summarymode(owl_global *g) { + return owl_variable_get_bool(&g->vars, "_summarymode"); +} +void owl_global_set_logpath(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "logpath", text); +} +char *owl_global_get_logpath(owl_global *g) { + return owl_variable_get_string(&g->vars, "logpath"); +} +void owl_global_set_classlogpath(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "classlogpath", text); +} +char *owl_global_get_classlogpath(owl_global *g) { + return owl_variable_get_string(&g->vars, "classlogpath"); +} +void owl_global_set_debug_file(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "debug_file", text); +} +char *owl_global_get_debug_file(owl_global *g) { + return owl_variable_get_string(&g->vars, "debug_file"); +} +void owl_global_set_zsigproc(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "zsigproc", text); +} +char *owl_global_get_zsigproc(owl_global *g) { + return owl_variable_get_string(&g->vars, "zsigproc"); +} +void owl_global_set_newmsgproc(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "newmsgproc", text); +} +char *owl_global_get_newmsgproc(owl_global *g) { + return owl_variable_get_string(&g->vars, "newmsgproc"); +} +void owl_global_set_zsig(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "zsig", text); +} +char *owl_global_get_zsig(owl_global *g) { + return owl_variable_get_string(&g->vars, "zsig"); +} +void owl_global_set_appendtosepbar(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "appendtosepbar", text); +} +char *owl_global_get_appendtosepbar(owl_global *g) { + return owl_variable_get_string(&g->vars, "appendtosepbar"); +} +void owl_global_set_zaway_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "zaway"); +} +void owl_global_set_zaway_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "zaway"); +} +int owl_global_is_zaway(owl_global *g) { + return owl_variable_get_bool(&g->vars, "zaway"); +} +void owl_global_set_zaway_msg(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "zaway_msg", text); +} +char *owl_global_get_zaway_msg(owl_global *g) { + return owl_variable_get_string(&g->vars, "zaway_msg"); +} +void owl_global_set_zaway_msg_default(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "zaway_msg_default", text); +} +char *owl_global_get_zaway_msg_default(owl_global *g) { + return owl_variable_get_string(&g->vars, "zaway_msg_default"); +} +void owl_global_set_aaway_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "aaway"); +} +void owl_global_set_aaway_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "aaway"); +} +int owl_global_is_aaway(owl_global *g) { + return owl_variable_get_bool(&g->vars, "aaway"); +} +void owl_global_set_aaway_msg(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "aaway_msg", text); +} +char *owl_global_get_aaway_msg(owl_global *g) { + return owl_variable_get_string(&g->vars, "aaway_msg"); +} +void owl_global_set_aaway_msg_default(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "aaway_msg_default", text); +} +char *owl_global_get_aaway_msg_default(owl_global *g) { + return owl_variable_get_string(&g->vars, "aaway_msg_default"); +} +void owl_global_set_view_home(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "view_home", text); +} +char *owl_global_get_view_home(owl_global *g) { + return owl_variable_get_string(&g->vars, "view_home"); +} +void owl_global_set_alert_filter(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "alert_filter", text); +} +char *owl_global_get_alert_filter(owl_global *g) { + return owl_variable_get_string(&g->vars, "alert_filter"); +} +void owl_global_set_alert_action(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "alert_action", text); +} +char *owl_global_get_alert_action(owl_global *g) { + return owl_variable_get_string(&g->vars, "alert_action"); +} +void owl_global_set_tty(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "tty", text); +} +char *owl_global_get_tty(owl_global *g) { + return owl_variable_get_string(&g->vars, "tty"); +} +void owl_global_set_default_style(owl_global *g, char *text) { + owl_variable_set_string(&g->vars, "default_style", text); +} +char *owl_global_get_default_style(owl_global *g) { + return owl_variable_get_string(&g->vars, "default_style"); +} +void owl_global_set_edit_maxfillcols(owl_global *g, int n) { + owl_variable_set_int(&g->vars, "edit:maxfillcols", n); +} +int owl_global_get_edit_maxfillcols(owl_global *g) { + return owl_variable_get_int(&g->vars, "edit:maxfillcols"); +} +void owl_global_set_edit_maxwrapcols(owl_global *g, int n) { + owl_variable_set_int(&g->vars, "edit:maxwrapcols", n); +} +int owl_global_get_edit_maxwrapcols(owl_global *g) { + return owl_variable_get_int(&g->vars, "edit:maxwrapcols"); +} +void owl_global_set_aim_ignorelogin_timer(owl_global *g, int n) { + owl_variable_set_int(&g->vars, "aim_ignorelogin_timer", n); +} +int owl_global_get_aim_ignorelogin_timer(owl_global *g) { + return owl_variable_get_int(&g->vars, "aim_ignorelogin_timer"); +} +void owl_global_set_typwin_lines(owl_global *g, int n) { + owl_variable_set_int(&g->vars, "typewinsize", n); +} +int owl_global_get_typwin_lines(owl_global *g) { + return owl_variable_get_int(&g->vars, "typewinsize"); +} +void owl_global_set_scrollmode(owl_global *g, int n) { + owl_variable_set_int(&g->vars, "scrollmode", n); +} +int owl_global_get_scrollmode(owl_global *g) { + return owl_variable_get_int(&g->vars, "scrollmode"); +} +void owl_global_set__followlast_on(owl_global *g) { + owl_variable_set_bool_on(&g->vars, "_followlast"); +} +void owl_global_set__followlast_off(owl_global *g) { + owl_variable_set_bool_off(&g->vars, "_followlast"); +} +int owl_global_is__followlast(owl_global *g) { + return owl_variable_get_bool(&g->vars, "_followlast"); +} + +/* -------------------------------- varstubs.c -------------------------------- */ + +/* -------------------------------- view.c -------------------------------- */ + +/* -------------------------------- viewwin.c -------------------------------- */ + +/* -------------------------------- zbuddylist.c -------------------------------- */ + +/* -------------------------------- zcrypt.c -------------------------------- */ + +/* -------------------------------- zephyr.c -------------------------------- */ + +/* -------------------------------- zwrite.c -------------------------------- */ + @@ -0,0 +1,191 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <stdlib.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: view.c,v 1.10 2009/03/29 12:38:23 kretch Exp $"; + +void owl_view_create(owl_view *v, char *name, owl_filter *f, owl_style *s) +{ + v->name=owl_strdup(name); + v->filter=f; + v->style=s; + owl_messagelist_create(&(v->ml)); + owl_view_recalculate(v); +} + +char *owl_view_get_name(owl_view *v) +{ + return(v->name); +} + +/* if the message matches the filter then add to view */ +void owl_view_consider_message(owl_view *v, owl_message *m) +{ + if (owl_filter_message_match(v->filter, m)) { + owl_messagelist_append_element(&(v->ml), m); + } +} + +/* remove all messages, add all the global messages that match the + * filter. + */ +void owl_view_recalculate(owl_view *v) +{ + int i, j; + owl_messagelist *gml; + owl_messagelist *ml; + owl_message *m; + + gml=owl_global_get_msglist(&g); + ml=&(v->ml); + + /* nuke the old list */ + owl_list_free_simple((owl_list *) ml); + owl_messagelist_create(&(v->ml)); + + /* find all the messages we want */ + j=owl_messagelist_get_size(gml); + for (i=0; i<j; i++) { + m=owl_messagelist_get_element(gml, i); + if (owl_filter_message_match(v->filter, m)) { + owl_messagelist_append_element(ml, m); + } + } +} + +void owl_view_new_filter(owl_view *v, owl_filter *f) +{ + v->filter=f; + owl_view_recalculate(v); +} + +void owl_view_set_style(owl_view *v, owl_style *s) +{ + v->style=s; +} + +owl_style *owl_view_get_style(owl_view *v) +{ + return(v->style); +} + +char *owl_view_get_style_name(owl_view *v) { + return(owl_style_get_name(v->style)); +} + +owl_message *owl_view_get_element(owl_view *v, int index) +{ + return(owl_messagelist_get_element(&(v->ml), index)); +} + +void owl_view_delete_element(owl_view *v, int index) +{ + owl_messagelist_delete_element(&(v->ml), index); +} + +void owl_view_undelete_element(owl_view *v, int index) +{ + owl_messagelist_undelete_element(&(v->ml), index); +} + +int owl_view_get_size(owl_view *v) +{ + return(owl_messagelist_get_size(&(v->ml))); +} + +/* Returns the position in the view with a message closest + * to the passed msgid. */ +int owl_view_get_nearest_to_msgid(owl_view *v, int targetid) +{ + int first, last, mid = 0, max, bestdist, curid = 0; + + first = 0; + last = max = owl_view_get_size(v) - 1; + while (first <= last) { + mid = (first + last) / 2; + curid = owl_message_get_id(owl_view_get_element(v, mid)); + if (curid == targetid) { + return(mid); + } else if (curid < targetid) { + first = mid + 1; + } else { + last = mid - 1; + } + } + bestdist = abs(targetid-curid); + if (curid < targetid && mid+1 < max) { + curid = owl_message_get_id(owl_view_get_element(v, mid+1)); + mid = (bestdist < abs(targetid-curid)) ? mid : mid+1; + } + else if (curid > targetid && mid-1 >= 0) { + curid = owl_message_get_id(owl_view_get_element(v, mid-1)); + mid = (bestdist < abs(targetid-curid)) ? mid : mid-1; + } + return mid; +} + +int owl_view_get_nearest_to_saved(owl_view *v) +{ + int cachedid; + + cachedid=owl_filter_get_cachedmsgid(v->filter); + if (cachedid<0) return(0); + return (owl_view_get_nearest_to_msgid(v, cachedid)); +} + +/* saves the current message position in the filter so it can + * be restored later if we switch back to this filter. */ +void owl_view_save_curmsgid(owl_view *v, int curid) +{ + owl_filter_set_cachedmsgid(v->filter, curid); +} + +/* fmtext should already be initialized */ +void owl_view_to_fmtext(owl_view *v, owl_fmtext *fm) +{ + owl_fmtext_append_normal(fm, "Name: "); + owl_fmtext_append_normal(fm, v->name); + owl_fmtext_append_normal(fm, "\n"); + + owl_fmtext_append_normal(fm, "Filter: "); + owl_fmtext_append_normal(fm, owl_filter_get_name(v->filter)); + owl_fmtext_append_normal(fm, "\n"); + + owl_fmtext_append_normal(fm, "Style: "); + owl_fmtext_append_normal(fm, owl_style_get_name(v->style)); + owl_fmtext_append_normal(fm, "\n"); +} + +char *owl_view_get_filtname(owl_view *v) +{ + return(owl_filter_get_name(v->filter)); +} + +void owl_view_free(owl_view *v) +{ + owl_list_free_simple((owl_list *) &(v->ml)); + if (v->name) owl_free(v->name); +} diff --git a/viewwin.c b/viewwin.c new file mode 100644 index 0000000..eec75fe --- /dev/null +++ b/viewwin.c @@ -0,0 +1,178 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <string.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: viewwin.c,v 1.9 2009/03/29 12:38:23 kretch Exp $"; + +#define BOTTOM_OFFSET 1 + +/* initialize the viewwin e. 'win' is an already initialzed curses + * window that will be used by viewwin + */ +void owl_viewwin_init_text(owl_viewwin *v, WINDOW *win, int winlines, int wincols, char *text) +{ + owl_fmtext_init_null(&(v->fmtext)); + if (text) { + owl_fmtext_append_normal(&(v->fmtext), text); + if (text[strlen(text)-1]!='\n' && text[0]!='\0') { + owl_fmtext_append_normal(&(v->fmtext), "\n"); + } + v->textlines=owl_fmtext_num_lines(&(v->fmtext)); + } + v->topline=0; + v->rightshift=0; + v->winlines=winlines; + v->wincols=wincols; + v->curswin=win; + v->onclose_hook = NULL; +} + +void owl_viewwin_append_text(owl_viewwin *v, char *text) { + owl_fmtext_append_normal(&(v->fmtext), text); + v->textlines=owl_fmtext_num_lines(&(v->fmtext)); +} + +/* initialize the viewwin e. 'win' is an already initialzed curses + * window that will be used by viewwin + */ +void owl_viewwin_init_fmtext(owl_viewwin *v, WINDOW *win, int winlines, int wincols, owl_fmtext *fmtext) +{ + owl_fmtext_copy(&(v->fmtext), fmtext); + v->textlines=owl_fmtext_num_lines(&(v->fmtext)); + v->topline=0; + v->rightshift=0; + v->winlines=winlines; + v->wincols=wincols; + v->curswin=win; +} + +void owl_viewwin_set_curswin(owl_viewwin *v, WINDOW *w, int winlines, int wincols) +{ + v->curswin=w; + v->winlines=winlines; + v->wincols=wincols; +} + +void owl_viewwin_set_onclose_hook(owl_viewwin *v, void (*onclose_hook) (owl_viewwin *vwin, void *data), void *onclose_hook_data) { + v->onclose_hook = onclose_hook; + v->onclose_hook_data = onclose_hook_data; +} + +/* regenerate text on the curses window. */ +/* if update == 1 then do a doupdate() */ +void owl_viewwin_redisplay(owl_viewwin *v, int update) +{ + owl_fmtext fm1, fm2; + + werase(v->curswin); + wmove(v->curswin, 0, 0); + + owl_fmtext_init_null(&fm1); + owl_fmtext_init_null(&fm2); + + owl_fmtext_truncate_lines(&(v->fmtext), v->topline, v->winlines-BOTTOM_OFFSET, &fm1); + owl_fmtext_truncate_cols(&fm1, v->rightshift, v->wincols-1+v->rightshift, &fm2); + + owl_fmtext_curs_waddstr(&fm2, v->curswin); + + /* print the message at the bottom */ + wmove(v->curswin, v->winlines-1, 0); + wattrset(v->curswin, A_REVERSE); + if (v->textlines - v->topline > v->winlines-BOTTOM_OFFSET) { + waddstr(v->curswin, "--More-- (Space to see more, 'q' to quit)"); + } else { + waddstr(v->curswin, "--End-- (Press 'q' to quit)"); + } + wattroff(v->curswin, A_REVERSE); + wnoutrefresh(v->curswin); + + if (update==1) { + doupdate(); + } + + owl_fmtext_free(&fm1); + owl_fmtext_free(&fm2); +} + +void owl_viewwin_pagedown(owl_viewwin *v) +{ + v->topline+=v->winlines - BOTTOM_OFFSET; + if ( (v->topline+v->winlines-BOTTOM_OFFSET) > v->textlines) { + v->topline = v->textlines - v->winlines + BOTTOM_OFFSET; + } +} + +void owl_viewwin_linedown(owl_viewwin *v) +{ + v->topline++; + if ( (v->topline+v->winlines-BOTTOM_OFFSET) > v->textlines) { + v->topline = v->textlines - v->winlines + BOTTOM_OFFSET; + } +} + +void owl_viewwin_pageup(owl_viewwin *v) +{ + v->topline-=v->winlines; + if (v->topline<0) v->topline=0; +} + +void owl_viewwin_lineup(owl_viewwin *v) +{ + v->topline--; + if (v->topline<0) v->topline=0; +} + +void owl_viewwin_right(owl_viewwin *v, int n) +{ + v->rightshift+=n; +} + +void owl_viewwin_left(owl_viewwin *v, int n) +{ + v->rightshift-=n; + if (v->rightshift<0) v->rightshift=0; +} + +void owl_viewwin_top(owl_viewwin *v) +{ + v->topline=0; + v->rightshift=0; +} + +void owl_viewwin_bottom(owl_viewwin *v) +{ + v->topline = v->textlines - v->winlines + BOTTOM_OFFSET; +} + +void owl_viewwin_free(owl_viewwin *v) +{ + if (v->onclose_hook) { + v->onclose_hook(v, v->onclose_hook_data); + v->onclose_hook = NULL; + v->onclose_hook_data = NULL; + } + owl_fmtext_free(&(v->fmtext)); +} diff --git a/zbuddylist.c b/zbuddylist.c new file mode 100644 index 0000000..7466cb2 --- /dev/null +++ b/zbuddylist.c @@ -0,0 +1,89 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include "owl.h" + +static const char fileIdent[] = "$Id"; + +void owl_zbuddylist_create(owl_zbuddylist *zb) +{ + owl_list_create(&(zb->zusers)); +} + +int owl_zbuddylist_adduser(owl_zbuddylist *zb, char *name) +{ + int i, j; + char *user; + + user=long_zuser(name); + + j=owl_list_get_size(&(zb->zusers)); + for (i=0; i<j; i++) { + if (!strcasecmp(user, owl_list_get_element(&(zb->zusers), i))) { + owl_free(user); + return(-1); + } + } + owl_list_append_element(&(zb->zusers), user); + return(0); +} + +int owl_zbuddylist_deluser(owl_zbuddylist *zb, char *name) +{ + int i, j; + char *user, *ptr; + + user=long_zuser(name); + + j=owl_list_get_size(&(zb->zusers)); + for (i=0; i<j; i++) { + ptr=owl_list_get_element(&(zb->zusers), i); + if (!strcasecmp(user, ptr)) { + owl_list_remove_element(&(zb->zusers), i); + owl_free(ptr); + owl_free(user); + return(0); + } + } + owl_free(user); + return(-1); +} + +int owl_zbuddylist_contains_user(owl_zbuddylist *zb, char *name) +{ + int i, j; + char *user; + + user=long_zuser(name); + + j=owl_list_get_size(&(zb->zusers)); + for (i=0; i<j; i++) { + if (!strcasecmp(user, owl_list_get_element(&(zb->zusers), i))) { + owl_free(user); + return(1); + } + } + owl_free(user); + return(0); +} diff --git a/zcrypt.c b/zcrypt.c new file mode 100644 index 0000000..c3a0407 --- /dev/null +++ b/zcrypt.c @@ -0,0 +1,795 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +/* The functions here with "owl" in their name were written for the + * Owl project. Other code is from Philip Lisiecki's + * (lisiecki@mit.edu) zcrypt program. + */ + +/* zcrypt.c -- Read in a data stream from stdin & dump a decrypted/encrypted * + * datastream. Reads the string to make the key from from the first * + * parameter. Encrypts or decrypts according to -d or -e flag. (-e is * + * default.) Will invoke zwrite if the -c option is provided for * + * encryption. If a zephyr class is specified & the keyfile name omitted * + * the ~/.crypt-table will be checked for "crypt-classname" and then * + * "crypt-default" for the keyfile name. */ + +static const char fileIdent[] = "$Id: zcrypt.c,v 1.13 2009/03/29 12:38:23 kretch Exp $"; + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include "owl.h" + +#ifdef OWL_ENABLE_ZCRYPT + +#define BASE_CODE 70 +#define LAST_CODE (BASE_CODE + 15) +#define OUTPUT_BLOCK_SIZE 16 +#include <unistd.h> +#include <sys/types.h> +#include <des.h> + +#define MAX_KEY 128 + +#ifndef TRUE +#define TRUE -1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define ZWRITE_OPT_NOAUTH (1<<0) +#define ZWRITE_OPT_SIGNATURE (1<<1) +#define ZWRITE_OPT_IGNOREVARS (1<<2) +#define ZWRITE_OPT_VERBOSE (1<<3) +#define ZWRITE_OPT_QUIET (1<<4) +#define ZCRYPT_OPT_MESSAGE (1<<5) +#define ZCRYPT_OPT_IGNOREDOT (1<<6) + +typedef struct +{ + int flags; + char *signature; + char *message; +} ZWRITEOPTIONS; + +char *GetZephyrVarKeyFile(char *whoami, char *class, char *instance); +char *BuildArgString(char **argv, int start, int end); +static int do_encrypt(char *keystring, int zephyr, char *class, char *instance, ZWRITEOPTIONS *zoptions, char* keyfile); +int do_decrypt(char *keystring); + +#ifndef HAVE_DES_ECB_ENCRYPT_PROTO +int des_ecb_encrypt(char [], char [], des_key_schedule, int); +#endif + +#define M_NONE 0 +#define M_ZEPHYR_ENCRYPT 1 +#define M_DECRYPT 2 +#define M_ENCRYPT 3 +#define M_RANDOMIZE 4 +#define M_SETKEY 5 + +int zcrypt(int argc, char *argv[]) { + char *fname = NULL; + int error = FALSE; + int zephyr = FALSE; + char *class = NULL, *instance = NULL; + int mode = M_NONE; + + extern int optind, opterr; + extern char *optarg; + char c; + + int messageflag = FALSE; + ZWRITEOPTIONS zoptions; + zoptions.flags = 0; + + while ((c = getopt(argc, argv, "ZDERSF:c:i:advqtluons:f:m")) != (char)EOF) { + switch(c) { + case 'Z': + /* Zephyr encrypt */ + mode = M_ZEPHYR_ENCRYPT; + break; + case 'D': + /* Decrypt */ + mode = M_DECRYPT; + break; + case 'E': + /* Encrypt */ + mode = M_ENCRYPT; + break; + case 'R': + /* Randomize the keyfile */ + mode = M_RANDOMIZE; + break; + case 'S': + /* Set a new key value from stdin */ + mode = M_SETKEY; + break; + case 'F': + /* Specify the keyfile explicitly */ + if (fname != NULL) error = TRUE; + fname = optarg; + break; + case 'c': + /* Zwrite/zcrypt: class name */ + if (class != NULL) error = TRUE; + class = optarg; + break; + case 'i': + /* Zwrite/zcrypt: instance name */ + if (instance != NULL) error = TRUE; + instance = optarg; + break; + case 'a': + /* Zwrite: authenticate (default) */ + zoptions.flags &= ~ZWRITE_OPT_NOAUTH; + break; + case 'd': + /* Zwrite: do not authenticate */ + zoptions.flags |= ZWRITE_OPT_NOAUTH; + break; + case 'v': + /* Zwrite: verbose */ + zoptions.flags |= ZWRITE_OPT_VERBOSE; + break; + case 'q': + /* Zwrite: quiet */ + zoptions.flags |= ZWRITE_OPT_QUIET; + break; + case 't': + /* Zwrite: no expand tabs (ignored) */ + break; + case 'l': + /* Zwrite: ignore '.' on a line by itself (ignored) */ + zoptions.flags |= ZCRYPT_OPT_IGNOREDOT; + break; + case 'u': + /* Zwrite: urgent message */ + instance = "URGENT"; + break; + case 'o': + /* Zwrite: ignore zephyr variables zwrite-class, zwrite-inst, */ + /* zwrite-opcode */ + zoptions.flags |= ZWRITE_OPT_IGNOREVARS; + break; + case 'n': + /* Zwrite: prevent PING message (always used) */ + break; + case 's': + /* Zwrite: signature */ + zoptions.flags |= ZWRITE_OPT_SIGNATURE; + zoptions.signature = optarg; + break; + case 'f': + /* Zwrite: file system specification (ignored) */ + break; + case 'm': + /* Message on rest of line */ + messageflag = TRUE; + break; + case '?': + error = TRUE; + break; + } + if (error || messageflag) break; + } + + if (class != NULL || instance != NULL) { + zephyr = TRUE; + } + + if (messageflag) { + zoptions.flags |= ZCRYPT_OPT_MESSAGE; + zoptions.message = BuildArgString(argv, optind, argc); + if (!zoptions.message) + { + printf("Memory allocation error.\n"); + error = TRUE; + } + } else if (optind < argc) { + error = TRUE; + } + + if (mode == M_NONE) mode = (zephyr?M_ZEPHYR_ENCRYPT:M_ENCRYPT); + + if (mode == M_ZEPHYR_ENCRYPT && !zephyr) error = TRUE; + + if (!error && fname == NULL && (class != NULL || instance != NULL)) { + fname = GetZephyrVarKeyFile(argv[0], class, instance); + } + + + if (error || fname == NULL) { + printf("Usage: %s [-Z|-D|-E|-R|-S] [-F Keyfile] [-c class] [-i instance]\n", argv[0]); + printf(" [-advqtluon] [-s signature] [-f arg] [-m message]\n"); + printf(" One or more of class, instance, and keyfile must be specified.\n"); + } else { + if (mode == M_RANDOMIZE) { + /* Choose a new, random key */ +/* + FILE *fkey = fopen(fname, "w"); + if (!fkey) + printf("Could not open key file for writing: %s\n", fname); + else + { + char string[100]; + fputs(fkey, string); + fclose(fkey); + } + */ + printf("Feature not yet implemented.\n"); + } else if (mode == M_SETKEY) { + /* Set a new, user-entered key */ + char newkey[MAX_KEY]; + FILE *fkey; + + if (isatty(0)) { + printf("Enter new key: "); + /* Really should read without echo!!! */ + fgets(newkey, MAX_KEY - 1, stdin); + } else { + fgets(newkey, MAX_KEY - 1, stdin); + } + + fkey = fopen(fname, "w"); + if (!fkey) { + printf("Could not open key file for writing: %s\n", fname); + } else { + if (fputs(newkey, fkey) != strlen(newkey) || putc('\n', fkey) != '\n') { + printf("Error writing to key file.\n"); + fclose(fkey); + } else { + fclose(fkey); + printf("Key update complete.\n"); + } + } + } else { + /* Encrypt/decrypt */ + FILE *fkey = fopen(fname, "r"); + if (!fkey) { + printf("Could not open key file: %s\n", fname); + } else { + char keystring[MAX_KEY]; + fgets(keystring, MAX_KEY-1, fkey); + if (mode == M_ZEPHYR_ENCRYPT || mode == M_ENCRYPT) { + do_encrypt(keystring, (mode == M_ZEPHYR_ENCRYPT), class, instance, + &zoptions, fname); + } else { + do_decrypt(keystring); + } + fclose(fkey); + } + } + } + + /* Always print the **END** message if -D is specified. */ + if (mode == M_DECRYPT) printf("**END**\n"); + + exit(0); +} + +/* The 'owl_zcrypt_decrypt' function was written by kretch for Owl. + * Decrypt the message in 'in' on class 'class' and instance + * 'instance' and leave the result in 'out'. Out must be a buffer + * allocated by the caller. + * + * return 0 on success, otherwise -1 + */ +int owl_zcrypt_decrypt(char *out, char *in, char *class, char *instance) { + char *fname, keystring[MAX_KEY], *inptr, *endptr; + FILE *fkey; + des_cblock key; + des_key_schedule schedule; + char input[8], output[9]; + int i, c1, c2; + + fname=GetZephyrVarKeyFile("zcrypt", class, instance); + if (!fname) return(-1); + fkey=fopen(fname, "r"); + if (!fkey) return(-1); + fgets(keystring, MAX_KEY-1, fkey); + fclose(fkey); + + strcpy(out, ""); + + output[0] = '\0'; /* In case no message at all */ + output[8] = '\0'; /* NULL at end will limit string length to 8 */ + + des_string_to_key(keystring, key); + des_key_sched(key, schedule); + + inptr=in; + endptr=in+strlen(in)-1; + while (inptr<endptr) { + for (i=0; i<8; i++) { + c1=(inptr[0])-BASE_CODE; + c2=(inptr[1])-BASE_CODE; + input[i]=c1 * 0x10 + c2; + inptr+=2; + } + des_ecb_encrypt(input, output, schedule, FALSE); + strcat(out, output); + } + + if (output[0]) { + if (output[strlen(output)-1] != '\n') { + strcat(out, "\n"); + } + } else { + strcat(out, "\n"); + } + return(0); +} + +int owl_zcrypt_encrypt(char *out, char *in, char *class, char *instance) { + /* static int do_encrypt(char *keystring, int zephyr, char *class, char *instance, ZWRITEOPTIONS *zoptions, char* keyfile) { */ + char *fname, keystring[MAX_KEY]; + FILE *fkey; + des_cblock key; + des_key_schedule schedule; + char input[8], output[8]; + int size, length, i; + char *inbuff = NULL, *inptr; + int use_buffer = FALSE; + int num_blocks=0, last_block_size=0; + + fname=GetZephyrVarKeyFile("zcrypt", class, instance); + if (!fname) return(-1); + fkey=fopen(fname, "r"); + if (!fkey) return(-1); + fgets(keystring, MAX_KEY-1, fkey); + fclose(fkey); + + des_string_to_key(keystring, key); + des_key_sched(key, schedule); + + inbuff=in; + length=strlen(inbuff); + num_blocks=(length+7)/8; + last_block_size=((length+7)%8)+1; + use_buffer=TRUE; + + strcpy(out, ""); + + inptr=inbuff; + while (TRUE) { + /* Get 8 bytes from buffer */ + if (num_blocks > 1) { + size = 8; + memcpy(input, inptr, size); + inptr+=8; + num_blocks--; + } else if (num_blocks == 1) { + size=last_block_size; + memcpy(input, inptr, size); + num_blocks--; + } else { + size=0; + } + + /* Check for EOF and pad the string to 8 chars, if needed */ + if (size == 0) break; /* END OF INPUT: BREAK FROM while LOOP! */ + + if (size<8) memset(input + size, 0, 8 - size); + + /* Encrypt and output the block */ + des_ecb_encrypt(input, output, schedule, TRUE); + + for (i = 0; i < 8; i++) { + sprintf(out + strlen(out), "%c", ((output[i] & 0xf0) >> 4) + BASE_CODE); + sprintf(out + strlen(out), "%c", (output[i] & 0x0f) + BASE_CODE); + } + + if (size < 8) break; + } + return(0); +} + +/* Build a space-separated string from argv from elements between start * + * and end - 1. malloc()'s the returned string. */ +char *BuildArgString(char **argv, int start, int end) { + int len = 1; + int i; + char *result; + + /* Compute the length of the string. (Plus 1 or 2) */ + for (i = start; i < end; i++) { + len += strlen(argv[i]) + 1; + } + + /* Allocate memory */ + result = (char *)malloc(len); + if (result) { + /* Build the string */ + char *ptr = result; + /* Start with an empty string, in case nothing is copied. */ + *ptr = '\0'; + /* Copy the arguments */ + for (i = start; i < end; i++) { + char *temp = argv[i]; + /* Add a space, if not the first argument */ + if (i != start) *ptr++ = ' '; + /* Copy argv[i], leaving ptr pointing to the '\0' copied from temp */ + while ((*ptr = *temp++)!=0) { + ptr++; + } + } + } + + return(result); +} + +#define MAX_BUFF 258 +#define MAX_SEARCH 3 +/* Find the class/instance in the .crypt-table */ +char *GetZephyrVarKeyFile(char *whoami, char *class, char *instance) { + char *keyfile = NULL; + char *varname[MAX_SEARCH]; + int length[MAX_SEARCH], i; + char buffer[MAX_BUFF]; + char *filename; + char result[MAX_SEARCH][MAX_BUFF]; + int numsearch = 0; + FILE *fsearch; + + memset(varname, 0, sizeof(varname)); + + /* Determine names to look for in .crypt-table */ + if (instance) { + varname[numsearch++] = owl_sprintf("crypt-%s-%s:", (class?class:"message"), instance); + } + if (class) { + varname[numsearch++] = owl_sprintf("crypt-%s:", class); + } + varname[numsearch++] = owl_strdup("crypt-default:"); + + /* Setup the result array, and determine string lengths */ + for (i = 0; i < numsearch; i++) { + result[i][0] = '\0'; + length[i] = strlen(varname[i]); + } + + /* Open~/.crypt-table */ + filename = owl_sprintf("%s/.crypt-table", getenv("HOME")); + fsearch = fopen(filename, "r"); + if (fsearch) { + /* Scan file for a match */ + while (!feof(fsearch)) { + fgets(buffer, MAX_BUFF - 3, fsearch); + for (i = 0; i < numsearch; i++) { + if (strncasecmp(varname[i], buffer, length[i]) == 0) { + int j; + for (j = length[i]; buffer[j] == ' '; j++) + ; + strcpy(result[i], &buffer[j]); + if (*result[i]) { + if (result[i][strlen(result[i])-1] == '\n') { + result[i][strlen(result[i])-1] = '\0'; + } + } + } + } + } + + /* Pick the "best" match found */ + keyfile = NULL; + for (i = 0; i < numsearch; i++) { + if (*result[i]) { + keyfile = result[i]; + break; + } + } + + if (keyfile == NULL) { + /* printf("Could not find key table entry.\n"); */ + } else { + /* Prepare result to be returned */ + char *temp = keyfile; + keyfile = (char *)malloc(strlen(temp) + 1); + if (keyfile) { + strcpy(keyfile, temp); + } else { + /* printf("Memory allocation error.\n"); */ + } + } + + fclose(fsearch); + } else { + /* printf("Could not open key table file: %s\n", filename); */ + } + + for(i = 0; i < MAX_SEARCH; i++) { + owl_free(varname[i]); + } + + owl_free(filename); + + return(keyfile); +} + +static pid_t zephyrpipe_pid = 0; + +/* Open a pipe to zwrite */ +static FILE *GetZephyrPipe(char *class, char *instance, ZWRITEOPTIONS *zoptions) { + int fildes[2]; + pid_t pid; + FILE *result; + char *argv[20]; + int argc = 0; + + if (pipe(fildes) < 0) return(NULL); + pid = fork(); + + if (pid < 0) { + /* Error: clean up */ + close(fildes[0]); + close(fildes[1]); + result = NULL; + } else if (pid == 0) { + /* Setup child process */ + argv[argc++] = "zwrite"; + argv[argc++] = "-n"; /* Always send without ping */ + if (class) { + argv[argc++] = "-c"; + argv[argc++] = class; + } + if (instance) { + argv[argc++] = "-i"; + argv[argc++] = instance; + } + if (zoptions->flags & ZWRITE_OPT_NOAUTH) argv[argc++] = "-d"; + if (zoptions->flags & ZWRITE_OPT_QUIET) argv[argc++] = "-q"; + if (zoptions->flags & ZWRITE_OPT_VERBOSE) argv[argc++] = "-v"; + if (zoptions->flags & ZWRITE_OPT_SIGNATURE) { + argv[argc++] = "-s"; + argv[argc++] = zoptions->signature; + } + argv[argc++] = "-O"; + argv[argc++] = "crypt"; + argv[argc] = NULL; + close(fildes[1]); + if (fildes[0] != STDIN_FILENO) { + if (dup2(fildes[0], STDIN_FILENO) != STDIN_FILENO) exit(0); + close(fildes[0]); + } + close(fildes[0]); + execvp(argv[0], argv); + printf("Exec error: could not run zwrite\n"); + exit(0); + } else { + close(fildes[0]); + /* Create a FILE * for the zwrite pipe */ + result = (FILE *)fdopen(fildes[1], "w"); + zephyrpipe_pid = pid; + } + + return(result); +} + +/* Close the pipe to zwrite */ +void CloseZephyrPipe(FILE *pipe) { + fclose(pipe); + waitpid(zephyrpipe_pid, NULL, 0); + zephyrpipe_pid = 0; +} + +#define MAX_RESULT 2048 + + +void block_to_ascii(char *output, FILE *outfile) { + int i; + for (i = 0; i < 8; i++) { + putc(((output[i] & 0xf0) >> 4) + BASE_CODE, outfile); + putc( (output[i] & 0x0f) + BASE_CODE, outfile); + } +} + +#define MAX_LINE 128 + +/* Encrypt stdin, with prompt if isatty, and send to stdout, or to zwrite + if zephyr is set. */ +static int do_encrypt(char *keystring, int zephyr, char *class, char *instance, ZWRITEOPTIONS *zoptions, char* keyfile) { + des_cblock key; + des_key_schedule schedule; + char input[8], output[8]; + int size; + FILE *outfile = stdout; + int error = FALSE; + char *inbuff = NULL, *inptr; + int freein = FALSE; + int use_buffer = FALSE; + int num_blocks=0, last_block_size=0; + + des_string_to_key(keystring, key); + des_key_sched(key, schedule); + + if (zephyr) { + if (zoptions->flags & ZCRYPT_OPT_MESSAGE) { + /* Use the -m message */ + int length; + inbuff = zoptions->message; + length = strlen(inbuff); + num_blocks = (length + 7) / 8; + last_block_size = ((length + 7) % 8) + 1; + use_buffer = TRUE; + } else if (isatty(0)) { + /* tty input, so show the "Type your message now..." message */ + if (zoptions->flags & ZCRYPT_OPT_IGNOREDOT) { + printf("Type your message now. End with the end-of-file character.\n"); + } else { + printf("Type your message now. End with control-D or a dot on a line by itself.\n"); + } + use_buffer = TRUE; + if ((inptr = inbuff = (char *)malloc(MAX_RESULT)) == NULL) { + printf("Memory allocation error\n"); + return FALSE; + } + while (inptr - inbuff < MAX_RESULT - MAX_LINE - 20) { + fgets(inptr, MAX_LINE, stdin); + if (inptr[0]) { + if (inptr[0] == '.' && inptr[1] == '\n' && + !(zoptions->flags & ZCRYPT_OPT_IGNOREDOT)) { + inptr[0] = '\0'; + break; + } else { + inptr += strlen(inptr); + } + } else { + break; + } + } + num_blocks = (inptr - inbuff + 7) / 8; + last_block_size = ((inptr - inbuff + 7) % 8) + 1; + freein = TRUE; + } + + /* if (zephyr) */ + outfile = GetZephyrPipe(class, instance, zoptions); + if (!outfile) { + printf("Could not run zwrite\n"); + if (freein && inbuff) { + free(inbuff); + } + return(FALSE); + } + } + + inptr = inbuff; + + /* Encrypt the input (inbuff or stdin) and send it to outfile */ + while (TRUE) { + if (use_buffer) { + /* Get 8 bytes from buffer */ + if (num_blocks > 1) { + size = 8; + memcpy(input, inptr, size); + inptr += 8; + num_blocks--; + } else if (num_blocks == 1) { + size = last_block_size; + memcpy(input, inptr, size); + num_blocks--; + } else { + size = 0; + } + } else { + /* Get 8 bytes from stdin */ + size = fread(input, 1, 8, stdin); + } + + /* Check for EOF and pad the string to 8 chars, if needed */ + if (size == 0) break; /* END OF INPUT: BREAK FROM while LOOP! */ + + if (size < 8) memset(input + size, 0, 8 - size); + + /* Encrypt and output the block */ + des_ecb_encrypt(input, output, schedule, TRUE); + block_to_ascii(output, outfile); + + if (size < 8) break; + } + + /* Close out the output */ + if (!error) putc('\n', outfile); + if (zephyr) CloseZephyrPipe(outfile); + + /* Free the input buffer, if necessary */ + if (freein && inbuff) free(inbuff); + + return(!error); +} + +/* Read a half-byte from stdin, skipping invalid characters. Returns -1 + if at EOF or file error */ +int read_ascii_nybble() { + char c; + + while (TRUE) { + if (fread(&c, 1, 1, stdin) == 0) { + return(-1); + } else if (c >= BASE_CODE && c <= LAST_CODE) { + return(c - BASE_CODE); + } + } +} + +/* Read both halves of the byte and return the single byte. Returns -1 + if at EOF or file error. */ +int read_ascii_byte() { + int c1, c2; + c1 = read_ascii_nybble(); + if (c1 >= 0) { + c2 = read_ascii_nybble(); + if (c2 >= 0) { + return c1 * 0x10 + c2; + } + } + return(-1); +} + +/* Read an 8-byte DES block from stdin */ +int read_ascii_block(char *input) { + int c, i; + + for (i = 0; i < 8; i++) { + c = read_ascii_byte(); + if (c < 0) return(FALSE); + input[i] = c; + } + + return(TRUE); +} + +/* Decrypt stdin */ +int do_decrypt(char *keystring) { + des_cblock key; + des_key_schedule schedule; + char input[8], output[9]; + + output[0] = '\0'; /* In case no message at all */ + output[8] = '\0'; /* NULL at end will limit string length to 8 */ + + des_string_to_key(keystring, key); + des_key_sched(key, schedule); + + while (read_ascii_block(input)) { + des_ecb_encrypt(input, output, schedule, FALSE); + printf("%s", output); + } + + if (output[0]) { + if (output[strlen(output)-1] != '\n') { + printf("\n"); + } + } else { + printf("\n"); + } + return(0); +} + +#endif diff --git a/zephyr.c b/zephyr.c new file mode 100644 index 0000000..0ae72b1 --- /dev/null +++ b/zephyr.c @@ -0,0 +1,1091 @@ +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <string.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: zephyr.c,v 1.61 2009/04/05 23:36:54 kretch Exp $"; + +#ifdef HAVE_LIBZEPHYR +Code_t ZResetAuthentication(); +#endif + +int owl_zephyr_initialize() +{ +#ifdef HAVE_LIBZEPHYR + int ret; + + if ((ret = ZInitialize()) != ZERR_NONE) { + com_err("owl",ret,"while initializing"); + return(1); + } + if ((ret = ZOpenPort(NULL)) != ZERR_NONE) { + com_err("owl",ret,"while opening port"); + return(1); + } +#endif + return(0); +} + + +int owl_zephyr_shutdown() +{ +#ifdef HAVE_LIBZEPHYR + unsuball(); + ZClosePort(); +#endif + return(0); +} + +int owl_zephyr_zpending() +{ +#ifdef HAVE_LIBZEPHYR + return(ZPending()); +#else + return(0); +#endif +} + +char *owl_zephyr_get_realm() +{ +#ifdef HAVE_LIBZEPHYR + return(ZGetRealm()); +#else + return(""); +#endif +} + +char *owl_zephyr_get_sender() +{ +#ifdef HAVE_LIBZEPHYR + return(ZGetSender()); +#else + return(""); +#endif +} + +#ifdef HAVE_LIBZEPHYR +int owl_zephyr_loadsubs_helper(ZSubscription_t subs[], int count) +{ + int i, ret = 0; + errcode_t code; + /* sub without defaults */ + code = ZSubscribeToSansDefaults(subs,count,0); + if (code != ZERR_NONE) { + owl_function_error("Error %s subscribing to zephyr notifications.", error_message(code)); + ret=-2; + } + + /* free stuff */ + for (i=0; i<count; i++) { + owl_free(subs[i].zsub_class); + owl_free(subs[i].zsub_classinst); + owl_free(subs[i].zsub_recipient); + } + return ret; +} +#endif + +/* Load zephyr subscriptions form 'filename'. If 'filename' is NULL, + * the default file $HOME/.zephyr.subs will be used. + * + * Returns 0 on success. If the file does not exist, return -1 if + * 'error_on_nofile' is 1, otherwise return 0. Return -1 if the file + * exists but can not be read. Return -2 if there is a failure from + * zephyr to load the subscriptions. + */ +int owl_zephyr_loadsubs(char *filename, int error_on_nofile) +{ +#ifdef HAVE_LIBZEPHYR + FILE *file; + char *tmp, *start; + char buffer[1024], subsfile[MAXPATHLEN]; + ZSubscription_t subs[3001]; + int count, ret; + struct stat statbuff; + + if (filename==NULL) { + sprintf(subsfile, "%s/%s", owl_global_get_homedir(&g), ".zephyr.subs"); + } else { + strcpy(subsfile, filename); + } + + ret=stat(subsfile, &statbuff); + if (ret) { + owl_function_debugmsg("loadsubs: could not stat file %s", subsfile); + if (error_on_nofile==1) return(-1); + return(0); + } + + ZResetAuthentication(); + count=0; + file=fopen(subsfile, "r"); + if (!file) { + owl_function_debugmsg("loadsubs: could not open file %s", subsfile); + return(-1); + } + while ( fgets(buffer, 1024, file)!=NULL ) { + if (buffer[0]=='#' || buffer[0]=='\n' || buffer[0]=='\n') continue; + + if (buffer[0]=='-') { + start=buffer+1; + } else { + start=buffer; + } + + if (count >= 3000) { + ret = owl_zephyr_loadsubs_helper(subs, count); + if (ret != 0) { + fclose(file); + return(ret); + } + count=0; + } + + /* add it to the list of subs */ + if ((tmp=(char *) strtok(start, ",\n\r"))==NULL) continue; + subs[count].zsub_class=owl_strdup(tmp); + if ((tmp=(char *) strtok(NULL, ",\n\r"))==NULL) continue; + subs[count].zsub_classinst=owl_strdup(tmp); + if ((tmp=(char *) strtok(NULL, " \t\n\r"))==NULL) continue; + subs[count].zsub_recipient=owl_strdup(tmp); + + /* if it started with '-' then add it to the global punt list */ + if (buffer[0]=='-') { + owl_function_zpunt(subs[count].zsub_class, subs[count].zsub_classinst, subs[count].zsub_recipient, 0); + } + + count++; + } + fclose(file); + + ret=owl_zephyr_loadsubs_helper(subs, count); + + return(ret); +#else + return(0); +#endif +} + +int owl_zephyr_loaddefaultsubs() +{ +#ifdef HAVE_LIBZEPHYR + ZSubscription_t subs[10]; + errcode_t code; + + code = ZSubscribeTo(subs,0,0); + if (code != ZERR_NONE) { + owl_function_error("Error %s subscribing to default zephyr notifications.", error_message(code)); + return(-1); + } + return(0); +#else + return(0); +#endif +} + +int owl_zephyr_loadloginsubs(char *filename) +{ +#ifdef HAVE_LIBZEPHYR + FILE *file; + ZSubscription_t subs[3001]; + char subsfile[MAXPATHLEN], buffer[1024]; + int count, ret, i; + struct stat statbuff; + errcode_t code; + + if (filename==NULL) { + sprintf(subsfile, "%s/%s", owl_global_get_homedir(&g), ".anyone"); + } else { + strcpy(subsfile, filename); + } + + ret=stat(subsfile, &statbuff); + if (ret) return(0); + + ret=0; + + ZResetAuthentication(); + /* need to redo this to do chunks, not just bag out after 3000 */ + count=0; + file=fopen(subsfile, "r"); + if (file) { + while ( fgets(buffer, 1024, file)!=NULL ) { + if (buffer[0]=='#' || buffer[0]=='\n' || buffer[0]=='\n') continue; + + if (count >= 3000) break; /* also tell the user */ + + buffer[strlen(buffer)-1]='\0'; + subs[count].zsub_class="login"; + subs[count].zsub_recipient="*"; + if (strchr(buffer, '@')) { + subs[count].zsub_classinst=owl_strdup(buffer); + } else { + subs[count].zsub_classinst=owl_sprintf("%s@%s", buffer, ZGetRealm()); + } + + count++; + } + fclose(file); + } else { + count=0; + ret=-1; + } + + /* sub with defaults */ + code = ZSubscribeToSansDefaults(subs,count,0); + if (code != ZERR_NONE) { + owl_function_error("Error %s subscribing to zephyr notifications.", error_message(code)); + ret=-2; + } + + /* free stuff */ + for (i=0; i<count; i++) { + owl_free(subs[i].zsub_classinst); + } + + return(ret); +#else + return(0); +#endif +} + +void unsuball() +{ +#if HAVE_LIBZEPHYR + int ret; + + ZResetAuthentication(); + ret=ZCancelSubscriptions(0); + if (ret != ZERR_NONE) { + com_err("owl",ret,"while unsubscribing"); + } +#endif +} + +int owl_zephyr_sub(char *class, char *inst, char *recip) +{ +#ifdef HAVE_LIBZEPHYR + ZSubscription_t subs[5]; + + subs[0].zsub_class=class; + subs[0].zsub_classinst=inst; + subs[0].zsub_recipient=recip; + + ZResetAuthentication(); + if (ZSubscribeTo(subs,1,0) != ZERR_NONE) { + owl_function_error("Error subbing to <%s,%s,%s>", class, inst, recip); + return(-2); + } + return(0); +#else + return(0); +#endif +} + + +int owl_zephyr_unsub(char *class, char *inst, char *recip) +{ +#ifdef HAVE_LIBZEPHYR + ZSubscription_t subs[5]; + errcode_t code; + + subs[0].zsub_class=class; + subs[0].zsub_classinst=inst; + subs[0].zsub_recipient=recip; + + ZResetAuthentication(); + code = ZUnsubscribeTo(subs,1,0); + if (code != ZERR_NONE) { + owl_function_error("Error %s unsubbing from <%s,%s,%s>", error_message(code), class, inst, recip); + return(-2); + } + return(0); +#else + return(0); +#endif +} + +/* return a pointer to the data in the Jth field, (NULL terminated by + * definition). Caller must free the return. + */ +#ifdef HAVE_LIBZEPHYR +char *owl_zephyr_get_field(ZNotice_t *n, int j) +{ + int i, count, save; + char *out; + + /* If there's no message here, just run along now */ + if (n->z_message_len == 0) + return(owl_strdup("")); + + count=save=0; + for (i=0; i<n->z_message_len; i++) { + if (n->z_message[i]=='\0') { + count++; + if (count==j) { + /* just found the end of the field we're looking for */ + return(owl_strdup(n->z_message+save)); + } else { + save=i+1; + } + } + } + /* catch the last field, which might not be null terminated */ + if (count==j-1) { + out=owl_malloc(n->z_message_len-save+5); + memcpy(out, n->z_message+save, n->z_message_len-save); + out[n->z_message_len-save]='\0'; + return(out); + } + + return(owl_strdup("")); +} +#else +char *owl_zephyr_get_field(void *n, int j) +{ + return(owl_strdup("")); +} +#endif + + +#ifdef HAVE_LIBZEPHYR +int owl_zephyr_get_num_fields(ZNotice_t *n) +{ + int i, fields; + + if(n->z_message_len == 0) + return 0; + + fields=1; + for (i=0; i<n->z_message_len; i++) { + if (n->z_message[i]=='\0') fields++; + } + + return(fields); +} +#else +int owl_zephyr_get_num_fields(void *n) +{ + return(0); +} +#endif + +#ifdef HAVE_LIBZEPHYR +/* return a pointer to the message, place the message length in k + * caller must free the return + */ +char *owl_zephyr_get_message(ZNotice_t *n) +{ + /* don't let ping messages have a body */ + if (!strcasecmp(n->z_opcode, "ping")) { + return(owl_strdup("")); + } + + /* deal with MIT Athena OLC messages */ + if (!strcasecmp(n->z_sender, "olc.matisse@ATHENA.MIT.EDU")) { + return(owl_zephyr_get_field(n, 1)); + } + /* deal with MIT NOC messages */ + else if (!strcasecmp(n->z_sender, "rcmd.achilles@ATHENA.MIT.EDU")) { + /* $opcode service on $instance $3.\n$4 */ + char *msg, *opcode, *instance, *field3, *field4; + + opcode = n->z_opcode; + instance = n->z_class_inst; + field3 = owl_zephyr_get_field(n, 3); + field4 = owl_zephyr_get_field(n, 4); + + msg = owl_sprintf("%s service on %s %s\n%s", opcode, instance, field3, field4); + if (msg) { + return msg; + } + } + + return(owl_zephyr_get_field(n, 2)); +} +#endif + +#ifdef HAVE_LIBZEPHYR +char *owl_zephyr_get_zsig(ZNotice_t *n, int *k) +{ + /* return a pointer to the zsig if there is one */ + + /* message length 0? No zsig */ + if (n->z_message_len==0) { + *k=0; + return(""); + } + + /* No zsig for OLC messages */ + if (!strcasecmp(n->z_sender, "olc.matisse@ATHENA.MIT.EDU")) { + return(""); + } + + /* Everything else is field 1 */ + *k=strlen(n->z_message); + return(n->z_message); +} +#else +char *owl_zephyr_get_zsig(void *n, int *k) +{ + return(""); +} +#endif + +int send_zephyr(char *opcode, char *zsig, char *class, char *instance, char *recipient, char *message) +{ +#ifdef HAVE_LIBZEPHYR + int ret; + ZNotice_t notice; + + memset(¬ice, 0, sizeof(notice)); + + ZResetAuthentication(); + + if (!zsig) zsig=""; + + notice.z_kind=ACKED; + notice.z_port=0; + notice.z_class=class; + notice.z_class_inst=instance; + if (!strcmp(recipient, "*") || !strcmp(recipient, "@")) { + notice.z_recipient=""; + } else { + notice.z_recipient=recipient; + } + notice.z_default_format="Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\nFrom: @bold{$1 <$sender>}\n\n$2"; + notice.z_sender=NULL; + if (opcode) notice.z_opcode=opcode; + + notice.z_message_len=strlen(zsig)+1+strlen(message); + notice.z_message=owl_malloc(notice.z_message_len+10); + strcpy(notice.z_message, zsig); + memcpy(notice.z_message+strlen(zsig)+1, message, strlen(message)); + + /* ret=ZSendNotice(¬ice, ZAUTH); */ + ret=ZSrvSendNotice(¬ice, ZAUTH, send_zephyr_helper); + + /* free then check the return */ + owl_free(notice.z_message); + ZFreeNotice(¬ice); + if (ret!=ZERR_NONE) { + owl_function_error("Error %s sending zephyr", error_message(ret)); + return(ret); + } + return(0); +#else + return(0); +#endif +} + +#ifdef HAVE_LIBZEPHYR +Code_t send_zephyr_helper(ZNotice_t *notice, char *buf, int len, int wait) +{ + int ret; + ret=ZSendPacket(buf, len, 0); + return(ret); +} +#endif + +void send_ping(char *to) +{ +#ifdef HAVE_LIBZEPHYR + send_zephyr("PING", "", "MESSAGE", "PERSONAL", to, ""); +#endif +} + +#ifdef HAVE_LIBZEPHYR +void owl_zephyr_handle_ack(ZNotice_t *retnotice) +{ + char *tmp; + + /* if it's an HMACK ignore it */ + if (retnotice->z_kind == HMACK) return; + + if (retnotice->z_kind == SERVNAK) { + owl_function_error("Authorization failure sending zephyr"); + } else if ((retnotice->z_kind != SERVACK) || !retnotice->z_message_len) { + owl_function_error("Detected server failure while receiving acknowledgement"); + } else if (!strcmp(retnotice->z_message, ZSRVACK_SENT)) { + if (!strcasecmp(retnotice->z_opcode, "ping")) { + return; + } else if (!strcasecmp(retnotice->z_class, "message") && + !strcasecmp(retnotice->z_class_inst, "personal")) { + tmp=short_zuser(retnotice->z_recipient); + owl_function_makemsg("Message sent to %s.", tmp); + free(tmp); + } else { + owl_function_makemsg("Message sent to -c %s -i %s\n", retnotice->z_class, retnotice->z_class_inst); + } + } else if (!strcmp(retnotice->z_message, ZSRVACK_NOTSENT)) { + #define BUFFLEN 1024 + if (retnotice->z_recipient == NULL + || *retnotice->z_recipient == 0 + || *retnotice->z_recipient == '@') { + char buff[BUFFLEN]; + owl_function_error("No one subscribed to class %s", retnotice->z_class); + snprintf(buff, BUFFLEN, "Could not send message to class %s: no one subscribed.\n", retnotice->z_class); + owl_function_adminmsg("", buff); + } else { + char buff[BUFFLEN]; + tmp = short_zuser(retnotice->z_recipient); + owl_function_error("%s: Not logged in or subscribing.", tmp); + if(strcmp(retnotice->z_class, "message")) { + snprintf(buff, BUFFLEN, + "Could not send message to %s: " + "not logged in or subscribing to class %s, instance %s.\n", + tmp, + retnotice->z_class, + retnotice->z_class_inst); + } else { + snprintf(buff, BUFFLEN, + "Could not send message to %s: " + "not logged in or subscribing to messages.\n", + tmp); + } + owl_function_adminmsg("", buff); + owl_log_outgoing_zephyr_error(tmp, buff); + owl_free(tmp); + } + } else { + owl_function_error("Internal error on ack (%s)", retnotice->z_message); + } +} +#else +void owl_zephyr_handle_ack(void *retnotice) +{ +} +#endif + +#ifdef HAVE_LIBZEPHYR +int owl_zephyr_notice_is_ack(ZNotice_t *n) +{ + if (n->z_kind == SERVNAK || n->z_kind == SERVACK || n->z_kind == HMACK) { + if (!strcasecmp(n->z_class, LOGIN_CLASS)) return(0); + return(1); + } + return(0); +} +#else +int owl_zephyr_notice_is_ack(void *n) +{ + return(0); +} +#endif + +void owl_zephyr_zaway(owl_message *m) +{ +#ifdef HAVE_LIBZEPHYR + char *tmpbuff, *myuser, *to; + owl_message *mout; + + /* bail if it doesn't look like a message we should reply to. Some + * of this defined by the way zaway(1) works + */ + if (strcasecmp(owl_message_get_class(m), "message")) return; + if (strcasecmp(owl_message_get_recipient(m), ZGetSender())) return; + if (!strcasecmp(owl_message_get_sender(m), "")) return; + if (!strcasecmp(owl_message_get_opcode(m), "ping")) return; + if (!strcasecmp(owl_message_get_opcode(m), "auto")) return; + if (!strcasecmp(owl_message_get_zsig(m), "Automated reply:")) return; + if (!strcasecmp(owl_message_get_sender(m), ZGetSender())) return; + if (owl_message_get_attribute_value(m, "isauto")) return; + + if (owl_global_is_smartstrip(&g)) { + to=owl_zephyr_smartstripped_user(owl_message_get_sender(m)); + } else { + to=owl_strdup(owl_message_get_sender(m)); + } + + send_zephyr("", + "Automated reply:", + owl_message_get_class(m), + owl_message_get_instance(m), + to, + owl_global_get_zaway_msg(&g)); + + myuser=short_zuser(to); + if (!strcasecmp(owl_message_get_instance(m), "personal")) { + tmpbuff = owl_sprintf("zwrite %s", myuser); + } else { + tmpbuff = owl_sprintf("zwrite -i %s %s", owl_message_get_instance(m), myuser); + } + owl_free(myuser); + owl_free(to); + + /* display the message as an admin message in the receive window */ + mout=owl_function_make_outgoing_zephyr(owl_global_get_zaway_msg(&g), tmpbuff, "Automated reply:"); + owl_function_add_message(mout); + owl_free(tmpbuff); +#endif +} + +#ifdef HAVE_LIBZEPHYR +void owl_zephyr_hackaway_cr(ZNotice_t *n) +{ + /* replace \r's with ' '. Gross-ish */ + int i; + + for (i=0; i<n->z_message_len; i++) { + if (n->z_message[i]=='\r') { + n->z_message[i]=' '; + } + } +} +#endif + +char *owl_zephyr_zlocate(char *user, int auth) +{ +#ifdef HAVE_LIBZEPHYR + int ret, numlocs; + int one = 1; + ZLocations_t locations; + char *myuser, *out, *tmp; + + ZResetAuthentication(); + ret=ZLocateUser(user,&numlocs,auth?ZAUTH:ZNOAUTH); + if (ret != ZERR_NONE) { + return(owl_sprintf("Error %s locating user %s\n", error_message(ret), user)); + } + + if (numlocs==0) { + myuser=short_zuser(user); + out=owl_sprintf("%s: Hidden or not logged in\n", myuser); + owl_free(myuser); + return(out); + } + + out=strdup(""); + for (;numlocs;numlocs--) { + /* should check return here, and fix loop */ + ZGetLocations(&locations,&one); + myuser=short_zuser(user); + tmp=owl_sprintf("%s%s: %s\t%s\t%s\n", + out, + myuser, + locations.host ? locations.host : "?", + locations.tty ? locations.tty : "?", + locations.time ? locations.time : "?"); + owl_free(out); + out=tmp; + owl_free(myuser); + } + return(out); +#else + return(owl_strdup("Zephyr not available")); +#endif +} + +void owl_zephyr_addsub(char *filename, char *class, char *inst, char *recip) +{ +#ifdef HAVE_LIBZEPHYR + char *line, subsfile[MAXPATHLEN], buff[LINE]; + FILE *file; + + line=owl_zephyr_makesubline(class, inst, recip); + + if (filename==NULL) { + sprintf(subsfile, "%s/%s", owl_global_get_homedir(&g), ".zephyr.subs"); + } else { + strcpy(subsfile, filename); + } + + /* if the file already exists, check to see if the sub is already there */ + file=fopen(subsfile, "r"); + if (file) { + while (fgets(buff, LINE, file)!=NULL) { + if (!strcasecmp(buff, line)) { + owl_function_error("Subscription already present in %s", subsfile); + owl_free(line); + fclose(file); + return; + } + } + fclose(file); + } + + /* if we get here then we didn't find it */ + file=fopen(subsfile, "a"); + if (!file) { + owl_function_error("Error opening file %s for writing", subsfile); + owl_free(line); + return; + } + fputs(line, file); + fclose(file); + owl_function_makemsg("Subscription added"); + + owl_free(line); +#endif +} + +void owl_zephyr_delsub(char *filename, char *class, char *inst, char *recip) +{ +#ifdef HAVE_LIBZEPHYR + char *line, *subsfile; + + line=owl_zephyr_makesubline(class, inst, recip); + line[strlen(line)-1]='\0'; + + if (!filename) { + subsfile=owl_sprintf("%s/.zephyr.subs", owl_global_get_homedir(&g)); + } else { + subsfile=owl_strdup(filename); + } + + owl_util_file_deleteline(subsfile, line, 1); + owl_free(subsfile); + owl_free(line); + owl_function_makemsg("Subscription removed"); +#endif +} + +/* caller must free the return */ +char *owl_zephyr_makesubline(char *class, char *inst, char *recip) +{ + return(owl_sprintf("%s,%s,%s\n", class, inst, !strcmp(recip, "") ? "*" : recip)); +} + +void owl_zephyr_zlog_in(void) +{ +#ifdef HAVE_LIBZEPHYR + char *exposure, *eset; + int ret; + + ZResetAuthentication(); + + eset=EXPOSE_REALMVIS; + exposure=ZGetVariable("exposure"); + if (exposure==NULL) { + eset=EXPOSE_REALMVIS; + } else if (!strcasecmp(exposure,EXPOSE_NONE)) { + eset = EXPOSE_NONE; + } else if (!strcasecmp(exposure,EXPOSE_OPSTAFF)) { + eset = EXPOSE_OPSTAFF; + } else if (!strcasecmp(exposure,EXPOSE_REALMVIS)) { + eset = EXPOSE_REALMVIS; + } else if (!strcasecmp(exposure,EXPOSE_REALMANN)) { + eset = EXPOSE_REALMANN; + } else if (!strcasecmp(exposure,EXPOSE_NETVIS)) { + eset = EXPOSE_NETVIS; + } else if (!strcasecmp(exposure,EXPOSE_NETANN)) { + eset = EXPOSE_NETANN; + } + + ret=ZSetLocation(eset); + owl_zephyr_process_events(NULL); + if (ret != ZERR_NONE) { + /* we should check this, but ZSetLocation almost *always* gives Internal Error. Sigh. */ + /* is this the "SENT" bug? if so, it is fixed... */ + /* owl_function_makemsg("Error setting location: %s", error_message(ret)); */ + } +#endif +} + +void owl_zephyr_zlog_out(void) +{ +#ifdef HAVE_LIBZEPHYR + int ret; + + ZResetAuthentication(); + ret=ZUnsetLocation(); + owl_zephyr_process_events(NULL); + if (ret != ZERR_NONE) { + /* owl_function_makemsg("Error unsetting location: %s", error_message(ret)); */ + } +#endif +} + +void owl_zephyr_addbuddy(char *name) +{ + char *filename; + FILE *file; + + filename=owl_sprintf("%s/.anyone", owl_global_get_homedir(&g)); + file=fopen(filename, "a"); + owl_free(filename); + if (!file) { + owl_function_error("Error opening zephyr buddy file for append"); + return; + } + fprintf(file, "%s\n", name); + fclose(file); +} + +void owl_zephyr_delbuddy(char *name) +{ + char *filename; + + filename=owl_sprintf("%s/.anyone", owl_global_get_homedir(&g)); + owl_util_file_deleteline(filename, name, 0); + owl_free(filename); +} + +/* return auth string */ +#ifdef HAVE_LIBZEPHYR +char *owl_zephyr_get_authstr(ZNotice_t *n) +{ + + if (!n) return("UNKNOWN"); + + if (n->z_auth == ZAUTH_FAILED) { + return ("FAILED"); + } else if (n->z_auth == ZAUTH_NO) { + return ("NO"); + } else if (n->z_auth == ZAUTH_YES) { + return ("YES"); + } else { + return ("UNKNOWN"); + } +} +#else +char *owl_zephyr_get_authstr(void *n) +{ + return(""); +} +#endif + +/* Returns a buffer of subscriptions or an error message. Caller must + * free the return. + */ +char *owl_zephyr_getsubs() +{ +#ifdef HAVE_LIBZEPHYR + int ret, num, i, one; + int buffsize; + ZSubscription_t sub; + char *out; + one=1; + + ret=ZRetrieveSubscriptions(0, &num); + if (ret==ZERR_TOOMANYSUBS) { + return(owl_strdup("Zephyr: too many subscriptions\n")); + } else if (ret || (num <= 0)) { + /* need to attach error_message(ret) here! */ + return(owl_strdup("Zephyr: error retriving subscriptions\n")); + } + + buffsize = (num + 1) * 50; + out=owl_malloc(buffsize); + strcpy(out, ""); + for (i=0; i<num; i++) { + if ((ret = ZGetSubscriptions(&sub, &one)) != ZERR_NONE) { + owl_free(out); + ZFlushSubscriptions(); + /* need to attach error_message(ret) here! */ + out=owl_strdup("Error while getting subscriptions\n"); + return(out); + } else { + int tmpbufflen; + char *tmpbuff; + tmpbuff = owl_sprintf("<%s,%s,%s>\n%s", sub.zsub_class, sub.zsub_classinst, sub.zsub_recipient, out); + tmpbufflen = strlen(tmpbuff) + 1; + if (tmpbufflen > buffsize) { + char *out2; + buffsize = tmpbufflen * 2; + out2 = owl_realloc(out, buffsize); + if (out2 == NULL) { + owl_free(out); + owl_free(tmpbuff); + ZFlushSubscriptions(); + out=owl_strdup("Realloc error while getting subscriptions\n"); + return(out); + } + out = out2; + } + strcpy(out, tmpbuff); + owl_free(tmpbuff); + } + } + + ZFlushSubscriptions(); + return(out); +#else + return(owl_strdup("Zephyr not available")); +#endif +} + +char *owl_zephyr_get_variable(char *var) +{ +#ifdef HAVE_LIBZEPHYR + return(ZGetVariable(var)); +#else + return(""); +#endif +} + +void owl_zephyr_set_locationinfo(char *host, char *val) +{ +#ifdef HAVE_LIBZEPHYR + ZInitLocationInfo(host, val); +#endif +} + +/* Strip a local realm fron the zephyr user name. + * The caller must free the return + */ +char *short_zuser(char *in) +{ + char *out, *ptr; + + out=owl_strdup(in); + ptr=strchr(out, '@'); + if (ptr) { + if (!strcasecmp(ptr+1, owl_zephyr_get_realm())) { + *ptr='\0'; + } + } + return(out); +} + +/* Append a local realm to the zephyr user name if necessary. + * The caller must free the return. + */ +char *long_zuser(char *in) +{ + if (strchr(in, '@')) { + return(owl_strdup(in)); + } + return(owl_sprintf("%s@%s", in, owl_zephyr_get_realm())); +} + +/* strip out the instance from a zsender's principal. Preserves the + * realm if present. daemon.webzephyr is a special case. The + * caller must free the return + */ +char *owl_zephyr_smartstripped_user(char *in) +{ + char *ptr, *realm, *out; + + out=owl_strdup(in); + + /* bail immeaditly if we don't have to do any work */ + ptr=strchr(in, '.'); + if (!strchr(in, '/') && !ptr) { + /* no '/' and no '.' */ + return(out); + } + if (ptr && strchr(in, '@') && (ptr > strchr(in, '@'))) { + /* There's a '.' but it's in the realm */ + return(out); + } + if (!strncasecmp(in, OWL_WEBZEPHYR_PRINCIPAL, strlen(OWL_WEBZEPHYR_PRINCIPAL))) { + return(out); + } + + /* remove the realm from ptr, but hold on to it */ + realm=strchr(out, '@'); + if (realm) realm[0]='\0'; + + /* strip */ + ptr=strchr(out, '.'); + if (!ptr) ptr=strchr(out, '/'); + ptr[0]='\0'; + + /* reattach the realm if we had one */ + if (realm) { + strcat(out, "@"); + strcat(out, realm+1); + } + + return(out); +} + +/* read the list of users in 'filename' as a .anyone file, and put the + * names of the zephyr users in the list 'in'. If 'filename' is NULL, + * use the default .anyone file in the users home directory. Returns + * -1 on failure, 0 on success. + */ +int owl_zephyr_get_anyone_list(owl_list *in, char *filename) +{ +#ifdef HAVE_LIBZEPHYR + char *ourfile, *tmp, buff[LINE]; + FILE *f; + + if (filename==NULL) { + tmp=owl_global_get_homedir(&g); + ourfile=owl_sprintf("%s/.anyone", owl_global_get_homedir(&g)); + } else { + ourfile=owl_strdup(filename); + } + + f=fopen(ourfile, "r"); + if (!f) { + owl_function_error("Error opening file %s: %s", ourfile, strerror(errno) ? strerror(errno) : ""); + owl_free(ourfile); + return(-1); + } + + while (fgets(buff, LINE, f)!=NULL) { + /* ignore comments, blank lines etc. */ + if (buff[0]=='#') continue; + if (buff[0]=='\n') continue; + if (buff[0]=='\0') continue; + + /* strip the \n */ + buff[strlen(buff)-1]='\0'; + + /* ingore from # on */ + tmp=strchr(buff, '#'); + if (tmp) tmp[0]='\0'; + + /* ingore from SPC */ + tmp=strchr(buff, ' '); + if (tmp) tmp[0]='\0'; + + /* stick on the local realm. */ + if (!strchr(buff, '@')) { + strcat(buff, "@"); + strcat(buff, ZGetRealm()); + } + owl_list_append_element(in, owl_strdup(buff)); + } + fclose(f); + owl_free(ourfile); + return(0); +#else + return(-1); +#endif +} + + +#ifdef HAVE_LIBZEPHYR +void owl_zephyr_process_events(owl_dispatch *d) { + int zpendcount=0; + ZNotice_t notice; + struct sockaddr_in from; + owl_message *m=NULL; + + while(owl_zephyr_zpending() && zpendcount < 20) { + if (owl_zephyr_zpending()) { + ZReceiveNotice(¬ice, &from); + zpendcount++; + + /* is this an ack from a zephyr we sent? */ + if (owl_zephyr_notice_is_ack(¬ice)) { + owl_zephyr_handle_ack(¬ice); + continue; + } + + /* if it's a ping and we're not viewing pings then skip it */ + if (!owl_global_is_rxping(&g) && !strcasecmp(notice.z_opcode, "ping")) { + continue; + } + + /* create the new message */ + m=owl_malloc(sizeof(owl_message)); + owl_message_create_from_znotice(m, ¬ice); + + owl_global_messagequeue_addmsg(&g, m); + } + } +} + +#else +void owl_zephyr_process_events(owl_dispatch *d) { + +} +#endif diff --git a/zwrite.c b/zwrite.c new file mode 100644 index 0000000..65cd75a --- /dev/null +++ b/zwrite.c @@ -0,0 +1,387 @@ +/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar + * + * This file is part of Owl. + * + * Owl 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 3 of the License, or + * (at your option) any later version. + * + * Owl is distributed in the hope that 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 Owl. If not, see <http://www.gnu.org/licenses/>. + * + * --------------------------------------------------------------- + * + * As of Owl version 2.1.12 there are patches contributed by + * developers of the branched BarnOwl project, Copyright (c) + * 2006-2009 The BarnOwl Developers. All rights reserved. + */ + +#include <string.h> +#include <pwd.h> +#include <sys/types.h> +#include <unistd.h> +#include "owl.h" + +static const char fileIdent[] = "$Id: zwrite.c,v 1.22 2009/04/05 23:36:54 kretch Exp $"; + +int owl_zwrite_create_from_line(owl_zwrite *z, char *line) +{ + int argc, badargs, myargc; + char **argv, **myargv; + char *zsigproc, *zsigowlvar, *zsigzvar, *ptr; + struct passwd *pw; + + badargs=0; + + /* start with null entires */ + z->realm=NULL; + z->class=NULL; + z->inst=NULL; + z->opcode=NULL; + z->zsig=NULL; + z->message=NULL; + z->cc=0; + z->noping=0; + owl_list_create(&(z->recips)); + + /* parse the command line for options */ + argv=myargv=owl_parseline(line, &argc); + if (argc<0) { + owl_function_error("Unbalanced quotes in zwrite"); + return(-1); + } + myargc=argc; + if (myargc && *(myargv[0])!='-') { + myargc--; + myargv++; + } + while (myargc) { + if (!strcmp(myargv[0], "-c")) { + if (myargc<2) { + badargs=1; + break; + } + z->class=owl_strdup(myargv[1]); + myargv+=2; + myargc-=2; + } else if (!strcmp(myargv[0], "-i")) { + if (myargc<2) { + badargs=1; + break; + } + z->inst=owl_strdup(myargv[1]); + myargv+=2; + myargc-=2; + } else if (!strcmp(myargv[0], "-r")) { + if (myargc<2) { + badargs=1; + break; + } + z->realm=owl_strdup(myargv[1]); + myargv+=2; + myargc-=2; + } else if (!strcmp(myargv[0], "-s")) { + if (myargc<2) { + badargs=1; + break; + } + z->zsig=owl_strdup(myargv[1]); + myargv+=2; + myargc-=2; + } else if (!strcmp(myargv[0], "-O")) { + if (myargc<2) { + badargs=1; + break; + } + z->opcode=owl_strdup(myargv[1]); + myargv+=2; + myargc-=2; + } else if (!strcmp(myargv[0], "-m")) { + if (myargc<2) { + badargs=1; + break; + } + /* we must already have users or a class or an instance*/ + if (owl_list_get_size(&(z->recips))<1 && (!z->class) && (!z->inst)) { + badargs=1; + break; + } + + /* Once we have -m, gobble up everything else on the line */ + myargv++; + myargc--; + z->message=owl_strdup(""); + while (myargc) { + z->message=realloc(z->message, strlen(z->message)+strlen(myargv[0])+5); + strcat(z->message, myargv[0]); + strcat(z->message, " "); + myargc--; + myargv++; + } + z->message[strlen(z->message)-1]='\0'; /* remove last space */ + break; + } else if (!strcmp(myargv[0], "-C")) { + z->cc=1; + myargv++; + myargc--; + } else if (!strcmp(myargv[0], "-n")) { + z->noping=1; + myargv++; + myargc--; + } else { + /* anything unattached is a recipient */ + owl_list_append_element(&(z->recips), strdup(myargv[0])); + myargv++; + myargc--; + } + } + + owl_parsefree(argv, argc); + + if (badargs) { + return(-1); + } + + /* now deal with defaults */ + if (z->class==NULL) z->class=owl_strdup("message"); + if (z->inst==NULL) z->inst=owl_strdup("personal"); + if (z->realm==NULL) z->realm=owl_strdup(""); + if (z->opcode==NULL) z->opcode=owl_strdup(""); + /* z->message is allowed to stay NULL */ + + if (!strcasecmp(z->class, "message") && + !strcasecmp(z->inst, "personal") && + owl_list_get_size(&(z->recips))==0) { + owl_function_error("You must specify a recipient for zwrite"); + return(-1); + } + + /* get a zsig, if not given */ + if (z->zsig==NULL) { + zsigproc = owl_global_get_zsigproc(&g); + zsigowlvar = owl_global_get_zsig(&g); + zsigzvar = owl_zephyr_get_variable("zwrite-signature"); + + if (zsigowlvar && *zsigowlvar) { + z->zsig=owl_strdup(zsigowlvar); + } else if (zsigproc && *zsigproc) { + FILE *file; + char buff[LINE], *openline; + + /* simple hack for now to nuke stderr */ + openline=owl_malloc(strlen(zsigproc)+40); + strcpy(openline, zsigproc); +#if !(OWL_STDERR_REDIR) + strcat(openline, " 2> /dev/null"); +#endif + file=popen(openline, "r"); + owl_free(openline); + if (!file) { + if (zsigzvar && *zsigzvar) { + z->zsig=owl_strdup(zsigzvar); + } + } else { + z->zsig=owl_malloc(LINE*5); + strcpy(z->zsig, ""); + while (fgets(buff, LINE, file)) { /* wrong sizing */ + strcat(z->zsig, buff); + } + pclose(file); + if (z->zsig[strlen(z->zsig)-1]=='\n') { + z->zsig[strlen(z->zsig)-1]='\0'; + } + } + } else if (zsigzvar) { + z->zsig=owl_strdup(zsigzvar); + } else if (((pw=getpwuid(getuid()))!=NULL) && (pw->pw_gecos)) { + z->zsig=strdup(pw->pw_gecos); + ptr=strchr(z->zsig, ','); + if (ptr) { + ptr[0]='\0'; + } + } + } + + return(0); +} + +void owl_zwrite_send_ping(owl_zwrite *z) +{ + int i, j; + char *to; + + if (z->noping) return; + + if (strcasecmp(z->class, "message") || + strcasecmp(z->inst, "personal")) { + return; + } + + /* if there are no recipients we won't send a ping, which + is what we want */ + j=owl_list_get_size(&(z->recips)); + for (i=0; i<j; i++) { + if (strcmp(z->realm, "")) { + to = owl_sprintf("%s@%s", (char *) owl_list_get_element(&(z->recips), i), z->realm); + } else { + to = owl_strdup(owl_list_get_element(&(z->recips), i)); + } + send_ping(to); + owl_free(to); + } + +} + +void owl_zwrite_set_message(owl_zwrite *z, char *msg) +{ + int i, j; + char *toline = NULL; + char *tmp = NULL; + + if (z->message) owl_free(z->message); + + j=owl_list_get_size(&(z->recips)); + if (j>0 && z->cc) { + toline = owl_strdup( "CC: "); + for (i=0; i<j; i++) { + tmp = toline; + if (strcmp(z->realm, "")) { + toline = owl_sprintf( "%s%s@%s ", toline, (char *) owl_list_get_element(&(z->recips), i), z->realm); + } else { + toline = owl_sprintf( "%s%s ", toline, (char *) owl_list_get_element(&(z->recips), i)); + } + owl_free(tmp); + tmp=NULL; + } + z->message=owl_sprintf("%s\n%s", toline, msg); + owl_free(toline); + } else { + z->message=owl_strdup(msg); + } +} + +char *owl_zwrite_get_message(owl_zwrite *z) +{ + if (z->message) return(z->message); + return(""); +} + +int owl_zwrite_is_message_set(owl_zwrite *z) +{ + if (z->message) return(1); + return(0); +} + +int owl_zwrite_send_message(owl_zwrite *z) +{ + int i, j; + char *to = NULL; + + if (z->message==NULL) return(-1); + + j=owl_list_get_size(&(z->recips)); + if (j>0) { + for (i=0; i<j; i++) { + if (strcmp(z->realm, "")) { + to = owl_sprintf("%s@%s", (char *) owl_list_get_element(&(z->recips), i), z->realm); + } else { +to = owl_strdup( owl_list_get_element(&(z->recips), i)); + } + send_zephyr(z->opcode, z->zsig, z->class, z->inst, to, z->message); + owl_free(to); + to = NULL; + } + } else { + to = owl_sprintf( "@%s", z->realm); + send_zephyr(z->opcode, z->zsig, z->class, z->inst, to, z->message); + } + owl_free(to); + return(0); +} + +int owl_zwrite_create_and_send_from_line(char *cmd, char *msg) +{ + owl_zwrite z; + int rv; + rv=owl_zwrite_create_from_line(&z, cmd); + if (rv) return(rv); + if (!owl_zwrite_is_message_set(&z)) { + owl_zwrite_set_message(&z, msg); + } + owl_zwrite_send_message(&z); + owl_zwrite_free(&z); + return(0); +} + +char *owl_zwrite_get_class(owl_zwrite *z) +{ + return(z->class); +} + +char *owl_zwrite_get_instance(owl_zwrite *z) +{ + return(z->inst); +} + +char *owl_zwrite_get_opcode(owl_zwrite *z) +{ + return(z->opcode); +} + +void owl_zwrite_set_opcode(owl_zwrite *z, char *opcode) +{ + if (z->opcode) owl_free(z->opcode); + z->opcode=owl_strdup(opcode); +} + +char *owl_zwrite_get_realm(owl_zwrite *z) +{ + return(z->realm); +} + +char *owl_zwrite_get_zsig(owl_zwrite *z) +{ + if (z->zsig) return(z->zsig); + return(""); +} + +int owl_zwrite_get_numrecips(owl_zwrite *z) +{ + return(owl_list_get_size(&(z->recips))); +} + +char *owl_zwrite_get_recip_n(owl_zwrite *z, int n) +{ + return(owl_list_get_element(&(z->recips), n)); +} + +int owl_zwrite_is_personal(owl_zwrite *z) +{ + /* return true if at least one of the recipients is personal */ + int i, j; + char *foo; + + j=owl_list_get_size(&(z->recips)); + for (i=0; i<j; i++) { + foo=owl_list_get_element(&(z->recips), i); + if (foo[0]!='@') return(1); + } + return(0); +} + +void owl_zwrite_free(owl_zwrite *z) +{ + owl_list_free_all(&(z->recips), &owl_free); + if (z->class) owl_free(z->class); + if (z->inst) owl_free(z->inst); + if (z->opcode) owl_free(z->opcode); + if (z->realm) owl_free(z->realm); + if (z->message) owl_free(z->message); + if (z->zsig) owl_free(z->zsig); +} |