--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+-- Prime Mover C plugin.
+-- © 2006 David Given.
+--
+-- This code is part of Prime Mover and is licensed under the MIT public
+-- license.
+
+-- pm includefile to compile *host* C programs.
+
+-- Standard Lua boilerplate.
+
+local io_open = io.open
+local string_gsub = string.gsub
+local string_gfind = string.gfind
+local table_insert = table.insert
+local table_getn = table.getn
+local filetime = pm.filetime
+
+-- Define some variables.
+
+CC = "gcc %CBUILDFLAGS% %CDYNINCLUDES% %CINCLUDES% %CEXTRAFLAGS% -c -o %out% %in%"
+CPROGRAM = "gcc %CBUILDFLAGS% %CLINKFLAGS% %CEXTRAFLAGS% -o %out% %in% %CLIBRARIES%"
+CDEPENDS = "gcc %CBUILDFLAGS% %CDYNINCLUDES% %CINCLUDES% %CEXTRAFLAGS% -MM -MG -MF %out% %in%"
+AR = "%RM% %out% && ar cr %out% %in%"
+
+CBUILDFLAGS = "-g -Os"
+CINCLUDES = {}
+CEXTRAFLAGS = ""
+CLINKFLAGS = ""
+CDYNINCLUDES = ""
+CLIBRARIES = ""
+
+--- Manage C file dependencies ----------------------------------------------
+
+local dependency_cache = {}
+local function load_dependency_file(fn)
+ local o = dependency_cache[fn]
+ if o then
+ return o
+ end
+
+ -- Read in the dependency file.
+
+ local f = io_open(fn)
+ if not f then
+ print("failed to open "..fn)
+ return nil
+ end
+ f = f:read("*a")
+
+ -- Massage the dependency file into a string containing one unescaped
+ -- filename per line.
+
+ f = string_gsub(f, "^.*[^\\]: *", "")
+ f = string_gsub(f, "\\\r?\n", "")
+ f = string_gsub(f, "([^\\]) +", "%1\n")
+ f = string_gsub(f, "\\", "")
+
+ -- Parse the string.
+
+ o = {}
+ for l in string_gfind(f, "[^\n]+") do
+ table_insert(o, l)
+ end
+
+ dependency_cache[fn] = o
+ return o
+end
+
+-- This clause specialises 'simple' to add support for smart dependencies of C
+-- files.
+
+simple_with_clike_dependencies = simple {
+ class = "simple_with_clike_dependencies",
+ makedepends = {"%CDEPENDS%"},
+
+ __init = function(self, p)
+ simple.__init(self, p)
+
+ -- If we're a class, don't verify.
+
+ if ((type(p) == "table") and p.class) then
+ return
+ end
+
+ -- If dynamicheaders is an object, turn it into a singleton list.
+
+ if self.dynamicheaders then
+ if (type(self.dynamicheaders) ~= "table") then
+ self:__error("doesn't know what to do with dynamicheaders, which ",
+ "should be a list or an object but was a ", type(self.dynamicheaders))
+ end
+ if self.dynamicheaders.class then
+ self.dynamicheaders = {self.dynamicheaders}
+ end
+ end
+ end,
+
+ __dependencies = function(self, inputs, outputs)
+ local obj = simple {
+ CDYNINCLUDES = self.CDYNINCLUDES,
+ command = self.makedepends,
+ outputs = {"%U%-%I%.d"},
+ unpack(inputs)
+ }
+ local o = obj:__build()
+ local depends = load_dependency_file(o[1])
+ if not depends then
+ self:__error("could not determine the dependencies for ",
+ pm.rendertable(inputs))
+ end
+ return depends
+ end,
+
+ __buildadditionalchildren = function(self)
+ self.CDYNINCLUDES = ""
+ if self.dynamicheaders then
+ for _, i in ipairs(self.dynamicheaders) do
+ local o = i:__build()
+ if o[1] then
+ self.CDYNINCLUDES = self.CDYNINCLUDES..' "-I'..string_gsub(o[1], "/[^/]*$", "")..'"'
+ end
+ end
+ end
+ end
+}
+
+-- These are the publically useful clauses.
+
+cfile = simple_with_clike_dependencies {
+ class = "cfile",
+ command = {"%CC%"},
+ outputs = {"%U%-%I%.o"},
+}
+
+cprogram = simple {
+ class = "cprogram",
+ command = {"%CPROGRAM%"},
+ outputs = {"%U%-%I%"},
+}
+
+clibrary = simple {
+ class = "clibrary",
+ command = {
+ "%AR%"
+ },
+ outputs = {"%U%-%I%.a"},
+}
--- /dev/null
+/* citadel.c
+ * Gaim Citadel plugin.
+ *
+ * © 2006 David Given.
+ * This code is licensed under the GPL v2. See the file COPYING in this
+ * directory for the full license text.
+ *
+ * $Id: auth.c 4258 2006-01-29 13:34:44 +0000 (Sun, 29 Jan 2006) dothebart $
+ */
+
+#define GAIM_PLUGINS
+#include "internal.h"
+#include "accountopt.h"
+#include "blist.h"
+#include "conversation.h"
+#include "debug.h"
+#include "notify.h"
+#include "prpl.h"
+#include "plugin.h"
+#include "util.h"
+#include "version.h"
+#include "sslconn.h"
+
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+
+#include "interface.h"
+
+extern int tolua_gaim_open(lua_State* L);
+extern void tolua_gaim_close(lua_State* L);
+
+#define VERSION "0.2"
+#define CITADEL_DEFAULT_SERVER "uncensored.citadel.org"
+#define CITADEL_DEFAULT_PORT 504
+#define CITADEL_POLL_INTERVAL 10
+#define LUA_MICROCODE "/plugindata/citadel.lua"
+
+struct citadel {
+ GaimAccount* ga;
+ GaimConnection* gc;
+ GaimSslConnection* gsc;
+ int fd;
+ lua_State* L;
+};
+
+static GaimConnection* hackgc;
+
+/* ======================================================================= */
+/* LUA CALLIN */
+/* ======================================================================= */
+
+static void wrappedcall(lua_State* L, int inparams, int outparams)
+{
+ int i;
+
+ i = lua_pcall(L, inparams, outparams, 0);
+ if (i)
+ {
+ gaim_debug(GAIM_DEBUG_MISC, "citadel", "lua error: %s\n",
+ lua_tostring(L, -1));
+ gaim_connection_error(hackgc, _("Internal error in plugin"));
+ return;
+ }
+
+ lua_getglobal(L, "citadel_schedule_now");
+ i = lua_pcall(L, 0, 0, 0);
+ if (i)
+ {
+ gaim_debug(GAIM_DEBUG_MISC, "citadel", "lua error in scheduler: %s\n",
+ lua_tostring(L, -1));
+ gaim_connection_error(hackgc, _("Internal error in plugin"));
+ return;
+ }
+}
+
+/* ======================================================================= */
+/* LUA CALLOUT */
+/* ======================================================================= */
+
+/* --- Input ------------------------------------------------------------- */
+
+static void input_cb(gpointer data, gint fd, GaimInputCondition cond)
+{
+ GaimConnection* gc = data;
+ lua_State* L = gc->proto_data;
+
+ lua_getglobal(L, "citadel_input");
+ wrappedcall(L, 0, 0);
+}
+
+static void input_ssl_cb(gpointer data, GaimSslConnection* gsc, GaimInputCondition cond)
+{
+ GaimConnection* gc = data;
+ lua_State* L = gc->proto_data;
+
+ lua_getglobal(L, "citadel_input");
+ wrappedcall(L, 0, 0);
+}
+
+char* interface_readdata(int fd, GaimSslConnection* gsc)
+{
+ static char buffer[1024];
+ int len;
+
+ /* Read in some data. */
+
+ if (gsc)
+ len = gaim_ssl_read(gsc, buffer, sizeof(buffer)-1);
+ else
+ len = read(fd, buffer, sizeof(buffer)-1);
+
+ if (len <= 0)
+ return NULL;
+
+ buffer[len] = '\0';
+ return buffer;
+}
+
+int interface_writedata(int fd, GaimSslConnection* gsc, char* buffer)
+{
+ int len = strlen(buffer);
+ char* p = buffer;
+
+ while (len > 0)
+ {
+ int i;
+
+ if (gsc)
+ i = gaim_ssl_write(gsc, p, len);
+ else
+ i = write(fd, p, len);
+
+ if (i < 0)
+ return 1;
+
+ p += i;
+ len -= i;
+ }
+
+ return 0;
+}
+
+/* --- Connection -------------------------------------------------------- */
+
+static void login_cb(gpointer data, gint fd, GaimInputCondition cond)
+{
+ GaimConnection* gc = data;
+ lua_State* L = gc->proto_data;
+
+ if (fd < 0)
+ {
+ gaim_connection_error(gc, _("Couldn't connect to host"));
+ return;
+ }
+
+ if (!g_list_find(gaim_connections_get_all(), gc))
+ {
+ close(fd);
+ return;
+ }
+
+ /* Register the input event handler. */
+
+ gc->inpa = gaim_input_add(fd, GAIM_INPUT_READ, input_cb, gc);
+
+ /* Register this file descriptor and tell Lua. */
+
+ lua_getglobal(L, "citadel_setfd");
+ lua_pushnumber(L, fd);
+ wrappedcall(L, 1, 0);
+}
+
+int interface_connect(GaimAccount* ga, GaimConnection* gc,
+ char* server, int port)
+{
+ return gaim_proxy_connect(ga, server, port, login_cb, gc);
+}
+
+void interface_disconnect(int fd, GaimSslConnection* gsc)
+{
+ if (gsc)
+ gaim_ssl_close(gsc);
+ else if (fd >= 0)
+ close(fd);
+}
+
+/* --- TLS setup --------------------------------------------------------- */
+
+static void ssl_setup_cb(gpointer data, GaimSslConnection* gsc,
+ GaimInputCondition cond)
+{
+ GaimConnection* gc = data;
+ lua_State* L = gc->proto_data;
+
+ if (!g_list_find(gaim_connections_get_all(), gc))
+ {
+ gaim_ssl_close(gsc);
+ return;
+ }
+
+ gaim_debug(GAIM_DEBUG_MISC, "citadel", "using gsc %p\n", gsc);
+ gaim_ssl_input_add(gsc, input_ssl_cb, gc);
+
+ /* Register this file descriptor and tell Lua. */
+
+ lua_getglobal(L, "citadel_setgsc");
+ {
+ GaimSslConnection** o = lua_newuserdata(L, sizeof(GaimSslConnection));
+ *o = gsc;
+ }
+ wrappedcall(L, 1, 0);
+}
+
+static void ssl_failure_cb(GaimSslConnection *gsc, GaimSslErrorType error,
+ gpointer data)
+{
+ GaimConnection* gc = data;
+
+ switch(error)
+ {
+ case GAIM_SSL_CONNECT_FAILED:
+ gaim_connection_error(gc, _("Connection Failed"));
+ break;
+
+ case GAIM_SSL_HANDSHAKE_FAILED:
+ gaim_connection_error(gc, _("SSL Handshake Failed"));
+ break;
+ }
+}
+
+void interface_tlson(GaimConnection* gc, GaimAccount* ga, int fd)
+{
+ gaim_input_remove(gc->inpa);
+ gc->inpa = 0;
+ gaim_ssl_connect_fd(ga, fd, ssl_setup_cb, ssl_failure_cb, gc);
+}
+
+/* --- Timer ------------------------------------------------------------- */
+
+static gboolean timer_cb(gpointer data)
+{
+ struct lua_State* L = data;
+
+ lua_getglobal(L, "citadel_timer");
+ wrappedcall(L, 0, 0);
+ return TRUE;
+}
+
+int interface_timeron(GaimConnection* gc, time_t interval)
+{
+ return gaim_timeout_add(interval, timer_cb, gc->proto_data);
+}
+
+void interface_timeroff(GaimConnection* gc, int timerhandle)
+{
+ gaim_timeout_remove(timerhandle);
+}
+
+/* ======================================================================= */
+/* CONNECT/DISCONNECT */
+/* ======================================================================= */
+
+static void citadel_login(GaimAccount *account)
+{
+ GaimConnection* gc;
+ lua_State* L;
+ int i;
+
+ /* Set up account settings. */
+
+ hackgc = gc = gaim_account_get_connection(account);
+ gc->flags |= GAIM_CONNECTION_NO_BGCOLOR
+ | GAIM_CONNECTION_FORMATTING_WBFO
+ | GAIM_CONNECTION_NO_FONTSIZE
+ | GAIM_CONNECTION_NO_URLDESC
+ | GAIM_CONNECTION_NO_IMAGES;
+
+ /* Initialise our private data. */
+
+ gc->proto_data = L = lua_open();
+ luaopen_base(L);
+ luaopen_table(L);
+ luaopen_string(L);
+ luaopen_math(L);
+ luaopen_debug(L);
+ luaopen_io(L);
+ tolua_gaim_open(L);
+
+ /* Register our private library. */
+
+// luaL_openlib(L, "gaimi", gaim_library, 0);
+
+ /* Load in our 'microcode'. */
+
+ {
+ GString* microcode = g_string_new(gaim_user_dir());
+ g_string_append(microcode, LUA_MICROCODE);
+
+
+ gaim_debug(GAIM_DEBUG_MISC, "citadel", "loading %s\n", microcode->str);
+ i = luaL_loadfile(L, microcode->str) ||
+ lua_pcall(L, 0, 0, 0);
+ g_string_free(microcode, TRUE);
+
+ if (i)
+ {
+ gaim_debug(GAIM_DEBUG_MISC, "citadel", "lua error on load: %s\n",
+ lua_tostring(L, -1));
+ gaim_connection_error(gc, _("Unable to initialise plugin"));
+ return;
+ }
+ }
+
+ /* Set the reentrancy counter. */
+
+ lua_pushnumber(L, 0);
+ lua_setglobal(L, " entrycount");
+
+ /* Tell the script to start connecting. */
+
+ lua_getglobal(L, "citadel_connect");
+ {
+ GaimAccount** o = lua_newuserdata(L, sizeof(GaimAccount*));
+ *o = account;
+ }
+ wrappedcall(L, 1, 0);
+}
+
+static void citadel_close(GaimConnection* gc)
+{
+ lua_State* L = gc->proto_data;
+
+ if (gc->inpa)
+ {
+ gaim_input_remove(gc->inpa);
+ gc->inpa = 0;
+ }
+
+ if (L)
+ {
+ gaim_debug(GAIM_DEBUG_MISC, "citadel", "telling lua to disconnect\n");
+ lua_getglobal(L, "citadel_close");
+ wrappedcall(L, 0, 0);
+ }
+
+ gaim_debug(GAIM_DEBUG_MISC, "citadel", "destroying lua VM\n");
+ lua_close(L);
+}
+
+/* ======================================================================= */
+/* MESSAGING */
+/* ======================================================================= */
+
+static int citadel_send_im(GaimConnection* gc, const char* who,
+ const char* what, GaimConvImFlags flags)
+{
+ lua_State* L = gc->proto_data;
+
+ lua_getglobal(L, "citadel_send_im");
+ lua_pushstring(L, who);
+ lua_pushstring(L, what);
+ lua_pushnumber(L, flags);
+ wrappedcall(L, 3, 0);
+
+ return 1;
+}
+
+/* ======================================================================= */
+/* PRESENCE */
+/* ======================================================================= */
+
+void citadel_add_buddy(GaimConnection* gc, GaimBuddy* buddy, GaimGroup* group)
+{
+ lua_State* L = gc->proto_data;
+
+ lua_getglobal(L, "citadel_add_buddy");
+ lua_pushstring(L, buddy->name);
+ wrappedcall(L, 1, 0);
+}
+
+void citadel_remove_buddy(GaimConnection* gc, GaimBuddy* buddy, GaimGroup* group)
+{
+ lua_State* L = gc->proto_data;
+
+ lua_getglobal(L, "citadel_remove_buddy");
+ lua_pushstring(L, buddy->name);
+ wrappedcall(L, 1, 0);
+}
+
+void citadel_alias_buddy(GaimConnection* gc, const char* who, const char* alias)
+{
+ lua_State* L = gc->proto_data;
+
+ lua_getglobal(L, "citadel_alias_buddy");
+ lua_pushstring(L, who);
+ wrappedcall(L, 1, 0);
+}
+
+void citadel_group_buddy(GaimConnection* gc, const char *who,
+ const char *old_group, const char *new_group)
+{
+ lua_State* L = gc->proto_data;
+
+ lua_getglobal(L, "citadel_group_buddy");
+ lua_pushstring(L, who);
+ lua_pushstring(L, old_group);
+ lua_pushstring(L, new_group);
+ wrappedcall(L, 3, 0);
+}
+
+/* This is really just to fill out a hole in the gaim API. */
+
+const char* gaim_group_get_name(GaimGroup* group)
+{
+ return group->name;
+}
+
+/* ======================================================================= */
+/* USER INFO */
+/* ======================================================================= */
+
+void citadel_get_info(GaimConnection* gc, const char* name)
+{
+ lua_State* L = gc->proto_data;
+
+ lua_getglobal(L, "citadel_get_info");
+ lua_pushstring(L, name);
+ wrappedcall(L, 1, 0);
+}
+
+/* ======================================================================= */
+/* MISCELLANEOUS */
+/* ======================================================================= */
+
+static void citadel_keepalive(GaimConnection* gc)
+{
+ lua_State* L = gc->proto_data;
+
+ lua_getglobal(L, "citadel_keepalive");
+ wrappedcall(L, 0, 0);
+}
+
+static const char* citadel_list_icon(GaimAccount* a, GaimBuddy* b)
+{
+ return "citadel";
+}
+
+static void citadel_list_emblems(GaimBuddy* b, char** se, char** sw,
+ char** nw, char** ne)
+{
+ if (b->present == GAIM_BUDDY_OFFLINE)
+ *se = "offline";
+}
+
+/* ======================================================================= */
+/* PLUGIN SETUP */
+/* ======================================================================= */
+
+static GaimPluginProtocolInfo protocol =
+{
+ OPT_PROTO_CHAT_TOPIC,
+ NULL, /* user_splits */
+ NULL, /* protocol_options */
+ NO_BUDDY_ICONS, /* icon_spec */
+ citadel_list_icon, /* list_icon */
+ citadel_list_emblems, /* list_emblems */
+ NULL, /* status_text */
+ NULL, /* tooltip_text */
+ NULL, //irc_away_states, /* away_states */
+ NULL, /* blist_node_menu */
+ NULL, //irc_chat_join_info, /* chat_info */
+ NULL, //irc_chat_info_defaults, /* chat_info_defaults */
+ citadel_login, /* login */
+ citadel_close, /* close */
+ citadel_send_im, /* send_im */
+ NULL, /* set_info */
+ NULL, /* send_typing */
+ citadel_get_info, /* get_info */
+ NULL, //irc_set_away, /* set_away */
+ NULL, /* set_idle */
+ NULL, /* change_passwd */
+ citadel_add_buddy, /* add_buddy */
+ NULL, /* add_buddies */
+ citadel_remove_buddy, /* remove_buddy */
+ NULL, /* remove_buddies */
+ NULL, /* add_permit */
+ NULL, /* add_deny */
+ NULL, /* rem_permit */
+ NULL, /* rem_deny */
+ NULL, /* set_permit_deny */
+ NULL, /* warn */
+ NULL, //irc_chat_join, /* join_chat */
+ NULL, /* reject_chat */
+ NULL, //irc_get_chat_name, /* get_chat_name */
+ NULL, //irc_chat_invite, /* chat_invite */
+ NULL, //irc_chat_leave, /* chat_leave */
+ NULL, /* chat_whisper */
+ NULL, //irc_chat_send, /* chat_send */
+ citadel_keepalive, /* keepalive */
+ NULL, /* register_user */
+ NULL, /* get_cb_info */
+ NULL, /* get_cb_away */
+ citadel_alias_buddy, /* alias_buddy */
+ citadel_group_buddy, /* group_buddy */
+ NULL, /* rename_group */
+ NULL, /* buddy_free */
+ NULL, /* convo_closed */
+ gaim_normalize_nocase, /* normalize */
+ NULL, /* set_buddy_icon */
+ NULL, /* remove_group */
+ NULL, /* get_cb_real_name */
+ NULL, //irc_chat_set_topic, /* set_chat_topic */
+ NULL, /* find_blist_chat */
+ NULL, //irc_roomlist_get_list, /* roomlist_get_list */
+ NULL, //irc_roomlist_cancel, /* roomlist_cancel */
+ NULL, /* roomlist_expand_category */
+ NULL, /* can_receive_file */
+ NULL, //irc_dccsend_send_file /* send_file */
+};
+
+static GaimPluginInfo info =
+{
+ GAIM_PLUGIN_MAGIC,
+ GAIM_MAJOR_VERSION,
+ GAIM_MINOR_VERSION,
+ GAIM_PLUGIN_PROTOCOL, /**< type */
+ NULL, /**< ui_requirement */
+ 0, /**< flags */
+ NULL, /**< dependencies */
+ GAIM_PRIORITY_DEFAULT, /**< priority */
+
+ "prpl-citadel", /**< id */
+ "Citadel", /**< name */
+ VERSION, /**< version */
+ N_("Citadel Protocol Plugin"), /** summary */
+ N_("Instant Messaging via Citadel"), /** description */
+ NULL, /**< author */
+ GAIM_WEBSITE, /**< homepage */
+
+ NULL, /**< load */
+ NULL, /**< unload */
+ NULL, /**< destroy */
+
+ NULL, /**< ui_info */
+ &protocol, /**< extra_info */
+ NULL, /**< prefs_info */
+ NULL
+};
+
+static void _init_plugin(GaimPlugin *plugin)
+{
+ GaimAccountUserSplit *split;
+ GaimAccountOption *option;
+
+ split = gaim_account_user_split_new(_("Server"), CITADEL_DEFAULT_SERVER, '@');
+ protocol.user_splits = g_list_append(protocol.user_splits, split);
+
+ option = gaim_account_option_int_new(_("Port"), "port", CITADEL_DEFAULT_PORT);
+ protocol.protocol_options = g_list_append(protocol.protocol_options, option);
+
+ option = gaim_account_option_bool_new(_("Use TLS"), "use_tls", TRUE);
+ protocol.protocol_options = g_list_append(protocol.protocol_options, option);
+
+ option = gaim_account_option_int_new(_("Polling interval"), "interval", CITADEL_POLL_INTERVAL);
+ protocol.protocol_options = g_list_append(protocol.protocol_options, option);
+
+ gaim_prefs_add_none("/plugins/prpl/citadel");
+}
+
+GAIM_INIT_PLUGIN(citadel, _init_plugin, info);
--- /dev/null
+-- citadel.lua
+-- Gaim Citadel plugin.
+--
+-- © 2006 David Given.
+-- This code is licensed under the GPL v2. See the file COPYING in this
+-- directory for the full license text.
+--
+-- $Id: auth.c 4258 2006-01-29 13:34:44 +0000 (Sun, 29 Jan 2006) dothebart $
+
+-----------------------------------------------------------------------------
+-- GLOBALS --
+-----------------------------------------------------------------------------
+
+local _
+local username, servername, port
+local ga, gc
+local fd, gsc
+local timerhandle
+local buddies = {}
+
+-----------------------------------------------------------------------------
+-- CONSTANTS --
+-----------------------------------------------------------------------------
+
+-- Special values returned as Citadel's response codes.
+
+local LISTING_FOLLOWS = 100
+local CIT_OK = 200
+local MORE_DATA = 300
+local SEND_LISTING = 400
+local ERROR = 500
+local BINARY_FOLLOWS = 600
+local SEND_BINARY = 700
+local START_CHAT_MODE = 800
+
+local INTERNAL_ERROR = 10
+local TOO_BIG = 11
+local ILLEGAL_VALUE = 12
+local NOT_LOGGED_IN = 20
+local CMD_NOT_SUPPORTED = 30
+local PASSWORD_REQUIRED = 40
+local ALREADY_LOGGED_IN = 41
+local USERNAME_REQUIRED = 42
+local HIGHER_ACCESS_REQUIRED = 50
+local MAX_SESSIONS_EXCEEDED = 51
+local RESOURCE_BUSY = 52
+local RESOURCE_NOT_OPEN = 53
+local NOT_HERE = 60
+local INVALID_FLOOR_OPERATION = 61
+local NO_SUCH_USER = 70
+local FILE_NOT_FOUND = 71
+local ROOM_NOT_FOUND = 72
+local NO_SUCH_SYSTEM = 73
+local ALREADY_EXISTS = 74
+local MESSAGE_NOT_FOUND = 75
+
+local ASYNC_MSG = 900
+local ASYNC_GEXP = 02
+
+-- Other Citadel settings.
+
+local CITADEL_DEFAULT_PORT = 504
+local CITADEL_CONFIG_ROOM = "My Citadel Config"
+local CITADEL_BUDDY_MSG = "__ Buddy List __"
+local CITADEL_POLL_INTERVAL = 5
+
+-----------------------------------------------------------------------------
+-- UTILITIES --
+-----------------------------------------------------------------------------
+
+--local stderr = io.stderr
+
+local function log(...)
+ local s = {}
+ for _, i in ipairs(arg) do
+ table.insert(s, tostring(i))
+ end
+ print("citadel: lua: "..table.concat(s))
+end
+
+local function unexpectederror()
+ error("The Citadel server said something unexpected. Giving up.")
+end
+
+local function warning(...)
+ local s = {}
+ for _, i in ipairs(arg) do
+ table.insert(s, tostring(i))
+ end
+ gaim_connection_notice(gc, s)
+end
+
+local olderror = error
+error = function(e)
+ log("error: ", e)
+ log("traceback: ", debug.traceback())
+ olderror(e)
+end
+
+-----------------------------------------------------------------------------
+-- SCHEDULER --
+-----------------------------------------------------------------------------
+
+local taskqueue = {}
+local idle
+local inscheduler = false
+
+local yield = coroutine.yield
+
+local function schedule_now()
+ if not inscheduler then
+ inscheduler = true
+
+ while taskqueue[1] do
+ -- Pull the first task off the queue, creating it if necessary.
+
+ local task = taskqueue[1]
+ if (type(task) == "function") then
+ task = coroutine.create(task)
+ taskqueue[1] = task
+ end
+
+ -- Run it.
+
+ local s, e = coroutine.resume(task)
+ if not s then
+ log("error: ", e)
+ log("traceback: ", debug.traceback())
+ gaim_connection_error(gc, e)
+ end
+
+ -- If it's not dead, then it must have yielded --- return back to C.
+
+ if (coroutine.status(task) ~= "dead") then
+ break
+ end
+
+ -- Otherwise, remove it from the queue and go again.
+
+ table.remove(taskqueue, 1)
+ end
+
+ inscheduler = false
+ end
+end
+
+local function queue(func)
+ table.insert(taskqueue, func)
+--[[
+ table.insert(taskqueue, function()
+ local i, e = pcall(func)
+ if not i then
+ log("coroutine died with error! ", e)
+ gaim_connection_error(gc, e)
+ end
+ end)
+--]]
+end
+
+local queued = {}
+local function lazyqueue(func)
+ if not queued[func] then
+ queued[func] = true
+ queue(
+ function()
+ queued[func] = nil
+ func()
+ end)
+ end
+end
+
+-----------------------------------------------------------------------------
+-- INPUT MANGLING --
+-----------------------------------------------------------------------------
+
+local inputbuffer = ""
+
+-- Read a single line of text from the server, maing Lua's coroutines do the
+-- vast bulk of the work of managing Gaim's state machine for us. Woo!
+
+local function readline()
+ -- Always yield at least once. Otherwise, Lua hogs all the CPU time.
+
+ yield()
+
+ while true do
+ if fd then
+ -- Read some data from the remote server, if any's
+ -- available.
+
+ local i = interface_readdata(fd, gsc)
+ if not i then
+ error("Unexpected disconnection from Citadel server")
+ end
+
+ inputbuffer = inputbuffer..i
+
+ -- Have we read a complete line of text?
+
+ local s, e, l = string.find(inputbuffer, "^([^\n]*)\n")
+ if l then
+ -- If so, return it.
+
+ inputbuffer = string.sub(inputbuffer, e+1)
+ return l
+ end
+ end
+
+ -- Otherwise, wait some more.
+
+ yield()
+ end
+end
+
+local function unpack_citadel_data_line(s, a)
+ a = a or {}
+ for i in string.gfind(s, "([^|]*)|?") do
+ table.insert(a, i)
+ end
+ return a
+end
+
+-- Read in an parse a packet from the Citadel server.
+
+local function get_response()
+ local message = {}
+
+ -- The first line of a message is of the format:
+ -- 123 String|String|String
+ --
+ -- The 123 is a response code.
+
+ local s = readline()
+ message.response = tonumber(string.sub(s, 1, 3))
+
+ s = string.sub(s, 5)
+ unpack_citadel_data_line(s, message)
+
+ -- If the response code is LISTING_FOLLOWS, then there's more data
+ -- coming.
+
+ if (message.response == LISTING_FOLLOWS) then
+ message.xargs = {}
+
+ while true do
+ s = readline()
+ if (s == "000") then
+ break
+ end
+ --log("Got xarg: ", s)
+ table.insert(message.xargs, s)
+ end
+ end
+
+ -- If the response code is BINARY_FOLLOWS, there's a big binary chunk
+ -- coming --- which we don't support.
+
+ if (message.response == BINARY_FOLLOWS) then
+ error("Server sent a binary chunk, which we don't support yet")
+ end
+
+ return message
+end
+
+-----------------------------------------------------------------------------
+-- OUTPUT MANGLING --
+-----------------------------------------------------------------------------
+
+local function writeline(...)
+ local s = table.concat(arg)
+
+ log("send: ", s)
+ interface_writedata(fd, gsc, s)
+ interface_writedata(fd, gsc, "\n")
+end
+
+-----------------------------------------------------------------------------
+-- PRESENCE MANAGEMENT --
+-----------------------------------------------------------------------------
+
+local function cant_save_buddy_list()
+ warning("Unable to send buddy list to server.")
+end
+
+local function save_buddy_list()
+ writeline("GOTO "..CITADEL_CONFIG_ROOM)
+ local m = get_response()
+ if (m.response ~= CIT_OK) then
+ cant_save_buddy_list()
+ return
+ end
+
+ -- Search and destroy any old buddy list.
+
+ writeline("MSGS ALL|0|1")
+ m = get_response()
+ if (m.response ~= START_CHAT_MODE) then
+ cant_save_buddy_list()
+ return
+ end
+
+ writeline("subj|"..CITADEL_BUDDY_MSG)
+ writeline("000")
+ m = nil
+ while true do
+ local s = readline()
+ if (s == "000") then
+ break
+ end
+ if (not m) and (s ~= "000") then
+ m = s
+ end
+ end
+
+ if m then
+ writeline("DELE "..m)
+ m = get_response()
+ if (m.response ~= CIT_OK) then
+ cant_save_buddy_list()
+ return
+ end
+ end
+
+ -- Save our buddy list.
+
+ writeline("ENT0 1||0|1|"..CITADEL_BUDDY_MSG.."|")
+ m = get_response()
+ if (m.response ~= SEND_LISTING) then
+ cant_save_buddy_list()
+ return
+ end
+
+ for name, _ in pairs(buddies) do
+ local b = gaim_find_buddy(ga, name)
+ if b then
+ local alias = gaim_buddy_get_alias(b) or ""
+ local group = gaim_find_buddys_group(b)
+ local groupname = gaim_group_get_name(group)
+ writeline(name.."|"..alias.."|"..groupname)
+ end
+ end
+ writeline("000")
+
+ -- Go back to the lobby.
+
+ writeline("GOTO _BASEROOM_")
+ get_response()
+end
+
+local function update_buddy_status()
+ writeline("RWHO")
+ local m = get_response()
+ if (m.response ~= LISTING_FOLLOWS) then
+ return
+ end
+ log("attempting to scan and update buddies")
+
+ local onlinebuddies = {}
+ for _, s in ipairs(m.xargs) do
+ local name = unpack_citadel_data_line(s)[2]
+ onlinebuddies[name] = true
+ end
+
+ for s, _ in pairs(onlinebuddies) do
+ serv_got_update(gc, s, true, 0, 0, 0, 0)
+ end
+end
+
+-----------------------------------------------------------------------------
+-- ENTRYPOINTS --
+-----------------------------------------------------------------------------
+
+function citadel_schedule_now()
+ schedule_now()
+end
+
+function citadel_input()
+ -- If there's no task, create one to handle this input.
+
+ if not taskqueue[1] then
+ queue(idle)
+ end
+end
+
+function citadel_setfd(_fd)
+ fd = _fd
+ log("fd = ", tonumber(fd))
+end
+
+function citadel_setgsc(_gsc)
+ gsc = tolua.cast(_gsc, "GaimSslConnection")
+ log("gsc registered")
+end
+
+function citadel_connect(_ga)
+ ga = tolua.cast(_ga, "GaimAccount")
+ gc = gaim_account_get_connection(ga)
+
+ queue(function()
+ local STEPS = 13
+
+ username = gaim_account_get_username(ga)
+ _, _, username, servername = string.find(username, "^(.*)@(.*)$")
+ port = gaim_account_get_int(ga, "port", CITADEL_DEFAULT_PORT);
+
+ log("connect to ", username, " on server ", servername, " port ", port)
+
+ -- Make connection.
+
+ gaim_connection_update_progress(gc, "Connecting", 1, STEPS)
+ local i = interface_connect(ga, gc, servername, port)
+ if (i ~= 0) then
+ error("Unable to create socket")
+ end
+
+ local m = get_response()
+ if (m.response ~= CIT_OK) then
+ error("Unexpected response from server")
+ end
+
+ -- Switch to TLS mode, if desired.
+
+ if gaim_account_get_bool(ga, "use_tls", true) then
+ gaim_connection_update_progress(gc, "Requesting TLS", 2, STEPS)
+ writeline("STLS")
+ m = get_response()
+ if (m.response ~= 200) then
+ error("This Citadel server does not support TLS.")
+ end
+
+ -- This will always work. If the handshake fails, Lua will be
+ -- shot and we don't need to worry about cleaning up.
+
+ gaim_connection_update_progress(gc, "TLS handshake", 3, STEPS)
+ interface_tlson(gc, ga, fd)
+
+ -- Wait for the gsc to be hooked up.
+
+ while not gsc do
+ yield()
+ end
+ end
+
+ -- Send username.
+
+ gaim_connection_update_progress(gc, "Sending username", 4, STEPS)
+ writeline("USER "..username)
+ m = get_response()
+ if (m.response == (ERROR+NO_SUCH_USER)) then
+ error("There is no user with name '", username, "' on this server.")
+ end
+ if (m.response ~= MORE_DATA) then
+ unexpectederror()
+ end
+
+ -- Send password.
+
+ gaim_connection_update_progress(gc, "Sending password", 5, STEPS)
+ writeline("PASS "..gaim_account_get_password(ga))
+ m = get_response()
+ if (m.response ~= CIT_OK) then
+ error("Incorrect password.")
+ end
+
+ -- Tell Citadel who we are.
+
+ gaim_connection_update_progress(gc, "Setting up", 6, STEPS)
+ writeline("IDEN 226|0|0.2|Gaim Citadel plugin|")
+ m = get_response()
+
+ -- Set asynchronous mode.
+
+ gaim_connection_update_progress(gc, "Setting up", 7, STEPS)
+ writeline("ASYN 1")
+ m = get_response()
+ if (m.response ~= CIT_OK) then
+ error("This Citadel server does not support instant messaging.")
+ end
+
+ (function()
+ -- Switch to private configuration room.
+
+ gaim_connection_update_progress(gc, "Setting up", 8, STEPS)
+ writeline("GOTO "..CITADEL_CONFIG_ROOM)
+ m = get_response()
+ if (m.response ~= CIT_OK) then
+ warning("Unable to fetch buddy list from server.")
+ return
+ end
+
+ -- Look for our preferences.
+
+ gaim_connection_update_progress(gc, "Setting up", 9, STEPS)
+ writeline("MSGS ALL|0|1")
+ m = get_response()
+ if (m.response ~= START_CHAT_MODE) then
+ warning("Unable to fetch buddy list from server.")
+ return
+ end
+
+ writeline("subj|"..CITADEL_BUDDY_MSG)
+ writeline("000")
+ m = nil
+ while true do
+ local s = readline()
+ if (s == "000") then
+ break
+ end
+ if (not m) and (s ~= "000") then
+ m = s
+ end
+ end
+
+ log("preference message in #", m)
+ if not m then
+ return
+ end
+
+ gaim_connection_update_progress(gc, "Setting up", 10, STEPS)
+ writeline("MSG0 "..m)
+ while true do
+ local s = readline()
+ if (s == "000") then
+ return
+ end
+ if (s == "text") then
+ break
+ end
+ end
+ while true do
+ local s = readline()
+ if (s == "000") then
+ break
+ end
+
+ local name, alias, groupname = unpack(unpack_citadel_data_line(s))
+ if not gaim_find_buddy(ga, name) then
+ local buddy = gaim_buddy_new(ga, name, alias)
+ local group = gaim_group_new(groupname)
+ log("adding new buddy ", name)
+ if buddy then
+ -- buddy is not garbage collected! This must succeed!
+ gaim_blist_add_buddy(buddy, nil, group, nil)
+ end
+ end
+ end
+ end)()
+
+ -- Update buddy list with who's online.
+
+ gaim_connection_update_progress(gc, "Setting up", 11, STEPS)
+ update_buddy_status()
+
+ -- Go back to the Lobby.
+
+ gaim_connection_update_progress(gc, "Setting up", 12, STEPS)
+ writeline("GOTO _BASEROOM_")
+ get_response()
+
+ -- Switch on the timer.
+
+ timerhandle = interface_timeron(gc,
+ gaim_account_get_int(ga, "interval", CITADEL_POLL_INTERVAL)*1000)
+
+ -- Done!
+
+ gaim_connection_update_progress(gc, "Connected", 13, STEPS)
+ gaim_connection_set_state(gc, GAIM_CONNECTED)
+ end)
+end
+
+function citadel_close()
+ interface_disconnect(fd or -1, gsc)
+ if timerhandle then
+ interface_timeroff(gc, timerhandle)
+ end
+ schedule_now = function() end
+end
+
+function citadel_send_im(who, what, flags)
+ queue(function()
+ writeline("SEXP ", who, "|-")
+ local m = get_response()
+ if (m.response ~= SEND_LISTING) then
+ serv_got_im(gc, "Citadel", "Unable to send message", GAIM_MESSAGE_ERROR, 0);
+ return
+ end
+ writeline(what)
+ writeline("000")
+ end)
+end
+
+function citadel_fetch_pending_messages()
+ queue(function()
+ while true do
+ writeline("GEXP")
+ local m = get_response()
+ if (m.response ~= LISTING_FOLLOWS) then
+ break
+ end
+
+ local s = table.concat(m.xargs)
+ --log("got message from ", m[4], " at ", m[2], ": ", s)
+ serv_got_im(gc, m[4], s, GAIM_MESSAGE_RECV, m[2])
+ end
+ end)
+end
+
+function citadel_get_info(name)
+ queue(function()
+ writeline("RBIO "..name)
+ local m = get_response()
+ if (m.response ~= LISTING_FOLLOWS) then
+ m = "That user has been boojumed."
+ else
+ m = table.concat(m.xargs, "<br>")
+ end
+
+ gaim_notify_userinfo(gc, name, name.."'s biography",
+ name, "Biography", m, nil, nil)
+ end)
+end
+
+function citadel_keepalive()
+ queue(function()
+ writeline("NOOP")
+ get_response()
+ end)
+end
+
+-----------------------------------------------------------------------------
+-- BUDDY LIST --
+-----------------------------------------------------------------------------
+
+function citadel_add_buddy(name)
+ if not buddies[name] then
+ buddies[name] = true
+ lazyqueue(update_buddy_status)
+ lazyqueue(save_buddy_list)
+ end
+end
+
+function citadel_remove_buddy(name)
+ if buddies[name] then
+ buddies[name] = nil
+ lazyqueue(save_buddy_list)
+ end
+end
+
+function citadel_alias_buddy(name)
+ if buddies[name] then
+ lazyqueue(save_buddy_list)
+ end
+end
+
+function citadel_group_buddy(name, oldgroup, newgroup)
+ if buddies[name] then
+ lazyqueue(save_buddy_list)
+ end
+end
+
+function citadel_timer()
+ log("tick!")
+ lazyqueue(update_buddy_status)
+end
+
+-----------------------------------------------------------------------------
+-- IDLE --
+-----------------------------------------------------------------------------
+
+idle = function()
+ queue(function()
+ local m = get_response()
+ if (m.response == (ASYNC_MSG+ASYNC_GEXP)) then
+ citadel_fetch_pending_messages()
+ end
+ end)
+end
--- /dev/null
+/* gaim.pkg
+ * Gaim Citadel plugin.
+ *
+ * © 2006 David Given.
+ * This code is licensed under the GPL v2. See the file COPYING in this
+ * directory for the full license text.
+ *
+ * $Id: auth.c 4258 2006-01-29 13:34:44 +0000 (Sun, 29 Jan 2006) dothebart $
+ */
+
+typedef unsigned int size_t;
+typedef bool gboolean;
+typedef long time_t;
+typedef void* GCallback;
+
+$typedef int bool;
+$#include "connection.h"
+$#include "sslconn.h"
+$#include "account.h"
+$#include "conversation.h"
+$#include "server.h"
+$#include "notify.h"
+$#include "util.h"
+
+$#include "interface.h"
+
+/* Private entrypoints */
+
+char* interface_readdata(int fd, GaimSslConnection* gsc);
+int interface_writedata(int fd, GaimSslConnection* gsc, char* data);
+int interface_connect(GaimAccount* ga, GaimConnection* gc, char* server, int port);
+void interface_disconnect(int fd, GaimSslConnection* gsc);
+void interface_tlson(GaimConnection* gc, GaimAccount* ga, int fd);
+int interface_timeron(GaimConnection* gc, time_t timeout);
+void interface_timeroff(GaimConnection* gc, int handle);
+
+/* --- connection.h ------------------------------------------------------ */
+
+typedef enum
+{
+ GAIM_CONNECTION_HTML = 0x0001, /**< Connection sends/receives in 'HTML'. */
+ GAIM_CONNECTION_NO_BGCOLOR = 0x0002, /**< Connection does not send/receive
+ background colors. */
+ GAIM_CONNECTION_AUTO_RESP = 0x0004, /**< Send auto responses when away. */
+ GAIM_CONNECTION_FORMATTING_WBFO = 0x0008, /**< The text buffer must be formatted as a whole */
+ GAIM_CONNECTION_NO_NEWLINES = 0x0010, /**< No new lines are allowed in outgoing messages */
+ GAIM_CONNECTION_NO_FONTSIZE = 0x0020, /**< Connection does not send/receive font sizes */
+ GAIM_CONNECTION_NO_URLDESC = 0x0040, /**< Connection does not support descriptions with links */
+ GAIM_CONNECTION_NO_IMAGES = 0x0080 /**< Connection does not support sending of images */
+} GaimConnectionFlags;
+
+typedef enum
+{
+ GAIM_DISCONNECTED = 0, /**< Disconnected. */
+ GAIM_CONNECTED, /**< Connected. */
+ GAIM_CONNECTING /**< Connecting. */
+
+} GaimConnectionState;
+
+void gaim_connection_set_state(GaimConnection *gc, GaimConnectionState state);
+void gaim_connection_set_account(GaimConnection *gc, GaimAccount *account);
+void gaim_connection_set_display_name(GaimConnection *gc, char *name);
+GaimConnectionState gaim_connection_get_state(GaimConnection *gc);
+GaimAccount *gaim_connection_get_account(GaimConnection *gc);
+char *gaim_connection_get_display_name(GaimConnection *gc);
+void gaim_connection_update_progress(GaimConnection *gc, char *text,
+ size_t step, size_t count);
+void gaim_connection_notice(GaimConnection *gc, char *text);
+void gaim_connection_error(GaimConnection *gc, char *reason);
+
+/* --- account.h --------------------------------------------------------- */
+
+void gaim_account_request_change_password(GaimAccount *account);
+void gaim_account_request_change_user_info(GaimAccount *account);
+void gaim_account_set_username(GaimAccount *account, char *username);
+void gaim_account_set_password(GaimAccount *account, char *password);
+void gaim_account_set_alias(GaimAccount *account, char *alias);
+void gaim_account_set_user_info(GaimAccount *account, char *user_info);
+void gaim_account_set_buddy_icon(GaimAccount *account, char *icon);
+void gaim_account_set_protocol_id(GaimAccount *account,
+ char *protocol_id);
+void gaim_account_set_remember_password(GaimAccount *account, gboolean value);
+void gaim_account_set_check_mail(GaimAccount *account, gboolean value);
+void gaim_account_set_auto_login(GaimAccount *account, char *ui,
+ gboolean value);
+void gaim_account_set_proxy_info(GaimAccount *account, GaimProxyInfo *info);
+void gaim_account_clear_settings(GaimAccount *account);
+void gaim_account_set_int(GaimAccount *account, char *name, int value);
+void gaim_account_set_string(GaimAccount *account, char *name,
+ char *value);
+void gaim_account_set_bool(GaimAccount *account, char *name,
+ gboolean value);
+void gaim_account_set_ui_int(GaimAccount *account, char *ui,
+ char *name, int value);
+void gaim_account_set_ui_string(GaimAccount *account, char *ui,
+ char *name, char *value);
+void gaim_account_set_ui_bool(GaimAccount *account, char *ui,
+ char *name, gboolean value);
+gboolean gaim_account_is_connected(GaimAccount *account);
+char *gaim_account_get_username(GaimAccount *account);
+char *gaim_account_get_password(GaimAccount *account);
+char *gaim_account_get_alias(GaimAccount *account);
+char *gaim_account_get_user_info(GaimAccount *account);
+char *gaim_account_get_buddy_icon(GaimAccount *account);
+char *gaim_account_get_protocol_id(GaimAccount *account);
+char *gaim_account_get_protocol_name(GaimAccount *account);
+GaimConnection *gaim_account_get_connection(GaimAccount *account);
+gboolean gaim_account_get_remember_password(GaimAccount *account);
+gboolean gaim_account_get_check_mail(GaimAccount *account);
+gboolean gaim_account_get_auto_login(GaimAccount *account, char *ui);
+int gaim_account_get_int(GaimAccount *account, char *name, int default_value);
+char *gaim_account_get_string(GaimAccount *account, char *name, char *default_value);
+gboolean gaim_account_get_bool(GaimAccount *account, char *name, gboolean default_value);
+int gaim_account_get_ui_int(GaimAccount *account, char *ui, char *name, int default_value);
+char *gaim_account_get_ui_string(GaimAccount *account, char *ui, char *name, char *default_value);
+gboolean gaim_account_get_ui_bool(GaimAccount *account, char *ui, char *name, gboolean default_value);
+
+/* --- server.h ---------------------------------------------------------- */
+
+void serv_login(GaimAccount *);
+void serv_close(GaimConnection *);
+void serv_touch_idle(GaimConnection *);
+int serv_send_im(GaimConnection *, const char *, const char *, GaimConvImFlags);
+void serv_get_info(GaimConnection *, const char *);
+void serv_set_idle(GaimConnection *, int);
+void serv_set_info(GaimConnection *, const char *);
+void serv_set_away(GaimConnection *, const char *, const char *);
+void serv_set_away_all(const char *);
+int serv_send_typing(GaimConnection *, const char *, int);
+void serv_change_passwd(GaimConnection *, const char *, const char *);
+void serv_add_buddy(GaimConnection *, GaimBuddy *);
+void serv_add_buddies(GaimConnection *, GList *);
+void serv_remove_buddy(GaimConnection *, GaimBuddy *, GaimGroup *);
+void serv_remove_buddies(GaimConnection *, GList *, GList *);
+void serv_remove_group(GaimConnection *, GaimGroup *);
+void serv_move_buddy(GaimBuddy *, GaimGroup *, GaimGroup *);
+void serv_rename_group(GaimConnection *, const char *, GaimGroup *, GList *);
+void serv_add_permit(GaimConnection *, const char *);
+void serv_add_deny(GaimConnection *, const char *);
+void serv_rem_permit(GaimConnection *, const char *);
+void serv_rem_deny(GaimConnection *, const char *);
+void serv_set_permit_deny(GaimConnection *);
+void serv_warn(GaimConnection *, const char *, gboolean);
+void serv_join_chat(GaimConnection *, GHashTable *);
+void serv_reject_chat(GaimConnection *, GHashTable *);
+void serv_chat_invite(GaimConnection *, int, const char *, const char *);
+void serv_chat_leave(GaimConnection *, int);
+void serv_chat_whisper(GaimConnection *, int, const char *, const char *);
+int serv_chat_send(GaimConnection *, int, const char *);
+void serv_alias_buddy(GaimBuddy *);
+void serv_got_alias(GaimConnection *gc, const char *who, const char *alias);
+void serv_got_eviled(GaimConnection *gc, const char *name, int lev);
+void serv_got_typing(GaimConnection *gc, const char *name, int timeout,
+ GaimTypingState state);
+void serv_set_buddyicon(GaimConnection *gc, const char *filename);
+void serv_got_typing_stopped(GaimConnection *gc, const char *name);
+void serv_got_im(GaimConnection *gc, const char *who, const char *msg,
+ GaimConvImFlags imflags, time_t mtime);
+void serv_got_update(GaimConnection *gc, const char *name, gboolean loggedin,
+ int evil, time_t signon, time_t idle, int type);
+void serv_finish_login(GaimConnection *gc);
+void serv_got_chat_invite(GaimConnection *gc, const char *name,
+ const char *who, const char *message,
+ GHashTable *data);
+GaimConversation *serv_got_joined_chat(GaimConnection *gc,
+ int id, const char *name);
+void serv_got_chat_left(GaimConnection *g, int id);
+void serv_got_chat_in(GaimConnection *g, int id, const char *who,
+ GaimConvChatFlags chatflags, const char *message, time_t mtime);
+void serv_send_file(GaimConnection *gc, const char *who, const char *file);
+
+/* --- conversation.h ---------------------------------------------------- */
+
+typedef enum
+{
+ GAIM_CONV_UNKNOWN = 0, /**< Unknown conversation type. */
+ GAIM_CONV_IM, /**< Instant Message. */
+ GAIM_CONV_CHAT, /**< Chat room. */
+ GAIM_CONV_MISC /**< A misc. conversation. */
+
+} GaimConversationType;
+
+typedef enum
+{
+ GAIM_UNSEEN_NONE = 0, /**< No unseen text in the conversation. */
+ GAIM_UNSEEN_TEXT, /**< Unseen text in the conversation. */
+ GAIM_UNSEEN_NICK, /**< Unseen text and the nick was said. */
+ GAIM_UNSEEN_EVENT /**< Unseen events in the conversation. */
+
+} GaimUnseenState;
+
+typedef enum
+{
+ GAIM_CONV_UPDATE_ADD = 0, /**< The buddy associated with the conversation
+ was added. */
+ GAIM_CONV_UPDATE_REMOVE, /**< The buddy associated with the conversation
+ was removed. */
+ GAIM_CONV_UPDATE_ACCOUNT, /**< The gaim_account was changed. */
+ GAIM_CONV_UPDATE_TYPING, /**< The typing state was updated. */
+ GAIM_CONV_UPDATE_UNSEEN, /**< The unseen state was updated. */
+ GAIM_CONV_UPDATE_LOGGING, /**< Logging for this conversation was
+ enabled or disabled. */
+ GAIM_CONV_UPDATE_TOPIC, /**< The topic for a chat was updated. */
+ /*
+ * XXX These need to go when we implement a more generic core/UI event
+ * system.
+ */
+ GAIM_CONV_ACCOUNT_ONLINE, /**< One of the user's accounts went online. */
+ GAIM_CONV_ACCOUNT_OFFLINE, /**< One of the user's accounts went offline. */
+ GAIM_CONV_UPDATE_AWAY, /**< The other user went away. */
+ GAIM_CONV_UPDATE_ICON, /**< The other user's buddy icon changed. */
+ GAIM_CONV_UPDATE_TITLE,
+ GAIM_CONV_UPDATE_CHATLEFT,
+
+ GAIM_CONV_UPDATE_FEATURES /**< The features for a chat have changed */
+
+} GaimConvUpdateType;
+
+typedef enum
+{
+ GAIM_NOT_TYPING = 0, /**< Not typing. */
+ GAIM_TYPING, /**< Currently typing. */
+ GAIM_TYPED /**< Stopped typing momentarily. */
+
+} GaimTypingState;
+
+typedef enum
+{
+ GAIM_MESSAGE_SEND = 0x0001, /**< Outgoing message. */
+ GAIM_MESSAGE_RECV = 0x0002, /**< Incoming message. */
+ GAIM_MESSAGE_SYSTEM = 0x0004, /**< System message. */
+ GAIM_MESSAGE_AUTO_RESP = 0x0008, /**< Auto response. */
+ GAIM_MESSAGE_COLORIZE = 0x0010, /**< Colorize nicks. */
+ GAIM_MESSAGE_NICK = 0x0020, /**< Contains your nick. */
+ GAIM_MESSAGE_NO_LOG = 0x0040, /**< Do not log. */
+ GAIM_MESSAGE_WHISPER = 0x0080, /**< Whispered message. */
+ GAIM_MESSAGE_ERROR = 0x0200, /**< Error message. */
+ GAIM_MESSAGE_DELAYED = 0x0400 /**< Delayed message. */
+} GaimMessageFlags;
+
+typedef enum
+{
+ GAIM_CBFLAGS_NONE = 0x0000, /**< No flags */
+ GAIM_CBFLAGS_VOICE = 0x0001, /**< Voiced user or "Participant" */
+ GAIM_CBFLAGS_HALFOP = 0x0002, /**< Half-op */
+ GAIM_CBFLAGS_OP = 0x0004, /**< Channel Op or Moderator */
+ GAIM_CBFLAGS_FOUNDER = 0x0008 /**< Channel Founder */
+} GaimConvChatBuddyFlags;
+
+/* --- prpl.h ------------------------------------------------------------ */
+
+typedef enum
+{
+ GAIM_CONV_IM_AUTO_RESP = 0x0001, /**< Auto response. */
+ GAIM_CONV_IM_IMAGES = 0x0002 /**< Contains images. */
+} GaimConvImFlags;
+
+typedef enum
+{
+ GAIM_CONV_CHAT_WHISPER = 0x0001, /**< Whispered message.*/
+ GAIM_CONV_CHAT_DELAYED = 0x0002 /**< Delayed message. */
+
+} GaimConvChatFlags;
+
+typedef enum {
+ GAIM_ICON_SCALE_DISPLAY = 0x01, /**< We scale the icon when we display it */
+ GAIM_ICON_SCALE_SEND = 0x02 /**< We scale the icon before we send it to the server */
+} GaimIconScaleRules;
+
+/* --- blist.h ----------------------------------------------------------- */
+
+typedef enum
+{
+ GAIM_BLIST_GROUP_NODE,
+ GAIM_BLIST_CONTACT_NODE,
+ GAIM_BLIST_BUDDY_NODE,
+ GAIM_BLIST_CHAT_NODE,
+ GAIM_BLIST_OTHER_NODE
+
+} GaimBlistNodeType;
+
+typedef enum
+{
+ GAIM_BUDDY_SIGNING_OFF = -1,
+ GAIM_BUDDY_OFFLINE = 0,
+ GAIM_BUDDY_ONLINE,
+ GAIM_BUDDY_SIGNING_ON
+
+} GaimBuddyPresenceState;
+
+GaimGroup *gaim_group_new(const char *name);
+GaimGroup *gaim_find_buddys_group(GaimBuddy *buddy);
+GaimBuddy *gaim_buddy_new(GaimAccount *account, const char *screenname, const char *alias);
+const char *gaim_group_get_name(GaimGroup *group); // fake --- added by plugin
+const char *gaim_buddy_get_alias_only(GaimBuddy *buddy);
+const char *gaim_buddy_get_contact_alias(GaimBuddy *buddy);
+const char *gaim_buddy_get_alias(GaimBuddy *buddy);
+GaimBuddy *gaim_find_buddy(GaimAccount *account, const char *name);
+void gaim_blist_add_buddy(GaimBuddy *buddy, GaimContact *contact, GaimGroup *group, GaimBlistNode *node);
+
+/* --- notify.h ---------------------------------------------------------- */
+
+typedef enum
+{
+ GAIM_NOTIFY_MESSAGE = 0, /**< Message notification. */
+ GAIM_NOTIFY_EMAIL, /**< Single e-mail notification. */
+ GAIM_NOTIFY_EMAILS, /**< Multiple e-mail notification. */
+ GAIM_NOTIFY_FORMATTED, /**< Formatted text. */
+ GAIM_NOTIFY_USERINFO, /**< Formatted userinfo text. */
+ GAIM_NOTIFY_URI /**< URI notification or display. */
+
+} GaimNotifyType;
+
+typedef enum
+{
+ GAIM_NOTIFY_MSG_ERROR = 0, /**< Error notification. */
+ GAIM_NOTIFY_MSG_WARNING, /**< Warning notification. */
+ GAIM_NOTIFY_MSG_INFO /**< Information notification. */
+
+} GaimNotifyMsgType;
+
+void *gaim_notify_message(void *handle, GaimNotifyMsgType type,
+ const char *title, const char *primary,
+ const char *secondary, GCallback cb,
+ void *user_data);
+void *gaim_notify_email(void *handle, const char *subject,
+ const char *from, const char *to,
+ const char *url, GCallback cb,
+ void *user_data);
+void *gaim_notify_emails(void *handle, size_t count, gboolean detailed,
+ const char **subjects, const char **froms,
+ const char **tos, const char **urls,
+ GCallback cb, void *user_data);
+void *gaim_notify_formatted(void *handle, const char *title,
+ const char *primary, const char *secondary,
+ const char *text, GCallback cb, void *user_data);
+void *gaim_notify_userinfo(GaimConnection *gc, const char *who,
+ const char *title, const char *primary,
+ const char *secondary, const char *text,
+ GCallback cb, void *user_data);
+void *gaim_notify_uri(void *handle, const char *uri);
+
+/* --- util.h ------------------------------------------------------------ */
+
+void gaim_quotedp_decode (const char *str, char **ret_str, int *ret_len);
+char *gaim_mime_decode_field (const char *str);
+const char *gaim_date(void);
+const char *gaim_date_full(void);
+time_t gaim_time_build(int year, int month, int day, int hour, int min, int sec);
+time_t gaim_str_to_time(const char *timestamp, gboolean utc);
+
+const gchar *gaim_home_dir(void);
+char *gaim_user_dir(void);
+void set_gaim_user_dir(const char *dir);
+int gaim_build_dir(const char *path, int mode);
+gboolean gaim_program_is_valid(const char *program);
+
+const char *gaim_normalize(const GaimAccount *account, const char *str);
+const char *gaim_normalize_nocase(const GaimAccount *account, const char *str);
--- /dev/null
+/* interface.h
+ * Gaim Citadel plugin.
+ *
+ * © 2006 David Given.
+ * This code is licensed under the GPL v2. See the file COPYING in this
+ * directory for the full license text.
+ *
+ * $Id: auth.c 4258 2006-01-29 13:34:44 +0000 (Sun, 29 Jan 2006) dothebart $
+ */
+
+#ifndef INTERFACE_H
+#define INTERFACE_H
+
+extern char* interface_readdata(int fd, GaimSslConnection* gsc);
+extern int interface_writedata(int fd, GaimSslConnection* gsc, char* data);
+extern int interface_connect(GaimAccount* ga, GaimConnection* gc, char* server, int port);
+extern void interface_disconnect(int fd, GaimSslConnection* gsc);
+extern void interface_tlson(GaimConnection* gc, GaimAccount* ga, int fd);
+extern int interface_timeron(GaimConnection* gc, time_t timeout);
+extern void interface_timeroff(GaimConnection* gc, int timerhandle);
+
+extern const char* gaim_group_get_name(GaimGroup* group);
+
+#endif
--- /dev/null
+-- citadel.c
+-- Gaim Citadel plugin.
+-- © 2006 David Given.
+-- This code is licensed under the GPL v2. See the file COPYING in this
+-- directory for the full license text.
+--
+-- $Id: auth.c 4258 2006-01-29 13:34:44 +0000 (Sun, 29 Jan 2006) dothebart $
+
+include "c.pm"
+
+GAIMINCLUDES = "-I/usr/include/gaim"
+GLIBINCLUDES = "-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include"
+LUAINCLUDES = "-I/usr/include/lua50"
+LUALIBRARIES = "-llualib50 -llua50"
+
+HOME = os.getenv("HOME")
+
+csharedlibrary = simple {
+ class = "csharedlibrary",
+ CBUILDFLAGS = "-g -O0 -Wall -fPIC -shared",
+
+ command = {
+ "%CPROGRAM%"
+ },
+ outputs = {"%U%-%I%.so"},
+}
+
+default = csharedlibrary {
+ CEXTRAFLAGS="-DLUA_USE_POSIX",
+ CINCLUDES="-I. %GAIMINCLUDES% %GLIBINCLUDES% %LUAINCLUDES%",
+ CLIBRARIES="%LUALIBRARIES% -ltolua",
+
+ cfile "citadel.c",
+
+ cfile {
+ simple {
+ outputs = {"%U%-%I%.c"},
+ command = {
+ "tolua -n gaim -o %out% %in%"
+ },
+
+ file "gaim.pkg"
+ }
+ },
+
+ install = {
+ pm.install("%HOME%/.gaim/plugins/citadel.so"),
+ pm.install("citadel.lua", "%HOME%/.gaim/plugindata/citadel.lua")
+ }
+}