site/api/atom.lua (154 lines of code) (raw):
--[[
 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 cross = require 'lib/cross'
local utils = require 'lib/utils'
local emls_thrd
-- func for fetching all child emails of a parent topic
local 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
function handle(r)
    cross.contentType(r, "application/xhtml+xml; charset=UTF-8")
    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
                            }
                        },
                        {
                            range = {
                                date = {
                                    gt = "now-1M"
                                }
                            }
                        }
                    }
                }
            },
            sort = {
                {
                    epoch = {
                        order = "desc"
                    }
                }
            },
            size = maxresults
        }
        -- 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
            if aaa.canAccessDoc(r, email, account) 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 = utils.findParent(r, doc, elastic)
            -- 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
                    if aaa.canAccessDoc(r, doc, account) 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
        -- use UTC (leading !)
        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)