[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-devel
Subject:    kio-nntp
From:       Paul Sprakes <pauls () sprakes ! co ! uk>
Date:       2004-03-24 20:30:33
Message-ID: 200403242030.33453.pauls () sprakes ! co ! uk
[Download RAW message or body]

I have attached a patch for kio-nntp which adds the following features:

1. Requests user auth info if missing from url (there is a bug for this).

2. Uses XOVER when listing groups. This gives it a vast speed improvement. If 
the server doesn't support XOVER then it should revert to the old method.

3. When XOVER (or XPAT, see below) is used. The user sees friendly subject 
names in konq instead of the cryptic message-id's.

4. Newsgroup searching via XPAT (if supported by the server). To use this 
append "?pattern=foo" to the end of the url. (Note that it internally 
converts the pattern to "*foo*").

It is not finished yet and it is also my first c++ project, however I was 
hoping to get some feedback and to try and solve a problem I have noticed.

When opening an article from within Konqueror with either Kate or KWrite, I 
end up getting two kio get requests. No other application does this (eg 
kedit) and I can't figure out if it is Kate/KWrite's fault or mine.

Thanks,

Paul.
 

["nntp.patch" (text/x-diff)]

? nntp/nntpold.cpp
Index: nntp/nntp.cpp
===================================================================
RCS file: /home/kde/kdebase/kioslave/nntp/nntp.cpp,v
retrieving revision 1.16
diff -u -r1.16 nntp.cpp
--- nntp/nntp.cpp	26 Jul 2003 10:41:22 -0000	1.16
+++ nntp/nntp.cpp	24 Mar 2004 19:45:17 -0000
@@ -84,26 +84,27 @@
 void NNTPProtocol::get(const KURL& url) {
   DBG << "get " << url.prettyURL() << endl;
   QString path = QDir::cleanDirPath(url.path());
-  QRegExp regMsgId = \
                QRegExp("^\\/?[a-z0-9\\.\\-_]+\\/<[a-zA-Z0-9\\.\\@\\-_]+>$",false);
-  int pos;
   QString group;
   QString msg_id;
+  //Path could be /group/<message-id> or /group/article-id
+  //server will handle both
 
-  // path should be like: /group/<msg_id>
-  if (regMsgId.search(path) != 0) {
+  QStringList tokenizer = QStringList::split('/', path);
+  if(tokenizer.count() < 2) {
     error(ERR_DOES_NOT_EXIST,path);
     return;
   }
-
-  pos = path.find('<');
-  group = path.left(pos);
-  msg_id = path.right(path.length()-pos);
-  if (group.left(1) == "/") group.remove(0,1);
-  if ((pos = group.find('/')) > 0) group = group.left(pos);
-  DBG << "get group: " << group << " msg: " << msg_id << endl;
+  group = tokenizer[0];
+  msg_id = tokenizer[1];
+  //pos = path.find('<');
+  //group = path.left(pos);
+  //msg_id = path.right(path.length()-pos);
+  //if (group.left(1) == "/") group.remove(0,1);
+  //if ((pos = group.find('/')) > 0) group = group.left(pos);
+  DBG << "get article: " << group << " msg: " << msg_id << endl;
 
   nntp_open(); // opens only if necessary
-
+  DBG << "nntp is open" << endl;
   // select group
   int res_code = send_cmd("GROUP "+group);
   if (res_code == 411){
@@ -292,12 +293,14 @@
     if (path.left(1) == "/") path.remove(0,1);
     if ((pos = path.find('/')) > 0) group = path.left(pos);
     else group = path;
-    if (fetchGroup(group)) finished();
+    QString pattern = url.queryItem("pattern");
+    fetchGroup(group, pattern);
+    finished();
   }
 }
 
 void NNTPProtocol::fetchGroups() {
-
+  nntp_open(); // opens only if necessary
   // send LIST command
   int res_code = send_cmd("LIST");
   if (res_code != 215) {
@@ -349,9 +352,121 @@
   if (entryList.count() > 0) listEntries(entryList);
 }
 
-bool NNTPProtocol::fetchGroup(QString& group) {
-  int res_code;
+bool NNTPProtocol::xhdr(QString& group, QString& first) { 
+  //TODO implements xhdr
+  return false;
+}
+
+bool NNTPProtocol::xover(QString& group, QString& first) {
+  //TODO send LIST OVERVIEW.FMT to discover which headers are listed
+  //So far (from two tested servers) we have
+  //Subject:
+  //From:
+  //Date:
+  //Message-ID:
+  //References:
+  //Bytes:
+  //Lines:
+  //Xref:full full indicates that the headers name is returned as prefix to header
+  //
+  //Each separated by a tab
+  //the format of each line is then
+  //article-id\tsubject\tfrom\tdate\tmessage-id\treferences\tbytes\tlines\txref
+  //if incorrect response return false
+  
+  
+  UDSEntryList entryList;
+
+  int res_code = send_cmd("XOVER "+first + "-");
+  if (res_code != 224) { //xhdr 221
+    DBG << "XOVER response = " << res_code << endl;
+    return false;
+  }
+  QCString line;
+  while (socket.readLine(line) && line != ".\r\n") {
+    //DBG << "Read a line " << line << endl;
+    int pos = line.find('\t');
+    if(pos > 0) {
+      QCString article_id = line.left(pos);
+      QCString fields = line.mid(pos + 1, line.length() - 3); // -3 removes the \r\n \
off the end +      QStringList tokenizer = QStringList::split( '\t', fields );
+      UDSEntry entry;
+      QString subject;
+      QString message_id;
+      long size = 0;
+      for(uint i = 0; i < tokenizer.count(); i++) {
+        switch(i) {
+            case 0://subject
+              subject = tokenizer[i];
+              break;
+            case 3: //message id
+              message_id = tokenizer[i];
+              break;
+            case 5: //size in bytes
+              size = tokenizer[i].toLong();
+              break;
+        }
+      }
+      fillUDSEntry(entry, subject, size * 8, false, true);
+      UDSAtom atom;
+      atom.m_uds = UDS_URL;
+      //TODO this could be nntps (when we handle nntps)
+      atom.m_str = "nntp://" + user + ":" + pass + "@" + host + "/" + group + "/" + \
message_id; +      atom.m_long = 0;
+      entry.append(atom);
+      entryList.append(entry);
+      if (entryList.count() >= UDS_ENTRY_CHUNK) {
+        listEntries(entryList);
+        entryList.clear();
+      }
+    }
+  }
+  if (entryList.count() > 0) { //any entries left?
+    listEntries(entryList);
+  }
+  return true;
+}
+
+bool NNTPProtocol::xpat(QString& group, QString& first, QString& pattern) {
+  DBG << "xpat called" << endl;
+  
+  int res_code = send_cmd("XPAT Subject " + first + "- *" + pattern + "*");
+  if (res_code != 221) {
+    DBG << "XPAT response = " << res_code << endl;
+    unexpected_response(res_code,"XPAT");
+    return false;
+  }
+  QCString line;
+  UDSEntryList entryList;
+  while (socket.readLine(line) && line != ".\r\n") {
+    int pos = line.find(' ');
+    if(pos > 0) {
+      QString article_id = line.left(pos);
+      QString subject = line.mid(pos + 1).stripWhiteSpace(); // -removes the \r\n \
off the end +      DBG << "art id = " << article_id << " subject = " << subject << \
endl; +      UDSEntry entry;
+      fillUDSEntry(entry, subject, 0, false, true);
+      UDSAtom atom;
+      atom.m_uds = UDS_URL;
+      atom.m_str = "nntp://" + user + ":" + pass + "@" + host + "/" + group + "/" + \
article_id; +      atom.m_long = 0;
+      entry.append(atom);
+      entryList.append(entry);
+      if (entryList.count() >= 2) {
+        listEntries(entryList);
+        entryList.clear();
+      }
+    }
+  }
+  if (entryList.count() > 0) { //any entries left?
+    listEntries(entryList);
+  }
+  return true;
+}
 
+bool NNTPProtocol::fetchGroup(QString& group, QString& pattern) {
+  int res_code;
+  
   // select group
   res_code = send_cmd("GROUP "+group);
   if (res_code == 411){
@@ -370,67 +485,74 @@
   {
     first = resp_line.mid(pos+1,pos2-pos-1);
   } else {
-    error(ERR_INTERNAL,i18n("Could not extract first message number from server \
                response:\n%1").
-      arg(resp_line));
+    error(ERR_INTERNAL,i18n("Could not extract first message number from server \
response:\n%1").arg(resp_line));  return false;
   }
 
   if (first.toLong() == 0L)
     return false;
-
-  UDSEntry entry;
-  UDSEntryList entryList;
-
-  // set art pointer to first article and get msg-id of it
-  res_code = send_cmd("STAT "+first);
-  if (res_code != 223) {
-    unexpected_response(res_code,"STAT");
-    return false;
-  }
-
-  //STAT res_line: 223 nnn <msg_id> ...
-  QString msg_id;
-  if ((pos = resp_line.find('<')) > 0 && (pos2 = resp_line.find('>',pos+1))) {
-    msg_id = resp_line.mid(pos,pos2-pos+1);
-    fillUDSEntry(entry, msg_id, 0, false, true);
-    entryList.append(entry);
-  } else {
-    error(ERR_INTERNAL,i18n("Could not extract first message id from server \
                response:\n%1").
-      arg(resp_line));
-    return false;
-  }
-
-  // go through all articles
-  while (true) {
-    res_code = send_cmd("NEXT");
-    if (res_code == 421) {
-      // last article reached
-      if (entryList.count()) listEntries(entryList);
-      return true;
-    } else if (res_code != 223) {
-      unexpected_response(res_code,"NEXT");
-      return false;
-    }
-
-    //res_line: 223 nnn <msg_id> ...
-    if ((pos = resp_line.find('<')) > 0 && (pos2 = resp_line.find('>',pos+1))) {
-      msg_id = resp_line.mid(pos,pos2-pos+1);
-      fillUDSEntry(entry, msg_id, 0, false, true);
-      entryList.append(entry);
-      if (entryList.count() >= UDS_ENTRY_CHUNK) {
-        listEntries(entryList);
-        entryList.clear();
+  //detect url params, if pattern detected use xpat and return
+  DBG << "Pattern = " << pattern << endl;
+  if(!pattern.isNull() && !pattern.isEmpty()) return xpat(group, first, pattern);
+  //no pattern param. first try xover
+  if(!xover(group, first)) {
+    //if xover didn't work try xhdr
+    if(!xhdr(group, first)) {
+      //if xhdr didn't work use old style
+      //STAT res_line: 223 nnn <msg_id> ...
+      UDSEntryList entryList;
+      QString msg_id;
+      res_code = send_cmd("STAT "+first);
+      if (res_code != 223) { //xhdr 221
+        DBG << "STAT response = " << res_code << endl;
+        unexpected_response(res_code,"STAT");
+        return false;
+      }
+      if ((pos = resp_line.find('<')) > 0 && (pos2 = resp_line.find('>',pos+1))) {
+          msg_id = resp_line.mid(pos,pos2-pos+1);
+          UDSEntry entry;
+          fillUDSEntry(entry, msg_id, 0, false, true);
+          entryList.append(entry);
+      } else {
+          error(ERR_INTERNAL,i18n("Could not extract first message id from server \
response:\n%1").arg(resp_line)); +          return false;
+      }
+  
+      // go through all articles
+      
+      while (true) {
+        res_code = send_cmd("NEXT");
+        if (res_code == 421) {
+          // last article reached
+          if (entryList.count()) listEntries(entryList);
+          return true;
+        } else if (res_code != 223) {
+          unexpected_response(res_code,"NEXT");
+          return false;
+        }
+    
+        //res_line: 223 nnn <msg_id> ...
+        if ((pos = resp_line.find('<')) > 0 && (pos2 = resp_line.find('>',pos+1))) {
+          msg_id = resp_line.mid(pos,pos2-pos+1);
+          UDSEntry entry;
+          fillUDSEntry(entry, msg_id, 0, false, true);
+          entryList.append(entry);
+          if (entryList.count() >= UDS_ENTRY_CHUNK) {
+            listEntries(entryList);
+            entryList.clear();
+          }
+        } else {
+          error(ERR_INTERNAL,i18n("Could not extract message id from server \
response:\n%1"). +          arg(resp_line));
+          return false;
+        }
       }
-    } else {
-      error(ERR_INTERNAL,i18n("Could not extract message id from server \
                response:\n%1").
-        arg(resp_line));
-      return false;
     }
   }
-return true; // Not reached  
+  return true;
 }
 
-void NNTPProtocol::fillUDSEntry(UDSEntry& entry, const QString& name, int size,
+void NNTPProtocol::fillUDSEntry(UDSEntry& entry, const QString& name, long size,
   bool posting_allowed, bool is_article) {
 
   long posting=0;
@@ -507,19 +629,20 @@
     {
       DBG << "connecting to " << host << ":" << port << endl;
       if (socket.connect(host,port)) {
-        DBG << "socket connection succeeded" << endl;
+        
         // read greeting
         int res_code = eval_resp();
-
+        
         /* expect one of
              200 server ready - posting allowed
              201 server ready - no posting allowed
         */
         if ( !(res_code == 200 || res_code == 201) ) {
           unexpected_response(res_code,"CONNECT");
+          DBG << "Connection failed :" << resp_line << endl;
           return;
         }
-
+        DBG << "socket connection succeeded" << endl;
         res_code = send_cmd("MODE READER");
 
         if ( !(res_code == 200 || res_code == 201) ) {
@@ -548,20 +671,33 @@
 int NNTPProtocol::send_cmd(const QString &cmd) {
   int res_code;
   QCString _cmd = cmd.utf8();
-
+  DBG << "sending cmd " << _cmd << endl;
   if (!socket.connected()) {
     ERR << "NOT CONNECTED, cannot send cmd " << cmd << endl;
     return 0;
   }
 
-  DBG << "sending cmd " << cmd << endl;
+  
 
   socket.writeLine(_cmd);
   res_code = eval_resp();
 
   // if authorization needed send user info
   if (res_code == 480) {
-    DBG << "auth needed, sending user info" << endl;
+    DBG << "auth needed, sending user info: " << user << " " << pass << endl; //TODO \
REMOVE PASSWORD!!!!!------------------------------------- +    KIO::AuthInfo \
authInfo; +    authInfo.username = user;
+    if(user.isEmpty() || pass.isEmpty()) {
+      authInfo.prompt = i18n("Username and password for your NNTP account:");
+      if (!openPassDlg(authInfo)) {
+        nntp_close();
+        return false;
+      } else {
+        user = authInfo.username;
+        pass = authInfo.password;
+      }
+    }
+
     _cmd = "AUTHINFO USER "; _cmd += user.utf8();
     socket.writeLine(_cmd);
     res_code = eval_resp();
@@ -749,6 +885,7 @@
   if (ks.lookup() < 0)
     {
       emit error(ERR_UNKNOWN_HOST, host); // untrue
+      DBG << "unknown host" << endl;
       return false;
     }
 
@@ -756,6 +893,7 @@
     {
       // code above doesn't use emit, but this is a signal
       emit error(ERR_COULD_NOT_CONNECT, host);
+      DBG << "couldn't connect" << endl;
       return false;
     }
 
Index: nntp/nntp.h
===================================================================
RCS file: /home/kde/kdebase/kioslave/nntp/nntp.h,v
retrieving revision 1.13
diff -u -r1.13 nntp.h
--- nntp/nntp.h	21 Nov 2000 13:07:27 -0000	1.13
+++ nntp/nntp.h	24 Mar 2004 19:45:17 -0000
@@ -120,9 +120,12 @@
    int eval_resp();     // get server response and check it for general errors
 
    void fetchGroups();  // fetch all availabel news groups
-   bool fetchGroup(QString& group); // fetch all messages from one news group
-   void fillUDSEntry(KIO::UDSEntry& entry, const QString& name, int size, bool \
                posting_allowed,
-      bool is_article); // makes an UDSEntry with file informations,
+   bool fetchGroup(QString& group, QString& pattern); // fetch all messages from one \
news group +   bool xover(QString& group, QString& first);
+   bool xhdr(QString& group, QString& first);
+   bool xpat(QString& group, QString& first, QString& pattern);
+   void fillUDSEntry(KIO::UDSEntry& entry, const QString& name, long size, bool \
posting_allowed, +   bool is_article); // makes an UDSEntry with file informations,
                         // used in stat and listDir
    // QString& errorStr(int resp_code); // gives the NNTP error message for a server \
response code  void unexpected_response(int res_code, const QString& command); // \
error handling for unexpected responses



>> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe <<


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic