| --[[ |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ]]-- |
| |
| -- This is atom.lua - the Atom feed for lists |
| |
| local JSON = require 'cjson' |
| local elastic = require 'lib/elastic' |
| local user = require 'lib/user' |
| local aaa = require 'lib/aaa' |
| local config = require 'lib/config' |
| local cross = require 'lib/cross' |
| |
| local emls_thrd |
| |
| -- func for fetching all child emails of a parent topic |
| function fetchChildren(r, pdoc, c, biglist) |
| c = (c or 0) + 1 |
| if c > 250 then |
| return {} |
| end |
| biglist = biglist or {} |
| local children = {} |
| local docs = elastic.find('in-reply-to:"' .. r:escape(pdoc['message-id'])..'"', 50, "mbox") |
| for k, doc in pairs(docs) do |
| if not biglist[doc['message-id']] then |
| biglist[doc['message-id']] = true |
| local mykids = fetchChildren(r, doc, c, biglist) |
| table.insert(emls_thrd, doc) |
| else |
| docs[k] = nil |
| end |
| end |
| return children |
| end |
| |
| -- func for finding the original email in a thread, if need be |
| function findParent(r, doc) |
| local step = 0 |
| while step < 50 do |
| step = step + 1 |
| if not doc['in-reply-to'] then |
| break |
| end |
| local docs = elastic.find('message-id:"' .. r:escape(doc['in-reply-to'])..'"', 1, "mbox") |
| if #docs == 0 then |
| break |
| end |
| doc = docs[1] |
| end |
| return doc |
| end |
| |
| function handle(r) |
| cross.contentType(r, "application/xhtml+xml") |
| local t = {} |
| local now = r:clock() |
| local tnow = now |
| local get = r:parseargs() |
| |
| -- make sure we have a list or a thread to display results from |
| if not get.list and not get.mid then |
| r:puts("<>") |
| return cross.OK |
| end |
| |
| -- default to any subject/body, 30 day view |
| -- but accept whatever the browser demands |
| local qs = "*" |
| local dd = 30 |
| local maxresults = 40 |
| local account = user.get(r) |
| local rights = nil |
| local listid = r:escape_html(get.list or "") |
| local listraw = "<" .. listid:gsub("@", ".") .. ">" |
| |
| -- search terms for ES |
| local sterm = { |
| term = { |
| list_raw = listraw |
| } |
| } |
| |
| |
| local emls = {} |
| emls_thrd = {} |
| |
| -- Get threads from list ID? |
| if get.list then |
| local threads = {} |
| local emails = {} |
| local emails_full = {} |
| local doc = elastic.raw { |
| _source = {'message-id','body','from','subject','epoch','list_raw', 'private'}, |
| query = { |
| bool = { |
| must = { |
| sterm, |
| { |
| query_string = { |
| default_field = "subject", |
| query = qs |
| } |
| } |
| }} |
| }, |
| |
| sort = { |
| { |
| epoch = { |
| order = "desc" |
| } |
| } |
| }, |
| size = maxresults |
| } |
| local h = #doc.hits.hits |
| |
| -- for each email found, check if we can access it and then digest it if so |
| for k = #doc.hits.hits, 1, -1 do |
| local v = doc.hits.hits[k] |
| local email = v._source |
| local canUse = true |
| if email.private then |
| if account and not rights then |
| rights = aaa.rights(r, account.credentials.uid or account.credentials.email) |
| end |
| canUse = false |
| if account then |
| local lid = email.list_raw:match("<[^.]+%.(.-)>") |
| for k, v in pairs(rights or {}) do |
| if v == "*" or v == lid then |
| canUse = true |
| break |
| end |
| end |
| end |
| end |
| if canUse then |
| local mid = email['message-id'] |
| local irt = email['in-reply-to'] |
| email.id = v._id |
| email.irt = irt |
| email.references = nil |
| email.to = nil |
| email['in-reply-to'] = nil |
| table.insert(emls, 1, email) |
| end |
| end |
| |
| -- Or get a thread? |
| elseif get.mid then |
| -- get the parent email |
| local doc = elastic.get("mbox", get.mid) |
| if doc then |
| -- make sure we have the real parent |
| local parent = findParent(r, doc) |
| |
| -- we got the original email, now let's find and process all kids |
| if parent then |
| table.insert(emls_thrd, parent) |
| fetchChildren(r, parent) |
| -- ensure access and process all children |
| for k, doc in pairs(emls_thrd) do |
| local canUse = true |
| if doc.private then |
| canUse = false |
| if account and not rights then |
| rights = aaa.rights(r, account.credentials.uid or account.credentials.email) |
| end |
| if account then |
| local lid = doc.list_raw:match("<[^.]+%.(.-)>") |
| for k, v in pairs(rights or {}) do |
| if v == "*" or v == lid then |
| canUse = true |
| break |
| end |
| end |
| end |
| end |
| if canUse then |
| table.insert(emls, doc) |
| end |
| end |
| end |
| end |
| end |
| |
| -- Generate the XML |
| local scheme = "https" |
| if r.port == 80 then |
| scheme = "http" |
| end |
| local hostname = ("%s://%s:%u"):format(scheme, r.hostname, r.port) |
| r:puts(([[<?xml version="1.0" encoding="UTF-8"?> |
| <feed xmlns="http://www.w3.org/2005/Atom"> |
| <title>%s Archives</title> |
| <link rel="self" href="%s/atom.lua?list=%s"/> |
| <link href="%s/list.html?%s"/> |
| <id>%s/list.html?%s</id> |
| ]]):format(listid, hostname, listid, hostname, listid, hostname, listid) ) |
| for k, eml in pairs(emls) do |
| local date = os.date("%Y-%m-%dT%H:%M:%S", eml.epoch) .. "Z" |
| r:puts(([[ |
| <entry> |
| <title>%s</title> |
| <author><name>%s</name></author> |
| <link rel="alternate" href="%s/thread.html/%s"/> |
| <id>urn:uuid:%s</id> |
| <updated>%s</updated> |
| <content type="xhtml"> |
| <div xmlns="http://www.w3.org/1999/xhtml"> |
| <pre> |
| %s |
| </pre> |
| </div> |
| </content> |
| </entry> |
| ]]):format(r |
| :escape_html(eml.subject), |
| r:escape_html(eml.from), |
| hostname, |
| r:escape_html(eml['message-id']), |
| r:escape_html(eml['message-id']), |
| date, |
| r:escape_html(eml.body:gsub("\x0F", "")) |
| )) |
| end |
| r:puts[[</feed>]] |
| return cross.OK |
| end |
| |
| cross.start(handle) |