[pan2/testing: 40/279] xzver and basic uploading support, experimental, non-functional at point of commit.



commit b95cdea376d23797834b43a454bf71074a14743c
Author: Heinrich MÃller <sphemuel stud informatik uni-erlangen de>
Date:   Wed May 25 06:40:34 2011 +0200

    xzver and basic uploading support, experimental, non-functional at point
    of commit.

 pan/data-impl/data-impl.h      |   17 ++++--
 pan/data-impl/server.cc        |   42 +++++++++++--
 pan/data/article.h             |    4 +-
 pan/data/data.h                |   18 ++++--
 pan/data/parts.cc              |    2 +-
 pan/data/server-info.h         |    6 ++
 pan/general/file-util.cc       |    6 ++-
 pan/general/file-util.h        |    6 +-
 pan/general/progress.cc        |    4 +-
 pan/general/string-view.h      |    6 +-
 pan/general/worker-pool.cc     |    1 -
 pan/gui/dl-headers-ui.cc       |    4 +-
 pan/gui/gui.cc                 |    3 +
 pan/gui/header-pane.cc         |   12 ++--
 pan/gui/pan.cc                 |   18 +++++-
 pan/gui/post-ui.cc             |   71 +++++++++-------------
 pan/gui/post-ui.h              |    4 +-
 pan/gui/prefs.h                |    4 +-
 pan/gui/server-ui.cc           |   23 +++++++-
 pan/gui/task-pane.cc           |    7 +-
 pan/tasks/Makefile.am          |    2 +
 pan/tasks/decoder.cc           |   14 +++--
 pan/tasks/nntp.cc              |   51 ++---------------
 pan/tasks/nntp.h               |   52 +++++++++++++++++
 pan/tasks/queue.cc             |   13 ++++-
 pan/tasks/task-article.cc      |    4 +-
 pan/tasks/task-post.cc         |    1 -
 pan/tasks/task-weak-ordering.h |    7 +-
 pan/tasks/task-xover.cc        |  125 ++++++++++++++++++++++++++++++++++------
 pan/tasks/task-xover.h         |   12 ++++
 30 files changed, 364 insertions(+), 175 deletions(-)
---
diff --git a/pan/data-impl/data-impl.h b/pan/data-impl/data-impl.h
index cc0855b..f794737 100644
--- a/pan/data-impl/data-impl.h
+++ b/pan/data-impl/data-impl.h
@@ -51,7 +51,7 @@ namespace pan
    *
    * Most of the files are stored in $PAN_HOME, which defaults to
    * $HOME/.pan2 if the PAN_HOME environmental variable isn't set.
-   * 
+   *
    * @ingroup data_impl
    */
   class DataImpl:
@@ -100,10 +100,11 @@ namespace pan
          int article_expiration_age;
          int max_connections;
          int rank;
+         int xzver;
          typedef sorted_vector<Quark,true,AlphabeticalQuarkOrdering> groups_t;
          groups_t groups;
 
-         Server(): port(119), article_expiration_age(31), max_connections(2), rank(1) {}
+         Server(): port(119), article_expiration_age(31), max_connections(2), rank(1), xzver(0) {}
       };
 
       typedef Loki::AssocVector<Quark,Server> servers_t;
@@ -123,11 +124,11 @@ namespace pan
       virtual void set_server_auth (const Quark       & server,
                                     const StringView  & username,
                                     const StringView  & password);
-                                                                                
+
       virtual void set_server_addr (const Quark       & server,
                                     const StringView  & host,
                                     const int           port);
-                                                                                
+
       virtual void set_server_limits (const Quark     & server,
                                       int               max_connections);
 
@@ -136,6 +137,10 @@ namespace pan
       virtual void set_server_article_expiration_age  (const Quark  & server,
                                                        int            days);
 
+      virtual void save_server_info (const Quark& server);
+
+      virtual void set_server_xzver_support (const Quark& server, const int val);
+
     public: // accessors
 
       virtual quarks_t get_servers () const {
@@ -148,7 +153,7 @@ namespace pan
       virtual bool get_server_auth (const Quark   & server,
                                     std::string   & setme_username,
                                     std::string   & setme_password) const;
-                                                                                
+
       virtual bool get_server_addr (const Quark   & server,
                                     std::string   & setme_host,
                                     int           & setme_port) const;
@@ -161,6 +166,8 @@ namespace pan
 
       virtual int get_server_article_expiration_age  (const Quark  & server) const;
 
+      virtual int get_server_xzver_support (const Quark& server) const;
+
     /**
     *** GROUPS
     **/
diff --git a/pan/data-impl/server.cc b/pan/data-impl/server.cc
index 7dfa952..a5da24f 100644
--- a/pan/data-impl/server.cc
+++ b/pan/data-impl/server.cc
@@ -107,9 +107,19 @@ DataImpl :: set_server_article_expiration_age  (const Quark  & server,
 
   s->article_expiration_age = std::max (0, days);
 
-  save_server_properties (*_data_io);
+//  save_server_properties (*_data_io);
 }
 
+void
+DataImpl :: set_server_xzver_support (const Quark& server, const int val)
+{
+  if (val<0 || val>1) return;
+  Server * s (find_server (server));
+  assert (s != 0);
+
+  s->xzver = val;
+
+}
 
 void
 DataImpl :: set_server_auth (const Quark       & server,
@@ -122,7 +132,7 @@ DataImpl :: set_server_auth (const Quark       & server,
   s->username = username;
   s->password = password;
 
-  save_server_properties (*_data_io);
+//  save_server_properties (*_data_io);
 }
 
 void
@@ -134,7 +144,7 @@ DataImpl :: set_server_addr (const Quark       & server,
   assert (s != 0);
   s->host = host;
   s->port = port;
-  save_server_properties (*_data_io);
+//  save_server_properties (*_data_io);
 }
 
 
@@ -145,7 +155,7 @@ DataImpl :: set_server_limits (const Quark   & server,
   Server * s (find_server (server));
   assert (s != 0);
   s->max_connections = max_connections;
-  save_server_properties (*_data_io);
+//  save_server_properties (*_data_io);
 }
 
 void
@@ -155,6 +165,14 @@ DataImpl :: set_server_rank (const Quark   & server,
   Server * s (find_server (server));
   assert (s != 0);
   s->rank = rank;
+//  save_server_properties (*_data_io);
+}
+
+void
+DataImpl :: save_server_info (const Quark& server)
+{
+  Server * s (find_server (server));
+  assert (s != 0);
   save_server_properties (*_data_io);
 }
 
@@ -229,6 +247,16 @@ DataImpl :: get_server_article_expiration_age  (const Quark  & server) const
   return retval;
 }
 
+int
+DataImpl :: get_server_xzver_support  (const Quark  & server) const
+{
+  int ret(0);
+  const Server * s (find_server (server));
+  if (s != 0)
+    ret = s->xzver;
+  return ret;
+}
+
 /***
 ****
 ***/
@@ -324,6 +352,7 @@ DataImpl :: load_server_properties (const DataIO& source)
     s.article_expiration_age = to_int(kv["expire-articles-n-days-old"], 31);
     s.rank = to_int(kv["rank"], 1);
     s.newsrc_filename = kv["newsrc"];
+    s.xzver = to_int(kv["xzver"],1);
     if (s.newsrc_filename.empty()) { // set a default filename
       std::ostringstream o;
       o << file::get_pan_home() << G_DIR_SEPARATOR << "newsrc-" << it->first;
@@ -331,7 +360,7 @@ DataImpl :: load_server_properties (const DataIO& source)
     }
   }
 
-  save_server_properties (*const_cast<DataIO*>(&source));
+//  save_server_properties (*const_cast<DataIO*>(&source));
 }
 
 namespace
