summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgregor herrmann <gregoa@debian.org>2013-05-25 15:29:32 -0400
committergregor herrmann <gregoa@debian.org>2013-05-25 15:29:32 -0400
commit5e10947726f3b3227a753406d89cfd0f02808cf9 (patch)
tree40534b2e84a7f97c27af2c3ddc769de921dac553
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--COPYING674
-rw-r--r--ChangeLog1094
-rw-r--r--Makefile.in105
-rw-r--r--aclocal.m4171
-rw-r--r--aim.c2796
-rwxr-xr-xathstatic32
-rw-r--r--buddy.c95
-rw-r--r--buddylist.c166
-rw-r--r--cmd.c283
-rwxr-xr-xcodelist.pl40
-rw-r--r--commands.c2623
-rw-r--r--config.h122
-rw-r--r--config.h.in121
-rwxr-xr-xconfigure6215
-rw-r--r--configure.in139
-rw-r--r--context.c124
-rw-r--r--debian/README.Debian7
-rw-r--r--debian/changelog208
-rw-r--r--debian/compat1
-rw-r--r--debian/control16
-rw-r--r--debian/copyright55
-rw-r--r--debian/dirs1
-rwxr-xr-xdebian/rules93
-rw-r--r--debian/shlibs.local1
-rw-r--r--debian/watch2
-rw-r--r--dict.c210
-rw-r--r--doc/code.txt199
-rw-r--r--doc/contributors31
-rw-r--r--doc/intro.txt462
-rw-r--r--doc/owl.183
-rw-r--r--editwin.c968
-rw-r--r--encapsulate.pl20
-rw-r--r--errqueue.c47
-rw-r--r--examples/owlconf.erik476
-rw-r--r--examples/owlconf.simple270
-rw-r--r--examples/owlconf.vtformat141
-rw-r--r--filter.c626
-rw-r--r--filterelement.c239
-rw-r--r--fmtext.c649
-rw-r--r--functions.c3590
-rw-r--r--global.c914
-rw-r--r--help.c167
-rw-r--r--history.c118
-rw-r--r--install.sh0
-rw-r--r--keybinding.c163
-rw-r--r--keymap.c319
-rw-r--r--keypress.c235
-rw-r--r--keys.c351
-rw-r--r--libfaim/Makefile.in21
-rw-r--r--libfaim/admin.c238
-rw-r--r--libfaim/adverts.c31
-rw-r--r--libfaim/aim.h1484
-rw-r--r--libfaim/aim_cbtypes.h310
-rw-r--r--libfaim/aim_internal.h226
-rw-r--r--libfaim/auth.c558
-rw-r--r--libfaim/bart.c165
-rw-r--r--libfaim/bos.c167
-rw-r--r--libfaim/bstream.c265
-rw-r--r--libfaim/buddylist.c285
-rw-r--r--libfaim/chat.c702
-rw-r--r--libfaim/chatnav.c434
-rw-r--r--libfaim/config.h.in83
-rwxr-xr-xlibfaim/configure3659
-rw-r--r--libfaim/configure.in13
-rw-r--r--libfaim/conn.c1053
-rw-r--r--libfaim/email.c216
-rw-r--r--libfaim/faimconfig.h59
-rw-r--r--libfaim/ft.c993
-rw-r--r--libfaim/icq.c684
-rw-r--r--libfaim/im.c2298
-rw-r--r--libfaim/invite.c34
-rw-r--r--libfaim/locate.c1319
-rw-r--r--libfaim/md5.c392
-rw-r--r--libfaim/md5.h93
-rw-r--r--libfaim/misc.c147
-rw-r--r--libfaim/msgcookie.c193
-rw-r--r--libfaim/odir.c245
-rw-r--r--libfaim/oscar.c6711
-rw-r--r--libfaim/popups.c64
-rw-r--r--libfaim/rxhandlers.c585
-rw-r--r--libfaim/rxqueue.c290
-rw-r--r--libfaim/search.c134
-rw-r--r--libfaim/service.c1089
-rw-r--r--libfaim/snac.c148
-rw-r--r--libfaim/ssi.c1963
-rw-r--r--libfaim/stats.c44
-rw-r--r--libfaim/tlv.c823
-rw-r--r--libfaim/translate.c27
-rw-r--r--libfaim/txqueue.c439
-rw-r--r--libfaim/util.c210
-rw-r--r--list.c124
-rw-r--r--logging.c450
-rw-r--r--mainwin.c180
-rw-r--r--message.c987
-rw-r--r--messagelist.c128
-rwxr-xr-xmkinstalldirs40
-rw-r--r--owl.c760
-rw-r--r--owl.h562
-rw-r--r--owl_prototypes.h1409
-rw-r--r--pair.c46
-rw-r--r--perlconfig.c362
-rw-r--r--perlglue.c186
-rw-r--r--perlglue.xs83
-rw-r--r--perlwrap.c235
-rw-r--r--perlwrap.pm231
-rw-r--r--popexec.c215
-rw-r--r--popwin.c156
-rw-r--r--regex.c114
-rw-r--r--select.c269
-rwxr-xr-xstubgen.pl44
-rw-r--r--style.c128
-rw-r--r--stylefunc.c794
-rw-r--r--tester.c192
-rw-r--r--text.c423
-rw-r--r--timer.c98
-rw-r--r--util.c830
-rw-r--r--variable.c983
-rw-r--r--varstubs.c460
-rw-r--r--view.c191
-rw-r--r--viewwin.c178
-rw-r--r--zbuddylist.c89
-rw-r--r--zcrypt.c795
-rw-r--r--zephyr.c1091
-rw-r--r--zwrite.c387
124 files changed, 67576 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -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 &lt; &gt; &amp; &quot; &nbsp; &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
+
diff --git a/aim.c b/aim.c
new file mode 100644
index 0000000..28ebce5
--- /dev/null
+++ b/aim.c
@@ -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
diff --git a/buddy.c b/buddy.c
new file mode 100644
index 0000000..c4980a9
--- /dev/null
+++ b/buddy.c
@@ -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);
+}
diff --git a/cmd.c b/cmd.c
new file mode 100644
index 0000000..4cfec74
--- /dev/null
+++ b/cmd.c
@@ -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
diff --git a/dict.c b/dict.c
new file mode 100644
index 0000000..3f798bb
--- /dev/null
+++ b/dict.c
@@ -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);
+}
diff --git a/help.c b/help.c
new file mode 100644
index 0000000..2f78660
--- /dev/null
+++ b/help.c
@@ -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);
+}
+
diff --git a/keys.c b/keys.c
new file mode 100644
index 0000000..c21754a
--- /dev/null
+++ b/keys.c
@@ -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, &params);
+
+ 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 &#2026; 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;
+}
diff --git a/list.c b/list.c
new file mode 100644
index 0000000..c845b93
--- /dev/null
+++ b/list.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 "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
diff --git a/owl.c b/owl.c
new file mode 100644
index 0000000..8a4e632
--- /dev/null
+++ b/owl.c
@@ -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 */
diff --git a/owl.h b/owl.h
new file mode 100644
index 0000000..a306373
--- /dev/null
+++ b/owl.h
@@ -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);
+
diff --git a/pair.c b/pair.c
new file mode 100644
index 0000000..ea03d9b
--- /dev/null
+++ b/pair.c
@@ -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;
+}
diff --git a/regex.c b/regex.c
new file mode 100644
index 0000000..4023a9f
--- /dev/null
+++ b/regex.c
@@ -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";
+}
diff --git a/style.c b/style.c
new file mode 100644
index 0000000..d254bb4
--- /dev/null
+++ b/style.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"
+
+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);
+}
diff --git a/text.c b/text.c
new file mode 100644
index 0000000..4781b34
--- /dev/null
+++ b/text.c
@@ -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, "&lt;", "<");
+ owl_free(out);
+ out=owl_text_substitute(out2, "&gt;", ">");
+ owl_free(out2);
+ out2=owl_text_substitute(out, "&amp;", "&");
+ owl_free(out);
+ out=owl_text_substitute(out2, "&quot;", "\"");
+ owl_free(out2);
+ out2=owl_text_substitute(out, "&nbsp;", " ");
+ owl_free(out);
+ out=owl_text_substitute(out2, "&ensp;", " ");
+ owl_free(out2);
+ out2=owl_text_substitute(out, "&emsp;", " ");
+ 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);
+}
diff --git a/timer.c b/timer.c
new file mode 100644
index 0000000..14b2fc3
--- /dev/null
+++ b/timer.c
@@ -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);
+}
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..a8f3a48
--- /dev/null
+++ b/util.c
@@ -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 -------------------------------- */
+
diff --git a/view.c b/view.c
new file mode 100644
index 0000000..1486d60
--- /dev/null
+++ b/view.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(&notice, 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(&notice, ZAUTH); */
+ ret=ZSrvSendNotice(&notice, ZAUTH, send_zephyr_helper);
+
+ /* free then check the return */
+ owl_free(notice.z_message);
+ ZFreeNotice(&notice);
+ 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(&notice, &from);
+ zpendcount++;
+
+ /* is this an ack from a zephyr we sent? */
+ if (owl_zephyr_notice_is_ack(&notice)) {
+ owl_zephyr_handle_ack(&notice);
+ 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, &notice);
+
+ 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);
+}