]> code.citadel.org Git - citadel.git/commitdiff
Initial revision
authorArt Cancro <ajc@citadel.org>
Sat, 1 Jan 2005 17:32:35 +0000 (17:32 +0000)
committerArt Cancro <ajc@citadel.org>
Sat, 1 Jan 2005 17:32:35 +0000 (17:32 +0000)
27 files changed:
rss2ctdl/COPYING.txt [new file with mode: 0644]
rss2ctdl/Makefile [new file with mode: 0644]
rss2ctdl/README.txt [new file with mode: 0644]
rss2ctdl/config.h [new file with mode: 0644]
rss2ctdl/conversions.c [new file with mode: 0644]
rss2ctdl/conversions.h [new file with mode: 0644]
rss2ctdl/digcalc.c [new file with mode: 0644]
rss2ctdl/digcalc.h [new file with mode: 0644]
rss2ctdl/do_feeds.sh [new file with mode: 0755]
rss2ctdl/io-internal.c [new file with mode: 0644]
rss2ctdl/io-internal.h [new file with mode: 0644]
rss2ctdl/main.c [new file with mode: 0644]
rss2ctdl/main.h [new file with mode: 0644]
rss2ctdl/md5.c [new file with mode: 0644]
rss2ctdl/md5.h [new file with mode: 0644]
rss2ctdl/net-support.c [new file with mode: 0644]
rss2ctdl/net-support.h [new file with mode: 0644]
rss2ctdl/netio.c [new file with mode: 0644]
rss2ctdl/netio.h [new file with mode: 0644]
rss2ctdl/os-support.c [new file with mode: 0644]
rss2ctdl/os-support.h [new file with mode: 0644]
rss2ctdl/rss2ctdl [new file with mode: 0755]
rss2ctdl/setup.h [new file with mode: 0644]
rss2ctdl/xmlparse.c [new file with mode: 0644]
rss2ctdl/xmlparse.h [new file with mode: 0644]
rss2ctdl/zlib_interface.c [new file with mode: 0644]
rss2ctdl/zlib_interface.h [new file with mode: 0644]