@@ -376,8 +405,7 @@ DataImpl :: save_server_properties (DataIO& data_io) const
          << indent(depth) << "<connection-limit>" << s->max_connections << "</connection-limit>\n"
          << indent(depth) << "<newsrc>" << s->newsrc_filename << "</newsrc>\n"
          << indent(depth) << "<rank>" << s->rank << "</rank>\n"
-         //todo
-         << indent(depth) << "<xzver>" << s->rank << "</xzver>\n";
+         << indent(depth) << "<xzver>" << s->xzver << "</xzver>\n";
 
     *out << indent(--depth) << "</server>\n";
   }
diff --git a/pan/data/article.h b/pan/data/article.h
index a2e0f9b..0d905b9 100644
--- a/pan/data/article.h
+++ b/pan/data/article.h
@@ -31,7 +31,7 @@ namespace pan
 {
   /**
    * A Usenet article, either single-part or multipart.
-   *
+   * 
    * To lessen the memory footprint of large binaries groups,
    * Pan folds multipart posts into a single Article object.
    * Only minimal information for any one part is kept
@@ -50,7 +50,6 @@ namespace pan
       void set_parts (const PartBatch& b) { parts.set_parts(b); }
       bool add_part (Parts::number_t num, const StringView& mid, Parts::bytes_t bytes) { return parts.add_part(num,mid,bytes,message_id); }
       void set_part_count (Parts::number_t num) { parts.set_part_count(num); }
-
       Parts::number_t get_total_part_count () const { return parts.get_total_part_count(); }
       Parts::number_t get_found_part_count () const { return parts.get_found_part_count(); }
       bool get_part_info (Parts::number_t      num,
@@ -87,7 +86,6 @@ namespace pan
 
     public:
       Article (): time_posted(0), lines(0), score(0), is_binary(false)  {}
-      Article (const bool bin): time_posted(0), lines(0), score(0), is_binary(bin)  {}
       void clear ();
 
     private:
diff --git a/pan/data/data.h b/pan/data/data.h
index 23e9823..4e03785 100644
--- a/pan/data/data.h
+++ b/pan/data/data.h
@@ -64,7 +64,7 @@ namespace pan
    *
    * Also, a read article should change back to unread if it changes
    * from an incomplete multipart to a complete multipart as new
-   * parts are added to it. 
+   * parts are added to it.
    *
    * @ingroup data
    */
@@ -132,7 +132,7 @@ namespace pan
     public:
       virtual void delete_profile (const std::string& profile_name) = 0;
       virtual void add_profile (const std::string& profile_name, const Profile& profile) = 0;
-      
+
     protected:
       Profiles () {}
   };
@@ -168,9 +168,9 @@ namespace pan
 
     public:
 
-      virtual ArticleCache& get_cache () = 0; 
+      virtual ArticleCache& get_cache () = 0;
 
-      virtual const ArticleCache& get_cache () const = 0; 
+      virtual const ArticleCache& get_cache () const = 0;
 
     public:
 
@@ -191,6 +191,8 @@ namespace pan
       virtual void set_server_limits (const Quark     & server,
                                       int               max_connections) = 0;
 
+      virtual void set_server_xzver_support (const Quark& server, int setme) = 0;
+
       virtual bool get_server_addr (const Quark   & server,
                                     std::string   & setme_address,
                                     int           & setme_port) const = 0;
@@ -201,6 +203,8 @@ namespace pan
 
       virtual int get_server_limits (const Quark & server) const = 0;
 
+      virtual int get_server_xzver_support (const Quark& server) const = 0;
+
     /*****************************************************************
     ***
     ***  OBSERVERS
@@ -354,7 +358,7 @@ namespace pan
            *
            * In the case of new articles, the Tree's existing filter
            * is applied to them and new articles that survive the filter
-           * are threaded into the tree. 
+           * are threaded into the tree.
            *
            * In the case of deleted articles, their children are
            * reparented to the youngest surviving ancestor and then the
@@ -442,7 +446,7 @@ namespace pan
                 (*it++)->on_tree_change (d);
             }
           }
-              
+
 
         /*************************************************************
         ***
@@ -555,7 +559,7 @@ namespace pan
 
       /**
        * Returns the high number of the most recent XOVER command
-       * run on the specified {server,group}, or 0 if it's never 
+       * run on the specified {server,group}, or 0 if it's never
        * been run there.
        */
       virtual uint64_t get_xover_high (const Quark  & group,
diff --git a/pan/data/parts.cc b/pan/data/parts.cc
index 8b3181e..11cfe71 100644
--- a/pan/data/parts.cc
+++ b/pan/data/parts.cc
@@ -87,7 +87,7 @@ namespace
     for (; b!=bmax; ++b)
       if (*k++ != *m++)
         break;
-
+    
     const int emax = std::min (shorter-b, (int)UCHAR_MAX);
     k = &key.back();
     m = &mid.back();
diff --git a/pan/data/server-info.h b/pan/data/server-info.h
index bcdc3f0..694f781 100644
--- a/pan/data/server-info.h
+++ b/pan/data/server-info.h
@@ -61,6 +61,10 @@ namespace pan
       virtual void set_server_rank (const Quark& server,
                                     int          rank) = 0;
 
+      virtual void set_server_xzver_support (const Quark& server, const int val) = 0;
+
+      virtual void save_server_info (const Quark& server) = 0;
+
     public: // accessors
 
       virtual bool get_server_auth (const Quark   & servername,
@@ -78,6 +82,8 @@ namespace pan
       virtual int get_server_limits (const Quark & server) const = 0;
 
       virtual int get_server_article_expiration_age  (const Quark  & server) const = 0;
+
+      virtual int get_server_xzver_support (const Quark& server) const = 0;
   };
 }
 
diff --git a/pan/general/file-util.cc b/pan/general/file-util.cc
index fc5cd50..a38367e 100644
--- a/pan/general/file-util.cc
+++ b/pan/general/file-util.cc
@@ -51,7 +51,11 @@ using namespace pan;
 std::string
 file :: get_pan_uulib_dir()
 {
-    return std::string("/home/imhotep/.pan2/uulib-encode-cache");
+  char * pch (g_build_filename (file::get_pan_home().c_str(), "uulib-encode-cache", NULL));
+  file :: ensure_dir_exists (pch);
+  std::string path (pch);
+  g_free (pch);
+  return path;
 }
 
 std::string
