Git Product home page Git Product logo

lucee-cfimap's Introduction

<cfimap /> for Lucee

<cfimap /> tag for Lucee Server. Read email using IMAP protocol.

WIP

Supported actions

  • Open
  • Close
  • GetHeaderOnly
  • ListAllFolders
  • CreateFolder
  • DeleteFolder
  • RenameFolder
  • MarkRead
  • MoveMail
  • Delete
  • GetAll

Installation

  • Save to Lucee context directory ("context/library/tag")
  • you will need to restart Lucee when you have added these files (and after editing a tag)

As custom tag

If you want use this as custom tag, import in your project

this.customTagPaths = ["/custom_tag_import"];

and then invoke like this:

<cf_imap name="myimap">

lucee-cfimap's People

Contributors

byrning avatar spraguey avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

lucee-cfimap's Issues

<cfimap> Doesnt save it as a Variable.

Hello There,

We recently found out, that in Lucee our old Tag doesnt realy work anymore. So i search for a solution and i found your Solution. Now if tried to implement it, but i have some issues. I copied the imap.cfc in the main Lucee tag folder and reloaded Lucee. On first glance it worked because i didnt get the Error that Connection is not a valid commad for the tag.

But now there is this Problem. In my script i get the List off all the Folders and search through it to find the one main folder, if the folder doesnt exist i creat a new one. But i get the Error from Lucce, that my Variable doesnt exist. Even though i just set it before.

Do you know what the problem is maybe? your code is only a extention to the lucee cfimap so it shouldnt remove some commands right? Did i implement the .cfc wrong maybe? DO i need to restructer a bit of the older Code?

The start of my Code & Error:

-cfimap action="open" server = "MyServer" username = "MyUser" secure="no" password= "MyPassword" connection = "MyConnection"-

-cfimap name="MyVariable" action="ListAllFolders" connection="MyConnection"-
-cfdump var="#MyVariable#"- -cfabort-

The Lucee Error:
Lucee 5.2.6.6-SNAPSHOT Error (expression)

Message | The key [MYVARIABLE] does not exist.

PS: Sorry about the layout, I dont use this Site often.

MOVEMAIL - The parameter folder to function getFolder is required but was not passed in.

