Ejemplo n.º 1
1
status_t
POP3Protocol::SyncMessages()
{
	bool leaveOnServer;
	if (fSettings.FindBool("leave_mail_on_server", &leaveOnServer) != B_OK)
		leaveOnServer = true;

	// create directory if not exist
	create_directory(fDestinationDir, 0777);

	printf("POP3Protocol::SyncMessages()\n");
	_ReadManifest();

	SetTotalItems(2);
	ReportProgress(0, 1, "Connect to server...");
	status_t error = Connect();
	if (error < B_OK) {
		ResetProgress();
		return error;
	}

	ReportProgress(0, 1, MDR_DIALECT_CHOICE("Getting UniqueIDs...",
		"固有のIDを取得中..."));
	error = _UniqueIDs();
	if (error < B_OK) {
		ResetProgress();
		return error;
	}

	BStringList toDownload;
	fManifest.NotHere(fUniqueIDs, &toDownload);

	int32 numMessages = toDownload.CountItems();
	if (numMessages == 0) {
		CheckForDeletedMessages();
		ResetProgress();
		return B_OK;
	}

	ResetProgress();
	SetTotalItems(toDownload.CountItems());

	printf("POP3: Messages to download: %i\n", (int)toDownload.CountItems());
	for (int32 i = 0; i < toDownload.CountItems(); i++) {
		const char* uid = toDownload.ItemAt(i);
		int32 toRetrieve = fUniqueIDs.IndexOf(uid);

		if (toRetrieve < 0) {
			// should not happen!
			error = B_NAME_NOT_FOUND;
			printf("POP3: uid %s index %i not found in fUniqueIDs!\n", uid,
				(int)toRetrieve);
			continue;
		}

		BPath path(fDestinationDir);
		BString fileName = "Downloading file... uid: ";
		fileName += uid;
		fileName.ReplaceAll("/", "_SLASH_");
		path.Append(fileName);
		BEntry entry(path.Path());
		BFile file(&entry, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
		error = file.InitCheck();
		if (error != B_OK) {
			printf("POP3: Can't create file %s\n ", path.Path());
			break;
		}
		BMailMessageIO mailIO(this, &file, toRetrieve);

		entry_ref ref;
		entry.GetRef(&ref);

		// the ref becomes invalid after renaming the file thus we already
		// write the status here
		MarkMessageAsRead(ref, B_UNREAD);

		int32 size = MessageSize(toRetrieve);
		if (fFetchBodyLimit < 0 || size <= fFetchBodyLimit) {
			error = mailIO.Seek(0, SEEK_END);
			if (error < 0) {
				printf("POP3: Failed to download body %s\n ", uid);
				break;
			}
			NotifyHeaderFetched(ref, &file);
			NotifyBodyFetched(ref, &file);

			if (!leaveOnServer)
				Delete(toRetrieve);
		} else {
			int32 dummy;
			error = mailIO.ReadAt(0, &dummy, 1);
			if (error < 0) {
				printf("POP3: Failed to download header %s\n ", uid);
				break;
			}
			NotifyHeaderFetched(ref, &file);
		}
		ReportProgress(0, 1);

		if (file.WriteAttr("MAIL:unique_id", B_STRING_TYPE, 0, uid,
			strlen(uid)) < 0) {
			error = B_ERROR;
		}

		file.WriteAttr("MAIL:size", B_INT32_TYPE, 0, &size, sizeof(int32));

		// save manifest in case we get disturbed
		fManifest += uid;
		_WriteManifest();
	}

	ResetProgress();

	CheckForDeletedMessages();
	Disconnect();
	return error;
}
Ejemplo n.º 2
0
status_t
POP3Protocol::Retrieve(int32 message, BPositionIO *write_to)
{
	status_t returnCode;
	BString cmd;
	cmd << "RETR " << message + 1 << CRLF;
	returnCode = RetrieveInternal(cmd.String(), message, write_to, true);
	runner->ReportProgress(0 /* bytes */, 1 /* messages */);

	if (returnCode == B_OK) { // Some debug code.
		int32 message_len = MessageSize(message);
 		write_to->Seek (0, SEEK_END);
		if (write_to->Position() != message_len) {
			printf ("POP3Protocol::Retrieve Note: message size is %d, was "
			"expecting %ld, for message #%ld.  Could be a transmission error "
			"or a bad POP server implementation (does it remove escape codes "
			"when it counts size?).\n",
			(int) write_to->Position(), message_len, message);
		}
	}

	return returnCode;
}
Ejemplo n.º 3
0
status_t
POP3Protocol::Retrieve(int32 message, BPositionIO* to)
{
	BString cmd;
	cmd << "RETR " << message + 1 << CRLF;
	status_t status = RetrieveInternal(cmd.String(), message, to, true);
	ReportProgress(1, 0);

	if (status == B_OK) {
		// Check if the actual message size matches the expected one
		int32 size = MessageSize(message);
 		to->Seek(0, SEEK_END);
		if (to->Position() != size) {
			printf("POP3Protocol::Retrieve Note: message size is %" B_PRIdOFF
				", was expecting %" B_PRId32 ", for message #%" B_PRId32
				".  Could be a transmission error or a bad POP server "
				"implementation (does it remove escape codes when it counts "
				"size?).\n", to->Position(), size, message);
		}
	}

	return status;
}
Ejemplo n.º 4
0
status_t
POP3Protocol::RetrieveInternal(const char *command, int32 message,
	BPositionIO *write_to, bool post_progress)
{
	const int bufSize = 1024 * 30;

	// To avoid waiting for the non-arrival of the next data packet, try to
	// receive only the message size, plus the 3 extra bytes for the ".\r\n"
	// after the message.  Of course, if we get it wrong (or it is a huge
	// message or has lines starting with escaped periods), it will then switch
	// back to receiving full buffers until the message is done.
	int amountToReceive = MessageSize (message) + 3;
	if (amountToReceive >= bufSize || amountToReceive <= 0)
		amountToReceive = bufSize - 1;

	BString bufBString; // Used for auto-dealloc on return feature.
	char *buf = bufBString.LockBuffer (bufSize);
	int amountInBuffer = 0;
	int amountReceived;
	int testIndex;
	char *testStr;
	bool cont = true;
	bool flushWholeBuffer = false;
	write_to->Seek(0,SEEK_SET);

	if (SendCommand(command) != B_OK)
		return B_ERROR;

	struct timeval tv;
	tv.tv_sec = POP3_RETRIEVAL_TIMEOUT / 1000000;
	tv.tv_usec = POP3_RETRIEVAL_TIMEOUT % 1000000;

	struct fd_set readSet;
	FD_ZERO(&readSet);
	FD_SET(fSocket, &readSet);

	while (cont) {
		int result = 0;

#ifdef USE_SSL
		if (fUseSSL && SSL_pending(fSSL))
			result = 1;
		else
#endif
		result = select(fSocket + 1, &readSet, NULL, NULL, &tv);
		if (result == 0) {
			// No data available, even after waiting a minute.
			fLog = "POP3 timeout - no data received after a long wait.";
			runner->Stop(true);
			return B_ERROR;
		}
		if (amountToReceive > bufSize - 1 - amountInBuffer)
			amountToReceive = bufSize - 1 - amountInBuffer;
#ifdef USE_SSL
		if (fUseSSL) {
			amountReceived = SSL_read(fSSL, buf + amountInBuffer,
				amountToReceive);
		} else
#endif
		amountReceived = recv(fSocket, buf + amountInBuffer, amountToReceive, 0);
		if (amountReceived < 0) {
			fLog = strerror(errno);
			return errno;
		}
		if (amountReceived == 0) {
			fLog = "POP3 data supposedly ready to receive but not received!";
			return B_ERROR;
		}

		amountToReceive = bufSize - 1; // For next time, read a full buffer.
		amountInBuffer += amountReceived;
		buf[amountInBuffer] = 0; // NUL stops tests past the end of buffer.

		// Look for lines starting with a period.  A single period by itself on
		// a line "\r\n.\r\n" marks the end of the message (thus the need for
		// at least five characters in the buffer for testing).  A period
		// "\r\n.Stuff" at the start of a line get deleted "\r\nStuff", since
		// POP adds one as an escape code to let you have message text with
		// lines starting with a period.  For convenience, assume that no
		// messages start with a period on the very first line, so we can
		// search for the previous line's "\r\n".

		for (testIndex = 0; testIndex <= amountInBuffer - 5; testIndex++) {
			testStr = buf + testIndex;
			if (testStr[0] == '\r' && testStr[1] == '\n' && testStr[2] == '.') {
				if (testStr[3] == '\r' && testStr[4] == '\n') {
					// Found the end of the message marker.  Ignore remaining data.
					if (amountInBuffer > testIndex + 5)
						printf ("POP3Protocol::RetrieveInternal Ignoring %d bytes "
							"of extra data past message end.\n",
							amountInBuffer - (testIndex + 5));
					amountInBuffer = testIndex + 2; // Don't include ".\r\n".
					buf[amountInBuffer] = 0;
					cont = false;
				} else {
					// Remove an extra period at the start of a line.
					// Inefficient, but it doesn't happen often that you have a
					// dot starting a line of text.  Of course, a file with a
					// lot of double period lines will get processed very
					// slowly.
					memmove (buf + testIndex + 2, buf + testIndex + 3,
						amountInBuffer - (testIndex + 3) + 1 /* for NUL at end */);
					amountInBuffer--;
					// Watch out for the end of buffer case, when the POP text
					// is "\r\n..X".  Don't want to leave the resulting
					// "\r\n.X" in the buffer (flush out the whole buffer),
					// since that will get mistakenly evaluated again in the
					// next loop and delete a character by mistake.
					if (testIndex >= amountInBuffer - 4 && testStr[2] == '.') {
						printf ("POP3Protocol::RetrieveInternal: Jackpot!  You have "
							"hit the rare situation with an escaped period at the "
							"end of the buffer.  Aren't you happy it decodes it "
							"correctly?\n");
						flushWholeBuffer = true;
					}
				}
			}
		}

		if (cont && !flushWholeBuffer) {
			// Dump out most of the buffer, but leave the last 4 characters for
			// comparison continuity, in case the line starting with a period
			// crosses a buffer boundary.
			if (amountInBuffer > 4) {
				write_to->Write(buf, amountInBuffer - 4);
				if (post_progress)
					runner->ReportProgress(amountInBuffer - 4,0);
				memmove (buf, buf + amountInBuffer - 4, 4);
				amountInBuffer = 4;
			}
		} else { // Dump everything - end of message or flushing the whole buffer.
			write_to->Write(buf, amountInBuffer);
			if (post_progress)
				runner->ReportProgress(amountInBuffer,0);
			amountInBuffer = 0;
		}
	}
	return B_OK;
}
Ejemplo n.º 5
0
status_t
POP3Protocol::SyncMessages()
{
	bool leaveOnServer;
	if (fSettings.FindBool("leave_mail_on_server", &leaveOnServer) != B_OK)
		leaveOnServer = true;

	// create directory if not exist
	create_directory(fDestinationDir, 0777);

	printf("POP3Protocol::SyncMessages()\n");
	_ReadManifest();

	SetTotalItems(2);
	ReportProgress(1, 0, B_TRANSLATE("Connect to server" B_UTF8_ELLIPSIS));

	status_t error = Connect();
	if (error != B_OK) {
		printf("POP3 could not connect: %s\n", strerror(error));
		ResetProgress();
		return error;
	}

	ReportProgress(1, 0, B_TRANSLATE("Getting UniqueIDs" B_UTF8_ELLIPSIS));

	error = _RetrieveUniqueIDs();
	if (error < B_OK) {
		ResetProgress();
		Disconnect();
		return error;
	}

	BStringList toDownload;
	NotHere(fManifest, fUniqueIDs, &toDownload);

	int32 numMessages = toDownload.CountStrings();
	if (numMessages == 0) {
		CheckForDeletedMessages();
		ResetProgress();
		Disconnect();
		return B_OK;
	}

	ResetProgress();
	SetTotalItems(toDownload.CountStrings());
	SetTotalItemsSize(fTotalSize);

	printf("POP3: Messages to download: %i\n", (int)toDownload.CountStrings());
	for (int32 i = 0; i < toDownload.CountStrings(); i++) {
		const char* uid = toDownload.StringAt(i);
		int32 toRetrieve = fUniqueIDs.IndexOf(uid);

		if (toRetrieve < 0) {
			// should not happen!
			error = B_NAME_NOT_FOUND;
			printf("POP3: uid %s index %i not found in fUniqueIDs!\n", uid,
				(int)toRetrieve);
			continue;
		}

		BPath path(fDestinationDir);
		BString fileName = "Downloading file... uid: ";
		fileName += uid;
		fileName.ReplaceAll("/", "_SLASH_");
		path.Append(fileName);
		BEntry entry(path.Path());
		BFile file(&entry, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
		error = file.InitCheck();
		if (error != B_OK) {
			printf("POP3: Can't create file %s\n ", path.Path());
			break;
		}
		BMailMessageIO mailIO(this, &file, toRetrieve);
		BMessage attributes;

		entry_ref ref;
		entry.GetRef(&ref);

		int32 size = MessageSize(toRetrieve);
		if (fFetchBodyLimit < 0 || size <= fFetchBodyLimit) {
			error = mailIO.Seek(0, SEEK_END);
			if (error < 0) {
				printf("POP3: Failed to download body %s\n ", uid);
				break;
			}
			ProcessMessageFetched(ref, file, attributes);

			if (!leaveOnServer)
				Delete(toRetrieve);
		} else {
			int32 dummy;
			error = mailIO.ReadAt(0, &dummy, 1);
			if (error < 0) {
				printf("POP3: Failed to download header %s\n ", uid);
				break;
			}
			ProcessHeaderFetched(ref, file, attributes);
		}
		ReportProgress(1, 0);

		if (file.WriteAttr("MAIL:unique_id", B_STRING_TYPE, 0, uid,
				strlen(uid)) < 0)
			error = B_ERROR;

		file.WriteAttr("MAIL:size", B_INT32_TYPE, 0, &size, sizeof(int32));
		write_read_attr(file, B_UNREAD);

		// save manifest in case we get disturbed
		fManifest.Add(uid);
		_WriteManifest();
	}

	ResetProgress();

	CheckForDeletedMessages();
	Disconnect();
	return error;
}