diff --git a/pan/general/file-util.h b/pan/general/file-util.h
index 6e6d3a6..7928ce5 100644
--- a/pan/general/file-util.h
+++ b/pan/general/file-util.h
@@ -49,10 +49,6 @@ namespace pan
    */
   namespace file
   {
-
-    /** Returns the directory for encoded files, should be temporary */
-    std::string get_pan_uulib_dir ();
-
     /** just like strerror but never returns NULL */
     const char * pan_strerror (int error_number);
 
@@ -62,6 +58,8 @@ namespace pan
      */
     std::string get_pan_home ();
 
+    std::string get_pan_uulib_dir();
+
     /**
      * If the specified directory doesn't exist, Pan tries to create it.
      * @param path
diff --git a/pan/general/progress.cc b/pan/general/progress.cc
index b292ded..a01a9e5 100644
--- a/pan/general/progress.cc
+++ b/pan/general/progress.cc
@@ -31,8 +31,8 @@ using namespace pan;
 
 void
 Progress :: fire_percentage (int p) {
-//  for (listeners_cit it(_listeners.begin()), end(_listeners.end()); it!=end; )
-//    (*it++)->on_progress_step (*this, p);
+  for (listeners_cit it(_listeners.begin()), end(_listeners.end()); it!=end; )
+    (*it++)->on_progress_step (*this, p);
 }
 void
 Progress :: fire_pulse () {
diff --git a/pan/general/string-view.h b/pan/general/string-view.h
index c7fb901..9b5c2d9 100644
--- a/pan/general/string-view.h
+++ b/pan/general/string-view.h
@@ -28,7 +28,7 @@
 namespace pan
 {
    /**
-    * A shallow copy a C string, plus utilities to let us
+    * A shallow copy a C string, plus utilities to let us 
     * substring, tokenize, walk, search, or otherwise
     * manipulate it without having to modify the original or
     * allocate new strings.
@@ -43,12 +43,12 @@ namespace pan
                             size_t       str_a_len,
                             const char * str_b,
                             size_t       str_b_len);
-
+                                                                                                                        
          static char* strchr (const char * haystack,
                               size_t       haystack_len,
                               char         needle)
            { return (char*) memchr (haystack, needle, haystack_len); }
-
+                                                                                                                        
          static char* strrchr (const char * haystack,
                                size_t       haystack_len,
                                char         needle);
diff --git a/pan/general/worker-pool.cc b/pan/general/worker-pool.cc
index bdbf809..6ff4b76 100644
--- a/pan/general/worker-pool.cc
+++ b/pan/general/worker-pool.cc
@@ -63,7 +63,6 @@ WorkerPool :: push_work (Worker *w, Worker::Listener *l, bool delete_worker)
   w->silent = false;
   w->pool = this;
   w->listener = l;
-  if (!l) std::cerr<<"oops, listener in push_work is null!\n";
   w->delete_worker = delete_worker;
   my_workers.insert (w);
   g_thread_pool_push (tpool, w, 0); // jump to worker_thread_func
diff --git a/pan/gui/dl-headers-ui.cc b/pan/gui/dl-headers-ui.cc
index 689c0e5..383a863 100644
--- a/pan/gui/dl-headers-ui.cc
+++ b/pan/gui/dl-headers-ui.cc
@@ -66,7 +66,7 @@ namespace
     if (response == GTK_RESPONSE_ACCEPT)
     {
       const bool mark_read (state->prefs.get_flag ("mark-group-read-before-xover", false));
-        // xzver
+
       foreach_const (quarks_t, state->groups, it) {
         Task * task;
         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(state->all_headers_rb)))
@@ -112,7 +112,7 @@ pan :: headers_dialog (Data& data, Prefs& prefs, Queue& queue,
       g_snprintf (buf, sizeof(buf), _("%d Groups"), (int)groups.size());
       title += buf;
     }
-
+    
     State * state = new State (data, prefs, queue);
     state->groups = groups;
     state->dialog = gtk_dialog_new_with_buttons (
diff --git a/pan/gui/gui.cc b/pan/gui/gui.cc
index bfce0ae..7606b85 100644
--- a/pan/gui/gui.cc
+++ b/pan/gui/gui.cc
@@ -1606,6 +1606,7 @@ void GUI :: do_read_selected_group ()
     else if (_prefs.get_flag("get-new-headers-when-entering-group", true)) {
       if (_prefs.get_flag ("mark-group-read-before-xover", false))
         _data.mark_group_read (group);
+//       todo xzver
       _queue.add_task (new TaskXOver (_data, group, TaskXOver::NEW), Queue::TOP);
     }
   }
@@ -1630,6 +1631,7 @@ void GUI :: do_xover_selected_groups ()
   foreach_const (quarks_v, groups, it) {
     if (mark_read)
       _data.mark_group_read (*it);
+      //todo xzver
     _queue.add_task (new TaskXOver (_data, *it, TaskXOver::NEW), Queue::TOP);
   }
 }
@@ -1642,6 +1644,7 @@ void GUI :: do_xover_subscribed_groups ()
   const bool mark_read (_prefs.get_flag ("mark-group-read-before-xover", false));
   foreach_const_r (quarks_v, groups, it) {
     if (mark_read)
+    //todo xzver
       _data.mark_group_read (*it);
     _queue.add_task (new TaskXOver (_data, *it, TaskXOver::NEW), Queue::TOP);
   }
diff --git a/pan/gui/header-pane.cc b/pan/gui/header-pane.cc
index 38d9deb..f2fa81e 100644
--- a/pan/gui/header-pane.cc
+++ b/pan/gui/header-pane.cc
@@ -130,7 +130,7 @@ namespace
                       const Quark         & message_id)
   {
     int offset (ICON_EMPTY);
-
+      
     if (queue.contains (message_id))
       offset = ICON_QUEUED;
     else if (cache.contains (message_id))
@@ -318,7 +318,7 @@ HeaderPane :: create_row (const EvolutionDateMaker & e,
 
   std::pair<mid_to_row_t::iterator,bool> result (_mid_to_row.insert (row));
   g_assert (result.second);
-
+  
   return row;
 }
 
@@ -687,7 +687,7 @@ HeaderPane :: on_tree_change (const Data::ArticleTree::Diffs& diffs)
     }
     _tree_store->insert_sorted (tmp);
   }
-
+     
   // reparent...
   if (do_thread && !diffs.reparented.empty()) {
     PanTreeStore::parent_to_children_t tmp;
@@ -919,7 +919,7 @@ HeaderPane :: on_button_pressed (GtkWidget * treeview, GdkEventButton *event, gp
     GtkTreeSelection * selection = gtk_tree_view_get_selection(tv);
     GtkTreePath *path;
     if (gtk_tree_view_get_path_at_pos (tv,
-                                       (gint) event->x,
+                                       (gint) event->x, 
                                        (gint) event->y,
                                        &path, NULL, NULL, NULL))
     {
@@ -1564,7 +1564,6 @@ HeaderPane :: on_selection_changed_idle (gpointer self_gpointer)
     "download-selected-article",
     "save-articles",
     "save-articles-from-nzb",
-    "save-articles-to-nzb",
     "read-selected-article",
     "show-selected-article-info",
     "mark-article-read",
@@ -1631,6 +1630,7 @@ HeaderPane :: HeaderPane (ActionManager       & action_manager,
   g_signal_connect (sel, "changed", G_CALLBACK(on_selection_changed), this);
   on_selection_changed (sel, this);
 
+
   g_signal_connect (w, "button-release-event", G_CALLBACK(on_button_pressed), this);
   g_signal_connect (w, "button-press-event", G_CALLBACK(on_button_pressed), this);
   g_signal_connect (w, "row-collapsed", G_CALLBACK(row_collapsed_cb), NULL);
@@ -1868,7 +1868,7 @@ namespace
 }
 
 /**
-***
+*** 
 **/
 
 void
diff --git a/pan/gui/pan.cc b/pan/gui/pan.cc
index 5268ef7..555c660 100644
--- a/pan/gui/pan.cc
+++ b/pan/gui/pan.cc
@@ -36,6 +36,7 @@ extern "C" {
 #include <pan/tasks/socket-impl-gio.h>
 #include <pan/tasks/task-groups.h>
 #include <pan/tasks/task-xover.h>
+#include <pan/tasks/task-xzver.h>
 #include <pan/tasks/nzb.h>
 #include <pan/data-impl/data-impl.h>
 #include <pan/icons/pan-pixbufs.h>
@@ -118,6 +119,15 @@ namespace
     g_free (foo);
   }
 
+  void add_xzver_test_task (GtkObject *, gpointer user_data)
+  {
+    DataAndQueue * foo (static_cast<DataAndQueue*>(user_data));
+    const quarks_t new_servers (foo->data->get_servers());
+    foreach (quarks_t, new_servers, it)
+      foo->queue->add_task (new TaskXZVer (*it, *foo->data));
+    g_free (foo);
+  }
+
   gboolean queue_upkeep_timer_cb (gpointer queue_gpointer)
   {
     static_cast<Queue*>(queue_gpointer)->upkeep ();
@@ -155,7 +165,9 @@ namespace
         DataAndQueue * foo = g_new0 (DataAndQueue, 1);
         foo->data = &data;
         foo->queue = &queue;
-        g_signal_connect (w, "destroy", G_CALLBACK(add_grouplist_task), foo);
+        g_signal_connect (w, "destroy", G_CALLBACK(add_xzver_test_task), foo);
+//        g_signal_connect (w, "destroy", G_CALLBACK(add_grouplist_task), foo);
+
       }
 
       register_shutdown_signals ();
@@ -175,7 +187,7 @@ namespace
     ~PanKiller() { q.remove_listener(this); }
 
     /** Method from Queue::Listener interface: quits program on zero sized Q*/
-    void on_queue_size_changed (Queue&, int active, int total) 
+    void on_queue_size_changed (Queue&, int active, int total)
       {  if (!active && !total) mainloop_quit();  }
 
     // all below methods from Queue::Listener interface are noops
@@ -278,7 +290,7 @@ main (int argc, char *argv[])
       nzb = true;
     else if (!strcmp (tok, "--version"))
       { std::cerr << "Pan " << VERSION << '\n'; return 0; }
-    else if (!strcmp (tok, "-o") && i<argc-1) 
+    else if (!strcmp (tok, "-o") && i<argc-1)
       nzb_output_path = argv[++i];
     else if (!memcmp (tok, "--output=", 9))
       nzb_output_path = tok+9;
diff --git a/pan/gui/post-ui.cc b/pan/gui/post-ui.cc
index 4271798..018bd9f 100644
--- a/pan/gui/post-ui.cc
+++ b/pan/gui/post-ui.cc
@@ -369,8 +369,6 @@ bool
 PostUI :: check_message (const Quark& server, GMimeMessage * msg)
 {
 
-  std::cerr<<"check message\n";
-
   MessageCheck :: unique_strings_t errors;
   MessageCheck :: Goodness goodness;
 
@@ -486,22 +484,11 @@ PostUI :: send_now ()
 {
   if (!check_charset())
     return;
-  GMimeMessage * message (new_message_from_ui (POSTING));
+  GMimeMessage * message (new_message_from_ui (POSTING,get_body()));
   if (!maybe_post_message (message))
     g_object_unref (G_OBJECT(message));
 }
 
-//void
-//PostUI :: send_binfiles_now()
-//{
-//  if (!check_charset())
-//    return;
-//  GMimeMessage * message (new_message_from_ui (POSTING));
-//
-//  //change headers according to
-//  g_object_unref (G_OBJECT(message));
-//}
-
 void
 PostUI :: done_sending_message (GMimeMessage * message, bool ok)
 {
@@ -593,16 +580,18 @@ PostUI :: on_progress_finished (Progress&, int status) // posting finished
   int bufsz=2048;
   char buf[bufsz];
 
-  _post_task->remove_listener (this);
-  gtk_widget_destroy (_post_dialog);
-
   if (_file_queue_empty) {
-    GMimeMessage * message (((TaskPost*)_post_task)->get_message ());
-    if (status != OK) // error posting.. stop.
-      done_sending_message (message, false);
-    else
-      maybe_mail_message (message);
+    _post_task->remove_listener (this);
+    gtk_widget_destroy (_post_dialog);
+
   }
+//  if (_file_queue_empty) {
+//    GMimeMessage * message (((TaskPost*)_post_task)->get_message ());
+//    if (status != OK) // error posting.. stop.
+//      done_sending_message (message, false);
+//    else
+//      maybe_mail_message (message);
+//  }
 //  else
 //  {
 //    GMimeMessage * message (((TaskUpload*)_upload_task)->get_message ());
@@ -726,13 +715,12 @@ PostUI :: maybe_post_message (GMimeMessage * message)
     FileQueue::articles_it it = _file_queue.begin();
 
     for (; it != _file_queue.end(); it, ++it, ++i) {
-      GMimeMessage* msg = new_message_from_ui(POSTING);
-      _queue.add_task (new TaskUpload (*it,profile.posting_server,msg
-                                     ,0,TaskUpload::YENC,i),
-                                     Queue::BOTTOM);
+      // get message body first, later on the attachment's content is added to the message
+      GMimeMessage* msg = new_message_from_ui (BINPOST, get_body());
+      TaskUpload* t =new TaskUpload (*it,server,msg,0,TaskUpload::YENC,i);
+      _queue.add_task (t,Queue::BOTTOM);
     }
-  //dbg
-//    close_window(true); // dont wait for the upload queue
+    close_window(true); // dont wait for the upload queue
   }
 
 
@@ -1004,7 +992,7 @@ namespace
 }
 
 GMimeMessage*
-PostUI :: new_message_from_ui (Mode mode)
+PostUI :: new_message_from_ui (Mode mode, std::string body)
 {
   GMimeMessage * msg (g_mime_message_new (false));
 
@@ -1063,11 +1051,11 @@ PostUI :: new_message_from_ui (Mode mode)
   g_free (pch);
 
   // User-Agent
-  if (mode==POSTING && _prefs.get_flag (USER_AGENT_PREFS_KEY, true))
+  if ((mode==POSTING || mode == BINPOST ) && _prefs.get_flag (USER_AGENT_PREFS_KEY, true))
     g_mime_object_set_header ((GMimeObject *) msg, "User-Agent", get_user_agent());
 
   // Message-ID
-  if (mode==POSTING && _prefs.get_flag (MESSAGE_ID_PREFS_KEY, false)) {
+  if ((mode==POSTING || mode == BINPOST ) && _prefs.get_flag (MESSAGE_ID_PREFS_KEY, false)) {
     const std::string message_id = !profile.fqdn.empty()
       ? GNKSA::generate_message_id (profile.fqdn)
       : GNKSA::generate_message_id_from_email_address (profile.address);
@@ -1075,10 +1063,9 @@ PostUI :: new_message_from_ui (Mode mode)
   }
 
   // body & charset
-  std::string body (get_body());
   GMimeStream * stream = g_mime_stream_mem_new_with_buffer (body.c_str(), body.size());
   const std::string charset ((mode==POSTING && !_charset.empty()) ? _charset : "UTF-8");
-  if (charset != "UTF-8") {
+  if (charset != "UTF-8") {  //dbg (??)
     // add a wrapper to convert from UTF-8 to $charset
     GMimeStream * tmp = g_mime_stream_filter_new (stream);
     g_object_unref (stream);
@@ -1090,7 +1077,12 @@ PostUI :: new_message_from_ui (Mode mode)
   GMimeDataWrapper * content_object = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_DEFAULT);
   g_object_unref (stream);
   GMimePart * part = g_mime_part_new ();
-  pch = g_strdup_printf ("text/plain; charset=%s", charset.c_str());
+
+  if (mode==BINPOST)
+    pch = g_strdup_printf ("multipart/mixed; charset=%s", charset.c_str());
+  else
+    pch = g_strdup_printf ("text/plain; charset=%s", charset.c_str());
+
   GMimeContentType * type = g_mime_content_type_new_from_string (pch);
   g_free (pch);
   g_mime_object_set_content_type ((GMimeObject *) part, type); // part owns type now. type isn't refcounted.
@@ -1123,7 +1115,7 @@ PostUI :: save_draft ()
 
   if (gtk_dialog_run(GTK_DIALOG(d)) == GTK_RESPONSE_ACCEPT)
   {
-    GMimeMessage * msg = new_message_from_ui (DRAFTING);
+    GMimeMessage * msg = new_message_from_ui (DRAFTING, get_body());
     char * filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(d));
     draft_filename = filename;
 
@@ -1824,7 +1816,6 @@ PostUI :: create_main_tab ()
   // Newsgroup
 
   ++row;
-  std::cerr<<" create main tab\n";
   g_snprintf (buf, sizeof(buf), "<b>%s:</b>", _("_Newsgroups"));
   l = gtk_label_new_with_mnemonic (buf);
   gtk_label_set_use_markup (GTK_LABEL(l), true);
@@ -2010,8 +2001,8 @@ PostUI :: update_filequeue_tab()
     {
       gtk_list_store_append (store, &iter);
       gtk_list_store_set (store, &iter,
-                        0, (*it)->basename,
-                        1, (*it)->byte_count,
+                        0, (*it).basename,
+                        1, (*it).byte_count/1024,
                         -1);
     }
 
@@ -2055,7 +2046,6 @@ PostUI :: PostUI (GtkWindow    * parent,
   _charset (DEFAULT_CHARSET),
   _group_entry_changed_id (0),
   _group_entry_changed_idle_tag (0),
-  //binpost
   _file_queue_empty(true)
 {
   g_assert (profiles.has_profiles());
@@ -2153,9 +2143,6 @@ PostUI :: prompt_user_for_queueable_files (FileQueue& queue, GtkWindow * parent,
 		GSList * tmp_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER (w));
 		if (_file_queue_empty) _file_queue_empty=!_file_queue_empty;
 		GSList * cur = g_slist_nth (tmp_list,0);
-    const Profile profile (get_current_profile ());
-    const StringView subject(gtk_entry_get_text (GTK_ENTRY(_subject_entry)));
-    struct stat stat_buf;
 
     for (; cur; cur = cur->next)
 		{
diff --git a/pan/gui/post-ui.h b/pan/gui/post-ui.h
index 43b16bf..584ecd3 100644
--- a/pan/gui/post-ui.h
+++ b/pan/gui/post-ui.h
@@ -129,8 +129,8 @@ namespace pan
       void add_actions (GtkWidget* box);
       void apply_profile_to_body ();
       void apply_profile_to_headers ();
-      enum Mode { DRAFTING, POSTING };
-      GMimeMessage * new_message_from_ui (Mode mode);
+      enum Mode { DRAFTING, POSTING, BINPOST };
+      GMimeMessage * new_message_from_ui (Mode mode, std::string body);
       Profile get_current_profile ();
       bool check_message (const Quark& server, GMimeMessage*);
       bool check_charset ();
diff --git a/pan/gui/prefs.h b/pan/gui/prefs.h
index d475b8f..0969fca 100644
--- a/pan/gui/prefs.h
+++ b/pan/gui/prefs.h
@@ -25,9 +25,7 @@
 #include <string>
 #include <vector>
 #include <pan/general/string-view.h>
-extern "C" {
-  #include <gtk/gtk.h>
-}
+#include <gtk/gtk.h>
 
 namespace pan
 {
diff --git a/pan/gui/server-ui.cc b/pan/gui/server-ui.cc
index ffca9d9..7bfa708 100644
--- a/pan/gui/server-ui.cc
+++ b/pan/gui/server-ui.cc
@@ -91,7 +91,7 @@ namespace
 
     d->server = server;
 
-    int port(119), max_conn(4), age(31*3), rank(1);
+    int port(119), max_conn(4), age(31*3), rank(1), xzver(0);
     std::string addr, user, pass;
     if (!server.empty()) {
       d->data.get_server_addr (server, addr, port);
@@ -99,6 +99,7 @@ namespace
       age = d->data.get_server_article_expiration_age (server);
       rank = d->data.get_server_rank (server);
       max_conn = d->data.get_server_limits (server);
+      xzver = d->data.get_server_xzver_support(server);
     }
 
     pan_entry_set_text (d->address_entry, addr);
@@ -131,6 +132,18 @@ namespace
         break;
       }
     } while (gtk_tree_model_iter_next(model, &iter));
+
+    // set the xzver combo box
+    combo = GTK_COMBO_BOX (d->xzver_compression_combo);
+    model = gtk_combo_box_get_model (combo);
+    if (gtk_tree_model_get_iter_first(model, &iter)) do {
+      int that;
+      gtk_tree_model_get (model, &iter, 1, &that, -1);
+      if (that == xzver) {
+        gtk_combo_box_set_active_iter (combo, &iter);
+        break;
+      }
+    } while (gtk_tree_model_iter_next(model, &iter));
   }
 
   void
@@ -159,6 +172,10 @@ namespace
       combo = GTK_COMBO_BOX (d->rank_combo);
       if (gtk_combo_box_get_active_iter (combo, &iter))
         gtk_tree_model_get (gtk_combo_box_get_model(combo), &iter, 1, &rank, -1);
+      int xzver(0);
+      combo = GTK_COMBO_BOX (d->xzver_compression_combo);
+      if (gtk_combo_box_get_active_iter (combo, &iter))
+        gtk_tree_model_get (gtk_combo_box_get_model(combo), &iter, 1, &xzver, -1);
       const char * err_msg (0);
       if (addr.empty())
         err_msg = _("Please specify the server's address.");
@@ -180,6 +197,8 @@ namespace
         d->data.set_server_limits (d->server, max_conn);
         d->data.set_server_article_expiration_age (d->server, age);
         d->data.set_server_rank (d->server, rank);
+        d->data.set_server_xzver_support (d->server, xzver);
+        d->data.save_server_info(d->server);
         d->queue.upkeep ();
       }
     }
@@ -319,7 +338,7 @@ pan :: server_edit_dialog_new (Data& data, Queue& queue, GtkWindow * window, con
     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w), renderer2, TRUE);
     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w), renderer2, "text", 0, NULL);
     gtk_combo_box_set_active (GTK_COMBO_BOX(w), 0);
