I am Fetching content of the mail through IMAP, but I am not able to properly Fetching content.
My flow is like.
1> Connecting to IMAP.
2> Authenticate user.
3> Retrive the No of Folder.
4> Select Folder.
5> Retrive list of UID of Selected Folder.
6> And Fetching mail content according to UID which already selected folder.
But I ma not able to fetching a proper content beacause the things is that let say I have 6 mail and I am retriving those mail UID.
And passing to one by one into the FETCH Command; but the FECTH Command is retriving one last mail content every time.
I don't know why it's happening. my FETCH Command is like this.
byte[] commandBytes = System.Text.Encoding.ASCII.GetBytes(("$ FETCH " + UID + " (BODY[HEADER.FIELDS (SUBJECT FROM DATE)])" + "\r\n").ToCharArray());
Is there any solution then let me kwon..
Thanks..!!
If you're passing a UID to the server, you must use UID FETCH, not FETCH:
byte[] commandBytes = System.Text.Encoding.ASCII.GetBytes(("$ UID FETCH " + UID + " (BODY[HEADER.FIELDS (SUBJECT FROM DATE)])" + "\r\n").ToCharArray());
Related
We have several JIRA issues which have over 1000 duplicated, bogus, spam-like comments. How can we quickly delete them?
Background:
We disabled a user in active directory (Exchange) but not JIRA, so JIRA kept trying to email them updates. The email server gave a bounce-back message, and JIRA dutifully logged it to the task, which caused it to send another update, and a feedback loop was born.
The messages have this format:
Delivery has failed to these recipients or groups:
mail#example.com<mail#example.com>
The e-mail address you entered couldn't be found. Please check the recipient's e-mail address and try to resend the message. If the problem continues, please contact your helpdesk.
Diagnostic information for administrators:
Generating server: emailserver.example.com
user#example.com
#550 5.1.1 RESOLVER.ADR.RecipNotFound; not found ##
Original message headers:
Received: from jiraserver.example.com (10.0.0.999) by emailserver.example.com (10.0.0.999)
with Microsoft SMTP Server id nn.n.nnn.n; Mon, 13 Jun 2016 15:57:04 -0500
Date: Mon, 13 Jun 2016 15:57:03 -0500
Our research did not discover an easy way without using purchased plug-ins such as Script Runner or "hacking" the database, which we wanted to avoid.
Note:
We came up with a solution and are posting here to share.
I created a python script to remove all comments for a specific Jira issue.
It uses the API from Jira.
'''
This script removes all comments from a specified jira issue
Please provide Jira-Issue-Key/Id, Jira-URL, Username, PAssword in the variables below.
'''
import sys
import json
import requests
import urllib3
# Jira Issue Key or Id where comments are deleted from
JIRA_ISSUE_KEY = 'TST-123'
# URL to Jira
URL_JIRA = 'https://jira.contoso.com'
# Username with enough rights to delete comments
JIRA_USERNAME = 'admin'
# Password to Jira User
JIRA_PASSWORD = 'S9ev6ZpQ4sy2VFH2_bjKKQAYRUlDfW7ujNnrIq9Lbn5w'
''' ----- ----- Do not change anything below ----- ----- '''
# Ignore SSL problem (certificate) - self signed
urllib3.disable_warnings()
# get issue comments:
# https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-comment-get
URL_GET_COMMENT = '{0}/rest/api/latest/issue/{1}/comment'.format(URL_JIRA, JIRA_ISSUE_KEY)
# delete issue comment:
# https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-comment-id-delete
URL_DELETE_COMMENT = '{0}/rest/api/2/issue/{1}/comment/{2}'
def user_yesno():
''' Asks user for input yes or no, responds with boolean '''
allowed_response_yes = {'yes', 'y'}
allowed_response_no = {'no', 'n'}
user_response = input().lower()
if user_response in allowed_response_yes:
return True
elif user_response in allowed_response_no:
return False
else:
sys.stdout.write("Please respond with 'yes' or 'no'")
return False
# get jira comments
RESPONSE = requests.get(URL_GET_COMMENT, verify=False, auth=(JIRA_USERNAME, JIRA_PASSWORD))
# check if http response is OK (200)
if RESPONSE.status_code != 200:
print('Exit-101: Could not connect to api [HTTP-Error: {0}]'.format(RESPONSE.status_code))
sys.exit(101)
# parse response to json
JSON_RESPONSE = json.loads(RESPONSE.text)
# get user confirmation to delete all comments for issue
print('You want to delete {0} comments for issue {1}? (yes/no)' \
.format(len(JSON_RESPONSE['comments']), JIRA_ISSUE_KEY))
if user_yesno():
for jira_comment in JSON_RESPONSE['comments']:
print('Deleting Jira comment {0}'.format(jira_comment['id']))
# send delete request
RESPONSE = requests.delete(
URL_DELETE_COMMENT.format(URL_JIRA, JIRA_ISSUE_KEY, jira_comment['id']),
verify=False, auth=(JIRA_USERNAME, JIRA_PASSWORD))
# check if http response is No Content (204)
if RESPONSE.status_code != 204:
print('Exit-102: Could not connect to api [HTTP-Error: {0}; {1}]' \
.format(RESPONSE.status_code, RESPONSE.text))
sys.exit(102)
else:
print('User abort script...')
source control: https://gist.github.com/fty4/151ee7070f2a3f9da2cfa9b1ee1c132d
Use the JIRA REST API through the Chrome JavaScript Console.
Background:
We didn't want to write a full application for what we hope is an isolated occurrence. We originally planned to use PowerShell's Invoke-WebRequest. However, authentication proved to be a challenge. The API supports Basic Authentication, though it's only recommended when using SSL, which we weren't using for our internal server. Also, our initial tests resulted in 401 errors (perhaps due to a bug).
However, the API also supports cookie-based authentication, so as long as you are generating the request from a browser which has a valid JIRA session, it just works. We chose that method.
Solution details:
First, find and review the relevant comment and issue IDs:
SELECT * FROM jira..jiraaction WHERE actiontype = 'comment' AND actionbody LIKE '%RESOLVER.ADR.RecipNotFound%';
This might be a slow query depending on the size of your JIRA data. It seems to be indexed on the issueid, so if you know that, specify it. Also, add other criteria to this query so that it only represents the comments you wish to delete.
The solution below is written for comments on a single issue, but with some additional JavaScript could be expanded to support multiple issues.
We need the list of comment IDs for use in the Chrome JavaScript console. A useful format is a comma-delimited list of strings, which you can create as follows:
SELECT '"' + CONVERT(VARCHAR(50),ID) + '", ' FROM jira..jiraaction WHERE actiontype = 'comment' AND actionbody LIKE '%RESOLVER.ADR.RecipNotFound%' AND issueid = #issueid FOR XML PATH('')
(This is not necessarily the best way to concatenate strings in SQL, but it's simple and works for this purpose.)
Now, open a new browser session and authenticate to your JIRA instance. We used Chrome, but any browser with a JavaScript console should do.
Take the string produced by that query and drop it in the JavaScript console inside of a statement like this:
CommentIDs = [StringFromSQL];
You will need to trim the trailing comma manually (or adjust the above query to do so for you). It will look like this:
CommentIDs = ["1234", "2345"];
When you run that command, you will have created a JavaScript array with all of those comment IDs.
Now we arrive at the meat of the technique. We will loop over the contents of that array and make a new AJAX call to the REST API using XMLHttpRequest (often abbreviated XHR). (There is also a jQuery option.)
for (let s of CommentIDs) {let r = new XMLHttpRequest; r.open("DELETE","http://jira.example.com/rest/api/2/issue/11111/comment/"+s,true); r.send();}
You must replace "11111" with the relevant issue ID. You can repeat this for multiple issue IDs, or you can build a multi-dimensional array and a fancier loop.
This is not elegant. It doesn't have any error handling, but you can monitor the progress using the Chrome JavaScript API.
I would use a jira-python script or a ScriptRunner groovy script. Even for a one-off bulk update, because it is easier to test and requires no database access.
Glad it worked for you though!
We solved this problem, which occurs from time to time, with ScriptRunner and a Groovy script:
// this script takes some time, when executing it in console, it takes a long time to repsonse, and then the console retunrs "null"
// - but it kepps running in the backgorund, give it some time - at least 1 second per comment and attachment to delete.
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.comments.Comment
import com.atlassian.jira.issue.comments.CommentManager
import com.atlassian.jira.issue.attachment.Attachment
import com.atlassian.jira.issue.managers.DefaultAttachmentManager
import com.atlassian.jira.issue.AttachmentManager
import org.apache.log4j.Logger
import org.apache.log4j.Level
log.setLevel(Level.DEBUG)
// NRS-1959
def issueKeys = ['XS-8071', 'XS-8060', 'XS-8065', 'XRFS-26', 'NRNM-45']
def deleted_attachments = 0
def deleted_comments = 0
IssueManager issueManager = ComponentAccessor.issueManager
CommentManager commentManager = ComponentAccessor.commentManager
AttachmentManager attachmentManager = ComponentAccessor.attachmentManager
issueKeys.each{ issueKey ->
MutableIssue issue = issueManager.getIssueObject(issueKey)
List<Comment> comments = commentManager.getComments(issue)
comments.each {comment ->
if (comment.body.contains('550 5.1.1 The email account that you tried to reach does not exist')) {
log.info issueKey + " DELETE comment:"
//log.debug comment.body
commentManager.delete(comment)
deleted_comments++
} else {
log.info issueKey + " KEEP comment:"
log.debug comment.body
}
}
List<Attachment> attachments = attachmentManager.getAttachments(issue)
attachments.each {attachment ->
if (attachment.filename.equals('icon.png')) {
log.info issueKey + " DELETE attachment " + attachment.filename
attachmentManager.deleteAttachment(attachment)
deleted_attachments++
} else {
log.info issueKey + " KEEP attachment " + attachment.filename
}
}
}
log.info "${deleted_comments} deleted comments, and ${deleted_attachments} deleted attachments"
return "${deleted_comments} deleted comments, and ${deleted_attachments} deleted attachments"
I'm designing a system that has a lot of requirements around user management/permissions, so I decided to use Spring Security ACL to manage the permissions at the Domain Objects level.
Although, using ACLs to maintain the relations between Users and Entities force us to rely on that to present the data on the UI.
The PostFilter solution that is provided by Spring Security does a good job filtering the objects that a User can/cannot see but it has a big performance issue when we're dealing with an entity that has hundreds/thousands of entries, because we need to load everything from the database and then discard the objects that user isn't allowed to "see".
That problem is described here - SEC-2409 - but it'll take some time until the feature is available. So, I'm trying to find a workaround to use Spring Security ACL but avoid the performance issue.
I thought about implementing some code to retrieve the Objects that a User can access (after the authentication process) and keep that information available to be used on every request to allow the developers to use that info to perform the queries and not relying on the PostFilter.
In order to implement that, I'm trying to find a way to retrieve the list of permissions for a given principal/granted authority but I'm not able to find a way to do that with the available AclService implementations.
Example: aclService.getObjectIdentityList(<sid>,<acl_class>)
Note: The method should use the inheritance structure and include all the ObjectIdentities that are inherited from a parent entry
Any suggestion to get the data or another approach to solve this problem?
UPDATE
I already found a way to retrieve the List of objects that a User can access.
List<ObjectIdentity> childObjects = aclService.findChildren(objectIdentity);
Map<ObjectIdentity, Acl> result = aclService.readAclsById(childObjects, sids);
And this approach work for us, because we just have a few entities which the access is controlled by ACLs, so we can construct the list of ObjectsIdentities that a User has access.
Although, the Map that is being return, is returning all the ACLs for the ObjectIdentities that are being passed and then I need to check the if the user has access to each ObjectIdentity that is being returned.
Do you have an easy way to do this or to simplify all of this logic?
The current approach to handling larger data sets is to update your query to include the currently logged in user within your query. For example, you can use Spring Security and Spring Data integration to update your query to refer to the current user:
#Query("select d from MyDomain d where d.owner = #{principal.name}")
Obviously this is not ideal because you need to manage the permissions manually. Once we resolve SEC-2409 Spring can do a lot of the heavy lifting for you automatically.
I ran into this question from needing a solution to deal with the lack of SEC-2409. Looking at JdbcAclService.findChildren() lead me to this
private final String FIND_OBJECTS_WITH_ACCESS = ""+
"SELECT " +
" obj.object_id_identity AS obj_id, " +
" class.class AS class " +
"FROM " +
" acl_object_identity obj, " +
" acl_class class, " +
" acl_entry entry " +
"WHERE " +
" obj.object_id_class = class.id " +
" and entry.granting = true " +
" and entry.acl_object_identity = obj.id " +
" and entry.sid = (SELECT id FROM acl_sid WHERE sid = ?) " +
" and obj.object_id_class = (SELECT id FROM acl_class WHERE acl_class.class = ?) " +
"GROUP BY " +
" obj.object_id_identity, " +
" class.class ";
public List<ObjectIdentity> getObjectsWithAccess(Class clazz, String sid) {
Object[] args = { sid, clazz.getName() };
List<ObjectIdentity> objects = _jdbcTemplate.query(FIND_OBJECTS_WITH_ACCESS, args, getRowMapper());
return objects.size() == 0 ? null : objects;
}
private RowMapper<ObjectIdentity> getRowMapper() {
return (rs, rowNum) -> {
String javaType = rs.getString("class");
Long identifier = rs.getLong("obj_id");
return new ObjectIdentityImpl(javaType, identifier);
};
}
I'm currently working on the imap class by barbushin. It's the only php class over the internet I can find regardless to any encoding issue. Thanks to the coder.
I have a list of messages in a table. Each message sending a message id as GET (say $mid). When a link clicked, the page turned into a view page. It should open that message and display the relevant content right? But it is not. Every message has the same content (the 1st content). The code is designed for gmail but I use it for my client. And it's work.
This is a code:
require_once('../ImapMailbox.php');
define('EMAIL', 'my#domain.com');
define('PASSWORD', '*********');
define('ATTACHMENTS_DIR', dirname(__FILE__) . '/attachments');
$mailbox = new ImapMailbox('{imap.gmail.com:993/imap/ssl}INBOX', EMAIL, PASSWORD, ATTACHMENTS_DIR, 'utf-8');
$mails = array();
// Get some mail
$mailsIds = $mailbox->searchMailBox('ALL');
if(!$mailsIds) {
die('Mailbox is empty');
}
$mailId = reset($mailsIds);
$mail = $mailbox->getMail($mailId);
var_dump($mail);
var_dump($mail->getAttachments());
The original is here: https://github.com/barbushin/php-imap
Finally, I found my way home. According to the script there's a line says "mailId". Which is straight forward what is it about.
It was set to the first array by reset(). So the only thing I need to do is extract the message id from it ($mailId is an array of ids). So I simply add an array behind it.
$mailId=$mailsIds[$_GET[uid]];
While $_GET[uid] is a message id sent from a previous page.
I'm trying to read email from GMail using gmail-xoauth Gem. I want to read the email and leave its unread status.
First, I tried reading just the header. Works.
imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs = nil, verify = false)
imap.authenticate('XOAUTH2', email, access_token)
imap.select('INBOX')
imap.search(["SINCE", since]).each do |message_id|
msg = imap.fetch(message_id,'RFC822.HEADER')[0].attr['RFC822.HEADER']
mail = Mail.read_from_string msg
puts mail.subject
end
Now, I want to read the body/text of the Email without marking it read.
This maybe be very late but I will leave it here for anyone else that stumbles onto this. If, for what ever reason you want to read the email and leave the flags intake, use:
imap.examine('INBOX')
instead of:
imap.select('INBOX')
From the Net::IMAP doc
Sends a EXAMINE command to select a mailbox so that messages in the mailbox can be accessed. Behaves the same as select(), except that the selected mailbox is identified as read-only.
Based on the documentation you need to use the store method. The documentation mentions:
store(set, attr, flags)
Sends a STORE command to alter data associated with messages in the mailbox, in particular their flags. The set parameter is a number or an array of numbers or a Range object. Each number is a message sequence number. attr is the name of a data item to store: ‘FLAGS’ means to replace the message’s flag list with the provided one; ‘+FLAGS’ means to add the provided flags; and ‘-FLAGS’ means to remove them. flags is a list of flags.
The return value is an array of Net::IMAP::FetchData. For example:
p imap.store(6..8, "+FLAGS", [:Deleted])
#=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
#<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
#<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
So you have to remove the :Seen flag
imap.store(message_id, "-FLAGS", [:Seen])
I am new to google doc api, and using google doc api 3rd version with java ,
I m trying to share doc with permission and want to change permission on different call,
Here there is some code which i had designed to share persmission and update and delete permission , but some how i wil got some problem on each operation,
On first time i can share docs with permission , but once i had share and again try to share with other permission then it cant allow me to upda so what should be i missing at here ,
AclEntry acl = new AclEntry();
AclScope aclScope = new AclScope(AclScope.Type.USER,"xyz#gmail.com");
acl.setScope(aclScope);
//AclRole aclRole = new AclRole();
acl.setRole(AclRole.READER);
URL url = buildUrl(URL_DEFAULT + URL_DOCLIST_FEED + "/" + folderResId + URL_FOLDERS + "/" + entry.getResourceId());
System.out.println(url.toString());
System.out.println("url : "+entry.getAclFeedLink().getHref());
// For Insert
//service.insert(new URL(entry.getAclFeedLink().getHref()), acl);
// For Update
//service.getRequestFactory().setHeader("If-Match", "*");
//service.update(new URL(entry.getEditLink().getHref()), acl);
// For Delete
service.getRequestFactory().setHeader("If-Match", "*");
service.delete(new URL(entry.getEditLink().getHref()), aclScope);
after insert first tim eif itry to insert again i wil get this error..
com.google.gdata.util.VersionConflictException: This user already has access to the document.
After insert if i try to delete that then i wil get this error
com.google.gdata.util.InvalidEntryException: Unexpected resource version ID
thnaks in advance,
Just a comment about your first error message: according to my experience with this api, this is the behavior that is in fact expected, as long as you are trying to insert an acl record that is equivalent to an already existent one. In other words, you can't insert a duplicate of an existing acl record, you always get that exception when you try to do so.