This tag works great for me except I've got a problem moving messages.
<cfimap action="MOVEMAIL" connection="Conn" folder="Inbox" messagenumber="1" newfolder="Test"`>

'The parameter folder to function getFolder is required but was not passed in.
The error occurred on line 135 in file WEB-INF/lucee/library/tag/imap/imap.cfc. '

Around Line 135 looks like this;
public function moveMail( required connection, required string newFolder, string messageNumber, string uid, string folder ){
var objFolder = getFolder( arguments.folder );
var objNewFolder = getFolder( arguments.newFolder );

Geeze looks OK to me. So not really knowing what I'm doing, I saw other calls like this elsewhere:
var objFolder = getFolder( arguments.connection, arguments.folder );
So I added arguments.connection, restarted Lucee no difference.

I also tried changing line 41 of the other imap.cfc... MoveMail: ['newFolder'], to MoveMail: ['newFolder', 'folder'], in the this.metadata.requiredAttributesPerAction section.

And many other futile things like using uid, checking the folder name I'm using works on other operations, looked at the case "movemail": section

I am completely stumped- looks like it should work, doesn't it?

Wrapper with more intuitive object implementation

Hi,
I am not using the tag, since I prefer to work in cfscript - and I have instantiated the object imap/imap.cfc directly. I find the implementation a tad clunky - I think it would have made more sense to connect on init, for instance, and not expose the connection object to the outside.
I guess this is no biggie, but just some thoughts!
:)

GetAll fails to save attachments with : in the filename on windows only

On linux it seems to encode the spaces, colons and plus signs-(using lots of $ signs). On windows attachments like "export-2018-03-28 22:31:46 +0000.csv" do not save at all. No error is thrown, the file simply is not there. I used windows 2012 with the latest snapshot release of Lucee.
Tiny issue but puzzling if someone emails you files with colons in the name especially if you developed on linux.
Thanks again for this great tag

No From email

The From email is not in the returned query?
Replyto email is available so I could just use that but am I missing something?

SSL issue

Hiya - a great little library/tag you have put together - I have still yet to test it thoroughly, but I bumped into a bug...

On my installation (Lucee 4.5/Tomcat/Windows) I had problem doing a secure connect. If I change line 21 in the connect method in imap/imap.cfc to this, then it works:

objProperties.put("mail.store.protocol", arguments.secure ? 'imaps' : 'imap');

added attachment saving

Thanks for this component!

I've changed it a bit as I wanted it purely script based, so I'm not submitting a PR.

I've added attachment saving with "attachmentpath" and "generateuniquefilenames" attributes, folder sorting, and a couple other things.

Grab what you want.

`component output="false" accessors="true" {

variables.connection = "";
variables.attachmentpath = "";
variables.generateuniquefilenames = false;

public function init(){
	return this;
}

public function connect(
	required string username,
	required string password,
	required string server,
	required boolean secure = false,
	required numeric timeout = 60,
	required numeric port = 143
){
	var clsSession = createObject("Java", "javax.mail.Session");
	var objProperties = createObject("Java", "java.util.Properties");
	var objStore = createObject("Java", "javax.mail.Store");
	var oTimeout = arguments.timeout * 1000;
	objProperties.init();
	objProperties.put("mail.store.protocol", arguments.secure ? 'imaps' : 'imap');
	objProperties.put("mail.imap.port", JavaCast( "int", arguments.port ) );
	objProperties.put("mail.imap.connectiontimeout", oTimeout);
	objProperties.put("mail.imap.timeout", oTimeout);
	objProperties.put("mail.imap.ssl.enable", JavaCast( "boolean", arguments.secure ) );
	objSession = clsSession.getInstance(objProperties);
	objStore = objSession.getStore();
	objStore.connect( arguments.server, arguments.username, arguments.password );

	variables.connection = objStore;
}

public function close() {
	variables.connection.close();
}

public function getHeaderOnly( string folder = "INBOX", startRow = 1, maxRows, uid, messageNumber ){

	var list = getMessages(arguments);
	return list;

}

public function getAll( string folder = "INBOX", startRow = 1, maxRows, uid, messageNumber, attachmentpath = "", generateuniquefilenames = false) {

	variables.attachmentpath = arguments.attachmentpath;
	variables.generateuniquefilenames = arguments.generateuniquefilenames;
	var list = getMessages(arguments, true);
	return list;

}

public function ListAllFolders( string folder, boolean recurse = false ){

	var folders = getFolders( arguments.folder, JavaCast( "boolean", arguments.recurse ) );

	var columns = "fullname,name, new, unread, totalmessages, parent, sortfolder";
	var list = QueryNew(columns);
	var listSorted = new Query();
	var sortIndex = 10;
	loop from="1" to="#ArrayLen(folders)#" step="1" index="index"{
		if( folders[index].getType() != 2 ){
			sortIndex = 10;
			queryAddRow(list);
			querySetCell(list, "fullname", folders[index].getFullName());
			querySetCell(list, "name", folders[index].getName());
			querySetCell(list, "new", folders[index].getNewMessageCount());
			querySetCell(list, "parent", folders[index].getParent().getName());
			querySetCell(list, "unread", folders[index].getUnreadMessageCount());
			querySetCell(list, "totalmessages", folders[index].getMessageCount());
			if (folders[index].getFullName() == "inbox") sortIndex = 1;
			else if (folders[index].getFullName() == "drafts") sortIndex = 2;
			else if (folders[index].getFullName() == "sent") sortIndex = 3;
			else if (folders[index].getFullName() == "junk") sortIndex = 4;
			else if (folders[index].getFullName() == "trash") sortIndex = 5;
			querySetCell(list, "sortfolder", sortIndex );
		}

	}

    listSorted.setDBType('query');
    listSorted.setAttributes(qryimap=list);
    listSorted.setSQL('select * from qryimap order by sortfolder');

	return listSorted.execute().getResult();

}

public function markRead( string folder ){

	var flag = CreateObject("Java", "javax.mail.Flags$Flag");
	var objFolder = getFolder( arguments.folder );
	objFolder.open( objFolder.READ_WRITE );
	var messages = objFolder.getMessages();

	loop from="1" to="#ArrayLen( messages )#" step="1" index="index"{
		messages[index].setFlag(flag.SEEN, true);
	}
	objFolder.close(true);

	return messages;

}

public function delete( string folder ){

	var flag = CreateObject("Java", "javax.mail.Flags$Flag");
	var objFolder = getFolder( arguments.folder );
	objFolder.open( objFolder.READ_WRITE );
	var messages = objFolder.getMessages();

	loop from="1" to="#ArrayLen( messages )#" step="1" index="index"{
		messages[index].setFlag(flag.DELETED, true);
	}
	objFolder.close(true);

	return messages;

}

public function moveMail( required string newFolder, required string messageNumber, string folder ){

	var objFolder = getFolder( arguments.folder );
	var objNewFolder = getFolder( arguments.newFolder );

	objFolder.open( objFolder.READ_WRITE );
	var messages = objFolder.getMessages( JavaCast( "int[]", ListToArray(arguments.messageNumber)) );
	objFolder.copyMessages( messages, objNewFolder );
	objFolder.close(true);

	return messages;

}

public function createFolder( required string folder ){

	var objFolder = getFolder( arguments.folder );
	objFolder.create( 3 );

	return objFolder;

}

public function renameFolder( required string folder, required string newFolder ){

	var objFolder = getFolder( arguments.folder );
	var objNewFolder = getFolder( arguments.newFolder );
	objFolder.renameTo( objNewFolder );

	return objFolder;

}

public function deleteFolder( required string folder ){

	var objFolder = getFolder( arguments.folder );
	objFolder.delete( true );

}

private function getFolder( required string folder ){

	if( !len( arguments.folder ) ){
		arguments.folder = "INBOX";
	}
	var folder = variables.connection.getFolder( arguments.folder );

	return folder;

}

private function getFolders( string folder, boolean recurse = false ){

	if( !len(trim(arguments.folder)) ){
		var folders = arguments.recurse ? variables.connection.getDefaultFolder().list("*") : variables.connection.getDefaultFolder().list();
	}else{
		var folders = arguments.recurse ? variables.connection.getFolder( arguments.folder ).list("*") : variables.connection.getFolder( arguments.folder ).list();
	}

	return folders;

}

private function openFolder( string folder ){

	if( !structKeyExists( arguments, "folder" ) ){
		var objFolder = variables.connection.getDefaultFolder();
	}else{
		var objFolder = variables.connection.getFolder( arguments.folder );
	}
	objFolder.open( objFolder.READ_ONLY );

	return objFolder;

}

private function createQuery( required messages, required string columns, required boolean all=false ){

	var flag = CreateObject("Java", "javax.mail.Flags$Flag");
	var recipient = CreateObject("Java", "javax.mail.Message$RecipientType");

	var list = QueryNew( arguments.columns );

	loop from="1" to="#ArrayLen( messages )#" step="1" index="index"{
		if( isNull(messages[index]) ){
			continue;
		}
		queryAddRow(list);
		querySetCell( list, "answered", messages[index].isSet(flag.ANSWERED) );
		if( arguments.all ) querySetCell( list, "attachmentfiles", getFileName( messages[index] ) );
		if( arguments.all ) querySetCell( list, "attachments", hasAttachments( messages[index] ) );
		if( arguments.all ) querySetCell( list, "body", getHtmlBody( messages[index] ) );
		querySetCell( list, "cc", IsArray( messages[index].getRecipients( recipient.CC ) ) ? ArrayToList(messages[index].getRecipients( recipient.TO )) : "" );
		querySetCell( list, "deleted", messages[index].isSet(flag.DELETED) );
		querySetCell( list, "draft", messages[index].isSet(flag.DRAFT) );
		querySetCell( list, "flagged", messages[index].isSet(flag.FLAGGED) );
		querySetCell( list, "from", messages[index].getSender().toString() );
		querySetCell( list, "header", ArrayToList( createObject( "java", "java.util.Collections" ).list( messages[index].getAllHeaderLines() ) ) );
		if( arguments.all ) querySetCell( list, "htmlbody", getHtmlBody( messages[index] ) );
		querySetCell( list, "lines", messages[index].getLineCount() );
		querySetCell( list, "messageid", messages[index].getMessageID() );
		querySetCell( list, "messagenumber", messages[index].getMessageNumber() );
		querySetCell( list, "recent", messages[index].isSet(flag.RECENT) );
		querySetCell( list, "replyto", ArrayToList(messages[index].getReplyTo()) );
		querySetCell( list, "rxddate", messages[index].getReceivedDate() );
		querySetCell( list, "seen", messages[index].isSet(flag.SEEN) );
		querySetCell( list, "sentDate", messages[index].getSentDate() );
		querySetCell( list, "size", messages[index].getSize() );
		querySetCell( list, "subject", messages[index].getSubject() );
		if( arguments.all ) querySetCell( list, "textbody", getText( messages[index] ) );
		querySetCell( list, "to", IsArray( messages[index].getRecipients( recipient.TO ) ) ? ArrayToList(messages[index].getRecipients( recipient.TO )) : messages[index].getRecipients( recipient.TO ) );
		querySetCell( list, "uid", messages[index].getFolder().getUID( messages[index] ) );
		if( arguments.all ) querySetCell( list, "user", messages[index].isSet(flag.USER) );
	}

	return list;

}

private function savePartAttachment( required messagepart ){

	var fileCheckCount = 1;

	if ((compareNoCase( arguments.messagepart.getDisposition(), arguments.messagepart.ATTACHMENT ) || compareNoCase( arguments.messagepart.getDisposition(), arguments.messagepart.INLINE ) ) and len(trim(arguments.messagepart.getFileName())) ) {
		if (!len(trim(variables.attachmentpath))) throw "Need attachmentpath in order to save attachments.";

		var saveFileAs = trim(arguments.messagepart.getFileName());

		if (variables.generateuniquefilenames) {
			var fileExt = trim(listLast(saveFileAs, "."));
			if (trim(listFirst(saveFileAs, ".")) eq "" or fileExt eq "" or REFindNoCase("[^a-z0-9-_ \.]", saveFileAs)) throw "File name is suspicious.";

			while ( fileExists(variables.attachmentpath & saveFileAs) ) {
				saveFileAs = trim(arguments.messagepart.getFileName());
				saveFileAs = REReplaceNoCase(saveFileAs, "\.#fileExt#$", "") & "-#fileCheckCount++#." & fileExt;
			}
		}

		arguments.messagepart.saveFile( variables.attachmentpath & saveFileAs );
		return variables.attachmentpath & saveFileAs;
	}

	return "";

}

private function getFileName( required message ){

	if( !hasAttachments(arguments.message) ) {
		return "";
	}

	var fileName = [];
	var savedAttachment = "";

	var multiPart = arguments.message.getContent();

	for ( i=0; i LT multiPart.getCount(); i++ ) {

		var bodyPart = multiPart.getBodyPart( i );

		if ( bodyPart.isMimeType( "multipart/*" ) ) {
			for ( fa=0; fa LT bodyPart.getContent().getCount(); fa++ ) {
				var attachPart = bodyPart.getContent().getBodyPart(fa);

				savedAttachment = savePartAttachment( attachPart );
				if (savedAttachment neq "") fileName.append( savedAttachment );
			}
		}
		else {
			savedAttachment = savePartAttachment( bodyPart );
			if (savedAttachment neq "") fileName.append( savedAttachment );
		}

	}

	return fileName.toList(chr(9));

}

private function getText( required message ){

    if ( arguments.message.isMimeType( "multipart/*" ) ) {

		var multiPart = arguments.message.getContent();
		for ( i=0; i LT multiPart.getCount(); i++ ) {

			var bodyPart = multiPart.getBodyPart( i );

			if( bodyPart.isMimeType("text/plain") ){
				return bodyPart.getContent();
			}

		}

    }

}

private function getHtmlBody( required message ){

    if ( arguments.message.isMimeType( "multipart/*" ) ) {

		var multiPart = arguments.message.getContent();

		for ( i=0; i LT multiPart.getCount(); i++ ) {

			var bodyPart = multiPart.getBodyPart( i );

    		if ( bodyPart.isMimeType( "text/html" ) ) {
    			return bodyPart.getContent();
    		}

		}

    }

	return "";

}

boolean function hasAttachments( message ){

    if ( arguments.message.isMimeType( "multipart/*" ) ) {

		var multiPart = arguments.message.getContent();

		for ( i=0; i LT multiPart.getCount(); i++ ) {

			var bodyPart = multiPart.getBodyPart( i );

    		if (compareNoCase( bodyPart.getDisposition(), bodyPart.ATTACHMENT ) || compareNoCase( bodyPart.getDisposition(), bodyPart.INLINE ) ) {
				return true;
    		}

		}

    }

	return false;

}


public any function getMessages( struct attr, boolean getAll = false ) {

	var messages = [];
	var columns = "answered, cc, deleted, draft, flagged, from, header, lines, messageid,
	messagenumber, recent, replyto, rxddate, seen, sentDate, size, subject, to, uid";
	var objFolder = getFolder( arguments.attr.folder );
	objFolder.open( objFolder.READ_ONLY );

	if( structKeyExists( arguments.attr, "uid" ) and len(trim(arguments.attr.uid)) ) {
		var messages = objFolder.getMessagesByUID( listToArray(arguments.attr.uid) );
	}elseif( structKeyExists( arguments.attr, "messageNumber") and len(trim(arguments.attr.messageNumber)) ){
		var messages = objFolder.getMessage( arguments.attr.messageNumber );
	}elseif( !structKeyExists( arguments.attr, "maxRows") or (objFolder.getMessageCount() lt arguments.attr.startRow)){
		var messages = objFolder.getMessages();
	}else{
		var messages = objFolder.getMessages( arguments.attr.startRow, arguments.attr.startRow + arguments.attr.maxRows - 1 );
	}

	if( arguments.getAll ){
		columns = ListAppend( columns, "attachmentfiles, attachments, body, htmlbody, textbody, user", "," );
	}

	var list = createQuery( messages, columns, arguments.getAll );

	objFolder.close( false );

	return list;
}

}
`

Using the cfimap tag

Hey,

First of all, thanks for this code! When I found out the lucee IMAP tag was different from the coldfusion one, I freaked out a bit. In example: I wanted to get emails from a given folder, this is not an option in the lucee tag.

So I guess you made the coldfusion imap tag for lucee? I'm currently stuck at an error and thought maybe you have an idea what is going wrong. These are the steps I followed:

  • I downloaded the zip and took the imap.cfc from the imap folder.
  • I put this in lucee -> libraray -> tag (I think the path has changed with a newer version?)
  • I noticed I got an error from the old imap tag I was using, so that's good news.
  • But now I'm stuck at retrieving a simple test with the following code:
<cfimap 
    server = "imap.gmail.com" 
    username = "xxxxxx" 
    action="open" 
    secure="yes" 
    password = "xxxxx" 
    connection = "mail.example.com" >

  <cfimap   
    action="getHeaderOnly"
    name="test1"
    connection="mail.example.com" >

    <cfdump var = "#test1#">

<cfimap action="close" connection = "mail.example.com">

It's giving an error saying that variable "test1" doesn't exist. I also saw an other post here on github describing the same issue. Did I do something wrong with the installation? Any inside would be very helpfull.

Thanks again!
Roland

lucee-cfimap for idiots

hi

i don't know if this is really the place for this, but i couldn't find an email address - but i was wondering how this works? after installing, does this replace the lucee cfimap tag? or is this called differently?

sorry for a probably a stupid question, but i've never had to install or use a tag (if it is a tag?!) like this before

does this have the same functionality as the adobe coldfusion tag? it seems to from your description

thanks

luke

Error when using maxrows

I tried to connect to my IMAP server (dovecot) and call "getHeaderOnly". It currently have 10 messages in the INBOX, and I use a maxRows of 51.

Doing so gives me this error:

lucee.runtime.exp.NativeException: 11 > 10 at 
com.sun.mail.imap.IMAPFolder.checkRange(IMAPFolder.java:444) at 
com.sun.mail.imap.IMAPFolder.getMessage(IMAPFolder.java:1591) at 
javax.mail.Folder.getMessages(Folder.java:884) at 
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at 
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at 
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at 
java.base/java.lang.reflect.Method.invoke(Method.java:566) at 
lucee.runtime.reflection.pairs.MethodInstance.invoke(MethodInstance.java:56) at 
lucee.runtime.reflection.Reflector.callMethod(Reflector.java:891)
[...]

If I remove the maxRows attributes, the error disappear.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.