-    HIG::workarea_add_row (t, &row, _("Serverside _Header-compression support:"), w, NULL);
+    HIG::workarea_add_row (t, &row, _("_Header-compression support (XZVER) :"), w, NULL);
 
   d->server = server;
   edit_dialog_populate (data, server, d);
diff --git a/pan/gui/task-pane.cc b/pan/gui/task-pane.cc
index 8e7c938..15066e5 100644
--- a/pan/gui/task-pane.cc
+++ b/pan/gui/task-pane.cc
@@ -208,11 +208,11 @@ TaskPane :: update_status (const task_states_t& tasks)
   {
     Task * task (*it);
     const Queue::TaskState state (tasks.get_state (task));
-    if (state == Queue::RUNNING || state == Queue::DECODING || state == Queue::ENCODING)
+    if (state == Queue::RUNNING || state == Queue::DECODING)
       ++running_count;
     else if (state == Queue::STOPPED)
       ++stopped_count;
-    else if (state == Queue::QUEUED || state == Queue::QUEUED_FOR_DECODE || state == Queue::QUEUED_FOR_ENCODE)
+    else if (state == Queue::QUEUED || state == Queue::QUEUED_FOR_DECODE)
       ++queued_count;
 
     if (state==Queue::RUNNING || state==Queue::QUEUED)
@@ -225,7 +225,7 @@ TaskPane :: update_status (const task_states_t& tasks)
     g_snprintf (buf, sizeof(buf), _("Pan: Tasks (%d Queued, %d Running, %d Stopped)"), queued_count, running_count, stopped_count);
   else if (running_count || queued_count)
     g_snprintf (buf, sizeof(buf), _("Pan: Tasks (%d Queued, %d Running)"), queued_count, running_count);
-  else
+  else 
     g_snprintf (buf, sizeof(buf), _("Pan: Tasks"));
   gtk_window_set_title (GTK_WINDOW(_root), buf);
 
@@ -240,7 +240,6 @@ TaskPane :: update_status (const task_states_t& tasks)
     minutes = tmp % 60ul; tmp /= 60ul;
     hours   = tmp;
   }
