author | harlekin <harlekin> | 2004-02-29 21:55:06 (UTC) |
---|---|---|
committer | harlekin <harlekin> | 2004-02-29 21:55:06 (UTC) |
commit | dcf152e23f7cc85fe2e46521e07b64e2288efdda (patch) (side-by-side diff) | |
tree | 8aac8095aed8dc9a9efab7005b8f1c53cce82536 | |
parent | 225b92ec28bbe3a9368e8534323a3c335432e447 (diff) | |
download | opie-dcf152e23f7cc85fe2e46521e07b64e2288efdda.zip opie-dcf152e23f7cc85fe2e46521e07b64e2288efdda.tar.gz opie-dcf152e23f7cc85fe2e46521e07b64e2288efdda.tar.bz2 |
beginning of nntp stuff
-rw-r--r-- | noncore/net/mail/accountitem.cpp | 190 | ||||
-rw-r--r-- | noncore/net/mail/accountitem.h | 40 | ||||
-rw-r--r-- | noncore/net/mail/accountview.cpp | 7 | ||||
-rw-r--r-- | noncore/net/mail/libmailwrapper/abstractmail.cpp | 6 | ||||
-rw-r--r-- | noncore/net/mail/libmailwrapper/abstractmail.h | 1 | ||||
-rw-r--r-- | noncore/net/mail/libmailwrapper/libmailwrapper.pro | 6 | ||||
-rw-r--r-- | noncore/net/mail/libmailwrapper/nntpwrapper.cpp | 241 | ||||
-rw-r--r-- | noncore/net/mail/libmailwrapper/nntpwrapper.h | 48 | ||||
-rw-r--r-- | noncore/net/mail/libmailwrapper/pop3wrapper.cpp | 2 | ||||
-rw-r--r-- | noncore/net/mail/nntpconfigui.ui | 103 |
10 files changed, 637 insertions, 7 deletions
diff --git a/noncore/net/mail/accountitem.cpp b/noncore/net/mail/accountitem.cpp index c8f6ec4..32a96ff 100644 --- a/noncore/net/mail/accountitem.cpp +++ b/noncore/net/mail/accountitem.cpp @@ -116,192 +116,382 @@ void POP3viewItem::disconnect() void POP3viewItem::setOnOffline() { if (!account->getOffline()) { disconnect(); } account->setOffline(!account->getOffline()); account->save(); SETPIX(PIXMAP_POP3FOLDER); refresh(); } void POP3viewItem::contextMenuSelected(int which) { switch (which) { case 0: disconnect(); break; case 1: setOnOffline(); break; } } POP3folderItem::~POP3folderItem() {} POP3folderItem::POP3folderItem( Folder *folderInit, POP3viewItem *parent , QListViewItem*after ) : AccountViewItem( parent,after ) { folder = folderInit; pop3 = parent; if (folder->getDisplayName().lower()!="inbox") { setPixmap( 0, PIXMAP_POP3FOLDER ); } else { setPixmap( 0, PIXMAP_INBOXFOLDER); } setText( 0, folder->getDisplayName() ); } void POP3folderItem::refresh(QList<RecMail>&target) { if (folder->may_select()) pop3->getWrapper()->listMessages( folder->getName(),target ); } RecBody POP3folderItem::fetchBody(const RecMail&aMail) { return pop3->getWrapper()->fetchBody(aMail); } QPopupMenu * POP3folderItem::getContextMenu() { QPopupMenu *m = new QPopupMenu(0); if (m) { m->insertItem(QObject::tr("Refresh header list",contextName),0); m->insertItem(QObject::tr("Delete all mails",contextName),1); m->insertItem(QObject::tr("Move/Copie all mails",contextName),2); } return m; } void POP3folderItem::downloadMails() { AccountView*bl = pop3->accountView(); if (!bl) return; bl->downloadMails(folder,pop3->getWrapper()); } void POP3folderItem::contextMenuSelected(int which) { AccountView * view = (AccountView*)listView(); switch (which) { case 0: /* must be 'cause pop3 lists are cached */ pop3->getWrapper()->logout(); view->refreshCurrent(); break; case 1: deleteAllMail(pop3->getWrapper(),folder); break; case 2: downloadMails(); break; default: break; } } /** + * NNTP Account stuff + */ +NNTPviewItem::NNTPviewItem( NNTPaccount *a, AccountView *parent ) + : AccountViewItem( parent ) +{ + account = a; + wrapper = AbstractMail::getWrapper( account ); + //FIXME + SETPIX(PIXMAP_POP3FOLDER); +#if 0 + if (!account->getOffline()) + { + setPixmap( 0, ); + } + else + { + setPixmap( 0, PIXMAP_OFFLINE ); + } +#endif + setText( 0, account->getAccountName() ); + setOpen( true ); +} + +NNTPviewItem::~NNTPviewItem() +{ + delete wrapper; +} + +AbstractMail *NNTPviewItem::getWrapper() +{ + return wrapper; +} + +void NNTPviewItem::refresh( QList<RecMail> & ) +{ + refresh(); +} + +void NNTPviewItem::refresh() +{ + if (account->getOffline()) return; + QList<Folder> *folders = wrapper->listFolders(); + QListViewItem *child = firstChild(); + while ( child ) + { + QListViewItem *tmp = child; + child = child->nextSibling(); + delete tmp; + } + Folder *it; + QListViewItem*item = 0; + for ( it = folders->first(); it; it = folders->next() ) + { + item = new NNTPfolderItem( it, this , item ); + item->setSelectable(it->may_select()); + } + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + folders->setAutoDelete(false); + delete folders; +} + +RecBody NNTPviewItem::fetchBody( const RecMail &mail ) +{ + qDebug( "NNTP fetchBody" ); + return wrapper->fetchBody( mail ); +} + +QPopupMenu * NNTPviewItem::getContextMenu() +{ + QPopupMenu *m = new QPopupMenu(0); + if (m) + { + if (!account->getOffline()) + { + m->insertItem(QObject::tr("Disconnect",contextName),0); + m->insertItem(QObject::tr("Set offline",contextName),1); + } + else + { + m->insertItem(QObject::tr("Set online",contextName),1); + } + } + return m; +} + +void NNTPviewItem::disconnect() +{ + QListViewItem *child = firstChild(); + while ( child ) + { + QListViewItem *tmp = child; + child = child->nextSibling(); + delete tmp; + } + wrapper->logout(); +} + +void NNTPviewItem::setOnOffline() +{ + if (!account->getOffline()) + { + disconnect(); + } + account->setOffline(!account->getOffline()); + account->save(); + //FIXME + SETPIX(PIXMAP_POP3FOLDER); + refresh(); +} + +void NNTPviewItem::contextMenuSelected(int which) +{ + switch (which) + { + case 0: + disconnect(); + break; + case 1: + setOnOffline(); + break; + } +} + +NNTPfolderItem::~NNTPfolderItem() +{} + +NNTPfolderItem::NNTPfolderItem( Folder *folderInit, NNTPviewItem *parent , QListViewItem*after ) + : AccountViewItem( parent,after ) +{ + folder = folderInit; + nntp = parent; + if (folder->getDisplayName().lower()!="inbox") + { + setPixmap( 0, PIXMAP_POP3FOLDER ); + } + else + { + setPixmap( 0, PIXMAP_INBOXFOLDER); + } + setText( 0, folder->getDisplayName() ); +} + +void NNTPfolderItem::refresh(QList<RecMail>&target) +{ + if (folder->may_select()) + nntp->getWrapper()->listMessages( folder->getName(),target ); +} + +RecBody NNTPfolderItem::fetchBody(const RecMail&aMail) +{ + return nntp->getWrapper()->fetchBody(aMail); +} + +QPopupMenu * NNTPfolderItem::getContextMenu() +{ + QPopupMenu *m = new QPopupMenu(0); + if (m) + { + m->insertItem(QObject::tr("Refresh header list",contextName),0); + m->insertItem(QObject::tr("Move/Copie all mails",contextName),1); + } + return m; +} + +void NNTPfolderItem::downloadMails() +{ + AccountView*bl = nntp->accountView(); + if (!bl) return; + bl->downloadMails(folder,nntp->getWrapper()); +} + +void NNTPfolderItem::contextMenuSelected(int which) +{ + AccountView * view = (AccountView*)listView(); + switch (which) + { + case 0: + /* must be 'cause pop3 lists are cached */ + nntp->getWrapper()->logout(); + view->refreshCurrent(); + break; + case 1: + downloadMails(); + break; + default: + break; + } +} + +/** * IMAP Account stuff */ IMAPviewItem::IMAPviewItem( IMAPaccount *a, AccountView *parent ) : AccountViewItem( parent ) { account = a; wrapper = AbstractMail::getWrapper( account ); SETPIX(PIXMAP_IMAPFOLDER); setText( 0, account->getAccountName() ); setOpen( true ); } IMAPviewItem::~IMAPviewItem() { delete wrapper; } AbstractMail *IMAPviewItem::getWrapper() { return wrapper; } void IMAPviewItem::refresh(QList<RecMail>&) { refreshFolders(false); } const QStringList&IMAPviewItem::subFolders() { return currentFolders; } void IMAPviewItem::refreshFolders(bool force) { if (childCount()>0 && force==false) return; if (account->getOffline()) return; removeChilds(); currentFolders.clear(); QList<Folder> *folders = wrapper->listFolders(); Folder *it; QListViewItem*item = 0; QListViewItem*titem = 0; QString fname,del,search; int pos; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! folders->setAutoDelete(false); for ( it = folders->first(); it; it = folders->next() ) { if (it->getDisplayName().lower()=="inbox") { item = new IMAPfolderItem( it, this , item ); folders->remove(it); qDebug("inbox found"); break; } } for ( it = folders->first(); it; it = folders->next() ) { fname = it->getDisplayName(); currentFolders.append(it->getName()); pos = fname.findRev(it->Separator()); if (pos != -1) { fname = fname.left(pos); } IMAPfolderItem*pitem = (IMAPfolderItem*)findSubItem(fname); if (pitem) { titem = item; item = new IMAPfolderItem(it,pitem,pitem->firstChild(),this); /* setup the short name */ item->setText(0,it->getDisplayName().right(it->getDisplayName().length()-pos-1)); item = titem; } else { item = new IMAPfolderItem( it, this , item ); } } delete folders; } QPopupMenu * IMAPviewItem::getContextMenu() { QPopupMenu *m = new QPopupMenu(0); if (m) { if (!account->getOffline()) { m->insertItem(QObject::tr("Refresh folder list",contextName),0); m->insertItem(QObject::tr("Create new folder",contextName),1); m->insertSeparator(); m->insertItem(QObject::tr("Disconnect",contextName),2); diff --git a/noncore/net/mail/accountitem.h b/noncore/net/mail/accountitem.h index 99208b6..a138c9b 100644 --- a/noncore/net/mail/accountitem.h +++ b/noncore/net/mail/accountitem.h @@ -1,164 +1,204 @@ #ifndef __ACCOUNT_ITEM #define __ACCOUNT_ITEM #include <qlistview.h> #include <qlist.h> class POP3wrapper; class RecMail; class RecBody; class QPopupMenu; class Selectstore; class AccountView; class POP3account; +class NNTPaccount; class IMAPaccount; class AbstractMail; class Folder; class AccountViewItem : public QListViewItem { public: AccountViewItem( AccountView *parent ); AccountViewItem( QListViewItem *parent); AccountViewItem( QListViewItem *parent , QListViewItem*after ); virtual ~AccountViewItem(); virtual void refresh(QList<RecMail>&)=0; virtual RecBody fetchBody(const RecMail&)=0; virtual QPopupMenu * getContextMenu(){return 0;}; virtual void contextMenuSelected(int){} virtual AccountView*accountView(); virtual bool matchName(const QString&name)const; protected: AccountViewItem*findSubItem(const QString&path,AccountViewItem*start=0); virtual void init(); virtual void removeChilds(); virtual void deleteAllMail(AbstractMail*wrapper,Folder*f); static const QString contextName; AccountView*m_Backlink; Folder *folder; }; class POP3viewItem : public AccountViewItem { public: POP3viewItem( POP3account *a, AccountView *parent ); virtual ~POP3viewItem(); virtual void refresh( QList<RecMail> &target ); virtual RecBody fetchBody( const RecMail &mail ); AbstractMail *getWrapper(); virtual QPopupMenu * getContextMenu(); virtual void contextMenuSelected(int); protected: POP3account *account; virtual void refresh(); AbstractMail *wrapper; void disconnect(); void setOnOffline(); }; class POP3folderItem : public AccountViewItem { public: POP3folderItem( Folder *folder, POP3viewItem *parent , QListViewItem*after ); virtual ~POP3folderItem(); virtual void refresh(QList<RecMail>&); virtual RecBody fetchBody(const RecMail&); virtual QPopupMenu * getContextMenu(); virtual void contextMenuSelected(int); protected: void downloadMails(); POP3viewItem *pop3; }; + +class NNTPviewItem : public AccountViewItem +{ + +public: + NNTPviewItem( NNTPaccount *a, AccountView *parent ); + virtual ~NNTPviewItem(); + virtual void refresh( QList<RecMail> &target ); + virtual RecBody fetchBody( const RecMail &mail ); + AbstractMail *getWrapper(); + virtual QPopupMenu * getContextMenu(); + virtual void contextMenuSelected(int); + +protected: + NNTPaccount *account; + virtual void refresh(); + AbstractMail *wrapper; + void disconnect(); + void setOnOffline(); +}; + +class NNTPfolderItem : public AccountViewItem +{ + +public: + NNTPfolderItem( Folder *folder, NNTPviewItem *parent , QListViewItem*after ); + virtual ~NNTPfolderItem(); + virtual void refresh(QList<RecMail>&); + virtual RecBody fetchBody(const RecMail&); + virtual QPopupMenu * getContextMenu(); + virtual void contextMenuSelected(int); + +protected: + void downloadMails(); + NNTPviewItem *nntp; +}; + + + class IMAPviewItem : public AccountViewItem { friend class IMAPfolderItem; public: IMAPviewItem( IMAPaccount *a, AccountView *parent ); virtual ~IMAPviewItem(); virtual void refresh(QList<RecMail>&); virtual RecBody fetchBody(const RecMail&); AbstractMail *getWrapper(); virtual QPopupMenu * getContextMenu(); virtual void contextMenuSelected(int); const QStringList&subFolders(); virtual void refreshFolders(bool force=false); bool offline(); protected: virtual void createNewFolder(); IMAPaccount *account; AbstractMail *wrapper; QStringList currentFolders; }; class IMAPfolderItem : public AccountViewItem { public: IMAPfolderItem( Folder *folder, IMAPviewItem *parent , QListViewItem*after ); IMAPfolderItem( Folder *folder, IMAPfolderItem *parent , QListViewItem*after, IMAPviewItem *master ); virtual ~IMAPfolderItem(); virtual void refresh(QList<RecMail>&); virtual RecBody fetchBody(const RecMail&); virtual QPopupMenu * getContextMenu(); virtual void contextMenuSelected(int); virtual const QString& Delemiter()const; protected: virtual void createNewFolder(); virtual void deleteFolder(); virtual void downloadMails(); IMAPviewItem *imap; }; class MHviewItem : public AccountViewItem { friend class MHfolderItem; public: MHviewItem( const QString&aMboxPath, AccountView *parent ); virtual ~MHviewItem(); virtual void refresh( QList<RecMail> &target ); virtual RecBody fetchBody( const RecMail &mail ); AbstractMail *getWrapper(); virtual QPopupMenu * getContextMenu(); virtual void contextMenuSelected(int); QStringList subFolders(); virtual void refresh(bool force=false); protected: void downloadMails(); virtual void createFolder(); QString m_Path; AbstractMail *wrapper; QStringList currentFolders; }; class MHfolderItem : public AccountViewItem { public: MHfolderItem( Folder *folder, MHviewItem *parent , QListViewItem*after ); MHfolderItem( Folder *folder, MHfolderItem *parent, QListViewItem*after, MHviewItem*master); virtual ~MHfolderItem(); virtual void refresh(QList<RecMail>&); virtual RecBody fetchBody(const RecMail&); virtual QPopupMenu * getContextMenu(); virtual void contextMenuSelected(int); virtual Folder*getFolder(); protected: void downloadMails(); virtual void createFolder(); virtual void deleteFolder(); void initName(); MHviewItem *mbox; }; #endif diff --git a/noncore/net/mail/accountview.cpp b/noncore/net/mail/accountview.cpp index 2ddf834..c2185f2 100644 --- a/noncore/net/mail/accountview.cpp +++ b/noncore/net/mail/accountview.cpp @@ -1,163 +1,170 @@ #include "accountview.h" #include "accountitem.h" #include "selectstore.h" /* OPIE */ #include <libmailwrapper/settings.h> #include <libmailwrapper/mailwrapper.h> #include <libmailwrapper/mailtypes.h> #include <libmailwrapper/abstractmail.h> #include <qpe/qpeapplication.h> /* QT */ #include <qmessagebox.h> #include <qpopupmenu.h> AccountView::AccountView( QWidget *parent, const char *name, WFlags flags ) : QListView( parent, name, flags ) { connect( this, SIGNAL( selectionChanged( QListViewItem * ) ), SLOT( refresh( QListViewItem * ) ) ); connect( this, SIGNAL( mouseButtonPressed(int, QListViewItem *,const QPoint&,int ) ),this, SLOT( slotHold( int, QListViewItem *,const QPoint&,int ) ) ); setSorting(0); } AccountView::~AccountView() { imapAccounts.clear(); mhAccounts.clear(); } void AccountView::slotContextMenu(int id) { AccountViewItem *view = static_cast<AccountViewItem *>(currentItem()); if (!view) return; view->contextMenuSelected(id); } void AccountView::slotHold(int button, QListViewItem * item,const QPoint&,int) { if (button==1) {return;} if (!item) return; AccountViewItem *view = static_cast<AccountViewItem *>(item); QPopupMenu*m = view->getContextMenu(); if (!m) return; connect(m,SIGNAL(activated(int)),this,SLOT(slotContextMenu(int))); m->setFocus(); m->exec( QPoint( QCursor::pos().x(), QCursor::pos().y()) ); delete m; } void AccountView::populate( QList<Account> list ) { clear(); imapAccounts.clear(); mhAccounts.clear(); mhAccounts.append(new MHviewItem(AbstractMail::defaultLocalfolder(),this)); Account *it; for ( it = list.first(); it; it = list.next() ) { if ( it->getType().compare( "IMAP" ) == 0 ) { IMAPaccount *imap = static_cast<IMAPaccount *>(it); qDebug( "added IMAP " + imap->getAccountName() ); imapAccounts.append(new IMAPviewItem( imap, this )); } else if ( it->getType().compare( "POP3" ) == 0 ) { POP3account *pop3 = static_cast<POP3account *>(it); qDebug( "added POP3 " + pop3->getAccountName() ); /* must not be hold 'cause it isn't required */ (void) new POP3viewItem( pop3, this ); } + else if ( it->getType().compare( "NNTP" ) == 0 ) + { + NNTPaccount *nntp = static_cast<NNTPaccount *>(it); + qDebug( "added NNTP " + nntp->getAccountName() ); + /* must not be hold 'cause it isn't required */ + (void) new NNTPviewItem( nntp, this ); + } } } void AccountView::refresh(QListViewItem *item) { qDebug("AccountView refresh..."); if ( item ) { m_currentItem = item; QList<RecMail> headerlist; headerlist.setAutoDelete(true); AccountViewItem *view = static_cast<AccountViewItem *>(item); view->refresh(headerlist); emit refreshMailview(&headerlist); } } void AccountView::refreshCurrent() { m_currentItem = currentItem(); if ( !m_currentItem ) return; QList<RecMail> headerlist; headerlist.setAutoDelete(true); AccountViewItem *view = static_cast<AccountViewItem *>(m_currentItem); view->refresh(headerlist); emit refreshMailview(&headerlist); } void AccountView::refreshAll() { } RecBody AccountView::fetchBody(const RecMail&aMail) { QListViewItem*item = selectedItem (); if (!item) return RecBody(); AccountViewItem *view = static_cast<AccountViewItem *>(item); return view->fetchBody(aMail); } void AccountView::setupFolderselect(Selectstore*sels) { QPEApplication::showDialog( sels ); QStringList sFolders; unsigned int i = 0; for (i=0; i < mhAccounts.count();++i) { mhAccounts[i]->refresh(false); sFolders = mhAccounts[i]->subFolders(); sels->addAccounts(mhAccounts[i]->getWrapper(),sFolders); } for (i=0; i < imapAccounts.count();++i) { if (imapAccounts[i]->offline()) continue; imapAccounts[i]->refreshFolders(false); sels->addAccounts(imapAccounts[i]->getWrapper(),imapAccounts[i]->subFolders()); } } void AccountView::downloadMails(Folder*fromFolder,AbstractMail*fromWrapper) { AbstractMail*targetMail = 0; QString targetFolder = ""; Selectstore sels; setupFolderselect(&sels); if (!sels.exec()) return; targetMail = sels.currentMail(); targetFolder = sels.currentFolder(); if ( (fromWrapper==targetMail && fromFolder->getName()==targetFolder) || targetFolder.isEmpty()) { return; } if (sels.newFolder() && !targetMail->createMbox(targetFolder)) { QMessageBox::critical(0,tr("Error creating new Folder"), tr("<center>Error while creating<br>new folder - breaking.</center>")); return; } qDebug("Targetfolder: %s",targetFolder.latin1()); qDebug("Fromfolder: %s",fromFolder->getName().latin1()); fromWrapper->mvcpAllMails(fromFolder,targetFolder,targetMail,sels.moveMails()); refreshCurrent(); } diff --git a/noncore/net/mail/libmailwrapper/abstractmail.cpp b/noncore/net/mail/libmailwrapper/abstractmail.cpp index 592cd5e..741a8e1 100644 --- a/noncore/net/mail/libmailwrapper/abstractmail.cpp +++ b/noncore/net/mail/libmailwrapper/abstractmail.cpp @@ -1,120 +1,126 @@ #include "abstractmail.h" #include "imapwrapper.h" #include "pop3wrapper.h" +#include "nntpwrapper.h" #include "mhwrapper.h" #include "mboxwrapper.h" #include "mailtypes.h" #include <qstring.h> #include <qfile.h> #include <qtextstream.h> #include <stdlib.h> #include <libetpan/mailmime_content.h> #include <libetpan/mailmime.h> AbstractMail* AbstractMail::getWrapper(IMAPaccount *a) { return new IMAPwrapper(a); } AbstractMail* AbstractMail::getWrapper(POP3account *a) { return new POP3wrapper(a); } +AbstractMail* AbstractMail::getWrapper(NNTPaccount *a) +{ + return new NNTPwrapper(a); +} + AbstractMail* AbstractMail::getWrapper(const QString&a,const QString&name) { return new MHwrapper(a,name); } encodedString* AbstractMail::decode_String(const encodedString*text,const QString&enc) { qDebug("Decode string start"); char*result_text; size_t index = 0; /* reset for recursive use! */ size_t target_length = 0; result_text = 0; int mimetype = MAILMIME_MECHANISM_7BIT; if (enc.lower()=="quoted-printable") { mimetype = MAILMIME_MECHANISM_QUOTED_PRINTABLE; } else if (enc.lower()=="base64") { mimetype = MAILMIME_MECHANISM_BASE64; } else if (enc.lower()=="8bit") { mimetype = MAILMIME_MECHANISM_8BIT; } else if (enc.lower()=="binary") { mimetype = MAILMIME_MECHANISM_BINARY; } int err = mailmime_part_parse(text->Content(),text->Length(),&index,mimetype, &result_text,&target_length); encodedString* result = new encodedString(); if (err == MAILIMF_NO_ERROR) { result->setContent(result_text,target_length); } qDebug("Decode string finished"); return result; } QString AbstractMail::convert_String(const char*text) { size_t index = 0; char*res = 0; /* attention - doesn't work with arm systems! */ int err = mailmime_encoded_phrase_parse("iso-8859-1", text, strlen(text),&index, "iso-8859-1",&res); if (err != MAILIMF_NO_ERROR) { if (res) free(res); return QString(text); } if (res) { QString result(res); free(res); return result; } return QString(text); } /* cp & paste from launcher */ QString AbstractMail::gen_attachment_id() { QFile file( "/proc/sys/kernel/random/uuid" ); if (!file.open(IO_ReadOnly ) ) return QString::null; QTextStream stream(&file); return "{" + stream.read().stripWhiteSpace() + "}"; } int AbstractMail::createMbox(const QString&,const Folder*,const QString& delemiter,bool) { return 0; } QString AbstractMail::defaultLocalfolder() { QString f = getenv( "HOME" ); f += "/Applications/opiemail/localmail"; return f; } /* temporary - will be removed when implemented in all classes */ void AbstractMail::deleteMails(const QString &,QList<RecMail> &) { } void AbstractMail::mvcpAllMails(Folder*fromFolder,const QString&targetFolder,AbstractMail*targetWrapper,bool moveit) { QList<RecMail> t; listMessages(fromFolder->getName(),t); encodedString*st = 0; while (t.count()>0) { RecMail*r = t.at(0); st = fetchRawBody(*r); if (st) { targetWrapper->storeMessage(st->Content(),st->Length(),targetFolder); delete st; } diff --git a/noncore/net/mail/libmailwrapper/abstractmail.h b/noncore/net/mail/libmailwrapper/abstractmail.h index f93bab4..b6e1538 100644 --- a/noncore/net/mail/libmailwrapper/abstractmail.h +++ b/noncore/net/mail/libmailwrapper/abstractmail.h @@ -1,65 +1,66 @@ #ifndef __abstract_mail_ #define __abstract_mail_ #include <qobject.h> #include "settings.h" class RecMail; class RecBody; class RecPart; class IMAPwrapper; class POP3wrapper; class Folder; class encodedString; struct folderStat; class AbstractMail:public QObject { Q_OBJECT public: AbstractMail(){}; virtual ~AbstractMail(){} virtual QList<Folder>* listFolders()=0; virtual void listMessages(const QString & mailbox,QList<RecMail>&target )=0; virtual void statusFolder(folderStat&target_stat,const QString & mailbox="INBOX")=0; virtual RecBody fetchBody(const RecMail&mail)=0; virtual QString fetchTextPart(const RecMail&mail,const RecPart&part)=0; virtual encodedString* fetchDecodedPart(const RecMail&mail,const RecPart&part)=0; virtual encodedString* fetchRawPart(const RecMail&mail,const RecPart&part)=0; virtual encodedString* fetchRawBody(const RecMail&mail)=0; virtual void deleteMail(const RecMail&mail)=0; virtual void answeredMail(const RecMail&mail)=0; virtual int deleteAllMail(const Folder*)=0; virtual void deleteMails(const QString & FolderName,QList<RecMail> &target); virtual int deleteMbox(const Folder*)=0; virtual void storeMessage(const char*msg,size_t length, const QString&folder)=0; virtual void mvcpAllMails(Folder*fromFolder,const QString&targetFolder,AbstractMail*targetWrapper,bool moveit); virtual void mvcpMail(const RecMail&mail,const QString&targetFolder,AbstractMail*targetWrapper,bool moveit); virtual void cleanMimeCache(){}; /* mail box methods */ /* parameter is the box to create. * if the implementing subclass has prefixes, * them has to be appended automatic. */ virtual int createMbox(const QString&,const Folder*parentfolder=0,const QString& delemiter="/",bool getsubfolder=false); virtual void logout()=0; static AbstractMail* getWrapper(IMAPaccount *a); static AbstractMail* getWrapper(POP3account *a); + static AbstractMail* getWrapper(NNTPaccount *a); /* mbox only! */ static AbstractMail* getWrapper(const QString&a,const QString&name="Local Folders"); static QString defaultLocalfolder(); virtual const QString&getType()const=0; virtual const QString&getName()const=0; protected: static encodedString*decode_String(const encodedString*text,const QString&enc); static QString convert_String(const char*text); static QString gen_attachment_id(); }; #endif diff --git a/noncore/net/mail/libmailwrapper/libmailwrapper.pro b/noncore/net/mail/libmailwrapper/libmailwrapper.pro index 71f6cca..8ea04a4 100644 --- a/noncore/net/mail/libmailwrapper/libmailwrapper.pro +++ b/noncore/net/mail/libmailwrapper/libmailwrapper.pro @@ -1,48 +1,50 @@ TEMPLATE = lib CONFIG += qt warn_on debug HEADERS = mailwrapper.h \ imapwrapper.h \ mailtypes.h \ pop3wrapper.h \ abstractmail.h \ smtpwrapper.h \ genericwrapper.h \ mboxwrapper.h \ settings.h \ logindialog.h \ sendmailprogress.h \ statusmail.h \ - mhwrapper.h + mhwrapper.h \ + nntpwrapper.h SOURCES = imapwrapper.cpp \ mailwrapper.cpp \ mailtypes.cpp \ pop3wrapper.cpp \ abstractmail.cpp \ smtpwrapper.cpp \ genericwrapper.cpp \ mboxwrapper.cpp \ settings.cpp \ logindialog.cpp \ sendmailprogress.cpp \ statusmail.cpp \ - mhwrapper.cpp + mhwrapper.cpp \ + nntpwrapper.cpp INTERFACES = logindialogui.ui \ sendmailprogressui.ui INCLUDEPATH += $(OPIEDIR)/include CONFTEST = $$system( echo $CONFIG_TARGET_MACOSX ) contains( CONFTEST, y ){ LIBS += -lqpe -letpan -lssl -lcrypto -liconv }else{ LIBS += -lqpe -letpan -lssl -lcrypto } DESTDIR = $(OPIEDIR)/lib$(PROJMAK) TARGET = mailwrapper include ( $(OPIEDIR)/include.pro ) diff --git a/noncore/net/mail/libmailwrapper/nntpwrapper.cpp b/noncore/net/mail/libmailwrapper/nntpwrapper.cpp new file mode 100644 index 0000000..e73a890 --- a/dev/null +++ b/noncore/net/mail/libmailwrapper/nntpwrapper.cpp @@ -0,0 +1,241 @@ +#include "nntpwrapper.h" +#include "logindialog.h" +#include "mailtypes.h" + +#include <qfile.h> + +#include <stdlib.h> + +#include <libetpan/libetpan.h> +#include <libetpan/nntpdriver.h> + + + +#define HARD_MSG_SIZE_LIMIT 5242880 + +NNTPwrapper::NNTPwrapper( NNTPaccount *a ) +: Genericwrapper() { + account = a; + m_nntp = NULL; + msgTempName = a->getFileName()+"_msg_cache"; + last_msg_id = 0; +} + +NNTPwrapper::~NNTPwrapper() { + logout(); + QFile msg_cache(msgTempName); + if (msg_cache.exists()) { + msg_cache.remove(); + } +} + +void NNTPwrapper::nntp_progress( size_t current, size_t maximum ) { + qDebug( "NNTP: %i of %i", current, maximum ); +} + + +RecBody NNTPwrapper::fetchBody( const RecMail &mail ) { + int err = NEWSNNTP_NO_ERROR; + char *message = 0; + size_t length = 0; + + login(); + if ( !m_nntp ) { + return RecBody(); + } + + RecBody body; + mailmessage * mailmsg; + if (mail.Msgsize()>HARD_MSG_SIZE_LIMIT) { + qDebug("Message to large: %i",mail.Msgsize()); + return body; + } + + QFile msg_cache(msgTempName); + + cleanMimeCache(); + + if (mail.getNumber()!=last_msg_id) { + if (msg_cache.exists()) { + msg_cache.remove(); + } + msg_cache.open(IO_ReadWrite|IO_Truncate); + last_msg_id = mail.getNumber(); + err = mailsession_get_message(m_nntp->sto_session, mail.getNumber(), &mailmsg); + err = mailmessage_fetch(mailmsg,&message,&length); + msg_cache.writeBlock(message,length); + } else { + QString msg=""; + msg_cache.open(IO_ReadOnly); + message = new char[4096]; + memset(message,0,4096); + while (msg_cache.readBlock(message,4095)>0) { + msg+=message; + memset(message,0,4096); + } + delete message; + message = (char*)malloc(msg.length()+1*sizeof(char)); + memset(message,0,msg.length()+1); + memcpy(message,msg.latin1(),msg.length()); + /* transform to libetpan stuff */ + mailmsg = mailmessage_new(); + mailmessage_init(mailmsg, NULL, data_message_driver, 0, strlen(message)); + generic_message_t * msg_data; + msg_data = (generic_message_t *)mailmsg->msg_data; + msg_data->msg_fetched = 1; + msg_data->msg_message = message; + msg_data->msg_length = strlen(message); + } + body = parseMail(mailmsg); + + /* clean up */ + if (mailmsg) + mailmessage_free(mailmsg); + if (message) + free(message); + + return body; +} + + +void NNTPwrapper::listMessages(const QString &, QList<RecMail> &target ) +{ + login(); + if (!m_nntp) + return; + uint32_t res_messages,res_recent,res_unseen; + mailsession_status_folder(m_nntp->sto_session,"INBOX",&res_messages,&res_recent,&res_unseen); + parseList(target,m_nntp->sto_session,"INBOX"); +} + +void NNTPwrapper::login() +{ + if (account->getOffline()) + return; + /* we'll hold the line */ + if ( m_nntp != NULL ) + return; + + const char *server, *user, *pass; + uint16_t port; + int err = NEWSNNTP_NO_ERROR; + + server = account->getServer().latin1(); + port = account->getPort().toUInt(); + + if ( account->getUser().isEmpty() || account->getPassword().isEmpty() ) { + LoginDialog login( account->getUser(), account->getPassword(), NULL, 0, true ); + login.show(); + if ( QDialog::Accepted == login.exec() ) { + // ok + user = login.getUser().latin1(); + pass = login.getPassword().latin1(); + } else { + // cancel + qDebug( "NNTP: Login canceled" ); + return; + } + } else { + user = account->getUser().latin1(); + pass = account->getPassword().latin1(); + } + + // bool ssl = account->getSSL(); + + m_nntp=mailstorage_new(NULL); + + int conntypeset = account->ConnectionType(); + int conntype = 0; + if ( conntypeset == 3 ) { + conntype = CONNECTION_TYPE_COMMAND; + } else if ( conntypeset == 2 ) { + conntype = CONNECTION_TYPE_TLS; + } else if ( conntypeset == 1 ) { + conntype = CONNECTION_TYPE_STARTTLS; + } else if ( conntypeset == 0 ) { + conntype = CONNECTION_TYPE_TRY_STARTTLS; + } + + nntp_mailstorage_init(m_nntp,(char*)server, port, NULL, conntype, NNTP_AUTH_TYPE_PLAIN, + (char*)user,(char*)pass,0,0,0); + + err = mailstorage_connect(m_nntp); + + if (err != NEWSNNTP_NO_ERROR) { + qDebug( QString( "FEHLERNUMMER %1" ).arg( err ) ); + // Global::statusMessage(tr("Error initializing folder")); + mailstorage_free(m_nntp); + m_nntp = 0; + } +} + +void NNTPwrapper::logout() +{ + int err = NEWSNNTP_NO_ERROR; + if ( m_nntp == NULL ) + return; + mailstorage_free(m_nntp); + m_nntp = 0; +} + +QList<Folder>* NNTPwrapper::listFolders() { + QList<Folder> * folders = new QList<Folder>(); + folders->setAutoDelete( false ); + clist *result = 0; + + // int err = +// if ( err == _NO_ERROR ) { +// current = result->first; +// for ( current=clist_begin(result);current!=NULL;current=clist_next(current)) { + + +// Folder*inb=new Folder("INBOX","/"); + + +// folders->append(inb); + return folders; +} + + +void NNTPwrapper::answeredMail(const RecMail&) {} + +void NNTPwrapper::statusFolder(folderStat&target_stat,const QString&) { + login(); + target_stat.message_count = 0; + target_stat.message_unseen = 0; + target_stat.message_recent = 0; + if (!m_nntp) + return; + int r = mailsession_status_folder(m_nntp->sto_session,0,&target_stat.message_count, + &target_stat.message_recent,&target_stat.message_unseen); +} + + +encodedString* NNTPwrapper::fetchRawBody(const RecMail&mail) { + char*target=0; + size_t length=0; + encodedString*res = 0; + mailmessage * mailmsg = 0; + int err = mailsession_get_message(m_nntp->sto_session, mail.getNumber(), &mailmsg); + err = mailmessage_fetch(mailmsg,&target,&length); + if (mailmsg) + mailmessage_free(mailmsg); + if (target) { + res = new encodedString(target,length); + } + return res; +} + +const QString&NNTPwrapper::getType()const { + return account->getType(); +} + +const QString&NNTPwrapper::getName()const{ + return account->getAccountName(); +} + +void NNTPwrapper::deleteMail(const RecMail&mail) { +} + +int NNTPwrapper::deleteAllMail(const Folder*) { +} diff --git a/noncore/net/mail/libmailwrapper/nntpwrapper.h b/noncore/net/mail/libmailwrapper/nntpwrapper.h new file mode 100644 index 0000000..e47e68f --- a/dev/null +++ b/noncore/net/mail/libmailwrapper/nntpwrapper.h @@ -0,0 +1,48 @@ +#ifndef __NNTPWRAPPER +#define __NNTPWRAPPER + +#include "mailwrapper.h" +#include "genericwrapper.h" +#include <qstring.h> +#include <libetpan/clist.h> + +class encodedString; +struct mailstorage; +struct mailfolder; + +class NNTPwrapper : public Genericwrapper +{ + + Q_OBJECT + +public: + NNTPwrapper( NNTPaccount *a ); + virtual ~NNTPwrapper(); + + /* mailbox will be ignored */ + virtual void listMessages(const QString & mailbox, QList<RecMail> &target ); + /* should only get the subscribed one */ + virtual QList<Folder>* listFolders(); + /* mailbox will be ignored */ + virtual void statusFolder(folderStat&target_stat,const QString & mailbox="INBOX"); + + virtual void deleteMail(const RecMail&mail); + virtual void answeredMail(const RecMail&mail); + virtual int deleteAllMail(const Folder*); + + virtual RecBody fetchBody( const RecMail &mail ); + virtual encodedString* fetchRawBody(const RecMail&mail); + virtual void logout(); + virtual const QString&getType()const; + virtual const QString&getName()const; + static void nntp_progress( size_t current, size_t maximum ); + +protected: + void login(); + NNTPaccount *account; + mailstorage* m_nntp; + + +}; + +#endif diff --git a/noncore/net/mail/libmailwrapper/pop3wrapper.cpp b/noncore/net/mail/libmailwrapper/pop3wrapper.cpp index 14c2059..6fab401 100644 --- a/noncore/net/mail/libmailwrapper/pop3wrapper.cpp +++ b/noncore/net/mail/libmailwrapper/pop3wrapper.cpp @@ -1,104 +1,104 @@ #include <stdlib.h> #include "pop3wrapper.h" #include "mailtypes.h" #include "logindialog.h" #include <libetpan/libetpan.h> #include <qpe/global.h> #include <qfile.h> -#include <qstring.h> +//#include <qstring.h> /* we don't fetch messages larger than 5 MB */ #define HARD_MSG_SIZE_LIMIT 5242880 POP3wrapper::POP3wrapper( POP3account *a ) : Genericwrapper() { account = a; m_pop3 = NULL; msgTempName = a->getFileName()+"_msg_cache"; last_msg_id = 0; } POP3wrapper::~POP3wrapper() { logout(); QFile msg_cache(msgTempName); if (msg_cache.exists()) { msg_cache.remove(); } } void POP3wrapper::pop3_progress( size_t current, size_t maximum ) { qDebug( "POP3: %i of %i", current, maximum ); } RecBody POP3wrapper::fetchBody( const RecMail &mail ) { int err = MAILPOP3_NO_ERROR; char *message = 0; size_t length = 0; login(); if ( !m_pop3 ) { return RecBody(); } RecBody body; mailmessage * mailmsg; if (mail.Msgsize()>HARD_MSG_SIZE_LIMIT) { qDebug("Message to large: %i",mail.Msgsize()); return body; } QFile msg_cache(msgTempName); cleanMimeCache(); if (mail.getNumber()!=last_msg_id) { if (msg_cache.exists()) { msg_cache.remove(); } msg_cache.open(IO_ReadWrite|IO_Truncate); last_msg_id = mail.getNumber(); err = mailsession_get_message(m_pop3->sto_session, mail.getNumber(), &mailmsg); err = mailmessage_fetch(mailmsg,&message,&length); msg_cache.writeBlock(message,length); } else { QString msg=""; msg_cache.open(IO_ReadOnly); message = new char[4096]; memset(message,0,4096); while (msg_cache.readBlock(message,4095)>0) { msg+=message; memset(message,0,4096); } delete message; message = (char*)malloc(msg.length()+1*sizeof(char)); memset(message,0,msg.length()+1); memcpy(message,msg.latin1(),msg.length()); /* transform to libetpan stuff */ mailmsg = mailmessage_new(); mailmessage_init(mailmsg, NULL, data_message_driver, 0, strlen(message)); generic_message_t * msg_data; msg_data = (generic_message_t *)mailmsg->msg_data; msg_data->msg_fetched = 1; msg_data->msg_message = message; msg_data->msg_length = strlen(message); } body = parseMail(mailmsg); /* clean up */ if (mailmsg) mailmessage_free(mailmsg); if (message) free(message); return body; } void POP3wrapper::listMessages(const QString &, QList<RecMail> &target ) { login(); if (!m_pop3) return; uint32_t res_messages,res_recent,res_unseen; mailsession_status_folder(m_pop3->sto_session,"INBOX",&res_messages,&res_recent,&res_unseen); parseList(target,m_pop3->sto_session,"INBOX"); Global::statusMessage( tr("Mailbox contains %1 mail(s)").arg(res_messages)); diff --git a/noncore/net/mail/nntpconfigui.ui b/noncore/net/mail/nntpconfigui.ui index cc439f4..7769804 100644 --- a/noncore/net/mail/nntpconfigui.ui +++ b/noncore/net/mail/nntpconfigui.ui @@ -1,257 +1,352 @@ <!DOCTYPE UI><UI> <class>NNTPconfigUI</class> <widget> <class>QDialog</class> <property stdset="1"> <name>name</name> <cstring>NNTPconfigUI</cstring> </property> <property stdset="1"> <name>geometry</name> <rect> <x>0</x> <y>0</y> - <width>228</width> - <height>320</height> + <width>413</width> + <height>520</height> </rect> </property> <property stdset="1"> <name>caption</name> <string>Configure NNTP</string> </property> <property> <name>layoutMargin</name> </property> <property> <name>layoutSpacing</name> </property> + <vbox> + <property stdset="1"> + <name>margin</name> + <number>3</number> + </property> + <property stdset="1"> + <name>spacing</name> + <number>3</number> + </property> + <widget> + <class>QTabWidget</class> + <property stdset="1"> + <name>name</name> + <cstring>TabWidget2</cstring> + </property> + <property> + <name>layoutMargin</name> + </property> + <property> + <name>layoutSpacing</name> + </property> + <widget> + <class>QWidget</class> + <property stdset="1"> + <name>name</name> + <cstring>tab</cstring> + </property> + <attribute> + <name>title</name> + <string>Account</string> + </attribute> <grid> <property stdset="1"> <name>margin</name> - <number>4</number> + <number>3</number> </property> <property stdset="1"> <name>spacing</name> <number>3</number> </property> <widget row="2" column="1" > <class>QLineEdit</class> <property stdset="1"> <name>name</name> <cstring>serverLine</cstring> </property> </widget> <widget row="3" column="0" > <class>QLabel</class> <property stdset="1"> <name>name</name> <cstring>portLabel</cstring> </property> <property stdset="1"> <name>text</name> <string>Port</string> </property> </widget> <widget row="3" column="1" > <class>QLineEdit</class> <property stdset="1"> <name>name</name> <cstring>portLine</cstring> </property> </widget> <widget row="0" column="1" > <class>QLineEdit</class> <property stdset="1"> <name>name</name> <cstring>accountLine</cstring> </property> <property> <name>toolTip</name> <string>Name of the Account</string> </property> </widget> <widget row="0" column="0" > <class>QLabel</class> <property stdset="1"> <name>name</name> <cstring>accountLabel</cstring> </property> <property stdset="1"> <name>text</name> <string>Account</string> </property> </widget> <widget row="2" column="0" > <class>QLabel</class> <property stdset="1"> <name>name</name> <cstring>serverLabel</cstring> </property> <property stdset="1"> <name>enabled</name> <bool>true</bool> </property> <property stdset="1"> <name>caption</name> <string></string> </property> <property stdset="1"> <name>text</name> <string>Server</string> </property> <property> <name>layoutMargin</name> </property> <property> <name>layoutSpacing</name> </property> </widget> <widget row="1" column="0" rowspan="1" colspan="2" > <class>Line</class> <property stdset="1"> <name>name</name> <cstring>line1</cstring> </property> <property stdset="1"> <name>caption</name> <string></string> </property> <property stdset="1"> <name>orientation</name> <enum>Horizontal</enum> </property> <property> <name>layoutMargin</name> </property> <property> <name>layoutSpacing</name> </property> </widget> <widget row="4" column="1" > <class>QCheckBox</class> <property stdset="1"> <name>name</name> <cstring>sslBox</cstring> </property> <property stdset="1"> <name>text</name> <string>Use SSL</string> </property> </widget> - <widget row="5" column="0" rowspan="1" colspan="2" > + <widget row="5" column="0" rowspan="2" colspan="2" > <class>Line</class> <property stdset="1"> <name>name</name> <cstring>line2</cstring> </property> <property stdset="1"> <name>enabled</name> <bool>true</bool> </property> <property stdset="1"> <name>caption</name> <string></string> </property> <property stdset="1"> <name>orientation</name> <enum>Horizontal</enum> </property> <property> <name>layoutMargin</name> </property> <property> <name>layoutSpacing</name> </property> </widget> <widget row="7" column="0" > <class>QLabel</class> <property stdset="1"> <name>name</name> <cstring>userLabel</cstring> </property> <property stdset="1"> <name>text</name> <string>User</string> </property> </widget> <widget row="8" column="0" > <class>QLabel</class> <property stdset="1"> <name>name</name> <cstring>passLabel</cstring> </property> <property stdset="1"> <name>text</name> <string>Password</string> </property> </widget> <spacer row="9" column="1" > <property> <name>name</name> <cstring>spacer</cstring> </property> <property stdset="1"> <name>orientation</name> <enum>Vertical</enum> </property> <property stdset="1"> <name>sizeType</name> <enum>Expanding</enum> </property> <property> <name>sizeHint</name> <size> <width>20</width> <height>20</height> </size> </property> </spacer> <widget row="6" column="1" > <class>QCheckBox</class> <property stdset="1"> <name>name</name> <cstring>loginBox</cstring> </property> <property stdset="1"> <name>text</name> <string>Use Login</string> </property> </widget> <widget row="7" column="1" > <class>QLineEdit</class> <property stdset="1"> <name>name</name> <cstring>userLine</cstring> </property> <property stdset="1"> <name>enabled</name> <bool>false</bool> </property> </widget> <widget row="8" column="1" > <class>QLineEdit</class> <property stdset="1"> <name>name</name> <cstring>passLine</cstring> </property> <property stdset="1"> <name>enabled</name> <bool>false</bool> </property> <property stdset="1"> <name>echoMode</name> <enum>Password</enum> </property> </widget> </grid> </widget> + <widget> + <class>QWidget</class> + <property stdset="1"> + <name>name</name> + <cstring>tab</cstring> + </property> + <attribute> + <name>title</name> + <string>Groups</string> + </attribute> + <vbox> + <property stdset="1"> + <name>margin</name> + <number>3</number> + </property> + <property stdset="1"> + <name>spacing</name> + <number>3</number> + </property> + <widget> + <class>QListView</class> + <property stdset="1"> + <name>name</name> + <cstring>ListViewGroups</cstring> + </property> + </widget> + <widget> + <class>QPushButton</class> + <property stdset="1"> + <name>name</name> + <cstring>GetNGButton</cstring> + </property> + <property stdset="1"> + <name>text</name> + <string>Get newsgroup list from server</string> + </property> + </widget> + </vbox> + </widget> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>QListView</class> + <header location="global">qlistview.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image> + <name>image0</name> + <data format="XPM.GZ" length="646">789c6dd2c10ac2300c00d07bbf2234b7229d1ddec44f503c0ae2a154410f53d0ed20e2bf6bdb656dd6861dd23d9a66591b0587fd1654235ebded6f0edcd53e419d87ae7b1f4f9b8f906d0bfe012317426a70b07bdc2f3ec77f8ed6b89559061a0343d06a124cc105596482585094bc0ae599b04646c9018926491b2205e140c485cace25755c175d0a967b622ff900b8cc9c7d29af594ea722d589167f813aa852ba07d94b9dce296e883fe7bb163f23896753</data> + </image> +</images> <tabstops> <tabstop>accountLine</tabstop> <tabstop>serverLine</tabstop> <tabstop>portLine</tabstop> <tabstop>sslBox</tabstop> <tabstop>loginBox</tabstop> <tabstop>userLine</tabstop> <tabstop>passLine</tabstop> </tabstops> </UI> |