tdeioslave/imap4

imap4.cpp
1/**********************************************************************
2 *
3 * imap4.cpp - IMAP4rev1 KIOSlave
4 * Copyright (C) 2001-2002 Michael Haeckel <haeckel@kde.org>
5 * Copyright (C) 1999 John Corey <jcorey@fruity.ath.cx>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 * Send comments and bug fixes to jcorey@fruity.ath.cx
22 *
23 *********************************************************************/
24
59#ifdef HAVE_CONFIG_H
60#include <config.h>
61#endif
62
63#include "imap4.h"
64
65#include "rfcdecoder.h"
66
67#include <sys/stat.h>
68
69#include <stdio.h>
70#include <stdlib.h>
71#include <signal.h>
72#include <sys/types.h>
73#include <sys/wait.h>
74#include <errno.h>
75
76#ifdef HAVE_LIBSASL2
77extern "C" {
78#include <sasl/sasl.h>
79}
80#endif
81
82#include <tqbuffer.h>
83#include <tqdatetime.h>
84#include <tqregexp.h>
85#include <tdeprotocolmanager.h>
86#include <tdemessagebox.h>
87#include <kdebug.h>
88#include <tdeio/connection.h>
89#include <tdeio/slaveinterface.h>
90#include <tdeio/passdlg.h>
91#include <tdelocale.h>
92#include <kmimetype.h>
93#include <kmdcodec.h>
94
95#include <tdemacros.h>
96
97#define IMAP_PROTOCOL "imap"
98#define IMAP_SSL_PROTOCOL "imaps"
99const int ImapPort = 143;
100const int ImapsPort = 993;
101
102using namespace TDEIO;
103
104extern "C"
105{
106 void sigalrm_handler (int);
107 TDE_EXPORT int kdemain (int argc, char **argv);
108}
109
110int
111kdemain (int argc, char **argv)
112{
113 kdDebug(7116) << "IMAP4::kdemain" << endl;
114
115 TDEInstance instance ("tdeio_imap4");
116 if (argc != 4)
117 {
118 fprintf(stderr, "Usage: tdeio_imap4 protocol domain-socket1 domain-socket2\n");
119 ::exit (-1);
120 }
121
122#ifdef HAVE_LIBSASL2
123 if ( sasl_client_init( NULL ) != SASL_OK ) {
124 fprintf(stderr, "SASL library initialization failed!\n");
125 ::exit (-1);
126 }
127#endif
128
129 //set debug handler
130
131 IMAP4Protocol *slave;
132 if (strcasecmp (argv[1], IMAP_SSL_PROTOCOL) == 0)
133 slave = new IMAP4Protocol (argv[2], argv[3], true);
134 else if (strcasecmp (argv[1], IMAP_PROTOCOL) == 0)
135 slave = new IMAP4Protocol (argv[2], argv[3], false);
136 else
137 abort ();
138 slave->dispatchLoop ();
139 delete slave;
140
141#ifdef HAVE_LIBSASL2
142 sasl_done();
143#endif
144
145 return 0;
146}
147
148void
149sigchld_handler (int signo)
150{
151 // A signal handler that calls for example waitpid has to save errno
152 // before and restore it afterwards.
153 // (cf. https://www.securecoding.cert.org/confluence/display/cplusplus/ERR32-CPP.+Do+not+rely+on+indeterminate+values+of+errno)
154 const int save_errno = errno;
155 int pid, status;
156
157 while (signo == SIGCHLD)
158 {
159 pid = waitpid (-1, &status, WNOHANG);
160 if (pid <= 0)
161 {
162 // Reinstall signal handler, since Linux resets to default after
163 // the signal occurred ( BSD handles it different, but it should do
164 // no harm ).
165 signal (SIGCHLD, sigchld_handler);
166 break;
167 }
168 }
169
170 errno = save_errno;
171}
172
173IMAP4Protocol::IMAP4Protocol (const TQCString & pool, const TQCString & app, bool isSSL):TCPSlaveBase ((isSSL ? 993 : 143),
174 (isSSL ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL), pool,
175 app, isSSL), imapParser (), mimeIO (), outputBuffer(outputCache)
176{
177 outputBufferIndex = 0;
178 mySSL = isSSL;
179 readBuffer[0] = 0x00;
180 relayEnabled = false;
181 readBufferLen = 0;
182 cacheOutput = false;
183 decodeContent = false;
184 mTimeOfLastNoop = TQDateTime();
185}
186
187IMAP4Protocol::~IMAP4Protocol ()
188{
189 closeDescriptor();
190 kdDebug(7116) << "IMAP4: Finishing" << endl;
191}
192
193void
194IMAP4Protocol::get (const KURL & _url)
195{
196 if (!makeLogin()) return;
197 kdDebug(7116) << "IMAP4::get - " << _url.prettyURL() << endl;
198 TQString aBox, aSequence, aType, aSection, aValidity, aDelimiter, aInfo;
199 enum IMAP_TYPE aEnum =
200 parseURL (_url, aBox, aSection, aType, aSequence, aValidity, aDelimiter, aInfo);
201 if (aEnum != ITYPE_ATTACH)
202 mimeType (getMimeType(aEnum));
203 if (aInfo == "DECODE")
204 decodeContent = true;
205
206 if (aSequence == "0:0" && getState() == ISTATE_SELECT)
207 {
208 imapCommand *cmd = doCommand (imapCommand::clientNoop());
209 completeQueue.removeRef(cmd);
210 }
211
212 if (aSequence.isEmpty ())
213 {
214 aSequence = "1:*";
215 }
216
217 mProcessedSize = 0;
218 imapCommand *cmd = NULL;
219 if (!assureBox (aBox, true)) return;
220
221#ifdef USE_VALIDITY
222 if (selectInfo.uidValidityAvailable () && !aValidity.isEmpty ()
223 && selectInfo.uidValidity () != aValidity.toULong ())
224 {
225 // this url is stale
226 error (ERR_COULD_NOT_READ, _url.prettyURL());
227 return;
228 }
229 else
230#endif
231 {
232 // The "section" specified by the application can be:
233 // * empty (which means body, size and flags)
234 // * a known keyword, like STRUCTURE, ENVELOPE, HEADER, BODY.PEEK[...]
235 // (in which case the slave has some logic to add the necessary items)
236 // * Otherwise, it specifies the exact data items to request. In this case, all
237 // the logic is in the app.
238
239 TQString aUpper = aSection.upper();
240 if (aUpper.find ("STRUCTURE") != -1)
241 {
242 aSection = "BODYSTRUCTURE";
243 }
244 else if (aUpper.find ("ENVELOPE") != -1)
245 {
246 aSection = "UID RFC822.SIZE FLAGS ENVELOPE";
247 if (hasCapability("IMAP4rev1")) {
248 aSection += " BODY.PEEK[HEADER.FIELDS (REFERENCES)]";
249 } else {
250 // imap4 does not know HEADER.FIELDS
251 aSection += " RFC822.HEADER.LINES (REFERENCES)";
252 }
253 }
254 else if (aUpper == "HEADER")
255 {
256 aSection = "UID RFC822.HEADER RFC822.SIZE FLAGS";
257 }
258 else if (aUpper.find ("BODY.PEEK[") != -1)
259 {
260 if (aUpper.find ("BODY.PEEK[]") != -1)
261 {
262 if (!hasCapability("IMAP4rev1")) // imap4 does not know BODY.PEEK[]
263 aSection.replace("BODY.PEEK[]", "RFC822.PEEK");
264 }
265 aSection.prepend("UID RFC822.SIZE FLAGS ");
266 }
267 else if (aSection.isEmpty())
268 {
269 aSection = "UID BODY[] RFC822.SIZE FLAGS";
270 }
271 if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
272 {
273 // write the digest header
274 cacheOutput = true;
276 ("Content-Type: multipart/digest; boundary=\"IMAPDIGEST\"\r\n", 55);
277 if (selectInfo.recentAvailable ())
278 outputLineStr ("X-Recent: " +
279 TQString::number(selectInfo.recent ()) + "\r\n");
280 if (selectInfo.countAvailable ())
281 outputLineStr ("X-Count: " + TQString::number(selectInfo.count ()) +
282 "\r\n");
283 if (selectInfo.unseenAvailable ())
284 outputLineStr ("X-Unseen: " +
285 TQString::number(selectInfo.unseen ()) + "\r\n");
286 if (selectInfo.uidValidityAvailable ())
287 outputLineStr ("X-uidValidity: " +
288 TQString::number(selectInfo.uidValidity ()) +
289 "\r\n");
290 if (selectInfo.uidNextAvailable ())
291 outputLineStr ("X-UidNext: " +
292 TQString::number(selectInfo.uidNext ()) + "\r\n");
293 if (selectInfo.flagsAvailable ())
294 outputLineStr ("X-Flags: " + TQString::number(selectInfo.flags ()) +
295 "\r\n");
296 if (selectInfo.permanentFlagsAvailable ())
297 outputLineStr ("X-PermanentFlags: " +
298 TQString::number(selectInfo.permanentFlags ()) + "\r\n");
299 if (selectInfo.readWriteAvailable ()) {
300 if (selectInfo.readWrite()) {
301 outputLine ("X-Access: Read/Write\r\n", 22);
302 } else {
303 outputLine ("X-Access: Read only\r\n", 21);
304 }
305 }
306 outputLine ("\r\n", 2);
307 flushOutput(TQString());
308 cacheOutput = false;
309 }
310
311 if (aEnum == ITYPE_MSG || (aEnum == ITYPE_ATTACH && !decodeContent))
312 relayEnabled = true; // normal mode, relay data
313
314 if (aSequence != "0:0")
315 {
316 TQString contentEncoding;
317 if (aEnum == ITYPE_ATTACH && decodeContent)
318 {
319 // get the MIME header and fill getLastHandled()
320 TQString mySection = aSection;
321 mySection.replace("]", ".MIME]");
322 cmd = sendCommand (imapCommand::clientFetch (aSequence, mySection));
323 do
324 {
325 while (!parseLoop ()) ;
326 }
327 while (!cmd->isComplete ());
328 completeQueue.removeRef (cmd);
329 // get the content encoding now because getLastHandled will be cleared
330 if (getLastHandled() && getLastHandled()->getHeader())
331 contentEncoding = getLastHandled()->getHeader()->getEncoding();
332
333 // from here on collect the data
334 // it is send to the client in flushOutput in one go
335 // needed to decode the content
336 cacheOutput = true;
337 }
338
339 cmd = sendCommand (imapCommand::clientFetch (aSequence, aSection));
340 int res;
341 aUpper = aSection.upper();
342 do
343 {
344 while (!(res = parseLoop())) ;
345 if (res == -1) break;
346
347 mailHeader *lastone = 0;
348 imapCache *cache = getLastHandled ();
349 if (cache)
350 lastone = cache->getHeader ();
351
352 if (cmd && !cmd->isComplete ())
353 {
354 if ((aUpper.find ("BODYSTRUCTURE") != -1)
355 || (aUpper.find ("FLAGS") != -1)
356 || (aUpper.find ("UID") != -1)
357 || (aUpper.find ("ENVELOPE") != -1)
358 || (aUpper.find ("BODY.PEEK[0]") != -1
359 && (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)))
360 {
361 if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
362 {
363 // write the mime header (default is here message/rfc822)
364 outputLine ("--IMAPDIGEST\r\n", 14);
365 cacheOutput = true;
366 if (cache && cache->getUid () != 0)
367 outputLineStr ("X-UID: " +
368 TQString::number(cache->getUid ()) + "\r\n");
369 if (cache && cache->getSize () != 0)
370 outputLineStr ("X-Length: " +
371 TQString::number(cache->getSize ()) + "\r\n");
372 if (cache && !cache->getDate ().isEmpty())
373 outputLineStr ("X-Date: " + cache->getDate () + "\r\n");
374 if (cache && cache->getFlags () != 0)
375 outputLineStr ("X-Flags: " +
376 TQString::number(cache->getFlags ()) + "\r\n");
377 } else cacheOutput = true;
378 if ( lastone && !decodeContent )
379 lastone->outputPart (*this);
380 cacheOutput = false;
381 flushOutput(contentEncoding);
382 }
383 } // if not complete
384 }
385 while (cmd && !cmd->isComplete ());
386 if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
387 {
388 // write the end boundary
389 outputLine ("--IMAPDIGEST--\r\n", 16);
390 }
391
392 completeQueue.removeRef (cmd);
393 }
394 }
395
396 // just to keep everybody happy when no data arrived
397 data (TQByteArray ());
398
399 finished ();
400 relayEnabled = false;
401 cacheOutput = false;
402 kdDebug(7116) << "IMAP4::get - finished" << endl;
403}
404
405void
406IMAP4Protocol::listDir (const KURL & _url)
407{
408 kdDebug(7116) << " IMAP4::listDir - " << _url.prettyURL() << endl;
409
410 if (_url.path().isEmpty())
411 {
412 KURL url = _url;
413 url.setPath("/");
414 redirection( url );
415 finished();
416 return;
417 }
418
419 TQString myBox, mySequence, myLType, mySection, myValidity, myDelimiter, myInfo;
420 // parseURL with caching
421 enum IMAP_TYPE myType =
422 parseURL (_url, myBox, mySection, myLType, mySequence, myValidity,
423 myDelimiter, myInfo, true);
424
425 if (!makeLogin()) return;
426
427 if (myType == ITYPE_DIR || myType == ITYPE_DIR_AND_BOX)
428 {
429 TQString listStr = myBox;
430 imapCommand *cmd;
431
432 if (!listStr.isEmpty () && !listStr.endsWith(myDelimiter) &&
433 mySection != "FOLDERONLY")
434 listStr += myDelimiter;
435
436 if (mySection.isEmpty())
437 {
438 listStr += "%";
439 } else if (mySection == "COMPLETE") {
440 listStr += "*";
441 }
442 kdDebug(7116) << "IMAP4Protocol::listDir - listStr=" << listStr << endl;
443 cmd =
444 doCommand (imapCommand::clientList ("", listStr,
445 (myLType == "LSUB" || myLType == "LSUBNOCHECK")));
446 if (cmd->result () == "OK")
447 {
448 TQString mailboxName;
449 UDSEntry entry;
450 UDSAtom atom;
451 KURL aURL = _url;
452 if (aURL.path().find(';') != -1)
453 aURL.setPath(aURL.path().left(aURL.path().find(';')));
454
455 kdDebug(7116) << "IMAP4Protocol::listDir - got " << listResponses.count () << endl;
456
457 if (myLType == "LSUB")
458 {
459 // fire the same command as LIST to check if the box really exists
460 TQValueList<imapList> listResponsesSave = listResponses;
461 doCommand (imapCommand::clientList ("", listStr, false));
462 for (TQValueListIterator < imapList > it = listResponsesSave.begin ();
463 it != listResponsesSave.end (); ++it)
464 {
465 bool boxOk = false;
466 for (TQValueListIterator < imapList > it2 = listResponses.begin ();
467 it2 != listResponses.end (); ++it2)
468 {
469 if ((*it2).name() == (*it).name())
470 {
471 boxOk = true;
472 // copy the flags from the LIST-command
473 (*it) = (*it2);
474 break;
475 }
476 }
477 if (boxOk)
478 doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY"));
479 else // this folder is dead
480 kdDebug(7116) << "IMAP4Protocol::listDir - suppress " << (*it).name() << endl;
481 }
482 listResponses = listResponsesSave;
483 }
484 else // LIST or LSUBNOCHECK
485 {
486 for (TQValueListIterator < imapList > it = listResponses.begin ();
487 it != listResponses.end (); ++it)
488 {
489 doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY"));
490 }
491 }
492 entry.clear ();
493 listEntry (entry, true);
494 }
495 else
496 {
497 error (ERR_CANNOT_ENTER_DIRECTORY, _url.prettyURL());
498 completeQueue.removeRef (cmd);
499 return;
500 }
501 completeQueue.removeRef (cmd);
502 }
503 if ((myType == ITYPE_BOX || myType == ITYPE_DIR_AND_BOX)
504 && myLType != "LIST" && myLType != "LSUB" && myLType != "LSUBNOCHECK")
505 {
506 KURL aURL = _url;
507 aURL.setQuery (TQString());
508 const TQString encodedUrl = aURL.url(0, 106); // utf-8
509
510 if (!_url.query ().isEmpty ())
511 {
512 TQString query = KURL::decode_string (_url.query ());
513 query = query.right (query.length () - 1);
514 if (!query.isEmpty())
515 {
516 imapCommand *cmd = NULL;
517
518 if (!assureBox (myBox, true)) return;
519
520 if (!selectInfo.countAvailable() || selectInfo.count())
521 {
522 cmd = doCommand (imapCommand::clientSearch (query));
523 if (cmd->result() != "OK")
524 {
525 error(ERR_UNSUPPORTED_ACTION, _url.prettyURL());
526 completeQueue.removeRef (cmd);
527 return;
528 }
529 completeQueue.removeRef (cmd);
530
531 TQStringList list = getResults ();
532 int stretch = 0;
533
534 if (selectInfo.uidNextAvailable ())
535 stretch = TQString::number(selectInfo.uidNext ()).length ();
536 UDSEntry entry;
537 imapCache fake;
538
539 for (TQStringList::ConstIterator it = list.begin(); it != list.end();
540 ++it)
541 {
542 fake.setUid((*it).toULong());
543 doListEntry (encodedUrl, stretch, &fake);
544 }
545 entry.clear ();
546 listEntry (entry, true);
547 }
548 }
549 }
550 else
551 {
552 if (!assureBox (myBox, true)) return;
553
554 kdDebug(7116) << "IMAP4: select returned:" << endl;
555 if (selectInfo.recentAvailable ())
556 kdDebug(7116) << "Recent: " << selectInfo.recent () << "d" << endl;
557 if (selectInfo.countAvailable ())
558 kdDebug(7116) << "Count: " << selectInfo.count () << "d" << endl;
559 if (selectInfo.unseenAvailable ())
560 kdDebug(7116) << "Unseen: " << selectInfo.unseen () << "d" << endl;
561 if (selectInfo.uidValidityAvailable ())
562 kdDebug(7116) << "uidValidity: " << selectInfo.uidValidity () << "d" << endl;
563 if (selectInfo.flagsAvailable ())
564 kdDebug(7116) << "Flags: " << selectInfo.flags () << "d" << endl;
565 if (selectInfo.permanentFlagsAvailable ())
566 kdDebug(7116) << "PermanentFlags: " << selectInfo.permanentFlags () << "d" << endl;
567 if (selectInfo.readWriteAvailable ())
568 kdDebug(7116) << "Access: " << (selectInfo.readWrite ()? "Read/Write" : "Read only") << endl;
569
570#ifdef USE_VALIDITY
571 if (selectInfo.uidValidityAvailable ()
572 && selectInfo.uidValidity () != myValidity.toULong ())
573 {
574 //redirect
575 KURL newUrl = _url;
576
577 newUrl.setPath ("/" + myBox + ";UIDVALIDITY=" +
578 TQString::number(selectInfo.uidValidity ()));
579 kdDebug(7116) << "IMAP4::listDir - redirecting to " << newUrl.prettyURL() << endl;
580 redirection (newUrl);
581
582
583 }
584 else
585#endif
586 if (selectInfo.count () > 0)
587 {
588 int stretch = 0;
589
590 if (selectInfo.uidNextAvailable ())
591 stretch = TQString::number(selectInfo.uidNext ()).length ();
592 // kdDebug(7116) << selectInfo.uidNext() << "d used to stretch " << stretch << endl;
593 UDSEntry entry;
594
595 if (mySequence.isEmpty()) mySequence = "1:*";
596
597 bool withSubject = mySection.isEmpty();
598 if (mySection.isEmpty()) mySection = "UID RFC822.SIZE ENVELOPE";
599
600 bool withFlags = mySection.upper().find("FLAGS") != -1;
601 imapCommand *fetch =
602 sendCommand (imapCommand::
603 clientFetch (mySequence, mySection));
604 imapCache *cache;
605 do
606 {
607 while (!parseLoop ()) ;
608
609 cache = getLastHandled ();
610
611 if (cache && !fetch->isComplete())
612 doListEntry (encodedUrl, stretch, cache, withFlags, withSubject);
613 }
614 while (!fetch->isComplete ());
615 entry.clear ();
616 listEntry (entry, true);
617 }
618 }
619 }
620 if ( !selectInfo.alert().isNull() ) {
621 if ( !myBox.isEmpty() ) {
622 warning( i18n( "Message from %1 while processing '%2': %3" ).arg( myHost, myBox, selectInfo.alert() ) );
623 } else {
624 warning( i18n( "Message from %1: %2" ).arg( myHost, TQString(selectInfo.alert()) ) );
625 }
626 selectInfo.setAlert( 0 );
627 }
628
629 kdDebug(7116) << "IMAP4Protocol::listDir - Finishing listDir" << endl;
630 finished ();
631}
632
633void
634IMAP4Protocol::setHost (const TQString & _host, int _port,
635 const TQString & _user, const TQString & _pass)
636{
637 if (myHost != _host || myPort != _port || myUser != _user || myPass != _pass)
638 { // what's the point of doing 4 string compares to avoid 4 string copies?
639 // DF: I guess to avoid calling closeConnection() unnecessarily.
640 if (!myHost.isEmpty ())
641 closeConnection ();
642 myHost = _host;
643 if (_port == 0)
644 myPort = (mySSL) ? ImapsPort : ImapPort;
645 else
646 myPort = _port;
647 myUser = _user;
648 myPass = _pass;
649 }
650}
651
652void
653IMAP4Protocol::parseRelay (const TQByteArray & buffer)
654{
655 if (relayEnabled) {
656 // relay data immediately
657 data( buffer );
658 mProcessedSize += buffer.size();
659 processedSize( mProcessedSize );
660 } else if (cacheOutput)
661 {
662 // collect data
663 if ( !outputBuffer.isOpen() ) {
664 outputBuffer.open(IO_WriteOnly);
665 }
666 outputBuffer.at(outputBufferIndex);
667 outputBuffer.writeBlock(buffer, buffer.size());
668 outputBufferIndex += buffer.size();
669 }
670}
671
672void
674{
675 if (relayEnabled)
676 totalSize (len);
677}
678
679
680bool IMAP4Protocol::parseRead(TQByteArray & buffer, ulong len, ulong relay)
681{
682 char buf[8192];
683 while (buffer.size() < len)
684 {
685 ssize_t readLen = myRead(buf, TQMIN(len - buffer.size(), sizeof(buf) - 1));
686 if (readLen == 0)
687 {
688 kdDebug(7116) << "parseRead: readLen == 0 - connection broken" << endl;
689 error (ERR_CONNECTION_BROKEN, myHost);
690 setState(ISTATE_CONNECT);
691 closeConnection();
692 return FALSE;
693 }
694 if (relay > buffer.size())
695 {
696 TQByteArray relayData;
697 ssize_t relbuf = relay - buffer.size();
698 int currentRelay = TQMIN(relbuf, readLen);
699 relayData.setRawData(buf, currentRelay);
700 parseRelay(relayData);
701 relayData.resetRawData(buf, currentRelay);
702 }
703 {
704 TQBuffer stream (buffer);
705 stream.open (IO_WriteOnly);
706 stream.at (buffer.size ());
707 stream.writeBlock (buf, readLen);
708 stream.close ();
709 }
710 }
711 return (buffer.size() == len);
712}
713
714
715bool IMAP4Protocol::parseReadLine (TQByteArray & buffer, ulong relay)
716{
717 if (myHost.isEmpty()) return FALSE;
718
719 while (true) {
720 ssize_t copyLen = 0;
721 if (readBufferLen > 0)
722 {
723 while (copyLen < readBufferLen && readBuffer[copyLen] != '\n') copyLen++;
724 if (copyLen < readBufferLen) copyLen++;
725 if (relay > 0)
726 {
727 TQByteArray relayData;
728
729 if (copyLen < (ssize_t) relay)
730 relay = copyLen;
731 relayData.setRawData (readBuffer, relay);
732 parseRelay (relayData);
733 relayData.resetRawData (readBuffer, relay);
734// kdDebug(7116) << "relayed : " << relay << "d" << endl;
735 }
736 // append to buffer
737 {
738 TQBuffer stream (buffer);
739
740 stream.open (IO_WriteOnly);
741 stream.at (buffer.size ());
742 stream.writeBlock (readBuffer, copyLen);
743 stream.close ();
744// kdDebug(7116) << "appended " << copyLen << "d got now " << buffer.size() << endl;
745 }
746
747 readBufferLen -= copyLen;
748 if (readBufferLen)
749 memmove(readBuffer, &readBuffer[copyLen], readBufferLen);
750 if (buffer[buffer.size() - 1] == '\n') return TRUE;
751 }
752 if (!isConnectionValid())
753 {
754 kdDebug(7116) << "parseReadLine - connection broken" << endl;
755 error (ERR_CONNECTION_BROKEN, myHost);
756 setState(ISTATE_CONNECT);
757 closeConnection();
758 return FALSE;
759 }
760 if (!waitForResponse( responseTimeout() ))
761 {
762 error(ERR_SERVER_TIMEOUT, myHost);
763 setState(ISTATE_CONNECT);
764 closeConnection();
765 return FALSE;
766 }
767 readBufferLen = read(readBuffer, IMAP_BUFFER - 1);
768 if (readBufferLen == 0)
769 {
770 kdDebug(7116) << "parseReadLine: readBufferLen == 0 - connection broken" << endl;
771 error (ERR_CONNECTION_BROKEN, myHost);
772 setState(ISTATE_CONNECT);
773 closeConnection();
774 return FALSE;
775 }
776 }
777}
778
779void
780IMAP4Protocol::setSubURL (const KURL & _url)
781{
782 kdDebug(7116) << "IMAP4::setSubURL - " << _url.prettyURL() << endl;
783 TDEIO::TCPSlaveBase::setSubURL (_url);
784}
785
786void
787IMAP4Protocol::put (const KURL & _url, int, bool, bool)
788{
789 kdDebug(7116) << "IMAP4::put - " << _url.prettyURL() << endl;
790// TDEIO::TCPSlaveBase::put(_url,permissions,overwrite,resume);
791 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
792 enum IMAP_TYPE aType =
793 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
794
795 // see if it is a box
796 if (aType != ITYPE_BOX && aType != ITYPE_DIR_AND_BOX)
797 {
798 if (aBox[aBox.length () - 1] == '/')
799 aBox = aBox.right (aBox.length () - 1);
800 imapCommand *cmd = doCommand (imapCommand::clientCreate (aBox));
801
802 if (cmd->result () != "OK") {
803 error (ERR_COULD_NOT_WRITE, _url.prettyURL());
804 completeQueue.removeRef (cmd);
805 return;
806 }
807 completeQueue.removeRef (cmd);
808 }
809 else
810 {
811 TQPtrList < TQByteArray > bufferList;
812 int length = 0;
813
814 int result;
815 // Loop until we got 'dataEnd'
816 do
817 {
818 TQByteArray *buffer = new TQByteArray ();
819 dataReq (); // Request for data
820 result = readData (*buffer);
821 if (result > 0)
822 {
823 bufferList.append (buffer);
824 length += result;
825 } else {
826 delete buffer;
827 }
828 }
829 while (result > 0);
830
831 if (result != 0)
832 {
833 error (ERR_ABORTED, _url.prettyURL());
834 return;
835 }
836
837 imapCommand *cmd =
838 sendCommand (imapCommand::clientAppend (aBox, aSection, length));
839 while (!parseLoop ()) ;
840
841 // see if server is waiting
842 if (!cmd->isComplete () && !getContinuation ().isEmpty ())
843 {
844 bool sendOk = true;
845 ulong wrote = 0;
846
847 TQByteArray *buffer;
848 // send data to server
849 while (!bufferList.isEmpty () && sendOk)
850 {
851 buffer = bufferList.take (0);
852
853 sendOk =
854 (write (buffer->data (), buffer->size ()) ==
855 (ssize_t) buffer->size ());
856 wrote += buffer->size ();
857 processedSize(wrote);
858 delete buffer;
859 if (!sendOk)
860 {
861 error (ERR_CONNECTION_BROKEN, myHost);
862 completeQueue.removeRef (cmd);
863 setState(ISTATE_CONNECT);
864 closeConnection();
865 return;
866 }
867 }
868 parseWriteLine ("");
869 // Wait until cmd is complete, or connection breaks.
870 while (!cmd->isComplete () && getState() != ISTATE_NO)
871 parseLoop ();
872 if ( getState() == ISTATE_NO ) {
873 // TODO KDE4: pass cmd->resultInfo() as third argument.
874 // ERR_CONNECTION_BROKEN expects a host, no way to pass details about the problem.
875 error( ERR_CONNECTION_BROKEN, myHost );
876 completeQueue.removeRef (cmd);
877 closeConnection();
878 return;
879 }
880 else if (cmd->result () != "OK") {
881 error( ERR_SLAVE_DEFINED, cmd->resultInfo() );
882 completeQueue.removeRef (cmd);
883 return;
884 }
885 else
886 {
887 if (hasCapability("UIDPLUS"))
888 {
889 TQString uid = cmd->resultInfo();
890 if (uid.find("APPENDUID") != -1)
891 {
892 uid = uid.section(" ", 2, 2);
893 uid.truncate(uid.length()-1);
894 infoMessage("UID "+uid);
895 }
896 }
897 // MUST reselect to get the new message
898 else if (aBox == getCurrentBox ())
899 {
900 cmd =
901 doCommand (imapCommand::
902 clientSelect (aBox, !selectInfo.readWrite ()));
903 completeQueue.removeRef (cmd);
904 }
905 }
906 }
907 else
908 {
909 //error (ERR_COULD_NOT_WRITE, myHost);
910 // Better ship the error message, e.g. "Over Quota"
911 error (ERR_SLAVE_DEFINED, cmd->resultInfo());
912 completeQueue.removeRef (cmd);
913 return;
914 }
915
916 completeQueue.removeRef (cmd);
917 }
918
919 finished ();
920}
921
922void
923IMAP4Protocol::mkdir (const KURL & _url, int)
924{
925 kdDebug(7116) << "IMAP4::mkdir - " << _url.prettyURL() << endl;
926 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
927 parseURL(_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
928 kdDebug(7116) << "IMAP4::mkdir - create " << aBox << endl;
929 imapCommand *cmd = doCommand (imapCommand::clientCreate(aBox));
930
931 if (cmd->result () != "OK")
932 {
933 kdDebug(7116) << "IMAP4::mkdir - " << cmd->resultInfo() << endl;
934 error (ERR_COULD_NOT_MKDIR, _url.prettyURL());
935 completeQueue.removeRef (cmd);
936 return;
937 }
938 completeQueue.removeRef (cmd);
939
940 // start a new listing to find the type of the folder
941 enum IMAP_TYPE type =
942 parseURL(_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
943 if (type == ITYPE_BOX)
944 {
945 bool ask = ( aInfo.find( "ASKUSER" ) != -1 );
946 if ( ask &&
947 messageBox(QuestionYesNo,
948 i18n("The following folder will be created on the server: %1 "
949 "What do you want to store in this folder?").arg( aBox ),
950 i18n("Create Folder"),
951 i18n("&Messages"), i18n("&Subfolders")) == KMessageBox::No )
952 {
953 cmd = doCommand(imapCommand::clientDelete(aBox));
954 completeQueue.removeRef (cmd);
955 cmd = doCommand(imapCommand::clientCreate(aBox + aDelimiter));
956 if (cmd->result () != "OK")
957 {
958 error (ERR_COULD_NOT_MKDIR, _url.prettyURL());
959 completeQueue.removeRef (cmd);
960 return;
961 }
962 completeQueue.removeRef (cmd);
963 }
964 }
965
966 cmd = doCommand(imapCommand::clientSubscribe(aBox));
967 completeQueue.removeRef(cmd);
968
969 finished ();
970}
971
972void
973IMAP4Protocol::copy (const KURL & src, const KURL & dest, int, bool overwrite)
974{
975 kdDebug(7116) << "IMAP4::copy - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl;
976 TQString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
977 TQString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
978 enum IMAP_TYPE sType =
979 parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo);
980 enum IMAP_TYPE dType =
981 parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo);
982
983 // see if we have to create anything
984 if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX)
985 {
986 // this might be konqueror
987 int sub = dBox.find (sBox);
988
989 // might be moving to upper folder
990 if (sub > 0)
991 {
992 KURL testDir = dest;
993
994 TQString subDir = dBox.right (dBox.length () - dBox.findRev ('/'));
995 TQString topDir = dBox.left (sub);
996 testDir.setPath ("/" + topDir);
997 dType =
998 parseURL (testDir, topDir, dSection, dLType, dSequence, dValidity,
999 dDelimiter, dInfo);
1000
1001 kdDebug(7116) << "IMAP4::copy - checking this destination " << topDir << endl;
1002 // see if this is what the user wants
1003 if (dType == ITYPE_BOX || dType == ITYPE_DIR_AND_BOX)
1004 {
1005 kdDebug(7116) << "IMAP4::copy - assuming this destination " << topDir << endl;
1006 dBox = topDir;
1007 }
1008 else
1009 {
1010
1011 // maybe if we create a new mailbox
1012 topDir = "/" + topDir + subDir;
1013 testDir.setPath (topDir);
1014 kdDebug(7116) << "IMAP4::copy - checking this destination " << topDir << endl;
1015 dType =
1016 parseURL (testDir, topDir, dSection, dLType, dSequence, dValidity,
1017 dDelimiter, dInfo);
1018 if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX)
1019 {
1020 // ok then we'll create a mailbox
1021 imapCommand *cmd = doCommand (imapCommand::clientCreate (topDir));
1022
1023 // on success we'll use it, else we'll just try to create the given dir
1024 if (cmd->result () == "OK")
1025 {
1026 kdDebug(7116) << "IMAP4::copy - assuming this destination " << topDir << endl;
1027 dType = ITYPE_BOX;
1028 dBox = topDir;
1029 }
1030 else
1031 {
1032 completeQueue.removeRef (cmd);
1033 cmd = doCommand (imapCommand::clientCreate (dBox));
1034 if (cmd->result () == "OK")
1035 dType = ITYPE_BOX;
1036 else
1037 error (ERR_COULD_NOT_WRITE, dest.prettyURL());
1038 }
1039 completeQueue.removeRef (cmd);
1040 }
1041 }
1042
1043 }
1044 }
1045 if (sType == ITYPE_MSG || sType == ITYPE_BOX || sType == ITYPE_DIR_AND_BOX)
1046 {
1047 //select the source box
1048 if (!assureBox(sBox, true)) return;
1049 kdDebug(7116) << "IMAP4::copy - " << sBox << " -> " << dBox << endl;
1050
1051 //issue copy command
1052 imapCommand *cmd =
1053 doCommand (imapCommand::clientCopy (dBox, sSequence));
1054 if (cmd->result () != "OK")
1055 {
1056 kdError(5006) << "IMAP4::copy - " << cmd->resultInfo() << endl;
1057 error (ERR_COULD_NOT_WRITE, dest.prettyURL());
1058 completeQueue.removeRef (cmd);
1059 return;
1060 } else {
1061 if (hasCapability("UIDPLUS"))
1062 {
1063 TQString uid = cmd->resultInfo();
1064 if (uid.find("COPYUID") != -1)
1065 {
1066 uid = uid.section(" ", 2, 3);
1067 uid.truncate(uid.length()-1);
1068 infoMessage("UID "+uid);
1069 }
1070 }
1071 }
1072 completeQueue.removeRef (cmd);
1073 }
1074 else
1075 {
1076 error (ERR_ACCESS_DENIED, src.prettyURL());
1077 return;
1078 }
1079 finished ();
1080}
1081
1082void
1083IMAP4Protocol::del (const KURL & _url, bool isFile)
1084{
1085 kdDebug(7116) << "IMAP4::del - [" << (isFile ? "File" : "NoFile") << "] " << _url.prettyURL() << endl;
1086 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1087 enum IMAP_TYPE aType =
1088 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1089
1090 switch (aType)
1091 {
1092 case ITYPE_BOX:
1093 case ITYPE_DIR_AND_BOX:
1094 if (!aSequence.isEmpty ())
1095 {
1096 if (aSequence == "*")
1097 {
1098 if (!assureBox (aBox, false)) return;
1099 imapCommand *cmd = doCommand (imapCommand::clientExpunge ());
1100 if (cmd->result () != "OK") {
1101 error (ERR_CANNOT_DELETE, _url.prettyURL());
1102 completeQueue.removeRef (cmd);
1103 return;
1104 }
1105 completeQueue.removeRef (cmd);
1106 }
1107 else
1108 {
1109 // if open for read/write
1110 if (!assureBox (aBox, false)) return;
1111 imapCommand *cmd =
1112 doCommand (imapCommand::
1113 clientStore (aSequence, "+FLAGS.SILENT", "\\DELETED"));
1114 if (cmd->result () != "OK") {
1115 error (ERR_CANNOT_DELETE, _url.prettyURL());
1116 completeQueue.removeRef (cmd);
1117 return;
1118 }
1119 completeQueue.removeRef (cmd);
1120 }
1121 }
1122 else
1123 {
1124 if (getCurrentBox() == aBox)
1125 {
1126 imapCommand *cmd = doCommand(imapCommand::clientClose());
1127 completeQueue.removeRef(cmd);
1128 setState(ISTATE_LOGIN);
1129 }
1130 // We unsubscribe, otherwise we get ghost folders on UW-IMAP
1131 imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
1132 completeQueue.removeRef(cmd);
1133 cmd = doCommand(imapCommand::clientDelete (aBox));
1134 // If this doesn't work, we try to empty the mailbox first
1135 if (cmd->result () != "OK")
1136 {
1137 completeQueue.removeRef(cmd);
1138 if (!assureBox(aBox, false)) return;
1139 bool stillOk = true;
1140 if (stillOk)
1141 {
1142 imapCommand *cmd = doCommand(
1143 imapCommand::clientStore("1:*", "+FLAGS.SILENT", "\\DELETED"));
1144 if (cmd->result () != "OK") stillOk = false;
1145 completeQueue.removeRef(cmd);
1146 }
1147 if (stillOk)
1148 {
1149 imapCommand *cmd = doCommand(imapCommand::clientClose());
1150 if (cmd->result () != "OK") stillOk = false;
1151 completeQueue.removeRef(cmd);
1152 setState(ISTATE_LOGIN);
1153 }
1154 if (stillOk)
1155 {
1156 imapCommand *cmd = doCommand (imapCommand::clientDelete(aBox));
1157 if (cmd->result () != "OK") stillOk = false;
1158 completeQueue.removeRef(cmd);
1159 }
1160 if (!stillOk)
1161 {
1162 error (ERR_COULD_NOT_RMDIR, _url.prettyURL());
1163 return;
1164 }
1165 } else {
1166 completeQueue.removeRef (cmd);
1167 }
1168 }
1169 break;
1170
1171 case ITYPE_DIR:
1172 {
1173 imapCommand *cmd = doCommand (imapCommand::clientDelete (aBox));
1174 if (cmd->result () != "OK") {
1175 error (ERR_COULD_NOT_RMDIR, _url.prettyURL());
1176 completeQueue.removeRef (cmd);
1177 return;
1178 }
1179 completeQueue.removeRef (cmd);
1180 }
1181 break;
1182
1183 case ITYPE_MSG:
1184 {
1185 // if open for read/write
1186 if (!assureBox (aBox, false)) return;
1187 imapCommand *cmd =
1188 doCommand (imapCommand::
1189 clientStore (aSequence, "+FLAGS.SILENT", "\\DELETED"));
1190 if (cmd->result () != "OK") {
1191 error (ERR_CANNOT_DELETE, _url.prettyURL());
1192 completeQueue.removeRef (cmd);
1193 return;
1194 }
1195 completeQueue.removeRef (cmd);
1196 }
1197 break;
1198
1199 case ITYPE_UNKNOWN:
1200 case ITYPE_ATTACH:
1201 error (ERR_CANNOT_DELETE, _url.prettyURL());
1202 break;
1203 }
1204 finished ();
1205}
1206
1207/*
1208 * Copy a mail: data = 'C' + srcURL (KURL) + destURL (KURL)
1209 * Capabilities: data = 'c'. Result shipped in infoMessage() signal
1210 * No-op: data = 'N'
1211 * Namespace: data = 'n'. Result shipped in infoMessage() signal
1212 * The format is: section=namespace=delimiter
1213 * Note that the namespace can be empty
1214 * Unsubscribe: data = 'U' + URL (KURL)
1215 * Subscribe: data = 'u' + URL (KURL)
1216 * Change the status: data = 'S' + URL (KURL) + Flags (TQCString)
1217 * ACL commands: data = 'A' + command + URL (KURL) + command-dependent args
1218 * AnnotateMore commands: data = 'M' + 'G'et/'S'et + URL + entry + command-dependent args
1219 * Search: data = 'E' + URL (KURL)
1220 * Quota commands: data = 'Q' + 'R'oot/'G'et/'S'et + URL + entry + command-dependent args
1221 * Custom command: data = 'X' + 'N'ormal/'E'xtended + command + command-dependent args
1222 */
1223void
1224IMAP4Protocol::special (const TQByteArray & aData)
1225{
1226 kdDebug(7116) << "IMAP4Protocol::special" << endl;
1227 if (!makeLogin()) return;
1228
1229 TQDataStream stream(aData, IO_ReadOnly);
1230
1231 int tmp;
1232 stream >> tmp;
1233
1234 switch (tmp) {
1235 case 'C':
1236 {
1237 // copy
1238 KURL src;
1239 KURL dest;
1240 stream >> src >> dest;
1241 copy(src, dest, 0, FALSE);
1242 break;
1243 }
1244 case 'c':
1245 {
1246 // capabilities
1247 infoMessage(imapCapabilities.join(" "));
1248 finished();
1249 break;
1250 }
1251 case 'N':
1252 {
1253 // NOOP
1254 imapCommand *cmd = doCommand(imapCommand::clientNoop());
1255 if (cmd->result () != "OK")
1256 {
1257 kdDebug(7116) << "NOOP did not succeed - connection broken" << endl;
1258 completeQueue.removeRef (cmd);
1259 error (ERR_CONNECTION_BROKEN, myHost);
1260 return;
1261 }
1262 completeQueue.removeRef (cmd);
1263 finished();
1264 break;
1265 }
1266 case 'n':
1267 {
1268 // namespace in the form "section=namespace=delimiter"
1269 // entries are separated by ,
1270 infoMessage( imapNamespaces.join(",") );
1271 finished();
1272 break;
1273 }
1274 case 'U':
1275 {
1276 // unsubscribe
1277 KURL _url;
1278 stream >> _url;
1279 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1280 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1281 imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
1282 if (cmd->result () != "OK")
1283 {
1284 completeQueue.removeRef (cmd);
1285 error(ERR_SLAVE_DEFINED, i18n("Unsubscribe of folder %1 "
1286 "failed. The server returned: %2")
1287 .arg(_url.prettyURL())
1288 .arg(cmd->resultInfo()));
1289 return;
1290 }
1291 completeQueue.removeRef (cmd);
1292 finished();
1293 break;
1294 }
1295 case 'u':
1296 {
1297 // subscribe
1298 KURL _url;
1299 stream >> _url;
1300 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1301 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1302 imapCommand *cmd = doCommand(imapCommand::clientSubscribe(aBox));
1303 if (cmd->result () != "OK")
1304 {
1305 completeQueue.removeRef (cmd);
1306 error(ERR_SLAVE_DEFINED, i18n("Subscribe of folder %1 "
1307 "failed. The server returned: %2")
1308 .arg(_url.prettyURL())
1309 .arg(cmd->resultInfo()));
1310 return;
1311 }
1312 completeQueue.removeRef (cmd);
1313 finished();
1314 break;
1315 }
1316 case 'A':
1317 {
1318 // acl
1319 int cmd;
1320 stream >> cmd;
1321 if ( hasCapability( "ACL" ) ) {
1322 specialACLCommand( cmd, stream );
1323 } else {
1324 error( ERR_UNSUPPORTED_ACTION, "ACL" );
1325 }
1326 break;
1327 }
1328 case 'M':
1329 {
1330 // annotatemore
1331 int cmd;
1332 stream >> cmd;
1333 if ( hasCapability( "ANNOTATEMORE" ) ) {
1334 specialAnnotateMoreCommand( cmd, stream );
1335 } else {
1336 error( ERR_UNSUPPORTED_ACTION, "ANNOTATEMORE" );
1337 }
1338 break;
1339 }
1340 case 'Q':
1341 {
1342 // quota
1343 int cmd;
1344 stream >> cmd;
1345 if ( hasCapability( "QUOTA" ) ) {
1346 specialQuotaCommand( cmd, stream );
1347 } else {
1348 error( ERR_UNSUPPORTED_ACTION, "QUOTA" );
1349 }
1350 break;
1351 }
1352 case 'S':
1353 {
1354 // status
1355 KURL _url;
1356 TQCString newFlags;
1357 stream >> _url >> newFlags;
1358
1359 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1360 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1361 if (!assureBox(aBox, false)) return;
1362
1363 // make sure we only touch flags we know
1364 TQCString knownFlags = "\\SEEN \\ANSWERED \\FLAGGED \\DRAFT";
1365 const imapInfo info = getSelected();
1366 if ( info.permanentFlagsAvailable() && (info.permanentFlags() & imapInfo::User) ) {
1367 knownFlags += " KMAILFORWARDED KMAILTODO KMAILWATCHED KMAILIGNORED $FORWARDED $TODO $WATCHED $IGNORED";
1368 }
1369
1370 imapCommand *cmd = doCommand (imapCommand::
1371 clientStore (aSequence, "-FLAGS.SILENT", knownFlags));
1372 if (cmd->result () != "OK")
1373 {
1374 completeQueue.removeRef (cmd);
1375 error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
1376 "failed.").arg(_url.prettyURL()));
1377 return;
1378 }
1379 completeQueue.removeRef (cmd);
1380 if (!newFlags.isEmpty())
1381 {
1382 cmd = doCommand (imapCommand::
1383 clientStore (aSequence, "+FLAGS.SILENT", newFlags));
1384 if (cmd->result () != "OK")
1385 {
1386 completeQueue.removeRef (cmd);
1387 error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
1388 "failed.").arg(_url.prettyURL()));
1389 return;
1390 }
1391 completeQueue.removeRef (cmd);
1392 }
1393 finished();
1394 break;
1395 }
1396 case 's':
1397 {
1398 // seen
1399 KURL _url;
1400 bool seen;
1401 TQCString newFlags;
1402 stream >> _url >> seen;
1403
1404 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1405 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1406 if ( !assureBox(aBox, true) ) // read-only because changing SEEN should be possible even then
1407 return;
1408
1409 imapCommand *cmd;
1410 if ( seen )
1411 cmd = doCommand( imapCommand::clientStore( aSequence, "+FLAGS.SILENT", "\\SEEN" ) );
1412 else
1413 cmd = doCommand( imapCommand::clientStore( aSequence, "-FLAGS.SILENT", "\\SEEN" ) );
1414
1415 if (cmd->result () != "OK")
1416 {
1417 completeQueue.removeRef (cmd);
1418 error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
1419 "failed.").arg(_url.prettyURL()));
1420 return;
1421 }
1422 completeQueue.removeRef (cmd);
1423 finished();
1424 break;
1425 }
1426
1427 case 'E':
1428 {
1429 // search
1430 specialSearchCommand( stream );
1431 break;
1432 }
1433 case 'X':
1434 {
1435 // custom command
1436 specialCustomCommand( stream );
1437 break;
1438 }
1439 default:
1440 kdWarning(7116) << "Unknown command in special(): " << tmp << endl;
1441 error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(tmp)) );
1442 break;
1443 }
1444}
1445
1446void
1447IMAP4Protocol::specialACLCommand( int command, TQDataStream& stream )
1448{
1449 // All commands start with the URL to the box
1450 KURL _url;
1451 stream >> _url;
1452 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1453 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1454
1455 switch( command ) {
1456 case 'S': // SETACL
1457 {
1458 TQString user, acl;
1459 stream >> user >> acl;
1460 kdDebug(7116) << "SETACL " << aBox << " " << user << " " << acl << endl;
1461 imapCommand *cmd = doCommand(imapCommand::clientSetACL(aBox, user, acl));
1462 if (cmd->result () != "OK")
1463 {
1464 error(ERR_SLAVE_DEFINED, i18n("Setting the Access Control List on folder %1 "
1465 "for user %2 failed. The server returned: %3")
1466 .arg(_url.prettyURL())
1467 .arg(user)
1468 .arg(cmd->resultInfo()));
1469 return;
1470 }
1471 completeQueue.removeRef (cmd);
1472 finished();
1473 break;
1474 }
1475 case 'D': // DELETEACL
1476 {
1477 TQString user;
1478 stream >> user;
1479 kdDebug(7116) << "DELETEACL " << aBox << " " << user << endl;
1480 imapCommand *cmd = doCommand(imapCommand::clientDeleteACL(aBox, user));
1481 if (cmd->result () != "OK")
1482 {
1483 error(ERR_SLAVE_DEFINED, i18n("Deleting the Access Control List on folder %1 "
1484 "for user %2 failed. The server returned: %3")
1485 .arg(_url.prettyURL())
1486 .arg(user)
1487 .arg(cmd->resultInfo()));
1488 return;
1489 }
1490 completeQueue.removeRef (cmd);
1491 finished();
1492 break;
1493 }
1494 case 'G': // GETACL
1495 {
1496 kdDebug(7116) << "GETACL " << aBox << endl;
1497 imapCommand *cmd = doCommand(imapCommand::clientGetACL(aBox));
1498 if (cmd->result () != "OK")
1499 {
1500 error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
1501 "failed. The server returned: %2")
1502 .arg(_url.prettyURL())
1503 .arg(cmd->resultInfo()));
1504 return;
1505 }
1506 // Returning information to the application from a special() command isn't easy.
1507 // I'm reusing the infoMessage trick seen above (for capabilities), but this
1508 // limits me to a string instead of a stringlist. Using DQUOTE as separator,
1509 // because it's forbidden in userids by rfc3501
1510 kdDebug(7116) << getResults() << endl;
1511 infoMessage(getResults().join( "\"" ));
1512 finished();
1513 break;
1514 }
1515 case 'L': // LISTRIGHTS
1516 {
1517 // Do we need this one? It basically shows which rights are tied together, but that's all?
1518 error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) );
1519 break;
1520 }
1521 case 'M': // MYRIGHTS
1522 {
1523 kdDebug(7116) << "MYRIGHTS " << aBox << endl;
1524 imapCommand *cmd = doCommand(imapCommand::clientMyRights(aBox));
1525 if (cmd->result () != "OK")
1526 {
1527 error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
1528 "failed. The server returned: %2")
1529 .arg(_url.prettyURL())
1530 .arg(cmd->resultInfo()));
1531 return;
1532 }
1533 TQStringList lst = getResults();
1534 kdDebug(7116) << "myrights results: " << lst << endl;
1535 if ( !lst.isEmpty() ) {
1536 Q_ASSERT( lst.count() == 1 );
1537 infoMessage( lst.first() );
1538 }
1539 finished();
1540 break;
1541 }
1542 default:
1543 kdWarning(7116) << "Unknown special ACL command:" << command << endl;
1544 error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) );
1545 }
1546}
1547
1548void
1550{
1551 kdDebug(7116) << "IMAP4Protocol::specialSearchCommand" << endl;
1552 KURL _url;
1553 stream >> _url;
1554 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1555 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1556 if (!assureBox(aBox, true)) return;
1557
1558 imapCommand *cmd = doCommand (imapCommand::clientSearch( aSection ));
1559 if (cmd->result () != "OK")
1560 {
1561 error(ERR_SLAVE_DEFINED, i18n("Searching of folder %1 "
1562 "failed. The server returned: %2")
1563 .arg(aBox)
1564 .arg(cmd->resultInfo()));
1565 return;
1566 }
1567 completeQueue.removeRef(cmd);
1568 TQStringList lst = getResults();
1569 kdDebug(7116) << "IMAP4Protocol::specialSearchCommand '" << aSection <<
1570 "' returns " << lst << endl;
1571 infoMessage( lst.join( " " ) );
1572
1573 finished();
1574}
1575
1576void
1578{
1579 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand" << endl;
1580
1581 TQString command, arguments;
1582 int type;
1583 stream >> type;
1584 stream >> command >> arguments;
1585
1590 if ( type == 'N' ) {
1591 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: normal mode" << endl;
1592 imapCommand *cmd = doCommand (imapCommand::clientCustom( command, arguments ));
1593 if (cmd->result () != "OK")
1594 {
1595 error(ERR_SLAVE_DEFINED, i18n("Custom command %1:%2 "
1596 "failed. The server returned: %3")
1597 .arg(command)
1598 .arg(arguments)
1599 .arg(cmd->resultInfo()));
1600 return;
1601 }
1602 completeQueue.removeRef(cmd);
1603 TQStringList lst = getResults();
1604 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand '" << command <<
1605 ":" << arguments <<
1606 "' returns " << lst << endl;
1607 infoMessage( lst.join( " " ) );
1608
1609 finished();
1610 } else
1615 if ( type == 'E' ) {
1616 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: extended mode" << endl;
1617 imapCommand *cmd = sendCommand (imapCommand::clientCustom( command, TQString() ));
1618 while ( !parseLoop () ) ;
1619
1620 // see if server is waiting
1621 if (!cmd->isComplete () && !getContinuation ().isEmpty ())
1622 {
1623 const TQByteArray buffer = arguments.utf8();
1624
1625 // send data to server
1626 bool sendOk = (write (buffer.data (), buffer.size ()) == (ssize_t)buffer.size ());
1627 processedSize( buffer.size() );
1628
1629 if ( !sendOk ) {
1630 error ( ERR_CONNECTION_BROKEN, myHost );
1631 completeQueue.removeRef ( cmd );
1632 setState(ISTATE_CONNECT);
1633 closeConnection();
1634 return;
1635 }
1636 }
1637 parseWriteLine ("");
1638
1639 do
1640 {
1641 while (!parseLoop ()) ;
1642 }
1643 while (!cmd->isComplete ());
1644
1645 completeQueue.removeRef (cmd);
1646
1647 TQStringList lst = getResults();
1648 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: returns " << lst << endl;
1649 infoMessage( lst.join( " " ) );
1650
1651 finished ();
1652 }
1653}
1654
1655void
1656IMAP4Protocol::specialAnnotateMoreCommand( int command, TQDataStream& stream )
1657{
1658 // All commands start with the URL to the box
1659 KURL _url;
1660 stream >> _url;
1661 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1662 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1663
1664 switch( command ) {
1665 case 'S': // SETANNOTATION
1666 {
1667 // Params:
1668 // KURL URL of the mailbox
1669 // TQString entry (should be an actual entry name, no % or *; empty for server entries)
1670 // TQMap<TQString,TQString> attributes (name and value)
1671 TQString entry;
1672 TQMap<TQString, TQString> attributes;
1673 stream >> entry >> attributes;
1674 kdDebug(7116) << "SETANNOTATION " << aBox << " " << entry << " " << attributes.count() << " attributes" << endl;
1675 imapCommand *cmd = doCommand(imapCommand::clientSetAnnotation(aBox, entry, attributes));
1676 if (cmd->result () != "OK")
1677 {
1678 error(ERR_SLAVE_DEFINED, i18n("Setting the annotation %1 on folder %2 "
1679 " failed. The server returned: %3")
1680 .arg(entry)
1681 .arg(_url.prettyURL())
1682 .arg(cmd->resultInfo()));
1683 return;
1684 }
1685 completeQueue.removeRef (cmd);
1686 finished();
1687 break;
1688 }
1689 case 'G': // GETANNOTATION.
1690 {
1691 // Params:
1692 // KURL URL of the mailbox
1693 // TQString entry (should be an actual entry name, no % or *; empty for server entries)
1694 // TQStringList attributes (list of attributes to be retrieved, possibly with % or *)
1695 TQString entry;
1696 TQStringList attributeNames;
1697 stream >> entry >> attributeNames;
1698 kdDebug(7116) << "GETANNOTATION " << aBox << " " << entry << " " << attributeNames << endl;
1699 imapCommand *cmd = doCommand(imapCommand::clientGetAnnotation(aBox, entry, attributeNames));
1700 if (cmd->result () != "OK")
1701 {
1702 error(ERR_SLAVE_DEFINED, i18n("Retrieving the annotation %1 on folder %2 "
1703 "failed. The server returned: %3")
1704 .arg(entry)
1705 .arg(_url.prettyURL())
1706 .arg(cmd->resultInfo()));
1707 return;
1708 }
1709 // Returning information to the application from a special() command isn't easy.
1710 // I'm reusing the infoMessage trick seen above (for capabilities and acls), but this
1711 // limits me to a string instead of a stringlist. Let's use \r as separator.
1712 kdDebug(7116) << getResults() << endl;
1713 infoMessage(getResults().join( "\r" ));
1714 finished();
1715 break;
1716 }
1717 default:
1718 kdWarning(7116) << "Unknown special annotate command:" << command << endl;
1719 error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) );
1720 }
1721}
1722
1723void
1724IMAP4Protocol::specialQuotaCommand( int command, TQDataStream& stream )
1725{
1726 // All commands start with the URL to the box
1727 KURL _url;
1728 stream >> _url;
1729 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1730 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1731
1732 switch( command ) {
1733 case 'R': // GETQUOTAROOT
1734 {
1735 kdDebug(7116) << "QUOTAROOT " << aBox << endl;
1736 imapCommand *cmd = doCommand(imapCommand::clientGetQuotaroot( aBox ) );
1737 if (cmd->result () != "OK")
1738 {
1739 error(ERR_SLAVE_DEFINED, i18n("Retrieving the quota root information on folder %1 "
1740 "failed. The server returned: %2")
1741 .arg(_url.prettyURL())
1742 .arg(cmd->resultInfo()));
1743 return;
1744 }
1745 infoMessage(getResults().join( "\r" ));
1746 finished();
1747 break;
1748 }
1749 case 'G': // GETQUOTA
1750 {
1751 kdDebug(7116) << "GETQUOTA command" << endl;
1752 kdWarning(7116) << "UNIMPLEMENTED" << endl;
1753 break;
1754 }
1755 case 'S': // SETQUOTA
1756 {
1757 kdDebug(7116) << "SETQUOTA command" << endl;
1758 kdWarning(7116) << "UNIMPLEMENTED" << endl;
1759 break;
1760 }
1761 default:
1762 kdWarning(7116) << "Unknown special quota command:" << command << endl;
1763 error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) );
1764 }
1765}
1766
1767void
1768IMAP4Protocol::rename (const KURL & src, const KURL & dest, bool overwrite)
1769{
1770 kdDebug(7116) << "IMAP4::rename - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl;
1771 TQString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
1772 TQString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
1773 enum IMAP_TYPE sType =
1774 parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo, false);
1775 enum IMAP_TYPE dType =
1776 parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo, false);
1777
1778 if (dType == ITYPE_UNKNOWN)
1779 {
1780 switch (sType)
1781 {
1782 case ITYPE_BOX:
1783 case ITYPE_DIR:
1784 case ITYPE_DIR_AND_BOX:
1785 {
1786 if (getState() == ISTATE_SELECT && sBox == getCurrentBox())
1787 {
1788 kdDebug(7116) << "IMAP4::rename - close " << getCurrentBox() << endl;
1789 // mailbox can only be renamed if it is closed
1790 imapCommand *cmd = doCommand (imapCommand::clientClose());
1791 bool ok = cmd->result() == "OK";
1792 completeQueue.removeRef(cmd);
1793 if (!ok)
1794 {
1795 kdWarning(7116) << "Unable to close mailbox!" << endl;
1796 error(ERR_CANNOT_RENAME, src.path());
1797 return;
1798 }
1799 setState(ISTATE_LOGIN);
1800 }
1801 imapCommand *cmd = doCommand (imapCommand::clientRename (sBox, dBox));
1802 if (cmd->result () != "OK") {
1803 error (ERR_CANNOT_RENAME, src.path());
1804 completeQueue.removeRef (cmd);
1805 return;
1806 }
1807 completeQueue.removeRef (cmd);
1808 }
1809 break;
1810
1811 case ITYPE_MSG:
1812 case ITYPE_ATTACH:
1813 case ITYPE_UNKNOWN:
1814 error (ERR_CANNOT_RENAME, src.path());
1815 break;
1816 }
1817 }
1818 else
1819 {
1820 error (ERR_CANNOT_RENAME, src.path());
1821 return;
1822 }
1823 finished ();
1824}
1825
1826void
1827IMAP4Protocol::slave_status ()
1828{
1829 bool connected = (getState() != ISTATE_NO) && isConnectionValid();
1830 kdDebug(7116) << "IMAP4::slave_status " << connected << endl;
1831 slaveStatus ( connected ? myHost : TQString(), connected );
1832}
1833
1834void
1835IMAP4Protocol::dispatch (int command, const TQByteArray & data)
1836{
1837 kdDebug(7116) << "IMAP4::dispatch - command=" << command << endl;
1838 TDEIO::TCPSlaveBase::dispatch (command, data);
1839}
1840
1841void
1842IMAP4Protocol::stat (const KURL & _url)
1843{
1844 kdDebug(7116) << "IMAP4::stat - " << _url.prettyURL() << endl;
1845 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1846 // parseURL with caching
1847 enum IMAP_TYPE aType =
1848 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter,
1849 aInfo, true);
1850
1851 UDSEntry entry;
1852 UDSAtom atom;
1853
1854 atom.m_uds = UDS_NAME;
1855 atom.m_str = aBox;
1856 entry.append (atom);
1857
1858 if (!aSection.isEmpty())
1859 {
1860 if (getState() == ISTATE_SELECT && aBox == getCurrentBox())
1861 {
1862 imapCommand *cmd = doCommand (imapCommand::clientClose());
1863 bool ok = cmd->result() == "OK";
1864 completeQueue.removeRef(cmd);
1865 if (!ok)
1866 {
1867 error(ERR_COULD_NOT_STAT, aBox);
1868 return;
1869 }
1870 setState(ISTATE_LOGIN);
1871 }
1872 bool ok = false;
1873 TQString cmdInfo;
1874 if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
1875 ok = true;
1876 else
1877 {
1878 imapCommand *cmd = doCommand(imapCommand::clienStatus(aBox, aSection));
1879 ok = cmd->result() == "OK";
1880 cmdInfo = cmd->resultInfo();
1881 completeQueue.removeRef(cmd);
1882 }
1883 if (!ok)
1884 {
1885 bool found = false;
1886 imapCommand *cmd = doCommand (imapCommand::clientList ("", aBox));
1887 if (cmd->result () == "OK")
1888 {
1889 for (TQValueListIterator < imapList > it = listResponses.begin ();
1890 it != listResponses.end (); ++it)
1891 {
1892 if (aBox == (*it).name ()) found = true;
1893 }
1894 }
1895 completeQueue.removeRef (cmd);
1896 if (found)
1897 error(ERR_COULD_NOT_STAT, aBox);
1898 else
1899 error(TDEIO::ERR_DOES_NOT_EXIST, aBox);
1900 return;
1901 }
1902 if ((aSection == "UIDNEXT" && geStatus().uidNextAvailable())
1903 || (aSection == "UNSEEN" && geStatus().unseenAvailable()))
1904 {
1905 atom.m_uds = UDS_SIZE;
1906 atom.m_str = TQString();
1907 atom.m_long = (aSection == "UIDNEXT") ? geStatus().uidNext()
1908 : geStatus().unseen();
1909 entry.append(atom);
1910 }
1911 } else
1912 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX || aType == ITYPE_MSG ||
1913 aType == ITYPE_ATTACH)
1914 {
1915 ulong validity = 0;
1916 // see if the box is already in select/examine state
1917 if (aBox == getCurrentBox ())
1918 validity = selectInfo.uidValidity ();
1919 else
1920 {
1921 // do a status lookup on the box
1922 // only do this if the box is not selected
1923 // the server might change the validity for new select/examine
1924 imapCommand *cmd =
1925 doCommand (imapCommand::clienStatus (aBox, "UIDVALIDITY"));
1926 completeQueue.removeRef (cmd);
1927 validity = geStatus ().uidValidity ();
1928 }
1929 validity = 0; // temporary
1930
1931 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX)
1932 {
1933 // has no or an invalid uidvalidity
1934 if (validity > 0 && validity != aValidity.toULong ())
1935 {
1936 //redirect
1937 KURL newUrl = _url;
1938
1939 newUrl.setPath ("/" + aBox + ";UIDVALIDITY=" +
1940 TQString::number(validity));
1941 kdDebug(7116) << "IMAP4::stat - redirecting to " << newUrl.prettyURL() << endl;
1942 redirection (newUrl);
1943 }
1944 }
1945 else if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
1946 {
1947 //must determine if this message exists
1948 //cause konqueror will check this on paste operations
1949
1950 // has an invalid uidvalidity
1951 // or no messages in box
1952 if (validity > 0 && validity != aValidity.toULong ())
1953 {
1954 aType = ITYPE_UNKNOWN;
1955 kdDebug(7116) << "IMAP4::stat - url has invalid validity [" << validity << "d] " << _url.prettyURL() << endl;
1956 }
1957 }
1958 }
1959
1960 atom.m_uds = UDS_MIME_TYPE;
1961 atom.m_str = getMimeType (aType);
1962 entry.append (atom);
1963
1964 kdDebug(7116) << "IMAP4: stat: " << atom.m_str << endl;
1965 switch (aType)
1966 {
1967 case ITYPE_DIR:
1968 atom.m_uds = UDS_FILE_TYPE;
1969 atom.m_str = TQString();
1970 atom.m_long = S_IFDIR;
1971 entry.append (atom);
1972 break;
1973
1974 case ITYPE_BOX:
1975 case ITYPE_DIR_AND_BOX:
1976 atom.m_uds = UDS_FILE_TYPE;
1977 atom.m_str = TQString();
1978 atom.m_long = S_IFDIR;
1979 entry.append (atom);
1980 break;
1981
1982 case ITYPE_MSG:
1983 case ITYPE_ATTACH:
1984 atom.m_uds = UDS_FILE_TYPE;
1985 atom.m_str = TQString();
1986 atom.m_long = S_IFREG;
1987 entry.append (atom);
1988 break;
1989
1990 case ITYPE_UNKNOWN:
1991 error (ERR_DOES_NOT_EXIST, _url.prettyURL());
1992 break;
1993 }
1994
1995 statEntry (entry);
1996 kdDebug(7116) << "IMAP4::stat - Finishing stat" << endl;
1997 finished ();
1998}
1999
2000void IMAP4Protocol::openConnection()
2001{
2002 if (makeLogin()) connected();
2003}
2004
2005void IMAP4Protocol::closeConnection()
2006{
2007 if (getState() == ISTATE_NO) return;
2008 if (getState() == ISTATE_SELECT && metaData("expunge") == "auto")
2009 {
2010 imapCommand *cmd = doCommand (imapCommand::clientExpunge());
2011 completeQueue.removeRef (cmd);
2012 }
2013 if (getState() != ISTATE_CONNECT)
2014 {
2015 imapCommand *cmd = doCommand (imapCommand::clientLogout());
2016 completeQueue.removeRef (cmd);
2017 }
2018 closeDescriptor();
2019 setState(ISTATE_NO);
2020 completeQueue.clear();
2021 sentQueue.clear();
2022 lastHandled = 0;
2023 currentBox = TQString();
2024 readBufferLen = 0;
2025}
2026
2027bool IMAP4Protocol::makeLogin ()
2028{
2029 if (getState () == ISTATE_LOGIN || getState () == ISTATE_SELECT)
2030 return true;
2031
2032 kdDebug(7116) << "IMAP4::makeLogin - checking login" << endl;
2033 bool alreadyConnected = getState() == ISTATE_CONNECT;
2034 kdDebug(7116) << "IMAP4::makeLogin - alreadyConnected " << alreadyConnected << endl;
2035 if (alreadyConnected || connectToHost (myHost.latin1(), myPort))
2036 {
2037// fcntl (m_iSock, F_SETFL, (fcntl (m_iSock, F_GETFL) | O_NDELAY));
2038
2039 setState(ISTATE_CONNECT);
2040
2041 myAuth = metaData("auth");
2042 myTLS = metaData("tls");
2043 kdDebug(7116) << "myAuth: " << myAuth << endl;
2044
2045 imapCommand *cmd;
2046
2047 unhandled.clear ();
2048 if (!alreadyConnected) while (!parseLoop ()) ; //get greeting
2049 TQString greeting;
2050 if (!unhandled.isEmpty()) greeting = unhandled.first().stripWhiteSpace();
2051 unhandled.clear (); //get rid of it
2052 cmd = doCommand (new imapCommand ("CAPABILITY", ""));
2053
2054 kdDebug(7116) << "IMAP4: setHost: capability" << endl;
2055 for (TQStringList::Iterator it = imapCapabilities.begin ();
2056 it != imapCapabilities.end (); ++it)
2057 {
2058 kdDebug(7116) << "'" << (*it) << "'" << endl;
2059 }
2060 completeQueue.removeRef (cmd);
2061
2062 if (!hasCapability("IMAP4") && !hasCapability("IMAP4rev1"))
2063 {
2064 error(ERR_COULD_NOT_LOGIN, i18n("The server %1 supports neither "
2065 "IMAP4 nor IMAP4rev1.\nIt identified itself with: %2")
2066 .arg(myHost).arg(greeting));
2067 closeConnection();
2068 return false;
2069 }
2070
2071 if (metaData("nologin") == "on") return TRUE;
2072
2073 if (myTLS == "on" && !hasCapability(TQString("STARTTLS")))
2074 {
2075 error(ERR_COULD_NOT_LOGIN, i18n("The server does not support TLS.\n"
2076 "Disable this security feature to connect unencrypted."));
2077 closeConnection();
2078 return false;
2079 }
2080 if ((myTLS == "on" || (canUseTLS() && myTLS != "off")) &&
2081 hasCapability(TQString("STARTTLS")))
2082 {
2083 imapCommand *cmd = doCommand (imapCommand::clientStartTLS());
2084 if (cmd->result () == "OK")
2085 {
2086 completeQueue.removeRef(cmd);
2087 int tlsrc = startTLS();
2088 if (tlsrc == 1)
2089 {
2090 kdDebug(7116) << "TLS mode has been enabled." << endl;
2091 imapCommand *cmd2 = doCommand (new imapCommand ("CAPABILITY", ""));
2092 for (TQStringList::Iterator it = imapCapabilities.begin ();
2093 it != imapCapabilities.end (); ++it)
2094 {
2095 kdDebug(7116) << "'" << (*it) << "'" << endl;
2096 }
2097 completeQueue.removeRef (cmd2);
2098 } else {
2099 kdWarning(7116) << "TLS mode setup has failed. Aborting." << endl;
2100 error (ERR_COULD_NOT_LOGIN, i18n("Starting TLS failed."));
2101 closeConnection();
2102 return false;
2103 }
2104 } else completeQueue.removeRef(cmd);
2105 }
2106
2107 if (myAuth.isEmpty () || myAuth == "*") {
2108 if (hasCapability (TQString ("LOGINDISABLED"))) {
2109 error (ERR_COULD_NOT_LOGIN, i18n("LOGIN is disabled by the server."));
2110 closeConnection();
2111 return false;
2112 }
2113 }
2114 else {
2115 if (!hasCapability (TQString ("AUTH=") + myAuth)) {
2116 error (ERR_COULD_NOT_LOGIN, i18n("The authentication method %1 is not "
2117 "supported by the server.").arg(myAuth));
2118 closeConnection();
2119 return false;
2120 }
2121 }
2122
2123 if ( greeting.contains( TQRegExp( "Cyrus IMAP4 v2.1" ) ) ) {
2124 removeCapability( "ANNOTATEMORE" );
2125 }
2126
2127 // starting from Cyrus IMAP 2.3.9, shared seen flags are available
2128 TQRegExp regExp( "Cyrus\\sIMAP[4]{0,1}\\sv(\\d+)\\.(\\d+)\\.(\\d+)", false );
2129 if ( regExp.search( greeting ) >= 0 ) {
2130 const int major = regExp.cap( 1 ).toInt();
2131 const int minor = regExp.cap( 2 ).toInt();
2132 const int patch = regExp.cap( 3 ).toInt();
2133 if ( major > 2 || (major == 2 && (minor > 3 || (minor == 3 && patch > 9))) ) {
2134 kdDebug(7116) << k_funcinfo << "Cyrus IMAP >= 2.3.9 detected, enabling shared seen flag support" << endl;
2135 imapCapabilities.append( "x-kmail-sharedseen" );
2136 }
2137 }
2138
2139 kdDebug(7116) << "IMAP4::makeLogin - attempting login" << endl;
2140
2141 TDEIO::AuthInfo authInfo;
2142 authInfo.username = myUser;
2143 authInfo.password = myPass;
2144 authInfo.prompt = i18n ("Username and password for your IMAP account:");
2145
2146 kdDebug(7116) << "IMAP4::makeLogin - open_PassDlg said user=" << myUser << " pass=xx" << endl;
2147
2148 TQString resultInfo;
2149 if (myAuth.isEmpty () || myAuth == "*")
2150 {
2151 if (myUser.isEmpty () || myPass.isEmpty ()) {
2152 if(openPassDlg (authInfo)) {
2153 myUser = authInfo.username;
2154 myPass = authInfo.password;
2155 }
2156 }
2157 if (!clientLogin (myUser, myPass, resultInfo))
2158 error(TDEIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to login. Probably the "
2159 "password is wrong.\nThe server %1 replied:\n%2").arg(myHost).arg(resultInfo));
2160 }
2161 else
2162 {
2163#ifdef HAVE_LIBSASL2
2164 if (!clientAuthenticate (this, authInfo, myHost, myAuth, mySSL, resultInfo))
2165 error(TDEIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to authenticate via %1.\n"
2166 "The server %2 replied:\n%3").arg(myAuth).arg(myHost).arg(resultInfo));
2167 else {
2168 myUser = authInfo.username;
2169 myPass = authInfo.password;
2170 }
2171#else
2172 error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into tdeio_imap4."));
2173#endif
2174 }
2175 if ( hasCapability("NAMESPACE") )
2176 {
2177 // get all namespaces and save the namespace - delimiter association
2178 cmd = doCommand( imapCommand::clientNamespace() );
2179 if (cmd->result () == "OK")
2180 {
2181 kdDebug(7116) << "makeLogin - registered namespaces" << endl;
2182 }
2183 completeQueue.removeRef (cmd);
2184 }
2185 // get the default delimiter (empty listing)
2186 cmd = doCommand( imapCommand::clientList("", "") );
2187 if (cmd->result () == "OK")
2188 {
2189 TQValueListIterator < imapList > it = listResponses.begin();
2190 if ( it == listResponses.end() )
2191 {
2192 // empty answer - this is a buggy imap server
2193 // as a fallback we fire a normal listing and take the first answer
2194 completeQueue.removeRef (cmd);
2195 cmd = doCommand( imapCommand::clientList("", "%") );
2196 if (cmd->result () == "OK")
2197 {
2198 it = listResponses.begin();
2199 }
2200 }
2201 if ( it != listResponses.end() )
2202 {
2203 namespaceToDelimiter[TQString()] = (*it).hierarchyDelimiter();
2204 kdDebug(7116) << "makeLogin - delimiter for empty ns='" <<
2205 (*it).hierarchyDelimiter() << "'" << endl;
2206 if ( !hasCapability("NAMESPACE") )
2207 {
2208 // server does not support namespaces
2209 TQString nsentry = TQString::number( 0 ) + "=="
2210 + (*it).hierarchyDelimiter();
2211 imapNamespaces.append( nsentry );
2212 }
2213 }
2214 }
2215 completeQueue.removeRef (cmd);
2216 } else {
2217 kdDebug(7116) << "makeLogin - NO login" << endl;
2218 }
2219
2220 return getState() == ISTATE_LOGIN;
2221}
2222
2223void
2224IMAP4Protocol::parseWriteLine (const TQString & aStr)
2225{
2226 //kdDebug(7116) << "Writing: " << aStr << endl;
2227 TQCString writer = aStr.utf8();
2228 int len = writer.length();
2229
2230 // append CRLF if necessary
2231 if (len == 0 || (writer[len - 1] != '\n')) {
2232 len += 2;
2233 writer += "\r\n";
2234 }
2235
2236 // write it
2237 write(writer.data(), len);
2238}
2239
2240TQString
2241IMAP4Protocol::getMimeType (enum IMAP_TYPE aType)
2242{
2243 switch (aType)
2244 {
2245 case ITYPE_DIR:
2246 return "inode/directory";
2247 break;
2248
2249 case ITYPE_BOX:
2250 return "message/digest";
2251 break;
2252
2253 case ITYPE_DIR_AND_BOX:
2254 return "message/directory";
2255 break;
2256
2257 case ITYPE_MSG:
2258 return "message/rfc822";
2259 break;
2260
2261 // this should be handled by flushOutput
2262 case ITYPE_ATTACH:
2263 return "application/octet-stream";
2264 break;
2265
2266 case ITYPE_UNKNOWN:
2267 default:
2268 return "unknown/unknown";
2269 }
2270}
2271
2272
2273
2274void
2275IMAP4Protocol::doListEntry (const KURL & _url, int stretch, imapCache * cache,
2276 bool withFlags, bool withSubject)
2277{
2278 KURL aURL = _url;
2279 aURL.setQuery (TQString());
2280 const TQString encodedUrl = aURL.url(0, 106); // utf-8
2281 doListEntry(encodedUrl, stretch, cache, withFlags, withSubject);
2282}
2283
2284
2285
2286void
2287IMAP4Protocol::doListEntry (const TQString & encodedUrl, int stretch, imapCache * cache,
2288 bool withFlags, bool withSubject)
2289{
2290 if (cache)
2291 {
2292 UDSEntry entry;
2293 UDSAtom atom;
2294
2295 entry.clear ();
2296
2297 const TQString uid = TQString::number(cache->getUid());
2298
2299 atom.m_uds = UDS_NAME;
2300 atom.m_str = uid;
2301 atom.m_long = 0;
2302 if (stretch > 0)
2303 {
2304 atom.m_str = "0000000000000000" + atom.m_str;
2305 atom.m_str = atom.m_str.right (stretch);
2306 }
2307 if (withSubject)
2308 {
2309 mailHeader *header = cache->getHeader();
2310 if (header)
2311 atom.m_str += " " + header->getSubject();
2312 }
2313 entry.append (atom);
2314
2315 atom.m_uds = UDS_URL;
2316 atom.m_str = encodedUrl; // utf-8
2317 if (atom.m_str[atom.m_str.length () - 1] != '/')
2318 atom.m_str += '/';
2319 atom.m_str += ";UID=" + uid;
2320 atom.m_long = 0;
2321 entry.append (atom);
2322
2323 atom.m_uds = UDS_FILE_TYPE;
2324 atom.m_str = TQString();
2325 atom.m_long = S_IFREG;
2326 entry.append (atom);
2327
2328 atom.m_uds = UDS_SIZE;
2329 atom.m_long = cache->getSize();
2330 entry.append (atom);
2331
2332 atom.m_uds = UDS_MIME_TYPE;
2333 atom.m_str = "message/rfc822";
2334 atom.m_long = 0;
2335 entry.append (atom);
2336
2337 atom.m_uds = UDS_USER;
2338 atom.m_str = myUser;
2339 entry.append (atom);
2340
2341 atom.m_uds = TDEIO::UDS_ACCESS;
2342 atom.m_long = (withFlags) ? cache->getFlags() : S_IRUSR | S_IXUSR | S_IWUSR;
2343 entry.append (atom);
2344
2345 listEntry (entry, false);
2346 }
2347}
2348
2349void
2350IMAP4Protocol::doListEntry (const KURL & _url, const TQString & myBox,
2351 const imapList & item, bool appendPath)
2352{
2353 KURL aURL = _url;
2354 aURL.setQuery (TQString());
2355 UDSEntry entry;
2356 UDSAtom atom;
2357 int hdLen = item.hierarchyDelimiter().length();
2358
2359 {
2360 // mailboxName will be appended to the path if appendPath is true
2361 TQString mailboxName = item.name ();
2362
2363 // some beautification
2364 if (mailboxName.find (myBox) == 0 && mailboxName.length() > myBox.length())
2365 {
2366 mailboxName =
2367 mailboxName.right (mailboxName.length () - myBox.length ());
2368 }
2369 if (mailboxName[0] == '/')
2370 mailboxName = mailboxName.right (mailboxName.length () - 1);
2371 if (mailboxName.left(hdLen) == item.hierarchyDelimiter())
2372 mailboxName = mailboxName.right(mailboxName.length () - hdLen);
2373 if (mailboxName.right(hdLen) == item.hierarchyDelimiter())
2374 mailboxName.truncate(mailboxName.length () - hdLen);
2375
2376 atom.m_uds = UDS_NAME;
2377 if (!item.hierarchyDelimiter().isEmpty() &&
2378 mailboxName.find(item.hierarchyDelimiter()) != -1)
2379 atom.m_str = mailboxName.section(item.hierarchyDelimiter(), -1);
2380 else
2381 atom.m_str = mailboxName;
2382
2383 // konqueror will die with an assertion failure otherwise
2384 if (atom.m_str.isEmpty ())
2385 atom.m_str = "..";
2386
2387 if (!atom.m_str.isEmpty ())
2388 {
2389 atom.m_long = 0;
2390 entry.append (atom);
2391
2392 if (!item.noSelect ())
2393 {
2394 atom.m_uds = UDS_MIME_TYPE;
2395 if (!item.noInferiors ())
2396 {
2397 atom.m_str = "message/directory";
2398 } else {
2399 atom.m_str = "message/digest";
2400 }
2401 atom.m_long = 0;
2402 entry.append (atom);
2403 mailboxName += '/';
2404
2405 // explicitly set this as a directory for KFileDialog
2406 atom.m_uds = UDS_FILE_TYPE;
2407 atom.m_str = TQString();
2408 atom.m_long = S_IFDIR;
2409 entry.append (atom);
2410 }
2411 else if (!item.noInferiors ())
2412 {
2413 atom.m_uds = UDS_MIME_TYPE;
2414 atom.m_str = "inode/directory";
2415 atom.m_long = 0;
2416 entry.append (atom);
2417 mailboxName += '/';
2418
2419 // explicitly set this as a directory for KFileDialog
2420 atom.m_uds = UDS_FILE_TYPE;
2421 atom.m_str = TQString();
2422 atom.m_long = S_IFDIR;
2423 entry.append (atom);
2424 }
2425 else
2426 {
2427 atom.m_uds = UDS_MIME_TYPE;
2428 atom.m_str = "unknown/unknown";
2429 atom.m_long = 0;
2430 entry.append (atom);
2431 }
2432
2433 atom.m_uds = UDS_URL;
2434 TQString path = aURL.path();
2435 atom.m_str = aURL.url (0, 106); // utf-8
2436 if (appendPath)
2437 {
2438 if (path[path.length() - 1] == '/' && !path.isEmpty() && path != "/")
2439 path.truncate(path.length() - 1);
2440 if (!path.isEmpty() && path != "/"
2441 && path.right(hdLen) != item.hierarchyDelimiter()) {
2442 path += item.hierarchyDelimiter();
2443 }
2444 path += mailboxName;
2445 if (path.upper() == "/INBOX/") {
2446 // make sure the client can rely on INBOX
2447 path = path.upper();
2448 }
2449 }
2450 aURL.setPath(path);
2451 atom.m_str = aURL.url(0, 106); // utf-8
2452 atom.m_long = 0;
2453 entry.append (atom);
2454
2455 atom.m_uds = UDS_USER;
2456 atom.m_str = myUser;
2457 entry.append (atom);
2458
2459 atom.m_uds = UDS_ACCESS;
2460 atom.m_long = S_IRUSR | S_IXUSR | S_IWUSR;
2461 entry.append (atom);
2462
2463 atom.m_uds = UDS_EXTRA;
2464 atom.m_str = item.attributesAsString();
2465 atom.m_long = 0;
2466 entry.append (atom);
2467
2468 listEntry (entry, false);
2469 }
2470 }
2471}
2472
2473enum IMAP_TYPE
2474IMAP4Protocol::parseURL (const KURL & _url, TQString & _box,
2475 TQString & _section, TQString & _type, TQString & _uid,
2476 TQString & _validity, TQString & _hierarchyDelimiter,
2477 TQString & _info, bool cache)
2478{
2479 enum IMAP_TYPE retVal;
2480 retVal = ITYPE_UNKNOWN;
2481
2482 imapParser::parseURL (_url, _box, _section, _type, _uid, _validity, _info);
2483// kdDebug(7116) << "URL: query - '" << KURL::decode_string(_url.query()) << "'" << endl;
2484
2485 // get the delimiter
2486 TQString myNamespace = namespaceForBox( _box );
2487 kdDebug(7116) << "IMAP4::parseURL - namespace=" << myNamespace << endl;
2488 if ( namespaceToDelimiter.contains(myNamespace) )
2489 {
2490 _hierarchyDelimiter = namespaceToDelimiter[myNamespace];
2491 kdDebug(7116) << "IMAP4::parseURL - delimiter=" << _hierarchyDelimiter << endl;
2492 }
2493
2494 if (!_box.isEmpty ())
2495 {
2496 kdDebug(7116) << "IMAP4::parseURL - box=" << _box << endl;
2497
2498 if (makeLogin ())
2499 {
2500 if (getCurrentBox () != _box ||
2501 _type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK")
2502 {
2503 if ( cache )
2504 {
2505 // assume a normal box
2506 retVal = ITYPE_DIR_AND_BOX;
2507 } else
2508 {
2509 // start a listing for the box to get the type
2510 imapCommand *cmd;
2511
2512 cmd = doCommand (imapCommand::clientList ("", _box));
2513 if (cmd->result () == "OK")
2514 {
2515 for (TQValueListIterator < imapList > it = listResponses.begin ();
2516 it != listResponses.end (); ++it)
2517 {
2518 //kdDebug(7116) << "IMAP4::parseURL - checking " << _box << " to " << (*it).name() << endl;
2519 if (_box == (*it).name ())
2520 {
2521 if ( !(*it).hierarchyDelimiter().isEmpty() )
2522 _hierarchyDelimiter = (*it).hierarchyDelimiter();
2523 if ((*it).noSelect ())
2524 {
2525 retVal = ITYPE_DIR;
2526 }
2527 else if ((*it).noInferiors ())
2528 {
2529 retVal = ITYPE_BOX;
2530 }
2531 else
2532 {
2533 retVal = ITYPE_DIR_AND_BOX;
2534 }
2535 }
2536 }
2537 // if we got no list response for the box see if it's a prefix
2538 if ( retVal == ITYPE_UNKNOWN &&
2539 namespaceToDelimiter.contains(_box) ) {
2540 retVal = ITYPE_DIR;
2541 }
2542 } else {
2543 kdDebug(7116) << "IMAP4::parseURL - got error for " << _box << endl;
2544 }
2545 completeQueue.removeRef (cmd);
2546 } // cache
2547 }
2548 else // current == box
2549 {
2550 retVal = ITYPE_BOX;
2551 }
2552 }
2553 else
2554 kdDebug(7116) << "IMAP4::parseURL: no login!" << endl;
2555
2556 }
2557 else // empty box
2558 {
2559 // the root is just a dir
2560 kdDebug(7116) << "IMAP4: parseURL: box [root]" << endl;
2561 retVal = ITYPE_DIR;
2562 }
2563
2564 // see if it is a real sequence or a simple uid
2565 if (retVal == ITYPE_BOX || retVal == ITYPE_DIR_AND_BOX)
2566 {
2567 if (!_uid.isEmpty ())
2568 {
2569 if (_uid.find (':') == -1 && _uid.find (',') == -1
2570 && _uid.find ('*') == -1)
2571 retVal = ITYPE_MSG;
2572 }
2573 }
2574 if (retVal == ITYPE_MSG)
2575 {
2576 if ( (_section.find ("BODY.PEEK[", 0, false) != -1 ||
2577 _section.find ("BODY[", 0, false) != -1) &&
2578 _section.find(".MIME") == -1 &&
2579 _section.find(".HEADER") == -1 )
2580 retVal = ITYPE_ATTACH;
2581 }
2582 if ( _hierarchyDelimiter.isEmpty() &&
2583 (_type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK") )
2584 {
2585 // this shouldn't happen but when the delimiter is really empty
2586 // we try to reconstruct it from the URL
2587 if (!_box.isEmpty())
2588 {
2589 int start = _url.path().findRev(_box);
2590 if (start != -1)
2591 _hierarchyDelimiter = _url.path().mid(start-1, start);
2592 kdDebug(7116) << "IMAP4::parseURL - reconstructed delimiter:" << _hierarchyDelimiter
2593 << " from URL " << _url.path() << endl;
2594 }
2595 if (_hierarchyDelimiter.isEmpty())
2596 _hierarchyDelimiter = "/";
2597 }
2598 kdDebug(7116) << "IMAP4::parseURL - return " << retVal << endl;
2599
2600 return retVal;
2601}
2602
2603int
2604IMAP4Protocol::outputLine (const TQCString & _str, int len)
2605{
2606 if (len == -1) {
2607 len = _str.length();
2608 }
2609
2610 if (cacheOutput)
2611 {
2612 if ( !outputBuffer.isOpen() ) {
2613 outputBuffer.open(IO_WriteOnly);
2614 }
2615 outputBuffer.at(outputBufferIndex);
2616 outputBuffer.writeBlock(_str.data(), len);
2617 outputBufferIndex += len;
2618 return 0;
2619 }
2620
2621 TQByteArray temp;
2622 bool relay = relayEnabled;
2623
2624 relayEnabled = true;
2625 temp.setRawData (_str.data (), len);
2626 parseRelay (temp);
2627 temp.resetRawData (_str.data (), len);
2628
2629 relayEnabled = relay;
2630 return 0;
2631}
2632
2633void IMAP4Protocol::flushOutput(TQString contentEncoding)
2634{
2635 // send out cached data to the application
2636 if (outputBufferIndex == 0)
2637 return;
2638 outputBuffer.close();
2639 outputCache.resize(outputBufferIndex);
2640 if (decodeContent)
2641 {
2642 // get the coding from the MIME header
2643 TQByteArray decoded;
2644 if (contentEncoding.find("quoted-printable", 0, false) == 0)
2645 decoded = KCodecs::quotedPrintableDecode(outputCache);
2646 else if (contentEncoding.find("base64", 0, false) == 0)
2647 KCodecs::base64Decode(outputCache, decoded);
2648 else
2649 decoded = outputCache;
2650
2651 TQString mimetype = KMimeType::findByContent( decoded )->name();
2652 kdDebug(7116) << "IMAP4::flushOutput - mimeType " << mimetype << endl;
2653 mimeType(mimetype);
2654 decodeContent = false;
2655 data( decoded );
2656 } else {
2657 data( outputCache );
2658 }
2659 mProcessedSize += outputBufferIndex;
2660 processedSize( mProcessedSize );
2661 outputBufferIndex = 0;
2662 outputCache[0] = '\0';
2663 outputBuffer.setBuffer(outputCache);
2664}
2665
2666ssize_t IMAP4Protocol::myRead(void *data, ssize_t len)
2667{
2668 if (readBufferLen)
2669 {
2670 ssize_t copyLen = (len < readBufferLen) ? len : readBufferLen;
2671 memcpy(data, readBuffer, copyLen);
2672 readBufferLen -= copyLen;
2673 if (readBufferLen) memmove(readBuffer, &readBuffer[copyLen], readBufferLen);
2674 return copyLen;
2675 }
2676 if (!isConnectionValid()) return 0;
2677 waitForResponse( responseTimeout() );
2678 return read(data, len);
2679}
2680
2681bool
2682IMAP4Protocol::assureBox (const TQString & aBox, bool readonly)
2683{
2684 if (aBox.isEmpty()) return false;
2685
2686 imapCommand *cmd = 0;
2687
2688 if (aBox != getCurrentBox () || (!getSelected().readWrite() && !readonly))
2689 {
2690 // open the box with the appropriate mode
2691 kdDebug(7116) << "IMAP4Protocol::assureBox - opening box" << endl;
2692 selectInfo = imapInfo();
2693 cmd = doCommand (imapCommand::clientSelect (aBox, readonly));
2694 bool ok = cmd->result() == "OK";
2695 TQString cmdInfo = cmd->resultInfo();
2696 completeQueue.removeRef (cmd);
2697
2698 if (!ok)
2699 {
2700 bool found = false;
2701 cmd = doCommand (imapCommand::clientList ("", aBox));
2702 if (cmd->result () == "OK")
2703 {
2704 for (TQValueListIterator < imapList > it = listResponses.begin ();
2705 it != listResponses.end (); ++it)
2706 {
2707 if (aBox == (*it).name ()) found = true;
2708 }
2709 }
2710 completeQueue.removeRef (cmd);
2711 if (found) {
2712 if (cmdInfo.find("permission", 0, false) != -1) {
2713 // not allowed to enter this folder
2714 error(ERR_ACCESS_DENIED, cmdInfo);
2715 } else {
2716 error(ERR_SLAVE_DEFINED, i18n("Unable to open folder %1. The server replied: %2").arg(aBox).arg(cmdInfo));
2717 }
2718 } else {
2719 error(TDEIO::ERR_DOES_NOT_EXIST, aBox);
2720 }
2721 return false;
2722 }
2723 }
2724 else
2725 {
2726 // Give the server a chance to deliver updates every ten seconds.
2727 // Doing this means a server roundtrip and since assureBox is called
2728 // after every mail, we do it with a timeout.
2729 kdDebug(7116) << "IMAP4Protocol::assureBox - reusing box" << endl;
2730 if ( mTimeOfLastNoop.secsTo( TQDateTime::currentDateTime() ) > 10 ) {
2731 cmd = doCommand (imapCommand::clientNoop ());
2732 completeQueue.removeRef (cmd);
2733 mTimeOfLastNoop = TQDateTime::currentDateTime();
2734 kdDebug(7116) << "IMAP4Protocol::assureBox - noop timer fired" << endl;
2735 }
2736 }
2737
2738 // if it is the mode we want
2739 if (!getSelected().readWrite() && !readonly)
2740 {
2741 error(TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox);
2742 return false;
2743 }
2744
2745 return true;
2746}
IOSlave derived class.
Definition: imap4.h:53
virtual void del(const KURL &_url, bool isFile)
delete a mailbox
Definition: imap4.cpp:1083
virtual void parseRelay(const TQByteArray &buffer)
reimplement the parser relay hook to send the fetched data directly to an upper level
Definition: imap4.cpp:653
virtual void special(const TQByteArray &data)
Capabilites, NOOP, (Un)subscribe, Change status, Change ACL.
Definition: imap4.cpp:1224
virtual void listDir(const KURL &_url)
list a directory/mailbox
Definition: imap4.cpp:406
void specialSearchCommand(TQDataStream &)
Search current folder, the search string is passed as SECTION.
Definition: imap4.cpp:1549
void specialCustomCommand(TQDataStream &)
Send a custom command to the server.
Definition: imap4.cpp:1577
virtual void mkdir(const KURL &url, int permissions)
create a mailbox
Definition: imap4.cpp:923
void specialAnnotateMoreCommand(int command, TQDataStream &stream)
Send an annotation command which is identified by command.
Definition: imap4.cpp:1656
virtual void stat(const KURL &_url)
stat a mailbox, message, attachment
Definition: imap4.cpp:1842
virtual bool parseReadLine(TQByteArray &buffer, ulong relay=0)
reimplement the parser
Definition: imap4.cpp:715
virtual void parseWriteLine(const TQString &)
reimplement the parser
Definition: imap4.cpp:2224
virtual void get(const KURL &_url)
get a message or part of a message the data is normally sent as we get it from the server if you want...
Definition: imap4.cpp:194
virtual int outputLine(const TQCString &_str, int len=-1)
reimplement the mimeIO
Definition: imap4.cpp:2604
void specialACLCommand(int command, TQDataStream &stream)
Send an ACL command which is identified by command.
Definition: imap4.cpp:1447
enum IMAP_TYPE parseURL(const KURL &_url, TQString &_box, TQString &_section, TQString &_type, TQString &_uid, TQString &_validity, TQString &_hierarchyDelimiter, TQString &_info, bool cache=false)
Parses the given URL The return values are set by parsing the URL and querying the server.
Definition: imap4.cpp:2474
virtual void flushOutput(TQString contentEncoding=TQString())
send out cached data to the application
Definition: imap4.cpp:2633
virtual bool parseRead(TQByteArray &buffer, ulong len, ulong relay=0)
reimplement the parser read at least len bytes
Definition: imap4.cpp:680
encapulate a IMAP command
Definition: imapcommand.h:38
static imapCommand * clientSubscribe(const TQString &path)
Create a SUBSCRIBE command.
static imapCommand * clienStatus(const TQString &path, const TQString &parameters)
Create a STATUS command.
static imapCommand * clientCreate(const TQString &path)
Create a CREATE command.
static imapCommand * clientNoop()
Create a NOOP command.
static imapCommand * clientRename(const TQString &src, const TQString &dest)
Create a RENAME command.
static imapCommand * clientGetQuotaroot(const TQString &box)
Create a GETQUOTAROOT command.
static imapCommand * clientMyRights(const TQString &box)
Create a MYRIGHTS command.
static imapCommand * clientList(const TQString &reference, const TQString &path, bool lsub=false)
Create a LIST command.
static imapCommand * clientSetAnnotation(const TQString &box, const TQString &entry, const TQMap< TQString, TQString > &attributes)
Create a SETANNOTATION command.
static imapCommand * clientSetACL(const TQString &box, const TQString &user, const TQString &acl)
Create a SETACL command.
static imapCommand * clientGetACL(const TQString &box)
Create a GETACL command.
static imapCommand * clientDelete(const TQString &path)
Create a DELETE command.
bool isComplete()
is it complete?
Definition: imapcommand.cpp:76
static imapCommand * clientFetch(ulong uid, const TQString &fields, bool nouid=false)
Create a FETCH command.
static imapCommand * clientDeleteACL(const TQString &box, const TQString &user)
Create a DELETEACL command.
static imapCommand * clientUnsubscribe(const TQString &path)
Create a UNSUBSCRIBE command.
static imapCommand * clientNamespace()
Create a NAMESPACE command.
static imapCommand * clientSearch(const TQString &search, bool nouid=false)
Create a SEARCH command.
static imapCommand * clientExpunge()
Create a EXPUNGE command.
const TQString & resultInfo()
get information about the result
Definition: imapcommand.cpp:88
static imapCommand * clientStartTLS()
Create a STARTTLS command.
static imapCommand * clientClose()
Create a CLOSE command.
static imapCommand * clientLogout()
Create a LOGOUT command.
static imapCommand * clientCopy(const TQString &box, const TQString &sequence, bool nouid=false)
Create a COPY command.
static imapCommand * clientSelect(const TQString &path, bool examine=false)
Create a SELECT command.
const TQString & result()
get the result of the command
Definition: imapcommand.cpp:82
static imapCommand * clientCustom(const TQString &command, const TQString &arguments)
Create a custom command.
static imapCommand * clientGetAnnotation(const TQString &box, const TQString &entry, const TQStringList &attributeNames)
Create a GETANNOTATION command.
static imapCommand * clientStore(const TQString &set, const TQString &item, const TQString &data, bool nouid=false)
Create a STORE command.
static imapCommand * clientAppend(const TQString &box, const TQString &flags, ulong size)
Create a APPEND command.
const TQString getSubject()
get the unicode subject
Definition: mailheader.h:114
Definition: mimeio.h:29