-
   g_snprintf (buf, sizeof(buf), _("%lu tasks, %s, %.1f KiBps, ETA %d:%02d:%02d"),
               task_count, render_bytes(bytes), KiBps, hours, minutes, seconds);
   std::string line (buf);
diff --git a/pan/tasks/Makefile.am b/pan/tasks/Makefile.am
index a1b5b3d..fb621d3 100644
--- a/pan/tasks/Makefile.am
+++ b/pan/tasks/Makefile.am
@@ -10,6 +10,7 @@ libtasks_a_SOURCES = \
   task-groups.cc \
   task-post.cc \
   task-xover.cc \
+  task-xzver.cc \
   task-upload.cc \
   nntp.cc \
   nzb.cc \
@@ -33,6 +34,7 @@ noinst_HEADERS = \
   task-upload.h \
   task-weak-ordering.h \
   task-xover.h \
+  task-xzver.h \
   nntp.h  \
   nzb.h  \
   queue.h  \
diff --git a/pan/tasks/decoder.cc b/pan/tasks/decoder.cc
index 01616fd..5e8bfa8 100644
--- a/pan/tasks/decoder.cc
+++ b/pan/tasks/decoder.cc
@@ -171,8 +171,8 @@ Decoder :: do_work()
           file :: ensure_dir_exists (save_path.c_str());
 
         // find a unique filename...