diff --git a/rss2ctdl/COPYING.txt b/rss2ctdl/COPYING.txt
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, 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
+\f
+       Appendix: 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., 675 Mass Ave, Cambridge, MA 02139, 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.
diff --git a/rss2ctdl/Makefile b/rss2ctdl/Makefile
new file mode 100644 (file)
index 0000000..0265213
--- /dev/null
@@ -0,0 +1,12 @@
+CC=gcc
+OBJFILES=zlib_interface.o os-support.o io-internal.o md5.o digcalc.o \
+       net-support.o conversions.o xmlparse.o netio.o \
+       main.o
+CFLAGS=`xml2-config --cflags`
+LDFLAGS=`xml2-config --libs`
+
+rss2ctdl: $(OBJFILES)
+       $(CC) $(CFLAGS) $(OBJFILES) $(LDFLAGS) -o rss2ctdl
+
+clean: 
+       rm -f *.o rss2ctdl
diff --git a/rss2ctdl/README.txt b/rss2ctdl/README.txt
new file mode 100644 (file)
index 0000000..3e3faa6
--- /dev/null
@@ -0,0 +1,39 @@
+                 RSS2CTDL -- an RSS to Citadel gateway
+
+Main program (c)2004 by Art Cancro <http://uncensored.citadel.org>
+RSS parser (c)2003-2004 by Oliver Feiler <kiza@kcore.de / http://kiza.kcore.de>
+                        and Rene Puls <rpuls@gmx.net>
+RSS2CTDL is an RSS-to-Citadel gateway.  It allows you to pull external RSS
+feeds into Citadel rooms.  Feed URL's are polled whenever you run the program.
+Each item is converted to a Citadel message and submitted into the network
+queue.  The message-ID is derived from a unique hash of the GUID tag of
+each item.  If there is no GUID tag (which, unfortunately, is the case for
+the vast majority of feeds) then we hash the title/description/link tags
+instead.  We then dump it all into the queue and let the loopzapper handle
+the dupes.
+
+We are distributing RSS2CTDL as a standalone utility, only as a temporary
+measure.  Eventually it will be bundled with the Citadel server, and it will
+be invoked by the main server process.  At that time, this standalone
+distribution will be discontinued.
+
+RSS2CTDL requires the "libxml2" library, which is probably already installed
+on your host system.  If not, get it from http://www.xmlsoft.org
+
+Here's how to make it work:
+
+1. Run "make" to build the program.
+   (There is no "configure" and there is no "make install" either.  The
+   makefile will produce an "rss2ctdl" binary, which is all you need.)
+2. Edit the "do_feeds.sh" script to your liking.  Tell it the feeds you
+   want to receive, and the rooms you want to deposit them into.
+3. Create those rooms if they do not already exist.
+4. Configure your crontab to run do_feeds.sh every half hour.  (You can go
+   more or less often if you wish, but once every half hour seems to be the
+   frequency generally agreed upon in the community to be optimal.)
+
+Do be aware that rss2ctdl must have write access to $CTDL/network/spoolin
+in order to submit its messages into the Citadel spool.  In practice, this
+generally means that it should be run by the crontab of the user under which
+the Citadel service is running ... or by root if you wish.
diff --git a/rss2ctdl/config.h b/rss2ctdl/config.h
new file mode 100644 (file)
index 0000000..807edc4
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003 Oliver Feiler <kiza@kcore.de>
+ *
+ * config.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include "netio.h"
+
+/* Set your charset here. ISO-8859-1 is default. */
+#ifndef TARGET_CHARSET
+#define TARGET_CHARSET "ISO-8859-1"
+#endif
+
+struct feed {
+       char *feedurl;                                                  /* Non hashified URL */
+       char *feed;                                                     /* Raw XML */
+       int content_length;
+       char *title;
+       char *link;
+       char *description;
+       char *lastmodified;                                             /* Content of header as sent by the server. */
+       int lasthttpstatus;
+       char *content_type;
+       netio_error_type netio_error;                                   /* See netio.h */
+       int connectresult;                                              /* Socket errno */
+       char *cookies;                                                  /* Login cookies for this feed. */
+       char *authinfo;                                                 /* HTTP authinfo string. */
+       char *servauth;                                                 /* Server supplied authorization header. */
+       struct newsitem *items;
+       int problem;                                                    /* Set if there was a problem 
+                                                                        * downloading the feed. */
+       char *original;                                                 /* Original feed title. */
+};
+
+struct newsitem {
+       struct newsdata *data;
+       struct newsitem *next_ptr, *prev_ptr;   /* Pointer to next/prev item in double linked list */
+};
+
+struct newsdata {
+       struct feed *parent;
+       int readstatus;                                                 /* 0: unread, 1: read */
+       char *title;
+       char *link;
+       char *guid;                                                     /* Not always present */
+       char *description;
+};
+
+extern struct feed *first_ptr;
+
+#ifdef LOCALEPATH
+#      include <libintl.h>
+#      include <locale.h>
+#endif
+
+#ifdef LOCALEPATH
+#      define _(String) gettext (String)
+#else
+#      define _(String) (String)s
+#      define ngettext(Singular, Plural, n) (Plural)
+#endif
+
+#endif
diff --git a/rss2ctdl/conversions.c b/rss2ctdl/conversions.c
new file mode 100644 (file)
index 0000000..62e01c6
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de> and
+ *                     Rene Puls <rpuls@gmx.net>
+ *
+ * conversions.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <zlib.h>
+#include <libxml/HTMLparser.h>
+#include <langinfo.h>
+
+#include "setup.h"
+#include "conversions.h"
+#include "config.h"
+
+extern struct entity *first_entity;
+
+
+char *base64encode(char const *inbuf, unsigned int inbuf_size) {
+       static unsigned char const alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+       char *outbuf = NULL;
+       unsigned int inbuf_pos = 0;
+       unsigned int outbuf_pos = 0;
+       unsigned int outbuf_size = 0;
+       int bits = 0;
+       int char_count = 0;
+       
+       outbuf = malloc(1);
+       
+       while (inbuf_pos < inbuf_size) {
+       
+               bits |= *inbuf;
+               char_count++;
+               
+               if (char_count == 3) {
+                       outbuf = realloc(outbuf, outbuf_size+4);
+                       outbuf_size += 4;
+                       outbuf[outbuf_pos+0] = alphabet[bits >> 18];
+                       outbuf[outbuf_pos+1] = alphabet[(bits >> 12) & 0x3f];
+                       outbuf[outbuf_pos+2] = alphabet[(bits >> 6) & 0x3f];
+                       outbuf[outbuf_pos+3] = alphabet[bits & 0x3f];
+                       outbuf_pos += 4;
+                       bits = 0;
+                       char_count = 0;
+               }
+               
+               inbuf++;
+               inbuf_pos++;
+               bits <<= 8;
+       }
+       
+       if (char_count > 0) {
+               bits <<= 16 - (8 * char_count);
+               outbuf = realloc(outbuf, outbuf_size+4);
+               outbuf_size += 4;
+               outbuf[outbuf_pos+0] = alphabet[bits >> 18];
+               outbuf[outbuf_pos+1] = alphabet[(bits >> 12) & 0x3f];
+               if (char_count == 1) {
+                       outbuf[outbuf_pos+2] = '=';
+                       outbuf[outbuf_pos+3] = '=';
+               } else {
+                       outbuf[outbuf_pos+2] = alphabet[(bits >> 6) & 0x3f];
+                       outbuf[outbuf_pos+3] = '=';
+               }
+               outbuf_pos += 4;
+       }
+       
+       outbuf = realloc(outbuf, outbuf_size+1);
+       outbuf[outbuf_pos] = 0;
+       
+       return outbuf;
+}
+
+/* Returns NULL on invalid input */
+char* decodechunked(char * chunked, unsigned int *inputlen) {
+       char *orig = chunked, *dest = chunked;
+       unsigned long chunklen;
+
+       /* We can reuse the same buffer to dechunkify it:
+        * the data size will never increase. */
+       while((chunklen = strtoul(orig, &orig, 16))) {
+               /* process one more chunk: */
+               /* skip chunk-extension part */
+               while(*orig && (*orig != '\r'))
+                       orig++;
+               /* skip '\r\n' after chunk length */
+               orig += 2;
+               if(( chunklen > (chunked + *inputlen - orig)))
+                       /* insane chunk length. Well... */
+                       return NULL;
+               memmove(dest, orig, chunklen);
+               dest += chunklen;
+               orig += chunklen;
+               /* and go to the next chunk */
+       }
+       *dest = '\0';
+       *inputlen = dest - chunked;
+       
+       return chunked;
+}
+
+/* Remove leading whitspaces, newlines, tabs.
+ * This function should be safe for working on UTF-8 strings.
+ * tidyness: 0 = only suck chars from beginning of string
+ *           1 = extreme, vacuum everything along the string.
+ */
+void CleanupString (char * string, int tidyness) {
+       int len, i;
+       
+       /* If we are passed a NULL pointer, leave it alone and return. */
+       if (string == NULL)
+               return;
+       
+       len = strlen(string);
+       
+       while ((string[0] == '\n' || string [0] == ' ' || string [0] == '\t') &&
+                       (len > 0)) {
+               /* len=strlen(string) does not include \0 of string.
+                  But since we copy from *string+1 \0 gets included.
+                  Delicate code. Think twice before it ends in buffer overflows. */
+               memmove (string, string+1, len);
+               len--;
+       }
+       
+       len = strlen(string);
+       /* Eat newlines and tabs along the whole string. */
+       if (tidyness == 1) {
+               for (i = 0; i < len; i++) {
+                       if ((string[i] == '\t') || (string[i] == '\n'))
+                               string[i] = ' ';
+               }
+       }
+}
diff --git a/rss2ctdl/conversions.h b/rss2ctdl/conversions.h
new file mode 100644 (file)
index 0000000..a281625
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de> and
+ *                     Rene Puls <rpuls@gmx.net>
+ *
+ * conversions.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#ifndef CONVERSIONS_H
+#define CONVERSIONS_H
+
+char *base64encode(char const *inbuf, unsigned int inbuf_size);
+char* decodechunked(char * chunked, unsigned int *inputlen);
+void CleanupString (char * string, int tidyness);
+
+#endif
diff --git a/rss2ctdl/digcalc.c b/rss2ctdl/digcalc.c
new file mode 100644 (file)
index 0000000..4114c06
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * digcalc.c
+ *
+ * This is the sample implementation from RFC 2617.
+ * The code has been modified to work with Colin Plumb's
+ * MD5 implementation rather than using RSA's.
+ */
+
+#include "md5.h"
+
+#include <string.h>
+#include "digcalc.h"
+
+void CvtHex(
+    IN HASH Bin,
+    OUT HASHHEX Hex
+    )
+{
+    unsigned short i;
+    unsigned char j;
+
+    for (i = 0; i < HASHLEN; i++) {
+        j = (Bin[i] >> 4) & 0xf;
+        if (j <= 9)
+            Hex[i*2] = (j + '0');
+         else
+            Hex[i*2] = (j + 'a' - 10);
+        j = Bin[i] & 0xf;
+        if (j <= 9)
+            Hex[i*2+1] = (j + '0');
+         else
+            Hex[i*2+1] = (j + 'a' - 10);
+    };
+    Hex[HASHHEXLEN] = '\0';
+};
+
+/* calculate H(A1) as per spec */
+void DigestCalcHA1(
+    IN char * pszAlg,
+    IN char * pszUserName,
+    IN char * pszRealm,
+    IN char * pszPassword,
+    IN char * pszNonce,
+    IN char * pszCNonce,
+    OUT HASHHEX SessionKey
+    )
+{
+      struct MD5Context Md5Ctx;
+      HASH HA1;
+
+      MD5Init(&Md5Ctx);
+      MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName));
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm));
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword));
+      MD5Final(HA1, &Md5Ctx);
+      if (strcmp(pszAlg, "md5-sess") == 0) {
+
+            MD5Init(&Md5Ctx);
+            MD5Update(&Md5Ctx, HA1, HASHLEN);
+            MD5Update(&Md5Ctx, ":", 1);
+            MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+            MD5Update(&Md5Ctx, ":", 1);
+            MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+            MD5Final(HA1, &Md5Ctx);
+      };
+      CvtHex(HA1, SessionKey);
+};
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void DigestCalcResponse(
+    IN HASHHEX HA1,           /* H(A1) */
+    IN char * pszNonce,       /* nonce from server */
+    IN char * pszNonceCount,  /* 8 hex digits */
+    IN char * pszCNonce,      /* client nonce */
+    IN char * pszQop,         /* qop-value: "", "auth", "auth-int" */
+    IN char * pszMethod,      /* method from the request */
+    IN char * pszDigestUri,   /* requested URL */
+    IN HASHHEX HEntity,       /* H(entity body) if qop="auth-int" */
+    OUT HASHHEX Response      /* request-digest or response-digest */
+    )
+{
+      struct MD5Context Md5Ctx;
+      HASH HA2;
+      HASH RespHash;
+       HASHHEX HA2Hex;
+
+      /* calculate H(A2) */
+      MD5Init(&Md5Ctx);
+      MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod));
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
+      if (strcmp(pszQop, "auth-int") == 0) {
+            MD5Update(&Md5Ctx, ":", 1);
+            MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
+      };
+      MD5Final(HA2, &Md5Ctx);
+       CvtHex(HA2, HA2Hex);
+
+      /* calculate response */
+      MD5Init(&Md5Ctx);
+      MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+      MD5Update(&Md5Ctx, ":", 1);
+      if (*pszQop) {
+
+          MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
+          MD5Update(&Md5Ctx, ":", 1);
+          MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+          MD5Update(&Md5Ctx, ":", 1);
+          MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
+          MD5Update(&Md5Ctx, ":", 1);
+      };
+      MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
+      MD5Final(RespHash, &Md5Ctx);
+      CvtHex(RespHash, Response);
+};
diff --git a/rss2ctdl/digcalc.h b/rss2ctdl/digcalc.h
new file mode 100644 (file)
index 0000000..2251639
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * digcalc.h
+ * 
+ * This is the sample implementation from RFC 2617.
+ * The code has been modified to work with Colin Plumb's
+ * MD5 implementation rather than using RSA's.
+ */
+
+#define HASHLEN 16
+typedef char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN+1];
+#define IN
+#define OUT
+
+void CvtHex(
+    IN HASH Bin,
+    OUT HASHHEX Hex
+    );
+
+/* calculate H(A1) as per HTTP Digest spec */
+void DigestCalcHA1(
+    IN char * pszAlg,
+    IN char * pszUserName,
+    IN char * pszRealm,
+    IN char * pszPassword,
+    IN char * pszNonce,
+    IN char * pszCNonce,
+    OUT HASHHEX SessionKey
+    );
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void DigestCalcResponse(
+    IN HASHHEX HA1,           /* H(A1) */
+    IN char * pszNonce,       /* nonce from server */
+    IN char * pszNonceCount,  /* 8 hex digits */
+    IN char * pszCNonce,      /* client nonce */
+    IN char * pszQop,         /* qop-value: "", "auth", "auth-int" */
+    IN char * pszMethod,      /* method from the request */
+    IN char * pszDigestUri,   /* requested URL */
+    IN HASHHEX HEntity,       /* H(entity body) if qop="auth-int" */
+    OUT HASHHEX Response      /* request-digest or response-digest */
+    );
diff --git a/rss2ctdl/do_feeds.sh b/rss2ctdl/do_feeds.sh
new file mode 100755 (executable)
index 0000000..5c2e359
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Temporary RSS feed suck-o-matic script for Uncensored.
+#
+# This script is UNSUPPORTED.  It is part of a technology preview for
+# functionality which will eventually ship as part of the Citadel system. 
+
+# Paths to the RSS2CTDL binary and to the Citadel directory
+PROG=/usr/local/rss2ctdl/rss2ctdl
+CTDL=/appl/citadel
+
+# Do one of these for each feed.  You need the URL of the feed, the name
+# of the room to dump it into, and a domain name to stamp onto messages
+# and message ID's.
+#
+$PROG http://lxer.com/module/newswire/headlines.rss LXer lxer.com $CTDL
+$PROG http://slashdot.org/index.rss Slashdot slashdot.org $CTDL
+$PROG http://www.groklaw.net/backend/GrokLaw.rdf Groklaw groklaw.net $CTDL
+$PROG http://www.ioerror.us/feed/rss2/ Lizard ioerror.us $CTDL
diff --git a/rss2ctdl/io-internal.c b/rss2ctdl/io-internal.c
new file mode 100644 (file)
index 0000000..7d9cb21
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * io-internal.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <signal.h>
+
+#include "config.h"
+
+#include "main.h"
+#include "conversions.h"
+#include "netio.h"
+#include "xmlparse.h"
+#include "io-internal.h"
+
+extern char *browser;
+
+void GetHTTPErrorString (char * errorstring, int size, int httpstatus) {
+       switch (httpstatus) {
+               case 400:
+                       snprintf(errorstring, size, "Bad request");
+                       break;
+               case 402:
+                       snprintf(errorstring, size, "Payment required");
+                       break;
+               case 403:
+                       snprintf(errorstring, size, "Access denied");
+                       break;
+               case 500:
+                       snprintf(errorstring, size, "Internal server error");
+                       break;
+               case 501:
+                       snprintf(errorstring, size, "Not implemented");
+                       break;
+               case 502:
+               case 503:
+                       snprintf(errorstring, size, "Service unavailable");
+                       break;
+               default:
+                       sprintf(errorstring, "HTTP %d!", httpstatus);
+       }
+}
+
+void PrintUpdateError (int suppressoutput, struct feed * cur_ptr) {
+       netio_error_type err;
+       char errstr[256];
+       char httperrstr[64];
+
+       err = cur_ptr->netio_error;
+       
+       if (!suppressoutput) {
+               switch (err) {
+                       case NET_ERR_OK:
+                               break;
+                       case NET_ERR_URL_INVALID:
+                               fprintf(stderr, "%s: Invalid URL!\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_SOCK_ERR:
+                               fprintf(stderr, "%s: Couldn't create network socket!\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_HOST_NOT_FOUND:
+                               fprintf(stderr, "%s: Can't resolve host!\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_CONN_REFUSED:
+                               fprintf(stderr, "%s: Connection refused!\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_CONN_FAILED:
+                               fprintf(stderr, "%s: Couldn't connect to server: %s\n",
+                                       cur_ptr->title,
+                                       (strerror(cur_ptr->connectresult) ? strerror(cur_ptr->connectresult) : "(null)"));
+                               break;
+                       case NET_ERR_TIMEOUT:
+                               fprintf(stderr, "%s: Connection timed out.\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_UNKNOWN:
+                               break;
+                       case NET_ERR_REDIRECT_COUNT_ERR:
+                               fprintf(stderr, "%s: Too many HTTP redirects encountered! Giving up.\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_REDIRECT_ERR:
+                               fprintf(stderr, "%s: Server sent an invalid redirect!\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_HTTP_410:
+                       case NET_ERR_HTTP_404:
+                               fprintf(stderr, "%s: This feed no longer exists. Please unsubscribe!\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_HTTP_NON_200:
+                               GetHTTPErrorString(httperrstr, sizeof(httperrstr), cur_ptr->lasthttpstatus);
+                               fprintf(stderr, "%s: Could not download feed: %s\n", cur_ptr->title, httperrstr);
+                               break;
+                       case NET_ERR_HTTP_PROTO_ERR:
+                               fprintf(stderr, "%s: Error in server reply.\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_AUTH_FAILED:
+                               fprintf(stderr, "%s: Authentication failed!\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_AUTH_NO_AUTHINFO:
+                               fprintf(stderr, "%s: URL does not contain authentication information!\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_AUTH_GEN_AUTH_ERR:
+                               fprintf(stderr, "%s: Could not generate authentication information!\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_AUTH_UNSUPPORTED:
+                               fprintf(stderr, "%s: Unsupported authentication method requested by server!\n", cur_ptr->title);
+                               break;
+                       case NET_ERR_GZIP_ERR:
+                               fprintf(stderr, "%s: Error decompressing server reply!\n", cur_ptr->title);
+                               break;
+                       default:
+                               break;
+               }
+               /* Must be inside if(!suppressoutput) statement! */
+       }
+}
+
+
+/* Update given feed from server.
+ * Reload XML document and replace in memory cur_ptr->feed with it.
+ */
+int UpdateFeed (struct feed * cur_ptr) {
+       char *tmpname;
+       char *freeme;
+
+       if (cur_ptr == NULL) {
+               return 1;
+       }
+       
+       /* Need to work on a copy of ->feedurl, because DownloadFeed() changes the pointer. */
+       tmpname = strdup (cur_ptr->feedurl);
+       freeme = tmpname;       /* Need to make a copy, otherwise we cannot free all RAM. */
+       free (cur_ptr->feed);
+
+       cur_ptr->feed = DownloadFeed (tmpname, cur_ptr, 0);
+       free (freeme);
+
+       /* Set title and link structure to something.
+        * To the feedurl in this case so the program show something
+        * as placeholder instead of crash. */
+       if (cur_ptr->title == NULL)
+               cur_ptr->title = strdup (cur_ptr->feedurl);
+       if (cur_ptr->link == NULL)
+               cur_ptr->link = strdup (cur_ptr->feedurl);
+
+       /* If the download function returns a NULL pointer return from here. */
+       if (cur_ptr->feed == NULL) {
+       if (cur_ptr->problem == 1)
+                       PrintUpdateError (0, cur_ptr);
+               return 1;
+       }
+       
+       /* If there is no feed, return. */
+       if (cur_ptr->feed == NULL)
+               return 1;
+       
+       if ((DeXML (cur_ptr)) != 0) {
+               fprintf(stderr, "Invalid XML! Cannot parse this feed!\n");
+
+               /* Activate feed problem flag. */
+               cur_ptr->problem = 1;
+               return 1;
+       }
+       
+       /* We don't need these anymore. Free the raw XML to save some memory. */
+       free (cur_ptr->feed);
+       cur_ptr->feed = NULL;
+               
+       return 0;
+}
+
+
diff --git a/rss2ctdl/io-internal.h b/rss2ctdl/io-internal.h
new file mode 100644 (file)
index 0000000..fd7f2c7
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * io-internal.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+#ifndef IO_INTERNAL_H
+#define IO_INTERNAL_H
+
+int UpdateFeed (struct feed * cur_ptr);
+
+#endif
diff --git a/rss2ctdl/main.c b/rss2ctdl/main.c
new file mode 100644 (file)
index 0000000..c4e8190
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * $Id$
+ *
+ * rss2ctdl -- a utility to pull RSS feeds into Citadel rooms.
+ * 
+ * Main program is (c)2004 by Art Cancro
+ * RSS parser is (c)2003-2004 by Oliver Feiler
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include "config.h"
+#include "main.h"
+#include "io-internal.h"
+#include "conversions.h"
+#include "md5.h"
+#include "digcalc.h"
+
+struct feed *first_ptr = NULL;
+struct entity *first_entity = NULL;
+
+/*
+ * If you want to use a proxy server, you can hack the following two lines.
+ */
+char *proxyname = "";
+unsigned short proxyport = 0;
+
+/*
+ * Main function of program.
+ */
+int main (int argc, char *argv[]) {
+       struct feed *new_ptr;
+       char *url;
+       char tmp[512];
+       struct newsitem *itemptr;
+       FILE *fp;
+       char md5msgid[256];
+       MD5_CTX md5context;
+       HASHHEX md5context_hex;
+       
+#ifdef LOCALEPATH
+       setlocale (LC_ALL, "");
+       bindtextdomain ("rss2ctdl", LOCALEPATH);
+       textdomain ("rss2ctdl");
+#endif
+
+       if (argc != 5) {
+               fprintf(stderr,
+                       "%s: usage:\n %s <feedurl> <roomname> <nodefqdn> <ctdldir>\n",
+                       argv[0], argv[0]);
+               exit(1);
+       }
+
+       /* Init the pRNG. See about.c for usages of rand() ;) */
+       srand(time(0));
+
+       url = strdup(argv[1]);
+       CleanupString(url, 0);
+
+       /* Support that stupid feed:// "protocol" */
+       if (strncasecmp (url, "feed://", 7) == 0)
+               memcpy (url, "http", 4);
+       
+       /* If URL does not start with the procotol specification,
+       assume http://
+       -> tmp[512] -> we can "only" use max 504 chars from url ("http://" == 7). */
+       if ((strncasecmp (url, "http://", 7) != 0) &&
+               (strncasecmp (url, "https://", 8) != 0)) {
+               if (strlen (url) < 504) {
+                       strcpy (tmp, "http://");
+                       strncat (tmp, url, 504);
+                       free (url);
+                       url = strdup (tmp);
+               } else {
+                       free (url);
+                       return 2;
+               }
+       }
+
+       new_ptr = malloc (sizeof(struct feed));
+       new_ptr->feedurl = strdup(url);
+       new_ptr->feed = NULL;
+       new_ptr->content_length = 0;
+       new_ptr->title = NULL;
+       new_ptr->link = NULL;
+       new_ptr->description = NULL;
+       new_ptr->lastmodified = NULL;
+       new_ptr->lasthttpstatus = 0;
+       new_ptr->content_type = NULL;
+       new_ptr->netio_error = NET_ERR_OK;
+       new_ptr->connectresult = 0;
+       new_ptr->cookies = NULL;
+       new_ptr->authinfo = NULL;
+       new_ptr->servauth = NULL;
+       new_ptr->items = NULL;
+       new_ptr->problem = 0;
+       new_ptr->original = NULL;
+       
+       /* Don't need url text anymore. */
+       free (url);
+
+       /* Download new feed and DeXMLize it. */        
+       if ((UpdateFeed (new_ptr)) != 0) {
+               exit(1);
+       }
+
+       sprintf(tmp, "%s/network/spoolin/rssfeed.%ld", argv[4], time(NULL));
+       fp = fopen(tmp, "w");
+       if (fp == NULL) {
+               fprintf(stderr, "%s: cannot open %s: %s\n",
+                       argv[0], tmp, strerror(errno));
+               exit(errno);
+       }
+
+       for (itemptr = new_ptr->items; itemptr != NULL; itemptr = itemptr->next_ptr) {
+               fprintf(stderr, "--> %s\n", itemptr->data->title);
+               fprintf(fp, "%c", 255);                 /* Start of message */
+               fprintf(fp, "A");                       /* Non-anonymous */
+               fprintf(fp, "%c", 4);                   /* MIME */
+               fprintf(fp, "Prss%c", 0);               /* path */
+
+               /* The message ID will be an MD5 hash of the GUID.
+                * If there is no GUID present, we construct a message ID based
+                * on an MD5 hash of each item.  Citadel's loopzapper will automatically
+                * reject items with message ID's which have already been submitted.
+                */
+               MD5Init(&md5context);
+               if (itemptr->data->guid != NULL) {
+                       MD5Update(&md5context, itemptr->data->guid, strlen(itemptr->data->guid));
+               }
+               else {
+                       if (itemptr->data->title != NULL) {
+                               MD5Update(&md5context, itemptr->data->title, strlen(itemptr->data->title));
+                       }
+                       if (itemptr->data->description != NULL) {
+                               MD5Update(&md5context, itemptr->data->description, strlen(itemptr->data->description));
+                       }
+                       if (itemptr->data->link != NULL) {
+                               MD5Update(&md5context, itemptr->data->link, strlen(itemptr->data->link));
+                       }
+               }
+               MD5Final(md5msgid, &md5context);
+               CvtHex(md5msgid, md5context_hex);
+
+               fprintf(fp, "I%s@%s%c", md5context_hex, argv[3], 0);    /* ID */ 
+
+               fprintf(fp, "T%ld%c",  time(NULL),  0); /* time */
+               fprintf(fp, "Arss%c", 0);               /* author */
+               fprintf(fp, "O%s%c", argv[2], 0);       /* room */
+               fprintf(fp, "C%s%c", argv[2], 0);       /* room */
+               fprintf(fp, "N%s%c", argv[3], 0);       /* orig node */
+               if (itemptr->data->title != NULL) {
+                       fprintf(fp, "U%s%c", itemptr->data->title, 0);  /* subject */
+               }
+
+               fprintf(fp, "M");                       /* msg text */
+               fprintf(fp, "Content-type: text/html\r\n\r\n");
+               fprintf(fp, "<HTML><BODY>\r\n");
+               fprintf(fp, "%s\n", itemptr->data->description);
+               if (itemptr->data->link != NULL) {
+                       fprintf(fp, "<BR><BR>\r\n");
+                       fprintf(fp, "<A HREF=\"%s\">%s</A>\n",
+                               itemptr->data->link,
+                               itemptr->data->link);
+               }
+               fprintf(fp, "</BODY></HTML>\r\n");
+               fprintf(fp, "%c", 0);
+       }
+
+       fclose(fp);
+
+       /* Be lazy and let the operating system free all the memory. */
+       return(0);
+}
diff --git a/rss2ctdl/main.h b/rss2ctdl/main.h
new file mode 100644 (file)
index 0000000..a5440f4
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * main.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include "config.h"
+
+#endif
diff --git a/rss2ctdl/md5.c b/rss2ctdl/md5.c
new file mode 100644 (file)
index 0000000..6768812
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * md5.c
+ *
+ * This code has been slightly modified from its original.
+ * The endian check via evaluating endian.h has been
+ * replaced with the code in void byteReverse().
+ */
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include <string.h>            /* for memcpy() */
+#include "md5.h"
+
+void byteReverse(unsigned char *buf, unsigned longs);
+
+static int endian_check = -1;
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32 t;
+       static uint32 d = 0xdeadbeef;
+       unsigned char *b = (unsigned char *) &d;
+    
+    if (endian_check == -1) {
+               if (b[0] == 0xde)
+                   endian_check = 1;
+               else
+                   endian_check = 0;
+    }
+    
+    if (endian_check == 0)
+       return;
+    
+    do {
+       t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+           ((unsigned) buf[1] << 8 | buf[0]);
+       *(uint32 *) buf = t;
+       buf += 4;
+    } while (--longs);
+}
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    uint32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = (unsigned char *) ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           memcpy(p, buf, len);
+           return;
+       }
+       memcpy(p, buf, t);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+       buf += t;
+       len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       memcpy(ctx->in, buf, 64);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       memset(p, 0, count);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+
+       /* Now fill the next block with 56 bytes */
+       memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((uint32 *) ctx->in)[14] = ctx->bits[0];
+    ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (uint32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));       /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32 buf[4], uint32 const in[16])
+{
+    register uint32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
diff --git a/rss2ctdl/md5.h b/rss2ctdl/md5.h
new file mode 100644 (file)
index 0000000..5003e6d
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * md5.h
+ *
+ * This code has been slightly modified from its original.
+ * The endian check via evaluating endian.h has been
+ * replaced with the code in void byteReverse().
+ */
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __alpha
+typedef unsigned int uint32;
+#else
+typedef unsigned long uint32;
+#endif
+
+struct MD5Context {
+       uint32 buf[4];
+       uint32 bits[2];
+       unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+              unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(uint32 buf[4], uint32 const in[16]);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct MD5Context MD5_CTX;
+
+#endif /* !MD5_H */
diff --git a/rss2ctdl/net-support.c b/rss2ctdl/net-support.c
new file mode 100644 (file)
index 0000000..907f250
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * net-support.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "config.h"
+#include "conversions.h"
+
+#include "digcalc.h"
+
+char * ConstructBasicAuth (char * username, char * password) {
+       int len;
+       char * authinfo;
+       char * authstring;
+       char * tmpstr;
+
+       /* Create base64 authinfo.
+
+       RFC 2617. Basic HTTP authentication.
+       Authorization: Basic username:password[base64 encoded] */
+
+       /* Construct the cleartext authstring. */
+       len = strlen(username) + 1 + strlen(password) + 1;
+       authstring = malloc (len);
+       snprintf (authstring, len, "%s:%s", username, password);
+
+       tmpstr = base64encode (authstring, len-1);
+
+       /* "Authorization: Basic " + base64str + \r\n\0 */
+       len = 21 + strlen(tmpstr) + 3;
+       authinfo = malloc (len);
+       snprintf (authinfo, len, "Authorization: Basic %s\r\n", tmpstr);
+       
+       free (tmpstr);
+       free (authstring);
+       
+       return authinfo;
+}
+
+char * GetRandomBytes (void) {
+       char * randomness = NULL;
+       char raw[8];
+       int i;
+       FILE * devrandom;
+       
+       devrandom = fopen ("/dev/random", "r");
+       if (devrandom == NULL) {
+               /* Use rand() if we don't have access to /dev/random. */
+               for (i = 0; i <= 7; i++) {
+                       raw[i] = 1+(float)rand() / (float)RAND_MAX * 255;
+               }
+       } else {
+               fread (raw, 8, 1, devrandom);
+               fclose (devrandom);
+       }
+       
+       randomness = malloc (17);
+       snprintf (randomness, 17, "%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx",
+               raw[0], raw[1], raw[2], raw[3], raw[4], raw[5], raw[6], raw[7]);
+       
+       return randomness;
+}
+
+char * ConstructDigestAuth (char * username, char * password, char * url, char * authdata) {
+       char * authinfo;                        /* Authorization header as sent to the server. */
+       char * token;
+       int len;
+       char * realm = NULL;            /* Variables for the overcomplicated and annoying HTTP digest algo. */
+       char * qop = NULL;
+       char * nonce = NULL;
+       char * opaque = NULL;
+       char * cnonce;
+       char szNonceCount[9] = "00000001";      /* Can be always 1 if we never use the same cnonce twice. */
+       HASHHEX HA1;
+       HASHHEX HA2 = "";
+       HASHHEX Response;
+       
+       cnonce = GetRandomBytes();
+       
+       while (1) {
+               token = strsep (&authdata, ", ");
+               
+               if (token == NULL)
+                       break;
+               
+               if (strncasecmp (token, "realm", 5) == 0) {
+                       len = strlen(token)-8;
+                       memmove (token, token+7, len);
+                       token[len] = '\0';
+                       realm = strdup (token);
+               } else if (strncasecmp (token, "qop", 3) == 0) {
+                       len = strlen(token)-6;
+                       memmove (token, token+5, len);
+                       token[len] = '\0';
+                       qop = strdup (token);
+               } else if (strncasecmp (token, "nonce", 5) == 0) {
+                       len = strlen(token)-8;
+                       memmove (token, token+7, len);
+                       token[len] = '\0';
+                       nonce = strdup (token);
+               } else if (strncasecmp (token, "opaque", 6) == 0) {
+                       len = strlen(token)-9;
+                       memmove (token, token+8, len);
+                       token[len] = '\0';
+                       opaque = strdup (token);
+               }
+       }
+       
+       DigestCalcHA1 ("md5", username, realm, password, nonce, cnonce, HA1);
+       DigestCalcResponse(HA1, nonce, szNonceCount, cnonce, "auth", "GET", url, HA2, Response);
+
+       /* Determine length of Authorize header.
+        *
+        * Authorization: Digest username="(username)", realm="(realm)",
+        * nonce="(nonce)", uri="(url)", algorithm=MD5, response="(Response)",
+        * qop=(auth), nc=(szNonceCount), cnonce="deadbeef"
+        */
+       if (opaque == NULL)
+               len = 32 + strlen(username) + 10 + strlen(realm) + 10 + strlen(nonce) + 8 + strlen(url) + 28 + strlen(Response) + 16 + strlen(szNonceCount) + 10 + strlen(cnonce) + 4 ;
+       else
+               len = 32 + strlen(username) + 10 + strlen(realm) + 10 + strlen(nonce) + 8 + strlen(url) + 28 + strlen(Response) + 16 + strlen(szNonceCount) + 10 + strlen(cnonce) + 10 + strlen(opaque) + 4;
+
+       authinfo = malloc (len);
+       
+       if (opaque == NULL) {
+               snprintf (authinfo, len, "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", algorithm=MD5, response=\"%s\", qop=auth, nc=%s, cnonce=\"%s\"\r\n",
+                       username, realm, nonce, url, Response, szNonceCount, cnonce);
+       } else {
+               snprintf (authinfo, len, "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", algorithm=MD5, response=\"%s\", qop=auth, nc=%s, cnonce=\"%s\", opaque=\"%s\"\r\n",
+                       username, realm, nonce, url, Response, szNonceCount, cnonce, opaque);
+       }
+       
+       free (realm);
+       free (qop);
+       free (nonce);
+       free (cnonce);
+       free (opaque);
+       
+       return authinfo;
+}
+
+
+/*
+Authorization: Digest username="(username)", realm="(realm)",
+nonce="(nonce)", uri="(url)", algorithm=MD5, response="(Response)",
+qop=(auth), nc=(szNonceCount), cnonce="deadbeef"
+*/
+int NetSupportAuth (struct feed * cur_ptr, char * authdata, char * url, char * netbuf) {
+       char * header;
+       char * tmpstr;
+       char * freeme;
+       char * username = NULL;
+       char * password = NULL;
+       char * authtype = NULL;
+       
+       /* Reset cur_ptr->authinfo. */
+       free (cur_ptr->authinfo);
+       cur_ptr->authinfo = NULL;
+       
+       /* Catch invalid authdata. */
+       if (authdata == NULL) {
+               return 1;
+       } else if (strchr (authdata, ':') == NULL){
+               /* No authinfo found in URL. This should not happen. */
+               return 1;
+       }
+       
+       tmpstr = strdup (authdata);
+       freeme = tmpstr;
+       
+       strsep (&tmpstr, ":");
+       username = strdup (freeme);
+       password = strdup (tmpstr);
+       
+       /* Free allocated string in tmpstr. */
+       free (freeme);
+       
+       /* Extract requested auth type from webserver reply. */
+       header = strdup (netbuf);
+       freeme = header;
+       strsep (&header, " ");
+       authtype = header;
+       
+       /* Catch invalid server replies. authtype should contain at least _something_. */
+       if (authtype == NULL) {
+               free (freeme);
+               free (username);
+               free (password);
+               return -1;
+       }
+       
+       strsep (&header, " ");
+       /* header now contains:
+          Basic auth:  realm
+          Digest auth: realm + a lot of other stuff somehow needed by digest auth. */
+       
+       /* Determine auth type the server requests. */
+       if (strncasecmp (authtype, "Basic", 5) == 0) {
+               /* Basic auth. */
+               cur_ptr->authinfo = ConstructBasicAuth (username, password);
+       } else if (strncasecmp (authtype, "Digest", 6) == 0) {
+               /* Digest auth. */
+               cur_ptr->authinfo = ConstructDigestAuth (username, password, url, header);
+       } else {
+               /* Unkown auth type. */
+               free (freeme);
+               free (username);
+               free (password);
+               return -1;
+       }
+       
+       free (username);
+       free (password);
+       free (freeme);
+
+       if (cur_ptr->authinfo == NULL) {
+               return 2;
+       }
+       
+       return 0;
+}
+
+/* HTTP header may only contain ASCII characters.
+ *
+ * Ensure that we don't hit the terminating \0 in a string
+ * with the for loop.
+ * The function also ensures that there is no NULL byte in the string.
+ * If given binary data return at once if we read beyond
+ * the boundary of sizeof(header).
+ */
+int checkValidHTTPHeader (const unsigned char * header, int size) {
+       int i, len;
+       
+       len = strlen(header);
+       if (len > size)
+               return -1;
+       
+       for (i = 0; i < len; i++) {
+               if (((header[i] < 32) || (header[i] > 127)) &&
+                       (header[i] != '\r') && (header[i] != '\n'))
+                       return -1;
+       }
+       return 0;
+}
+
+int checkValidHTTPURL (const unsigned char * url) {
+       int i, len;
+       
+       if (strncasecmp(url, "http://", 7) != 0)
+               return -1;
+       
+       len = strlen(url);
+               
+       for (i = 0; i < len; i++) {
+               if ((url[i] < 32) || (url[i] > 126))
+                       return -1;
+       }
+       
+       return 0;
+}
+
diff --git a/rss2ctdl/net-support.h b/rss2ctdl/net-support.h
new file mode 100644 (file)
index 0000000..a0ba70b
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * net-support.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#ifndef NET_SUPPORT_H
+#define NET_SUPPORT_H
+
+int NetSupportAuth (struct feed * cur_ptr, char * authdata, char * url, char * netbuf);
+int checkValidHTTPHeader (const unsigned char * header, int size);
+int checkValidHTTPURL (const unsigned char * url);
+
+#endif
diff --git a/rss2ctdl/netio.c b/rss2ctdl/netio.c
new file mode 100644 (file)
index 0000000..f9a6370
--- /dev/null
@@ -0,0 +1,903 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * netio.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/* OS X needs this, otherwise socklen_t is not defined. */
+#ifdef __APPLE__
+#       define _BSD_SOCKLEN_T_
+#endif
+
+/* BeOS does not define socklen_t. Using uint as suggested by port creator. */
+#ifdef __BEOS__
+#       define socklen_t unsigned int
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+//#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <assert.h>
+
+#include "config.h"
+#include "main.h"
+#include "conversions.h"
+#include "net-support.h"
+#include "io-internal.h"
+#include "zlib_interface.h"
+
+static int const MAX_HTTP_REDIRECTS = 10;      /* Maximum number of redirects we will follow. */
+static int const NET_TIMEOUT = 20;                     /* Global network timeout in sec */
+static int const NET_READ = 1;
+static int const NET_WRITE = 2;
+
+extern char *proxyname;                                                /* Hostname of proxyserver. */
+extern unsigned short proxyport;                       /* Port on proxyserver to use. */
+
+/* Masquerade as Firefox on Linux to increase the share of both in web server statistics. */
+char *useragent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0";
+
+/* Waits NET_TIMEOUT seconds for the socket to return data.
+ *
+ * Returns
+ *
+ *     0       Socket is ready
+ *     -1      Error occured (netio_error is set)
+ */
+int NetPoll (struct feed * cur_ptr, int * my_socket, int rw) {
+       fd_set rfdsr;
+       fd_set rfdsw;
+       struct timeval tv;
+       int retval;                             /* FD_ISSET + assert == Heisenbug? */
+       
+       /* Set global network timeout */
+       tv.tv_sec = NET_TIMEOUT;
+       tv.tv_usec = 0;
+       
+       FD_ZERO(&rfdsr);
+       FD_ZERO(&rfdsw);
+       
+       if (rw == NET_READ) {
+               FD_SET(*my_socket, &rfdsr);
+               if (select (*my_socket+1, &rfdsr, NULL, NULL, &tv) == 0) {
+                       /* Timed out */
+                       cur_ptr->netio_error = NET_ERR_TIMEOUT;
+                       return -1;
+               }
+               retval = FD_ISSET (*my_socket, &rfdsr);
+               assert (retval);
+               if (!retval) {
+                       /* Wtf? */
+                       cur_ptr->netio_error = NET_ERR_UNKNOWN;
+                       return -1;
+               }
+       } else if (rw == NET_WRITE) {
+               FD_SET(*my_socket, &rfdsw);
+               if (select (*my_socket+1, NULL, &rfdsw, NULL, &tv) == 0) {
+                       /* Timed out */
+                       cur_ptr->netio_error = NET_ERR_TIMEOUT;
+                       return -1;
+               }
+               retval = FD_ISSET (*my_socket, &rfdsw);
+               assert (retval);
+               if (!retval) {
+                       /* Wtf? */
+                       cur_ptr->netio_error = NET_ERR_UNKNOWN;
+                       return -1;
+               }
+       } else {
+               cur_ptr->netio_error = NET_ERR_UNKNOWN;
+               return -1;
+       }
+       
+       return 0;
+}
+
+
+/* Connect network sockets.
+ *
+ * Returns
+ *
+ *     0       Connected
+ *     -1      Error occured (netio_error is set)
+ */
+int NetConnect (int * my_socket, char * host, struct feed * cur_ptr, int httpproto, int suppressoutput) {
+       char tmp[512];
+       struct sockaddr_in address;     
+       struct hostent *remotehost;
+       socklen_t len;
+       char *realhost;
+       unsigned short port;
+       
+       realhost = strdup(host);
+       if (sscanf (host, "%[^:]:%hd", realhost, &port) != 2) {
+               port = 80;
+       }
+       
+       /* Create a inet stream TCP socket. */
+       *my_socket = socket (AF_INET, SOCK_STREAM, 0);
+       if (*my_socket == -1) {
+               cur_ptr->netio_error = NET_ERR_SOCK_ERR;
+               return -1;
+       }
+       
+       /* If proxyport is 0 we didn't execute the if http_proxy statement in main
+          so there is no proxy. On any other value of proxyport do proxyrequests instead. */
+       if (proxyport == 0) {
+               /* Lookup remote IP. */
+               remotehost = gethostbyname (realhost);
+               if (remotehost == NULL) {
+                       close (*my_socket);
+                       free (realhost);
+                       cur_ptr->netio_error = NET_ERR_HOST_NOT_FOUND;
+                       return -1;
+               }
+               
+               /* Set the remote address. */
+               address.sin_family = AF_INET;
+               address.sin_port = htons(port);
+               memcpy (&address.sin_addr.s_addr, remotehost->h_addr_list[0], remotehost->h_length);
+                       
+               /* Connect socket. */
+               cur_ptr->connectresult = connect (*my_socket, (struct sockaddr *) &address, sizeof(address));
+               
+               /* Check if we're already connected.
+                  BSDs will return 0 on connect even in nonblock if connect was fast enough. */
+               if (cur_ptr->connectresult != 0) {
+                       /* If errno is not EINPROGRESS, the connect went wrong. */
+                       if (errno != EINPROGRESS) {
+                               close (*my_socket);
+                               free (realhost);
+                               cur_ptr->netio_error = NET_ERR_CONN_REFUSED;
+                               return -1;
+                       }
+                       
+                       if ((NetPoll (cur_ptr, my_socket, NET_WRITE)) == -1) {
+                               close (*my_socket);
+                               free (realhost);
+                               return -1;
+                       }
+                       
+                       /* We get errno of connect back via getsockopt SO_ERROR (into connectresult). */
+                       len = sizeof(cur_ptr->connectresult);
+                       getsockopt(*my_socket, SOL_SOCKET, SO_ERROR, &cur_ptr->connectresult, &len);
+                       
+                       if (cur_ptr->connectresult != 0) {
+                               close (*my_socket);
+                               free (realhost);
+                               cur_ptr->netio_error = NET_ERR_CONN_FAILED;     /* ->strerror(cur_ptr->connectresult) */
+                               return -1;
+                       }
+               }
+       } else {
+               /* Lookup proxyserver IP. */
+               remotehost = gethostbyname (proxyname);
+               if (remotehost == NULL) {
+                       close (*my_socket);
+                       free (realhost);
+                       cur_ptr->netio_error = NET_ERR_HOST_NOT_FOUND;
+                       return -1;
+               }
+               
+               /* Set the remote address. */
+               address.sin_family = AF_INET;
+               address.sin_port = htons(proxyport);
+               memcpy (&address.sin_addr.s_addr, remotehost->h_addr_list[0], remotehost->h_length);
+               
+               /* Connect socket. */
+               cur_ptr->connectresult = connect (*my_socket, (struct sockaddr *) &address, sizeof(address));
+               
+               /* Check if we're already connected.
+                  BSDs will return 0 on connect even in nonblock if connect was fast enough. */
+               if (cur_ptr->connectresult != 0) {
+                       if (errno != EINPROGRESS) {
+                               close (*my_socket);
+                               free (realhost);
+                               cur_ptr->netio_error = NET_ERR_CONN_REFUSED;
+                               return -1;
+                       }
+               
+                       if ((NetPoll (cur_ptr, my_socket, NET_WRITE)) == -1) {
+                               close (*my_socket);
+                               free (realhost);
+                               return -1;
+                       }
+                       
+                       len = sizeof(cur_ptr->connectresult);
+                       getsockopt(*my_socket, SOL_SOCKET, SO_ERROR, &cur_ptr->connectresult, &len);
+                       
+                       if (cur_ptr->connectresult != 0) {
+                               close (*my_socket);
+                               free (realhost);
+                               cur_ptr->netio_error = NET_ERR_CONN_FAILED;     /* ->strerror(cur_ptr->connectresult) */
+                               return -1;
+                       }
+               }
+       }
+       
+       free (realhost);
+       return 0;
+}
+
+
+/*
+ * Main network function.
+ * (Now with a useful function description *g*)
+ *
+ * This function returns the HTTP request's body (deflating gzip encoded data
+ * if needed).
+ * Updates passed feed struct with values gathered from webserver.
+ * Handles all redirection and HTTP status decoding.
+ * Returns NULL pointer if no data was received and sets netio_error.
+ */
+char * NetIO (int * my_socket, char * host, char * url, struct feed * cur_ptr, char * authdata, int httpproto, int suppressoutput) {
+       char netbuf[4096];                      /* Network read buffer. */
+       char *body;                                     /* XML body. */
+       unsigned int length;
+       FILE *stream;                           /* Stream socket. */
+       int chunked = 0;                        /* Content-Encoding: chunked received? */
+       int redirectcount;                      /* Number of HTTP redirects followed. */
+       char httpstatus[4];                     /* HTTP status sent by server. */
+       char servreply[128];                    /* First line of server reply */
+       char *tmpstatus;
+       char *savestart;                        /* Save start position of pointers. */
+       char *tmphost;                          /* Pointers needed to strsep operation. */
+       char *newhost;                          /* New hostname if we need to redirect. */
+       char *newurl;                           /* New document name ". */
+       char *newlocation;
+       char *tmpstring;                        /* Temp pointers. */
+       char *freeme, *freeme2;
+       char *redirecttarget;
+       int retval;
+       int handled;
+       int tmphttpstatus;
+       int inflate = 0;                        /* Whether feed data needs decompressed with zlib. */
+       int len;
+       char * inflatedbody;
+       int quirksmode = 0;                     /* IIS operation mode. */
+       int authfailed = 0;                     /* Avoid repeating failed auth requests endlessly. */
+
+       
+       if (!suppressoutput) {
+               if (cur_ptr->title == NULL)
+                       fprintf(stderr, "Downloading http://%s%s\n", host, url);
+               else
+                       fprintf(stderr, "Downloading %s\n", cur_ptr->title);
+
+       }
+       
+       redirectcount = 0;
+       
+       /* Goto label to redirect reconnect. */
+       tryagain:
+       
+       /* Reconstruct digest authinfo for every request so we don't reuse
+          the same nonce value for more than one request.
+          This happens one superflous time on 303 redirects. */
+       if ((cur_ptr->authinfo != NULL) && (cur_ptr->servauth != NULL)) {
+               if (strstr (cur_ptr->authinfo, " Digest ") != NULL) {
+                       NetSupportAuth(cur_ptr, authdata, url, cur_ptr->servauth);
+               }
+       }
+       
+       /* Open socket. */      
+       stream = fdopen (*my_socket, "r+");
+       if (stream == NULL) {
+               /* This is a serious non-continueable OS error as it will probably not
+                  go away if we retry.
+                  
+                  BeOS will stupidly return SUCCESS here making this code silently fail on BeOS. */
+               cur_ptr->netio_error = NET_ERR_SOCK_ERR;
+               return NULL;
+       }
+       
+       /* Again is proxyport == 0, non proxy mode, otherwise make proxy requests. */
+       if (proxyport == 0) {
+               /* Request URL from HTTP server. */
+               if (cur_ptr->lastmodified != NULL) {
+                       fprintf(stream,
+                                       "GET %s HTTP/1.0\r\nAccept-Encoding: gzip\r\nUser-Agent: %s\r\nConnection: close\r\nHost: %s\r\nIf-Modified-Since: %s\r\n%s%s\r\n",
+                                       url,
+                                       useragent,
+                                       host,
+                                       cur_ptr->lastmodified,
+                                       (cur_ptr->authinfo ? cur_ptr->authinfo : ""),
+                                       (cur_ptr->cookies ? cur_ptr->cookies : ""));
+               } else {
+                       fprintf(stream,
+                                       "GET %s HTTP/1.0\r\nAccept-Encoding: gzip\r\nUser-Agent: %s\r\nConnection: close\r\nHost: %s\r\n%s%s\r\n",
+                                       url,
+                                       useragent,
+                                       host,
+                                       (cur_ptr->authinfo ? cur_ptr->authinfo : ""),
+                                       (cur_ptr->cookies ? cur_ptr->cookies : ""));
+               }
+               fflush(stream);         /* We love Solaris, don't we? */
+       } else {
+               /* Request URL from HTTP server. */
+               if (cur_ptr->lastmodified != NULL) {
+                       fprintf(stream,
+                                       "GET http://%s%s HTTP/1.0\r\nAccept-Encoding: gzip\r\nUser-Agent: %s\r\nConnection: close\r\nHost: %s\r\nIf-Modified-Since: %s\r\n%s%s\r\n",
+                                       host,
+                                       url,
+                                       useragent,
+                                       host,
+                                       cur_ptr->lastmodified,
+                                       (cur_ptr->authinfo ? cur_ptr->authinfo : ""),
+                                       (cur_ptr->cookies ? cur_ptr->cookies : ""));
+               } else {
+                       fprintf(stream,
+                                       "GET http://%s%s HTTP/1.0\r\nAccept-Encoding: gzip\r\nUser-Agent: %s\r\nConnection: close\r\nHost: %s\r\n%s%s\r\n",
+                                       host,
+                                       url,
+                                       useragent,
+                                       host,
+                                       (cur_ptr->authinfo ? cur_ptr->authinfo : ""),
+                                       (cur_ptr->cookies ? cur_ptr->cookies : ""));
+               }
+               fflush(stream);         /* We love Solaris, don't we? */
+       }
+       
+       if ((NetPoll (cur_ptr, my_socket, NET_READ)) == -1) {
+               fclose (stream);
+               return NULL;
+       }
+       
+       if ((fgets (servreply, sizeof(servreply), stream)) == NULL) {
+               fclose (stream);
+               return NULL;
+       }
+       if (checkValidHTTPHeader(servreply, sizeof(servreply)) != 0) {
+               cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
+               fclose (stream);
+               return NULL;
+       }
+       
+       tmpstatus = strdup(servreply);
+       savestart = tmpstatus;
+
+       memset (httpstatus, 0, 4);      /* Nullify string so valgrind shuts up. */
+       /* Set pointer to char after first space.
+          HTTP/1.0 200 OK
+                   ^
+          Copy three bytes into httpstatus. */
+       strsep (&tmpstatus, " ");
+       if (tmpstatus == NULL) {
+               cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
+               fclose (stream);
+               free (savestart);       /* Probably more leaks when doing auth and abort here. */
+               return NULL;
+       }
+       strncpy (httpstatus, tmpstatus, 3);
+       free (savestart);
+       
+       cur_ptr->lasthttpstatus = atoi (httpstatus);
+       
+       /* If the redirectloop was run newhost and newurl were allocated.
+          We need to free them here. */
+       if ((redirectcount > 0) && (authdata == NULL)) {
+               free (host);
+               free (url);
+       }
+       
+       tmphttpstatus = cur_ptr->lasthttpstatus;
+       handled = 1;
+       /* Check HTTP server response and handle redirects. */
+       do {
+               switch (tmphttpstatus) {
+                       case 200:       /* OK */
+                               /* Received good status from server, clear problem field. */
+                               cur_ptr->netio_error = NET_ERR_OK;
+                               cur_ptr->problem = 0;
+                               break;
+                       case 300:       /* Multiple choice and everything 300 not handled is fatal. */
+                               cur_ptr->netio_error = NET_ERR_HTTP_NON_200;
+                               fclose (stream);
+                               return NULL;
+                       case 301:
+                               /* Permanent redirect. Change feed->feedurl to new location.
+                                  Done some way down when we have extracted the new url. */
+                       case 302:       /* Found */
+                       case 303:       /* See Other */
+                       case 307:       /* Temp redirect. This is HTTP/1.1 */
+                               redirectcount++;
+                       
+                               /* Give up if we reach MAX_HTTP_REDIRECTS to avoid loops. */
+                               if (redirectcount > MAX_HTTP_REDIRECTS) {
+                                       cur_ptr->netio_error = NET_ERR_REDIRECT_COUNT_ERR;
+                                       fclose (stream);
+                                       return NULL;
+                               }
+                               
+                               while (!feof(stream)) {
+                                       if ((fgets (netbuf, sizeof(netbuf), stream)) == NULL) {
+                                               /* Something bad happened. Server sent stupid stuff. */
+                                               cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
+                                               fclose (stream);
+                                               return NULL;
+                                       }
+                                       
+                                       if (checkValidHTTPHeader(netbuf, sizeof(netbuf)) != 0) {
+                                               cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
+                                               fclose (stream);
+                                               return NULL;
+                                       }
+       
+                                       /* Split netbuf into hostname and trailing url.
+                                          Place hostname in *newhost and tail into *newurl.
+                                          Close old connection and reconnect to server.
+                                          
+                                          Do not touch any of the following code! :P */
+                                       if (strncasecmp (netbuf, "Location", 8) == 0) {
+                                               redirecttarget = strdup (netbuf);
+                                               freeme = redirecttarget;
+                                               
+                                               /* Remove trailing \r\n from line. */
+                                               redirecttarget[strlen(redirecttarget)-2] = 0;
+                                               
+                                               /* In theory pointer should now be after the space char
+                                                  after the word "Location:" */
+                                               strsep (&redirecttarget, " ");
+                                               
+                                               if (redirecttarget == NULL) {
+                                                       cur_ptr->problem = 1;
+                                                       cur_ptr->netio_error = NET_ERR_REDIRECT_ERR;
+                                                       free (freeme);
+                                                       fclose (stream);
+                                                       return NULL;
+                                               }
+                                               
+                                               /* Location must start with "http", otherwise switch on quirksmode. */
+                                               if (strncmp(redirecttarget, "http", 4) != 0)
+                                                       quirksmode = 1;
+                                               
+                                               /* If the Location header is invalid we need to construct
+                                                  a correct one here before proceeding with the program.
+                                                  This makes headers like
+                                                  "Location: fuck-the-protocol.rdf" work.
+                                                  In violalation of RFC1945, RFC2616. */
+                                               if (quirksmode) {
+                                                       len = 7 + strlen(host) + strlen(redirecttarget) + 3;
+                                                       newlocation = malloc(len);
+                                                       memset (newlocation, 0, len);
+                                                       strcat (newlocation, "http://");
+                                                       strcat (newlocation, host);
+                                                       if (redirecttarget[0] != '/')
+                                                               strcat (newlocation, "/");
+                                                       strcat (newlocation, redirecttarget);
+                                               } else
+                                                       newlocation = strdup (redirecttarget);
+                                               
+                                               /* This also frees redirecttarget. */
+                                               free (freeme);
+                                               
+                                               /* Change cur_ptr->feedurl on 301. */
+                                               if (cur_ptr->lasthttpstatus == 301) {
+                                                       /* Check for valid redirection URL */
+                                                       if (checkValidHTTPURL(newlocation) != 0) {
+                                                               cur_ptr->problem = 1;
+                                                               cur_ptr->netio_error = NET_ERR_REDIRECT_ERR;
+                                                               fclose (stream);
+                                                               return NULL;
+                                                       }
+                                                       if (!suppressoutput) {
+                                                               fprintf(stderr, "URL points to permanent redirect, updating with new location...\n");
+                                                       }
+                                                       free (cur_ptr->feedurl);
+                                                       if (authdata == NULL)
+                                                               cur_ptr->feedurl = strdup (newlocation);
+                                                       else {
+                                                               /* Include authdata in newly constructed URL. */
+                                                               len = strlen(authdata) + strlen(newlocation) + 2;
+                                                               cur_ptr->feedurl = malloc (len);
+                                                               newurl = strdup(newlocation);
+                                                               freeme2 = newurl;
+                                                               strsep (&newurl, "/");
+                                                               strsep (&newurl, "/");
+                                                               snprintf (cur_ptr->feedurl, len, "http://%s@%s", authdata, newurl);
+                                                               free (freeme2);
+                                                       }
+                                               }
+                                               
+                                               freeme = newlocation;
+                                               strsep (&newlocation, "/");
+                                               strsep (&newlocation, "/");
+                                               tmphost = newlocation;
+                                               /* The following line \0-terminates tmphost in overwriting the first
+                                                  / after the hostname. */
+                                               strsep (&newlocation, "/");
+                                               
+                                               /* newlocation must now be the absolute path on newhost.
+                                                  If not we've been redirected to somewhere totally stupid
+                                                  (oh yeah, no offsite linking, go to our fucking front page).
+                                                  Say goodbye to the webserver in this case. In fact, we don't
+                                                  even say goodbye, but just drop the connection. */
+                                               if (newlocation == NULL) {
+                                                       cur_ptr->netio_error = NET_ERR_REDIRECT_ERR;
+                                                       fclose (stream);
+                                                       return NULL;
+                                               }
+                                               
+                                               newhost = strdup (tmphost);
+                                               newlocation--;
+                                               newlocation[0] = '/';
+                                               newurl = strdup (newlocation);
+                                       
+                                               free (freeme);
+                                               
+                                               /* Close connection. */ 
+                                               fclose (stream);
+                                               
+                                               /* Reconnect to server. */
+                                               if ((NetConnect (my_socket, newhost, cur_ptr, httpproto, suppressoutput)) != 0) {
+                                                       return NULL;
+                                               }
+                                       
+                                               host = newhost;
+                                               url = newurl;
+                                               
+                                               goto tryagain;
+                                       }
+                               }
+                               break;
+                       case 304:
+                               /* Not modified received. We can close stream and return from here.
+                                  Not very friendly though. :) */
+                               fclose (stream);
+                               /* Received good status from server, clear problem field. */
+                               cur_ptr->netio_error = NET_ERR_OK;
+                               cur_ptr->problem = 0;
+                               
+                               /* This should be freed everywhere where we return
+                                  and current feed uses auth. */
+                               if ((redirectcount > 0) && (authdata != NULL)) {
+                                       free (host);
+                                       free (url);
+                               }
+                               return NULL;
+                       case 401:
+                               /* Authorization.
+                                  Parse rest of header and rerequest URL from server using auth mechanism
+                                  requested in WWW-Authenticate header field. (Basic or Digest) */
+                               break;
+                       case 404:
+                               cur_ptr->netio_error = NET_ERR_HTTP_404;
+                               fclose (stream);
+                               return NULL;
+                       case 410: /* The feed is gone. Politely remind the user to unsubscribe. */
+                               cur_ptr->netio_error = NET_ERR_HTTP_410;
+                               fclose (stream);
+                               return NULL;
+                       case 400:
+                               cur_ptr->netio_error = NET_ERR_HTTP_NON_200;
+                               fclose (stream);
+                               return NULL;
+                       default:
+                               /* unknown error codes have to be treated like the base class */
+                               if (handled) {
+                                       /* first pass, modify error code to base class */
+                                       handled = 0;
+                                       tmphttpstatus -= tmphttpstatus % 100;
+                               } else {
+                                       /* second pass, give up on unknown error base class */
+                                       cur_ptr->netio_error = NET_ERR_HTTP_NON_200;
+                                       fclose (stream);
+                                       return NULL;
+                               }
+               }
+       } while(!handled);
+       
+       /* Read rest of HTTP header and parse what we need. */
+       while (!feof(stream)) { 
+               if ((NetPoll (cur_ptr, my_socket, NET_READ)) == -1) {
+                       fclose (stream);
+                       return NULL;
+               }
+
+               if ((fgets (netbuf, sizeof(netbuf), stream)) == NULL)
+                       break;
+               
+               if (checkValidHTTPHeader(netbuf, sizeof(netbuf)) != 0) {
+                       cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
+                       fclose (stream);
+                       return NULL;
+               }
+               
+               if (strncasecmp (netbuf, "Transfer-Encoding", 17) == 0) {
+                       /* Chunked transfer encoding. HTTP/1.1 extension.
+                          http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 */
+                       if (strstr (netbuf, "chunked") != NULL)
+                               chunked = 1;
+               }
+               /* Get last modified date. This is only relevant on HTTP 200. */
+               if ((strncasecmp (netbuf, "Last-Modified", 13) == 0) &&
+                       (cur_ptr->lasthttpstatus == 200)) {
+                       tmpstring = strdup(netbuf);
+                       freeme = tmpstring;
+                       strsep (&tmpstring, " ");
+                       if (tmpstring == NULL)
+                               free (freeme);
+                       else {
+                               free(cur_ptr->lastmodified);
+                               cur_ptr->lastmodified = strdup(tmpstring);
+                               if (cur_ptr->lastmodified[strlen(cur_ptr->lastmodified)-1] == '\n')
+                                       cur_ptr->lastmodified[strlen(cur_ptr->lastmodified)-1] = '\0';
+                               if (cur_ptr->lastmodified[strlen(cur_ptr->lastmodified)-1] == '\r')
+                                       cur_ptr->lastmodified[strlen(cur_ptr->lastmodified)-1] = '\0';
+                               free(freeme);
+                       }
+               }
+               if (strncasecmp (netbuf, "Content-Encoding", 16) == 0) {
+                       if (strstr (netbuf, "gzip") != NULL)
+                               inflate = 1;
+               }
+               if (strncasecmp (netbuf, "Content-Type", 12) == 0) {
+                       tmpstring = strdup(netbuf);
+                       freeme = tmpstring;
+                       strsep(&tmpstring, " ");
+                       if (tmpstring == NULL)
+                               free (freeme);
+                       else {
+                               freeme2 = NULL;
+                               freeme2 = strstr(tmpstring, ";");
+                               if (freeme2 != NULL)
+                                       freeme2[0] = '\0';
+                               free(cur_ptr->content_type);
+                               cur_ptr->content_type = strdup(tmpstring);
+                               if (cur_ptr->content_type[strlen(cur_ptr->content_type)-1] == '\n')
+                                       cur_ptr->content_type[strlen(cur_ptr->content_type)-1] = '\0';
+                               if (cur_ptr->content_type[strlen(cur_ptr->content_type)-1] == '\r')
+                                       cur_ptr->content_type[strlen(cur_ptr->content_type)-1] = '\0';
+                               free(freeme);
+                       }
+               }
+               /* HTTP authentication
+                *
+                * RFC 2617 */
+               if ((strncasecmp (netbuf, "WWW-Authenticate", 16) == 0) &&
+                       (cur_ptr->lasthttpstatus == 401)) {
+                       if (authfailed) {
+                               /* Don't repeat authrequest if it already failed before! */
+                               cur_ptr->netio_error = NET_ERR_AUTH_FAILED;
+                               fclose (stream);
+                               return NULL;
+                       }
+
+                       /* Remove trailing \r\n from line. */
+                       if (netbuf[strlen(netbuf)-1] == '\n')
+                               netbuf[strlen(netbuf)-1] = '\0';
+                       if (netbuf[strlen(netbuf)-1] == '\r')
+                               netbuf[strlen(netbuf)-1] = '\0';
+                       
+                       authfailed++;
+                       
+                       /* Make a copy of the WWW-Authenticate header. We use it to
+                          reconstruct a new auth reply on every loop. */
+                       free (cur_ptr->servauth);
+                       
+                       cur_ptr->servauth = strdup (netbuf);
+                       
+                       /* Load authinfo into cur_ptr->authinfo. */
+                       retval = NetSupportAuth(cur_ptr, authdata, url, netbuf);
+                       
+                       switch (retval) {
+                               case 1:
+                                       cur_ptr->netio_error = NET_ERR_AUTH_NO_AUTHINFO;
+                                       fclose (stream);
+                                       return NULL;
+                                       break;
+                               case 2:
+                                       cur_ptr->netio_error = NET_ERR_AUTH_GEN_AUTH_ERR;
+                                       fclose (stream);
+                                       return NULL;
+                                       break;
+                               case -1:
+                                       cur_ptr->netio_error = NET_ERR_AUTH_UNSUPPORTED;
+                                       fclose (stream);
+                                       return NULL;
+                                       break;
+                               default:
+                                       break;
+                       }
+                       
+                       /* Close current connection and reconnect to server. */
+                       fclose (stream);
+                       if ((NetConnect (my_socket, host, cur_ptr, httpproto, suppressoutput)) != 0) {
+                               return NULL;
+                       }
+
+                       /* Now that we have an authinfo, repeat the current request. */
+                       goto tryagain;
+               }
+               /* This seems to be optional and probably not worth the effort since we
+                  don't issue a lot of consecutive requests. */
+               /*if ((strncasecmp (netbuf, "Authentication-Info", 19) == 0) ||
+                       (cur_ptr->lasthttpstatus == 200)) {
+               
+               }*/
+               
+               /* HTTP RFC 2616, Section 19.3 Tolerant Applications.
+                  Accept CRLF and LF line ends in the header field. */
+               if ((strcmp(netbuf, "\r\n") == 0) || (strcmp(netbuf, "\n") == 0))
+                       break;
+       }
+       
+       /* If the redirectloop was run newhost and newurl were allocated.
+          We need to free them here.
+          But _after_ the authentication code since it needs these values! */
+       if ((redirectcount > 0) && (authdata != NULL)) {
+               free (host);
+               free (url);
+       }
+       
+       /**********************
+        * End of HTTP header *
+        **********************/
+       
+       /* Init pointer so strncat works.
+          Workaround class hack. */
+       body = malloc(1);
+       body[0] = '\0';
+       
+       length = 0;
+
+       /* Read stream until EOF and return it to parent. */
+       while (!feof(stream)) {
+               if ((NetPoll (cur_ptr, my_socket, NET_READ)) == -1) {
+                       fclose (stream);
+                       return NULL;
+               }
+               
+               /* Since we handle binary data if we read compressed input we
+                  need to use fread instead of fgets after reading the header. */ 
+               retval = fread (netbuf, 1, sizeof(netbuf), stream);
+               if (retval == 0)
+                       break;
+               body = realloc (body, length+retval);
+               memcpy (body+length, netbuf, retval);
+               length += retval;
+               if (retval != 4096)
+                       break;
+       }
+       body = realloc(body, length+1);
+       body[length] = '\0';
+       
+       cur_ptr->content_length = length;
+       
+       /* Close connection. */
+       fclose (stream);
+       
+       if (chunked) {
+               if (decodechunked(body, &length) == NULL) {
+                       free (body);
+                       cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
+                       return NULL;
+               }
+       }
+       
+       /* If inflate==1 we need to decompress the content.. */
+       if (inflate == 1) {
+               /* gzipinflate */
+               /*inflatedbody = gzip_uncompress (body, length, &cur_ptr->content_length);
+               if (inflatedbody == NULL) {
+                       free (body);
+                       cur_ptr->netio_error = NET_ERR_GZIP_ERR;
+                       return NULL;
+               }*/
+               if (jg_gzip_uncompress (body, length, (void **)&inflatedbody, &cur_ptr->content_length) != 0) {
+                       free (body);
+                       cur_ptr->netio_error = NET_ERR_GZIP_ERR;
+                       return NULL;
+               }
+               
+               /* Copy uncompressed data back to body. */
+               free (body);
+               body = inflatedbody;
+       }
+       
+       return body;
+}
+
+/* Returns allocated string with body of webserver reply.
+   Various status info put into struct feed *cur_ptr.
+   Set suppressoutput=1 to disable diagnostic output. */
+char *DownloadFeed(char *url, struct feed *cur_ptr, int suppressoutput) {
+       int my_socket = 0;
+       int url_fixup = 0;
+       char *host;                             /* Needs to freed. */
+       char *tmphost;
+       char *freeme;
+       char *returndata;
+       char *authdata = NULL;
+       char *tmpstr;
+       int httpproto = 0;                      /* 0: http; 1: https */
+       
+       if (checkValidHTTPURL(url) != 0) {
+               cur_ptr->problem = 1;
+               cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
+               return NULL;
+       }
+       /* strstr will match _any_ substring. Not good, use strncasecmp with length 5! */
+       if (strncasecmp (url, "https", 5) == 0)
+               httpproto = 1;
+       else
+               httpproto = 0;
+       
+       strsep (&url, "/");
+       strsep (&url, "/");
+       tmphost = url;
+       strsep (&url, "/");
+       if (url == NULL) {
+               /* Assume "/" is input is exhausted. */
+               url = strdup("/");
+               url_fixup = 1;
+       }
+       
+       /* If tmphost contains an '@', extract username and pwd. */
+       if (strchr (tmphost, '@') != NULL) {
+               tmpstr = tmphost;
+               strsep (&tmphost, "@");
+               authdata = strdup (tmpstr);
+       }
+       
+       host = strdup (tmphost);
+       
+       /* netio() might change pointer of host to something else if redirect
+          loop is executed. Make a copy so we can correctly free everything. */
+       freeme = host;
+       /* Only run if url was != NULL above. */
+       if (!url_fixup) {
+               url--;
+               url[0] = '/';
+               if (url[strlen(url)-1] == '\n') {
+                       url[strlen(url)-1] = '\0';
+               }
+       }
+       
+       if ((NetConnect (&my_socket, host, cur_ptr, httpproto, suppressoutput)) != 0) {
+               free (freeme);
+               free (authdata);
+               if (url_fixup)
+                       free(url);
+               cur_ptr->problem = 1;
+               return NULL;
+       }
+       returndata = NetIO (&my_socket, host, url, cur_ptr, authdata, httpproto, suppressoutput);
+       if ((returndata == NULL) && (cur_ptr->netio_error != NET_ERR_OK)) {
+               cur_ptr->problem = 1;
+       }
+       
+       /* url will be freed in the calling function. */
+       free (freeme);          /* This is *host. */
+       free (authdata);
+       if (url_fixup)
+               free(url);
+       
+       return returndata;
+}
diff --git a/rss2ctdl/netio.h b/rss2ctdl/netio.h
new file mode 100644 (file)
index 0000000..2ef3638
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * netio.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#ifndef NETIO_H
+#define NETIO_H
+
+struct feed;
+
+char *DownloadFeed (char *url, struct feed *cur_ptr, int suppressoutput);
+
+typedef enum {
+       NET_ERR_OK,
+       /* Init errors */
+       NET_ERR_URL_INVALID,
+       /* Connect errors */
+       NET_ERR_SOCK_ERR,
+       NET_ERR_HOST_NOT_FOUND,
+       NET_ERR_CONN_REFUSED,
+       NET_ERR_CONN_FAILED,
+       NET_ERR_TIMEOUT,
+       NET_ERR_UNKNOWN,
+       /* Transfer errors */
+       NET_ERR_REDIRECT_COUNT_ERR,
+       NET_ERR_REDIRECT_ERR,
+       NET_ERR_HTTP_410,
+       NET_ERR_HTTP_404,
+       NET_ERR_HTTP_NON_200,
+       NET_ERR_HTTP_PROTO_ERR,
+       NET_ERR_AUTH_FAILED,
+       NET_ERR_AUTH_NO_AUTHINFO,
+       NET_ERR_AUTH_GEN_AUTH_ERR,
+       NET_ERR_AUTH_UNSUPPORTED,
+       NET_ERR_GZIP_ERR
+} netio_error_type;
+
+#endif
diff --git a/rss2ctdl/os-support.c b/rss2ctdl/os-support.c
new file mode 100644 (file)
index 0000000..808aeb5
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * os-support.c
+ *
+ * Library support functions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "main.h"
+
+/******************************************************************************
+ * This is a replacement for strsep which is not portable (missing on Solaris).
+ *
+ * http://www.winehq.com/hypermail/wine-patches/2001/11/0024.html
+ *
+ * The following function was written by François Gouget.
+ */
+#ifdef SUN
+char* strsep(char** str, const char* delims)
+{
+    char* token;
+
+    if (*str==NULL) {
+        /* No more tokens */
+        return NULL;
+    }
+
+    token=*str;
+    while (**str!='\0') {
+        if (strchr(delims,**str)!=NULL) {
+            **str='\0';
+            (*str)++;
+            return token;
+        }
+        (*str)++;
+    }
+    /* There is no other token */
+    *str=NULL;
+   return token;
+}
+#endif
+
+/* strcasestr stolen from: http://www.unixpapa.com/incnote/string.html */
+char *s_strcasestr(char *a, char *b) {
+       size_t l;
+       char f[3];
+       int lena = strlen(a);
+       int lenb = strlen(b);
+       
+       snprintf(f, sizeof(f), "%c%c", tolower(*b), toupper(*b));
+       for (l = strcspn(a, f); l != lena; l += strcspn(a + l + 1, f) + 1)
+               if (strncasecmp(a + l, b, lenb) == 0)
+                       return(a + l);
+       return(NULL);
+}
+
+
+/* Private malloc wrapper. Aborts program execution if malloc fails. */
+void * s_malloc (size_t size) {
+       void *newmem;
+       
+       newmem = malloc (size);
+       
+       if (newmem == NULL) {
+               fprintf(stderr, "Error allocating memory: %s\n", strerror(errno));
+               abort();
+       }
+       
+       return newmem;
+}
diff --git a/rss2ctdl/os-support.h b/rss2ctdl/os-support.h
new file mode 100644 (file)
index 0000000..270583f
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * os-support.h
+ *
+ * Library support functions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#ifndef OS_SUPPORT_H
+#define OS_SUPPORT_H
+
+#ifdef SUN
+char* strsep(char** str, const char* delims);
+#endif
+
+char *s_strcasestr(char *a, char *b);
+void * s_malloc (size_t size);
+
+#endif
diff --git a/rss2ctdl/rss2ctdl b/rss2ctdl/rss2ctdl
new file mode 100755 (executable)
index 0000000..ec813ca
Binary files /dev/null and b/rss2ctdl/rss2ctdl differ
diff --git a/rss2ctdl/setup.h b/rss2ctdl/setup.h
new file mode 100644 (file)
index 0000000..40b7d92
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
+ *
+ * setup.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+#ifndef SETUP_H
+#define SETUP_H
+
+int Config (void);
+
+struct entity {
+       char * entity;
+       char * converted_entity;
+       int entity_length;
+       struct entity * next_ptr;
+};
+
+#endif
diff --git a/rss2ctdl/xmlparse.c b/rss2ctdl/xmlparse.c
new file mode 100644 (file)
index 0000000..6498d1f
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Rene Puls <rpuls@gmx.net> and
+ *                     Oliver Feiler <kiza@kcore.de>
+ *
+ * http://kiza.kcore.de/software/snownews/
+ * http://home.kcore.de/~kianga/study/c/xmlparse.c
+ *
+ * xmlparse.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <string.h>
+
+#include "config.h"
+#include "xmlparse.h"
+#include "conversions.h"
+
+int saverestore;
+struct newsitem *copy;
+struct newsitem *firstcopy;
+
+/* During the parsens one calls, if we meet a <channel> element. 
+ * The function returns a new Struct for the new feed. */
+
+void parse_rdf10_channel(struct feed *feed, xmlDocPtr doc, xmlNodePtr node) {
+       xmlNodePtr cur;
+       
+       /* Free everything before we write to it again. */
+       free (feed->title);
+       free (feed->link);
+       free (feed->description);
+               
+       if (feed->items != NULL) {
+               while (feed->items->next_ptr != NULL) {
+                       feed->items = feed->items->next_ptr;
+                       free (feed->items->prev_ptr->data->title);
+                       free (feed->items->prev_ptr->data->link);
+                       free (feed->items->prev_ptr->data->guid);
+                       free (feed->items->prev_ptr->data->description);
+                       free (feed->items->prev_ptr->data);
+                       free (feed->items->prev_ptr);
+               }
+               free (feed->items->data->title);
+               free (feed->items->data->link);
+               free (feed->items->data->guid);
+               free (feed->items->data->description);
+               free (feed->items->data);
+               free (feed->items);
+       }
+       
+       /* At the moment we have still no Items, so set the list to null. */
+       feed->items = NULL;
+       feed->title = NULL;
+       feed->link= NULL;
+       feed->description = NULL;
+       
+       /* Go through all <channel> tags and extract the information */
+       for (cur = node; cur != NULL; cur = cur->next) {
+               if (cur->type != XML_ELEMENT_NODE)
+                       continue;
+               if (xmlStrcmp(cur->name, "title") == 0) {
+                       feed->title = xmlNodeListGetString(doc, cur->children, 1);
+                       CleanupString (feed->title, 1);
+                       /* Remove trailing newline */
+                       if (feed->title != NULL) {
+                               if (strlen(feed->title) > 1) {
+                                       if (feed->title[strlen(feed->title)-1] == '\n')
+                                               feed->title[strlen(feed->title)-1] = '\0';
+                               }
+                       }
+               }
+               else if (xmlStrcmp(cur->name, "link") == 0) {
+                       feed->link = xmlNodeListGetString(doc, cur->children, 1);
+                       /* Remove trailing newline */
+                       if (feed->link != NULL) {
+                               if (strlen(feed->link) > 1) {
+                                       if (feed->link[strlen(feed->link)-1] == '\n')
+                                               feed->link[strlen(feed->link)-1] = '\0';
+                               }
+                       }
+               }
+               else if (xmlStrcmp(cur->name, "description") == 0) {
+                       feed->description = xmlNodeListGetString(doc, cur->children, 1);
+                       CleanupString (feed->description, 0);
+               }
+       }
+}
+
+
+void parse_rdf20_channel(struct feed *feed, xmlDocPtr doc, xmlNodePtr node)
+{
+       xmlNodePtr cur;
+       
+       /* Free everything before we write to it again. */
+       free (feed->title);
+       free (feed->link);
+       free (feed->description);
+               
+       if (feed->items != NULL) {
+               while (feed->items->next_ptr != NULL) {
+                       feed->items = feed->items->next_ptr;
+                       free (feed->items->prev_ptr->data->title);
+                       free (feed->items->prev_ptr->data->link);
+                       free (feed->items->prev_ptr->data->guid);
+                       free (feed->items->prev_ptr->data->description);
+                       free (feed->items->prev_ptr->data);
+                       free (feed->items->prev_ptr);
+               }
+               free (feed->items->data->title);
+               free (feed->items->data->link);
+               free (feed->items->data->guid);
+               free (feed->items->data->description);
+               free (feed->items->data);
+               free (feed->items);
+       }
+       
+       /* Im Augenblick haben wir noch keine Items, also die Liste auf NULL setzen. */
+       feed->items = NULL;
+       feed->title = NULL;
+       feed->link = NULL;
+       feed->description = NULL;
+       
+       /* Alle Tags im <channel> Tag durchgehen und die Informationen extrahieren */
+       for (cur = node; cur != NULL; cur = cur->next) {
+               if (cur->type != XML_ELEMENT_NODE)
+                       continue;
+               if (xmlStrcmp(cur->name, "title") == 0) {
+                       feed->title = xmlNodeListGetString(doc, cur->children, 1);
+                       CleanupString (feed->title, 1);
+                       /* Remove trailing newline */
+                       if (feed->title != NULL) {
+                               if (strlen(feed->title) > 1) {
+                                       if (feed->title[strlen(feed->title)-1] == '\n')
+                                               feed->title[strlen(feed->title)-1] = '\0';
+                               }
+                       }
+               }
+               else if (xmlStrcmp(cur->name, "link") == 0) {
+                       feed->link = xmlNodeListGetString(doc, cur->children, 1);
+                       /* Remove trailing newline */
+                       if (feed->link != NULL) {
+                               if (strlen(feed->link) > 1) {
+                                       if (feed->link[strlen(feed->link)-1] == '\n')
+                                               feed->link[strlen(feed->link)-1] = '\0';
+                               }
+                       }
+               }
+               else if (xmlStrcmp(cur->name, "description") == 0) {
+                       feed->description = xmlNodeListGetString(doc, cur->children, 1);
+                       CleanupString (feed->description, 0);
+               } else if (xmlStrcmp(cur->name, "item") == 0) {
+                       parse_rdf10_item(feed, doc, cur->children);
+               }
+       }
+}
+
+/* This function is called each mark, if we meet on. As parameter it needs the
+ * current new feed (new feed struct *), as well as the current XML
+ * document-acts and the current element, both comes directly of libxml.
+ */
+
+void parse_rdf10_item(struct feed *feed, xmlDocPtr doc, xmlNodePtr node) 
+{
+       xmlNodePtr cur;
+       xmlChar *readstatusstring;
+
+       struct newsitem *item;
+       struct newsitem *current;
+       
+       /* Speicher für ein neues Newsitem reservieren */
+       item = malloc(sizeof (struct newsitem));
+       item->data = malloc (sizeof (struct newsdata));
+       
+       item->data->title = NULL;
+       item->data->link = NULL;
+       item->data->guid = NULL;
+       item->data->description = NULL;
+       item->data->readstatus = 0;
+       item->data->parent = feed;
+               
+       /* Alle Tags im <item> Tag durchgehen und die Informationen extrahieren.
+          Selbe Vorgehensweise wie in der parse_channel() Funktion */
+       for (cur = node; cur != NULL; cur = cur->next) {
+               if (cur->type != XML_ELEMENT_NODE)
+                       continue;
+               if (xmlStrcmp(cur->name, "title") == 0) {
+                       item->data->title = xmlNodeListGetString(doc, cur->children, 1);
+                       CleanupString (item->data->title, 1);
+                       /* Remove trailing newline */
+                       if (item->data->title != NULL) {
+                               if (strlen(item->data->title) > 1) {
+                                       if (item->data->title[strlen(item->data->title)-1] == '\n')
+                                               item->data->title[strlen(item->data->title)-1] = '\0';
+                               }
+                       }
+               }
+               else if (xmlStrcmp(cur->name, "link") == 0) {
+                       item->data->link = xmlNodeListGetString(doc, cur->children, 1);
+                       if (item->data->link == NULL) {
+                               if (xmlStrcmp(cur->name, "guid") == 0)
+                                       item->data->link = xmlNodeListGetString(doc, cur->children, 1);
+                       }
+                       /* Remove trailing newline */
+                       if (item->data->link != NULL) {
+                               if (strlen(item->data->link) > 1) {
+                                       if (item->data->link[strlen(item->data->link)-1] == '\n')
+                                               item->data->link[strlen(item->data->link)-1] = '\0';
+                               }
+                       }
+               }
+               else if (xmlStrcmp(cur->name, "guid") == 0) {
+                       item->data->guid = xmlNodeListGetString(doc, cur->children, 1);
+                       if (item->data->guid == NULL) {
+                               if (xmlStrcmp(cur->name, "guid") == 0)
+                                       item->data->guid = xmlNodeListGetString(doc, cur->children, 1);
+                       }
+                       /* Remove trailing newline */
+                       if (item->data->guid != NULL) {
+                               if (strlen(item->data->guid) > 1) {
+                                       if (item->data->guid[strlen(item->data->guid)-1] == '\n')
+                                               item->data->guid[strlen(item->data->guid)-1] = '\0';
+                               }
+                       }
+               }
+               else if (xmlStrcmp(cur->name, "description") == 0) {
+                       item->data->description = xmlNodeListGetString(doc, cur->children, 1);
+                       CleanupString (item->data->description, 0);
+               }
+               else if (xmlStrcmp(cur->name, "readstatus") == 0) {
+                       /* Will cause memory leak otherwise, xmlNodeListGetString must be freed. */
+                       readstatusstring = xmlNodeListGetString(doc, cur->children, 1);
+                       item->data->readstatus = atoi (readstatusstring);
+                       xmlFree (readstatusstring);
+               }
+       }
+       
+       /* If saverestore == 1, restore readstatus. */
+       if (saverestore == 1) {
+               for (current = firstcopy; current != NULL; current = current->next_ptr) {
+                       if ((current->data->link != NULL) && (item->data->link != NULL)) {
+                               if ((current->data->title != NULL) && (item->data->title != NULL)) {
+                                       if ((strcmp(item->data->link, current->data->link) == 0) &&
+                                               (strcmp(item->data->title, current->data->title) == 0))
+                                               item->data->readstatus = current->data->readstatus;
+                               } else {
+                                       if (strcmp(item->data->link, current->data->link) == 0)
+                                               item->data->readstatus = current->data->readstatus;
+                               }
+                       }
+               }
+       }
+
+       item->next_ptr = NULL;
+       if (feed->items == NULL) {
+               item->prev_ptr = NULL;
+               feed->items = item;
+       } else {
+               item->prev_ptr = feed->items;
+               while (item->prev_ptr->next_ptr != NULL)
+                       item->prev_ptr = item->prev_ptr->next_ptr;
+               item->prev_ptr->next_ptr = item;
+       }
+}
+
+
+/* rrr */
+
+int DeXML (struct feed *cur_ptr) {
+       xmlDocPtr doc;
+       xmlNodePtr cur;
+       struct newsitem *cur_item;
+       
+       if (cur_ptr->feed == NULL)
+               return -1;
+       
+       saverestore = 0;
+       /* Wenn cur_ptr->items != NULL dann können wir uns item->readstatus
+          zwischenspeichern. */
+       if (cur_ptr->items != NULL) {
+               saverestore = 1;
+       
+               firstcopy = NULL;
+               
+               /* Copy current newsitem struct. */     
+               for (cur_item = cur_ptr->items; cur_item != NULL; cur_item = cur_item->next_ptr) {
+                       copy = malloc (sizeof(struct newsitem));
+                       copy->data = malloc (sizeof (struct newsdata));
+                       copy->data->title = NULL;
+                       copy->data->link = NULL;
+                       copy->data->guid = NULL;
+                       copy->data->description = NULL;
+                       copy->data->readstatus = cur_item->data->readstatus;
+                       if (cur_item->data->link != NULL)
+                               copy->data->link = strdup (cur_item->data->link);
+                       if (cur_item->data->title != NULL)
+                               copy->data->title = strdup (cur_item->data->title);
+                       
+                       copy->next_ptr = NULL;
+                       if (firstcopy == NULL) {
+                               copy->prev_ptr = NULL;
+                               firstcopy = copy;
+                       } else {
+                               copy->prev_ptr = firstcopy;
+                               while (copy->prev_ptr->next_ptr != NULL)
+                                       copy->prev_ptr = copy->prev_ptr->next_ptr;
+                               copy->prev_ptr->next_ptr = copy;
+                       }
+               }
+       }
+       
+       /* xmlRecoverMemory:
+          parse an XML in-memory document and build a tree.
+       In case the document is not Well Formed, a tree is built anyway. */
+       doc = xmlRecoverMemory(cur_ptr->feed, strlen(cur_ptr->feed));
+       
+       if (doc == NULL)
+               return 2;
+       
+       /* Das Root-Element finden (in unserem Fall sollte es "<RDF:RDF>" heißen.
+          Dabei wird das RDF: Prefix fürs Erste ignoriert, bis der Jaguar
+          herausfindet, wie man das genau auslesen kann (jau). */
+       cur = xmlDocGetRootElement(doc);
+       
+       if (cur == NULL) {
+               xmlFreeDoc (doc);
+               return 2;
+       }
+       
+       /* Überprüfen, ob das Element auch wirklich <RDF> heißt */
+       if (xmlStrcmp(cur->name, "RDF") == 0) {
+       
+               /* Jetzt gehen wir alle Elemente im Dokument durch. Diese Schleife
+                  selbst läuft jedoch nur durch die Elemente auf höchster Ebene
+                  (bei HTML wären das nur HEAD und BODY), wandert also nicht die 
+                  gesamte Struktur nach unten durch. Dafür sind die Funktionen zuständig, 
+                  die wir dann in der Schleife selbst aufrufen. */
+               for (cur = cur->children; cur != NULL; cur = cur->next) {
+                       if (cur->type != XML_ELEMENT_NODE)
+                               continue;
+                       if (xmlStrcmp(cur->name, "channel") == 0)
+                               parse_rdf10_channel(cur_ptr, doc, cur->children);
+                       if (xmlStrcmp(cur->name, "item") == 0)
+                               parse_rdf10_item(cur_ptr, doc, cur->children);
+                       /* Last-Modified is only used when reading from internal feeds (disk cache). */
+                       if (xmlStrcmp(cur->name, "lastmodified") == 0)
+                               cur_ptr->lastmodified = xmlNodeListGetString(doc, cur->children, 1);
+               }
+       } else if (xmlStrcmp(cur->name, "rss") == 0) {
+               for (cur = cur->children; cur != NULL; cur = cur->next) {
+                       if (cur->type != XML_ELEMENT_NODE)
+                               continue;
+                       if (xmlStrcmp(cur->name, "channel") == 0)
+                               parse_rdf20_channel(cur_ptr, doc, cur->children);
+               }
+       } else {
+               xmlFreeDoc(doc);
+               return 3;
+       }
+
+       xmlFreeDoc(doc);
+       
+       if (saverestore == 1) {
+               /* free struct newsitem *copy. */
+               while (firstcopy->next_ptr != NULL) {
+                       firstcopy = firstcopy->next_ptr;
+                       free (firstcopy->prev_ptr->data->link);
+                       free (firstcopy->prev_ptr->data->guid);
+                       free (firstcopy->prev_ptr->data->title);
+                       free (firstcopy->prev_ptr->data);
+                       free (firstcopy->prev_ptr);
+               }
+               free (firstcopy->data->link);
+               free (firstcopy->data->guid);
+               free (firstcopy->data->title);
+               free (firstcopy->data);
+               free (firstcopy);
+       }
+       
+       if (cur_ptr->original != NULL)
+               free (cur_ptr->original);
+
+       /* Set -> title to something if it's a NULL pointer to avoid crash with strdup below. */
+       if (cur_ptr->title == NULL)
+               cur_ptr->title = strdup (cur_ptr->feedurl);
+       cur_ptr->original = strdup (cur_ptr->title);
+       
+       return 0;
+}
diff --git a/rss2ctdl/xmlparse.h b/rss2ctdl/xmlparse.h
new file mode 100644 (file)
index 0000000..f929f14
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003-2004 Rene Puls <rpuls@gmx.net>
+ *
+ * xmlparse.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#ifndef XMLPARSE_H
+#define XMLPARSE_H
+
+#include <libxml/parser.h>
+
+void parse_rdf10_item(struct feed *feed, xmlDocPtr doc, xmlNodePtr node);
+void parse_rdf10_channel(struct feed * feed, xmlDocPtr doc, xmlNodePtr node);
+void parse_rdf20_channel(struct feed * feed, xmlDocPtr doc, xmlNodePtr node);
+int DeXML (struct feed * cur_ptr);
+
+#endif
diff --git a/rss2ctdl/zlib_interface.c b/rss2ctdl/zlib_interface.c
new file mode 100644 (file)
index 0000000..6979d33
--- /dev/null
@@ -0,0 +1,165 @@
+/* Low-level function, decompresses deflate compressed data. Used by gzip_uncompress below. */
+
+#include "zlib_interface.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <zlib.h>
+#include <string.h>
+
+int JG_ZLIB_DEBUG = 0;
+
+struct gzip_header {
+       unsigned char magic[2];
+       unsigned char method;
+       unsigned char flags;
+       unsigned char mtime[4];
+       unsigned char xfl;
+       unsigned char os;
+};
+
+struct gzip_footer {
+       unsigned char crc32[4];
+       unsigned char size[4];
+};
+
+int jg_zlib_uncompress(void * const in_buf, int in_size, 
+                                      void **out_buf_ptr, int *out_size,
+                                          int gzip)
+{
+       char tmpstring[1024];
+       z_stream stream;
+       char *out_buf = NULL;
+       int out_buf_bytes = 0;
+       char tmp_buf[4096];
+       int result;
+       int new_bytes;
+       
+       /* Prepare the stream structure. */
+       stream.zalloc = NULL;
+       stream.zfree = NULL;
+       stream.opaque = NULL;
+       stream.next_in = in_buf;        
+       stream.avail_in = in_size;
+       stream.next_out = tmp_buf;
+       stream.avail_out = sizeof tmp_buf;
+       
+       if (out_size != NULL)
+               *out_size = 0;
+       
+       if (gzip)
+               result = inflateInit2(&stream, MAX_WBITS + 32); /* UNTESTED */
+       else
+               result = inflateInit2(&stream, -MAX_WBITS);
+       
+       if (result != 0) {
+               if (JG_ZLIB_DEBUG)
+                       fprintf(stderr, "inflateInit2 failed: %d\n", result);
+               return JG_ZLIB_ERROR_OLDVERSION;
+       }
+       
+       do {
+               /* Should be Z_FINISH? */
+               result = inflate(&stream, Z_NO_FLUSH);
+               switch (result) {
+                       case Z_BUF_ERROR:
+                               if (stream.avail_in == 0)
+                                       goto DONE; /* zlib bug */
+                       case Z_ERRNO:
+                       case Z_NEED_DICT:
+                       case Z_MEM_ERROR:
+                       case Z_DATA_ERROR:
+                       case Z_VERSION_ERROR:
+                               inflateEnd(&stream);
+                               free(out_buf);
+                               if (JG_ZLIB_DEBUG) {
+                                       snprintf (tmpstring, sizeof(tmpstring), "ERROR: zlib_uncompress: %d %s\n", result, stream.msg);
+                                       fprintf(stderr, tmpstring);
+                               }
+                               return JG_ZLIB_ERROR_UNCOMPRESS;
+               }
+               if (stream.avail_out < sizeof tmp_buf) {
+                       /* Add the new uncompressed data to our output buffer. */
+                       new_bytes = sizeof tmp_buf - stream.avail_out;
+                       out_buf = realloc(out_buf, out_buf_bytes + new_bytes);
+                       memcpy(out_buf + out_buf_bytes, tmp_buf, new_bytes);
+                       out_buf_bytes += new_bytes;
+                       stream.next_out = tmp_buf;
+                       stream.avail_out = sizeof tmp_buf;
+               } else {
+                       /* For some reason, inflate() didn't write out a single byte. */
+                       inflateEnd(&stream);
+                       free(out_buf);
+                       if (JG_ZLIB_DEBUG)
+                               fprintf(stderr, "ERROR: No output during decompression\n");
+                       return JG_ZLIB_ERROR_NODATA;
+               }
+       } while (result != Z_STREAM_END);
+       
+DONE:
+       
+       inflateEnd(&stream);
+       
+       /* Null-terminate the output buffer so it can be handled like a string. */
+       out_buf = realloc(out_buf, out_buf_bytes + 1);
+       out_buf[out_buf_bytes] = 0;
+       
+       /* The returned size does NOT include the additionall null byte! */
+       if (out_size != NULL)
+               *out_size = out_buf_bytes;
+       
+       *out_buf_ptr = out_buf;
+
+       return 0;
+}
+
+/* Decompressed gzip,deflate compressed data. This is what the webservers usually send. */
+
+int jg_gzip_uncompress(void *in_buf, int in_size, 
+                                          void **out_buf_ptr, int *out_size) 
+{
+       char tmpstring[1024];
+       struct gzip_header *header;
+       char *data_start;
+       int offset = sizeof *header;
+       
+       header = in_buf;
+       
+       if (out_size != NULL)
+               *out_size = 0;
+       
+       if ((header->magic[0] != 0x1F) || (header->magic[1] != 0x8B)) {
+               if (JG_ZLIB_DEBUG)
+                       fprintf(stderr, "ERROR: Invalid magic bytes for GZIP data\n");
+               return JG_ZLIB_ERROR_BAD_MAGIC;
+       }
+       
+       if (header->method != 8) {
+               if (JG_ZLIB_DEBUG)
+                       fprintf(stderr, "ERROR: Compression method is not deflate\n");
+               return JG_ZLIB_ERROR_BAD_METHOD;
+       }
+       
+       if (header->flags != 0 && header->flags != 8) {
+               if (JG_ZLIB_DEBUG) {
+                       snprintf (tmpstring, sizeof(tmpstring), "ERROR: Unsupported flags %d", header->flags);
+                       fprintf(stderr, "ERROR: %s\n", tmpstring);
+               }
+               return JG_ZLIB_ERROR_BAD_FLAGS;
+       }
+       
+       if (header->flags & 8) {
+               /* skip the file name */
+               while (offset < in_size) {
+                       if (((char *)in_buf)[offset] == 0) {
+                               offset++;
+                               break;
+                       }
+                       offset++;
+               }
+       }
+       
+       data_start = (char *)in_buf + offset;
+
+       return jg_zlib_uncompress(data_start, in_size - offset - 8, 
+                                                         out_buf_ptr, out_size, 0);
+}
diff --git a/rss2ctdl/zlib_interface.h b/rss2ctdl/zlib_interface.h
new file mode 100644 (file)
index 0000000..1ba72c1
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef JG_ZLIB_INTERFACE
+#define JG_ZLIB_INTERFACE
+
+enum JG_ZLIB_ERROR {
+       JG_ZLIB_ERROR_OLDVERSION = -1,
+       JG_ZLIB_ERROR_UNCOMPRESS = -2,
+       JG_ZLIB_ERROR_NODATA = -3,
+       JG_ZLIB_ERROR_BAD_MAGIC = -4,
+       JG_ZLIB_ERROR_BAD_METHOD = -5,
+       JG_ZLIB_ERROR_BAD_FLAGS = -6
+};
+
+extern int JG_ZLIB_DEBUG;
+
+int jg_zlib_uncompress(void *in_buf, int in_size, 
+                                      void **out_buf_ptr, int *out_size,
+                                          int gzip);
+
+int jg_gzip_uncompress(void *in_buf, int in_size, 
+                                          void **out_buf_ptr, int *out_size);
+
+#endif