-        char * fname = file::get_unique_fname(save_path.c_str(),
-                                              (item->filename
+        char * fname = file::get_unique_fname(save_path.c_str(), 
+                                              (item->filename 
                                                && *item->filename)
                                               ? item->filename
                                               : "pan-saved-file" );
@@ -296,11 +296,13 @@ Decoder :: progress_update_timer_func (gpointer decoder)
   if (!task || self->was_cancelled()) return false;
 
   self->mut.lock();
-    const double percent (self->percent);
-    const std::string f (content_to_utf8 (self->current_file));
-    task->set_step(int(percent));
-    task->set_status_va (_("Decoding %s"), f.c_str());
+  const double percent (self->percent);
+  const std::string f (content_to_utf8 (self->current_file));
   self->mut.unlock();
+
+  task->set_step(int(percent));
+  task->set_status_va (_("Decoding %s"), f.c_str());
+
   return true; // keep timer func running
 }
 
diff --git a/pan/tasks/nntp.cc b/pan/tasks/nntp.cc
index 2d45eaa..45891f2 100644
--- a/pan/tasks/nntp.cc
+++ b/pan/tasks/nntp.cc
@@ -54,51 +54,6 @@ namespace
    }
 };
 
-namespace
-{
-   enum
-   {
-      AUTH_REQUIRED              = 480,
-      AUTH_NEED_MORE             = 381,
-      AUTH_ACCEPTED              = 281,
-      AUTH_REJECTED              = 482,
-
-      SERVER_READY               = 200,
-      SERVER_READY_NO_POSTING    = 201,
-      SERVER_READY_STREAMING_OK  = 203,
-
-      GOODBYE                    = 205,
-
-      GROUP_RESPONSE             = 211,
-      GROUP_NONEXISTENT          = 411,
-
-      INFORMATION_FOLLOWS        = 215,
-
-      XOVER_FOLLOWS              = 224,
-      XOVER_NO_ARTICLES          = 420,
-
-      ARTICLE_FOLLOWS            = 220,
-
-      NEWGROUPS_FOLLOWS          = 231,
-
-      ARTICLE_POSTED_OK          = 240,
-      SEND_ARTICLE_NOW           = 340,
-      NO_POSTING                 = 440,
-      POSTING_FAILED             = 441,
-
-      TOO_MANY_CONNECTIONS       = 400,
-
-      NO_GROUP_SELECTED          = 412,
-      NO_SUCH_ARTICLE_NUMBER     = 423,
-      NO_SUCH_ARTICLE            = 430,
-
-      ERROR_CMD_NOT_UNDERSTOOD   = 500,
-      ERROR_CMD_NOT_SUPPORTED    = 501,
-      NO_PERMISSION              = 502,
-      FEATURE_NOT_SUPPORTED      = 503
-   };
-}
-
 void
 NNTP :: fire_done_func (Health health, const StringView& response)
 {
@@ -223,6 +178,8 @@ NNTP :: on_socket_response (Socket * sock UNUSED, const StringView& line_in)
          break;
 
       case XOVER_FOLLOWS:
+        if (_listener)
+            _listener->on_xover_follows (this, line);
       case ARTICLE_FOLLOWS:
       case NEWGROUPS_FOLLOWS:
       case INFORMATION_FOLLOWS:
@@ -233,6 +190,8 @@ NNTP :: on_socket_response (Socket * sock UNUSED, const StringView& line_in)
       case AUTH_REJECTED:
       case NO_GROUP_SELECTED:
       case ERROR_CMD_NOT_UNDERSTOOD:
+        if (_listener)
+          _listener->on_cmd_not_understood (this, line);
       case ERROR_CMD_NOT_SUPPORTED:
       case NO_PERMISSION:
       case FEATURE_NOT_SUPPORTED: {
@@ -283,7 +242,7 @@ NNTP :: on_socket_response (Socket * sock UNUSED, const StringView& line_in)
    switch (state) {
       case CMD_FAIL: fire_done_func (ERR_COMMAND, line); more = false; break;
       case CMD_DONE: if (_commands.empty()) fire_done_func (OK, line); more = false; break;
-      case CMD_MORE: more = true; break; // keep listining for more on this command
+      case CMD_MORE: more = true; break; // keep listening for more on this command
       case CMD_NEXT: more = false; break; // no more responses on this command; wait for next...
       case CMD_RETRY: fire_done_func (ERR_NETWORK, line); more = false; break;
       default: abort(); break;
diff --git a/pan/tasks/nntp.h b/pan/tasks/nntp.h
index 01480cc..57796e8 100644
--- a/pan/tasks/nntp.h
+++ b/pan/tasks/nntp.h
@@ -38,6 +38,49 @@ namespace pan
   class NNTP: private Socket::Listener
   {
     public:
+      enum ResponseType
+      {
+        AUTH_REQUIRED              = 480,
+        AUTH_NEED_MORE             = 381,
+        AUTH_ACCEPTED              = 281,
+        AUTH_REJECTED              = 482,
+
+        SERVER_READY               = 200,
+        SERVER_READY_NO_POSTING    = 201,
+        SERVER_READY_STREAMING_OK  = 203,
+
+        GOODBYE                    = 205,
+
+        GROUP_RESPONSE             = 211,
+        GROUP_NONEXISTENT          = 411,
+
+        INFORMATION_FOLLOWS        = 215,
+
+        XOVER_FOLLOWS              = 224,
+        XOVER_NO_ARTICLES          = 420,
+
+        ARTICLE_FOLLOWS            = 220,
+
+        NEWGROUPS_FOLLOWS          = 231,
+
+        ARTICLE_POSTED_OK          = 240,
+        SEND_ARTICLE_NOW           = 340,
+        NO_POSTING                 = 440,
+        POSTING_FAILED             = 441,
+
+        TOO_MANY_CONNECTIONS       = 400,
+
+        NO_GROUP_SELECTED          = 412,
+        NO_SUCH_ARTICLE_NUMBER     = 423,
+        NO_SUCH_ARTICLE            = 430,
+
+        ERROR_CMD_NOT_UNDERSTOOD   = 500,
+        ERROR_CMD_NOT_SUPPORTED    = 501,
+        NO_PERMISSION              = 502,
+        FEATURE_NOT_SUPPORTED      = 503
+     };
+
+    public:
 
       /**
        * Base class for objects that listen for NNTP events.
@@ -88,6 +131,15 @@ namespace pan
                                     unsigned long        estimated_qty UNUSED,
                                     uint64_t             low           UNUSED,
                                     uint64_t             high          UNUSED) {}
+
+
+        //both functions are implemented for xzver testing(!)
+        virtual void on_xover_follows (NNTP               * nntp UNUSED,
+                                       const StringView   & line UNUSED) {}
+
+        virtual void on_cmd_not_understood (NNTP               * nntp UNUSED,
+                                            const StringView   & line UNUSED) {}
+
        };
 
       public:
diff --git a/pan/tasks/queue.cc b/pan/tasks/queue.cc
index ce9de63..d4ee76f 100644
--- a/pan/tasks/queue.cc
+++ b/pan/tasks/queue.cc
@@ -550,6 +550,9 @@ Queue :: task_is_active (const Task * task) const
   if (task && task==_decoder_task)
     return true;
 
+  if (task && task==_encoder_task)
+    return true;
+
   bool task_has_nntp (false);
   foreach_const (nntp_to_task_t, _nntp_to_task, it)
     if ((task_has_nntp = task==it->second))
@@ -599,13 +602,17 @@ Queue :: get_all_task_states (task_states_t& setme)
   setme.tasks.reserve(_tasks.size());
 
   std::vector<Task *> & need_decode = setme._need_decode.get_container();
-  need_decode.clear();
+  std::vector<Task *> & need_encode = setme._need_encode.get_container();
+  need_decode.clear(); need_encode.clear();
   need_decode.reserve(setme.tasks.capacity());
+  need_encode.reserve(setme.tasks.capacity());
 
   foreach(TaskSet, _tasks, it) {
     setme.tasks.push_back(*it);
     if ((*it)->get_state()._work == Task::NEED_DECODER)
       need_decode.push_back(*it);
+    if ((*it)->get_state()._work == Task::NEED_ENCODER)
+      need_encode.push_back(*it);
   }
   setme._need_decode.sort();
 
@@ -627,6 +634,7 @@ Queue :: get_all_task_states (task_states_t& setme)
   running.insert (running.end(), tmp.begin(), tmp.end());
 
   setme._decoding = _decoder_task;
+  setme._encoding = _encoder_task;
 }
 
 void
@@ -678,6 +686,7 @@ Queue :: check_in (Decoder* decoder UNUSED, Task* task)
 {
   // take care of our decoder counting...
   _decoder_task = 0;
+  _encoder_task = 0;
 
   // notify the listeners if the task isn't active anymore...
   if (!task_is_active (task))
@@ -698,7 +707,7 @@ Queue :: check_in (Decoder* decoder UNUSED, Task* task)
 }
 
 void
-Queue :: check_in (Encoder* decoder UNUSED, Task* task)
+Queue :: check_in (Encoder* encoder UNUSED, Task* task)
 {
   // take care of our decoder counting...
   _encoder_task = 0;
diff --git a/pan/tasks/task-article.cc b/pan/tasks/task-article.cc
index e529d52..3c77f7b 100644
--- a/pan/tasks/task-article.cc
+++ b/pan/tasks/task-article.cc
@@ -132,7 +132,7 @@ TaskArticle :: ~TaskArticle ()
   // ensure our on_worker_done() doesn't get called after we're dead
   if (_decoder)
       _decoder->cancel_silently();
-
+  
   _cache.release (_article.get_part_mids());
 }
 
@@ -291,7 +291,7 @@ TaskArticle :: on_nntp_done  (NNTP             * nntp,
       }
       break;
   }
-
+  
   update_work (nntp);
   check_in (nntp, health);
 }
diff --git a/pan/tasks/task-post.cc b/pan/tasks/task-post.cc
index d605d18..18e7747 100644
--- a/pan/tasks/task-post.cc
+++ b/pan/tasks/task-post.cc
@@ -57,7 +57,6 @@ TaskPost :: use_nntp (NNTP * nntp)
   _state.set_working ();
 
   char * text = g_mime_object_to_string (GMIME_OBJECT(_message));
-  std::cerr<<text<<std::endl;
   nntp->post (text, this);
   g_free (text);
 }
diff --git a/pan/tasks/task-weak-ordering.h b/pan/tasks/task-weak-ordering.h
index 242315c..6f6d2fd 100644
--- a/pan/tasks/task-weak-ordering.h
+++ b/pan/tasks/task-weak-ordering.h
@@ -34,14 +34,15 @@ namespace pan
    */
   struct TaskWeakOrdering
   {
-    const Quark BODIES, CANCEL, GROUPS, POST, SAVE, XOVER;
+    const Quark BODIES, CANCEL, GROUPS, POST, SAVE, XOVER, XZVER_TEST;
     TaskWeakOrdering ():
       BODIES ("BODIES"),
       CANCEL ("CANCEL"),
       GROUPS ("GROUPS"),
       POST ("POST"),
       SAVE ("SAVE"),
-      XOVER ("XOVER") {}
+      XOVER ("XOVER"),
+      XZVER_TEST ("XZVER_TEST") {}
 
     int get_rank_for_type (const Quark& type) const
     {
@@ -49,7 +50,7 @@ namespace pan
 
       if (type==BODIES || type==POST || type==CANCEL)
         rank = 0;
-      else if (type==XOVER || type==GROUPS)
+      else if (type==XOVER || type==GROUPS || type==XZVER_TEST)
         rank = 1;
       else if (type==SAVE)
         rank = 2;
diff --git a/pan/tasks/task-xover.cc b/pan/tasks/task-xover.cc
index 40b292f..a3a4b80 100644
--- a/pan/tasks/task-xover.cc
+++ b/pan/tasks/task-xover.cc
@@ -17,12 +17,18 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+int cnt = 0; //dbg
+
 #include <config.h>
 #include <cassert>
 #include <cerrno>
 extern "C" {
+  #include <stdio.h>
+  #define PROTOTYPES
+  #include <uulib/uudeview.h>
   #include <glib/gi18n.h>
   #include <gmime/gmime-utils.h>
+  #include <zlib.h>
 }
 #include <pan/general/debug.h>
 #include <pan/general/macros.h>
@@ -31,6 +37,7 @@ extern "C" {
 #include <pan/data/data.h>
 #include "nntp.h"
 #include "task-xover.h"
+#include "task-xzver.h"
 
 using namespace pan;
 
@@ -75,6 +82,38 @@ namespace
       snprintf (buf, sizeof(buf), _("Sampling headers for \"%s\""), group.c_str());
     return std::string (buf);
   }
+
+  void decompress(const Quark& server, std::string stream)
+  {
+    char buf[512];
+    int res(UURET_OK);
+    const char* server_string = server.to_string().c_str();
+    const char* uulib_dir = (file :: get_pan_uulib_dir()).c_str();
+
+    g_snprintf(buf,512,"%s/%s.%s", uulib_dir, "headers.gz.yenc",server_string);
+    std::ofstream out(buf);
+
+    out << "=ybegin line=128 size=-1 name=headers.gz\r\n";
+    out<< stream;
+    out << "\r\n=yend crc=FFFFFFFF\r\n.\r\n";
+    out.flush();
+    out.close();
+    UULoadFile (NULL, 0, 0);
+    g_snprintf(buf,512,"%s/%s.%s", uulib_dir, "headers.gz",server_string);
+    if ((res=UUDecodeFile(UUGetFileListItem (0), buf))==UURET_OK) {
+      gzFile zf = gzopen(buf,"rb");
+      g_snprintf(buf,512,"%s/%s.%s", uulib_dir, "headers",server_string);
+      std::ofstream gz_buf(buf);
+      int res_gz(0);
+      while (1)
+      {
+        if ((res_gz=gzread(zf,buf,512))<=0) break;
+        gz_buf<<buf;
+      }
+      gzclose(zf);
+      gz_buf.close();
+    }
+  }
 }
 
 TaskXOver :: TaskXOver (Data         & data,
@@ -92,18 +131,23 @@ TaskXOver :: TaskXOver (Data         & data,
   _bytes_so_far (0),
   _parts_so_far (0ul),
   _articles_so_far (0ul),
-  _total_minitasks (0)
+  _total_minitasks (0),
+  _need_decompress (0ul)
 {
-  debug ("ctor for " << group);
 
   // add a ``GROUP'' MiniTask for each server that has this group
   // initialize the _high lookup table to boundaries
   const MiniTask group_minitask (MiniTask::GROUP);
   quarks_t servers;
-  _data.group_get_servers (group, servers);
+  _data.group_get_servers (group, _servers);
+
+  //dbg
+  servers = _servers;
+
   foreach_const (quarks_t, servers, it)
     if (_data.get_server_limits(*it))
     {
+      if(_data.get_server_xzver_support(*it)==1) std::cerr<<"xzver support on server"<<*it<<std::endl;
       _server_to_minitasks[*it].push_front (group_minitask);
       _high[*it] = data.get_xover_high (group, *it);
     }
@@ -128,7 +172,9 @@ void
 TaskXOver :: use_nntp (NNTP* nntp)
 {
   const Quark& server (nntp->_server);
-  debug ("got an nntp from " << nntp->_server);
+
+  // test for compression
+  const bool compression (_data.get_server_xzver_support(server) == 1);
 
   // if this is the first nntp we've gotten, ref the xover data
   if (!_group_xover_is_reffed) {
@@ -156,7 +202,10 @@ TaskXOver :: use_nntp (NNTP* nntp)
       case MiniTask::XOVER:
         debug ("XOVER " << mt._low << '-' << mt._high << " to " << server);
         _last_xover_number[nntp] = mt._low;
-        nntp->xover (_group, mt._low, mt._high, this);
+        if (!compression)
+          nntp->xover (_group, mt._low, mt._high, this);
+        else
+          nntp->xzver (_group, mt._low, mt._high, this);
         break;
       default:
         assert (0);
@@ -176,13 +225,16 @@ TaskXOver :: on_nntp_group (NNTP          * nntp,
                             uint64_t        low,
                             uint64_t        high)
 {
-  const Quark& servername (nntp->_server);
+  const Quark& server (nntp->_server);
+
+  // test for compression
+  const bool compression (_data.get_server_xzver_support(server) == 1);
 
   // new connections can tickle this...
-  if (_servers_that_got_xover_minitasks.count(servername))
+  if (_servers_that_got_xover_minitasks.count(server))
     return;
 
-  _servers_that_got_xover_minitasks.insert (servername);
+  _servers_that_got_xover_minitasks.insert (server);
 
   debug ("got GROUP result from " << nntp->_server << " (" << nntp << "): "
          << " qty " << qty
@@ -210,14 +262,24 @@ TaskXOver :: on_nntp_group (NNTP          * nntp,
   {
     //std::cerr << LINE_ID << " okay, I'll try to get articles in [" << l << "..." << h << ']' << std::endl;
     add_steps (h-l);
-    const int INCREMENT (1000);
-    MiniTasks_t& minitasks (_server_to_minitasks[servername]);
+
+    int INCREMENT(0);
+
+    if (!compression)
+     INCREMENT = 1000;
+    else
+      INCREMENT = 10000;
+
+    MiniTasks_t& minitasks (_server_to_minitasks[server]);
     for (uint64_t m=l; m<=h; m+=INCREMENT) {
       MiniTask mt (MiniTask::XOVER, m, m+INCREMENT);
-      debug ("adding MiniTask for " << servername << ": xover [" << mt._low << '-' << mt._high << ']');
+      debug ("adding MiniTask for " << server << ": xover [" << mt._low << '-' << mt._high << ']');
       minitasks.push_front (mt);
       ++_total_minitasks;
     }
+    // give server its stream
+
+
   }
   else
   {
@@ -267,6 +329,25 @@ void
 TaskXOver :: on_nntp_line (NNTP               * nntp,
                            const StringView   & line)
 {
+    const bool compression (_data.get_server_xzver_support(nntp->_server) == 1);
+    if (!compression)
+      on_nntp_line_process(nntp,line);
+    else {
+      _streams[nntp->_server] += line.str;
+      _bytes_so_far += line.len;
+
+      // emit a status update
+      uint64_t& prev = _last_xover_number[nntp];
+      increment_step (1);
+      if (!(_parts_so_far % 500))
+        set_status_va (_("%s (%lu parts, %lu articles)"), _short_group_name.c_str(), _parts_so_far, _articles_so_far);
+    }
+}
+
+void
+TaskXOver :: on_nntp_line_process (NNTP               * nntp,
+                                   const StringView   & line)
+{
   pan_return_if_fail (nntp != 0);
   pan_return_if_fail (!nntp->_server.empty());
   pan_return_if_fail (!nntp->_group.empty());
@@ -348,11 +429,12 @@ TaskXOver :: on_nntp_line (NNTP               * nntp,
 void
 TaskXOver :: on_nntp_done (NNTP              * nntp,
                            Health              health,
-                           const StringView  & response UNUSED)
+                           const StringView  & response)
 {
-  //std::cerr << LINE_ID << " nntp " << nntp->_server << " (" << nntp << ") done; checking in.  health==" << health << std::endl;
-  update_work (true);
-  check_in (nntp, health);
+
+    std::cerr<<"nntp done on server "<<nntp->_server<<std::endl;
+    update_work (true);
+    check_in (nntp, health);
 }
 
 void
@@ -362,6 +444,8 @@ TaskXOver :: update_work (bool subtract_one_from_nntp_count)
   if (subtract_one_from_nntp_count)
     --nntp_count;
 
+      std::cerr<<"update work "<<std::endl;
+
   // find any servers we still need
   quarks_t servers;
   foreach_const (server_to_minitasks_t, _server_to_minitasks, it)
@@ -375,14 +459,20 @@ TaskXOver :: update_work (bool subtract_one_from_nntp_count)
   else if (nntp_count)
     _state.set_working ();
   else {
-    _state.set_completed ();
-    set_finished (OK);
+      foreach_const (stream_t, _streams, it)
+      {
+        decompress(it->first, it->second);
+//      feed_lines(_stream);
+      }
+      _state.set_completed ();
+      set_finished (OK);
   }
 }
 
 unsigned long
 TaskXOver :: get_bytes_remaining () const
 {
+  return 0ul;
   unsigned int minitasks_left (0);
   foreach_const (server_to_minitasks_t, _server_to_minitasks, it)
     minitasks_left += it->second.size();
@@ -391,5 +481,6 @@ TaskXOver :: get_bytes_remaining () const
   if (percent_done < 0.1) // impossible to estimate
     return 0;
   const unsigned long total_bytes = (unsigned long)(_bytes_so_far / percent_done);
+  std::cerr<<std::endl<<"bytes remaining: "<<total_bytes - _bytes_so_far<<", percent: "<<percent_done<<std::endl;
   return total_bytes - _bytes_so_far;
 }
diff --git a/pan/tasks/task-xover.h b/pan/tasks/task-xover.h
index 8701808..8b63163 100644
--- a/pan/tasks/task-xover.h
+++ b/pan/tasks/task-xover.h
@@ -22,10 +22,15 @@
 
 #include <map>
 #include <vector>
+#include <sstream>
+#include <iostream>
+#include <fstream>
 
+#include <pan/general/log.h>
 #include <pan/data/data.h>
 #include <pan/tasks/task.h>
 #include <pan/tasks/nntp.h>
+#include <pan/general/file-util.h>
 
 namespace pan
 {
@@ -48,6 +53,7 @@ namespace pan
 
     private: // NNTP::Listener
       virtual void on_nntp_line (NNTP*, const StringView&);
+      virtual void on_nntp_line_process (NNTP*, const StringView&);
       virtual void on_nntp_done (NNTP*, Health, const StringView&);
       virtual void on_nntp_group (NNTP*, const Quark&, unsigned long, uint64_t, uint64_t);
 
@@ -61,7 +67,9 @@ namespace pan
       };
       typedef std::deque<MiniTask> MiniTasks_t;
       typedef std::map<Quark,MiniTasks_t> server_to_minitasks_t;
+      typedef std::map<Quark,std::string> stream_t;
       server_to_minitasks_t _server_to_minitasks;
+      stream_t _streams;
 
     private: // implementation
       Data& _data;
@@ -80,6 +88,10 @@ namespace pan
       unsigned long _parts_so_far;
       unsigned long _articles_so_far;
       unsigned long _total_minitasks;
+      unsigned long _need_decompress;
+      std::stringstream _stream;
+      quarks_t _servers; // dbg
+
   };
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]