Compare commits

...

21 commits

Author SHA1 Message Date
3c972dd34e added more parts to moz config.xml 2021-08-11 16:59:03 +01:00
7f8e95692c changed name to mailautoconf in readme.md file 2021-08-11 16:06:39 +01:00
580de6c37d changed name to mailautoconf 2021-08-11 16:04:34 +01:00
c2b818ecb8 added baseUrl to config 2021-08-11 12:23:14 +01:00
6ab3e1f76b tests 2021-08-11 12:10:12 +01:00
d6aec92545 tests 2021-08-11 12:09:01 +01:00
da722c9781 added example of client request for ms-autodiscover 2021-08-11 11:57:38 +01:00
be1d8d74f1 tests 2021-08-11 11:40:20 +01:00
3165c90205 tests 2021-08-11 11:24:04 +01:00
3be342b2f9 tests 2021-08-11 11:22:45 +01:00
4ed1a3ec37 tests 2021-08-11 11:21:40 +01:00
8a2fb1f269 tests 2021-08-11 10:53:50 +01:00
f5bbf49fd1 tests 2021-08-11 10:52:27 +01:00
3a45e551a5 added ms-autodiscover 2021-08-11 10:50:03 +01:00
4411a0aaf1 added ms-autodiscover 2021-08-11 09:33:06 +01:00
1e27206820 added check for logon domain in autoconfig generation 2021-08-10 21:41:15 +01:00
d139c1f2fc correct outgoing server config in moz-auto 2021-08-10 21:28:00 +01:00
a3bc938449 added mozilla autoconfig file generation 2021-08-10 21:14:47 +01:00
7342c1ccda spelling 2021-08-10 17:24:47 +01:00
0c3c234520 began to add dashboard example 2021-08-10 17:22:53 +01:00
22b9c8c0bd Merge pull request 'changed docker-run to podman-run because I prefer that' (#1) from main into master
Reviewed-on: https://gitea.wilde.cloud/psw/davdiscover/pulls/1
2021-08-10 17:12:46 +01:00
13 changed files with 247 additions and 35 deletions

View file

@ -1,11 +1,11 @@
# DAVDiscover - a simple, configurable autodiscover service for distributed and self-hosted groupware services. # MailAutoConf - a simple, configurable autodiscover service for distributed and self-hosted groupware services.
## What is DAVDiscover? ## What is MailAutoConf?
DAVDiscover is a self-hosted service which provides IMAP, SMTP, CalDAV, CardDav, etc MailAutoConf is a self-hosted service which provides IMAP, SMTP, CalDAV, CardDav, etc
URLs to authenticated clients for ease in set up on mobile devices and also desktop/laptop computers. URLs to authenticated clients for ease in set up on mobile devices and also desktop/laptop computers.
## What ~~does~~ will DAVDiscover do? ## What ~~does~~ will MailAutoConf do?
DAVDiscover is currently in _very_ early stages, with a _very_ limited set of features. MailAutoConf is currently in _very_ early stages, with a _very_ limited set of features.
My hope for DAVDiscover is to mimic the AutoDiscover service found in Microsoft Exchange services, My hope for MailAutoConf is to mimic the AutoDiscover service found in Microsoft Exchange services,
but with the intent of providing a set of URLS for each service which may be self-hosted and/or distributed, but with the intent of providing a set of URLS for each service which may be self-hosted and/or distributed,
primary IMAP, SMTP, CalDAV and CardDAV URLS, but hopefully more services can be added in the future. primary IMAP, SMTP, CalDAV and CardDAV URLS, but hopefully more services can be added in the future.
@ -15,7 +15,7 @@ Getting a set up like this configured on a mobile device is fairly involved for
There are many points where set up configuration mistakes can happen, leading to service outage for a user, and the difficult job of There are many points where set up configuration mistakes can happen, leading to service outage for a user, and the difficult job of
the IT consultant trying to talk the user through setting the device up over the phone. the IT consultant trying to talk the user through setting the device up over the phone.
DAVDiscover intends to patch this problem by providing the URLS and information (Port numbers, SSL/TLS type, domain name, etc.) for each service MailAutoConf intends to patch this problem by providing the URLs and information (Port numbers, SSL/TLS type, domain name, etc.) for each service
in JSON format allowing for the connecting device to automatically set up this information on the device. in JSON format allowing for the connecting device to automatically set up this information on the device.
## What problems do I expect? ## What problems do I expect?
@ -27,13 +27,13 @@ In the perfect world, this service starts to look so fantastic that mobile devic
in their own code as an Account Type (i.e. ActiveSync, Office365, iCloud, IMAP, etc. are all already there), but I'm not sure if I see that happening just yet. in their own code as an Account Type (i.e. ActiveSync, Office365, iCloud, IMAP, etc. are all already there), but I'm not sure if I see that happening just yet.
### Another problem is authentication. ### Another problem is authentication.
I'd like all clients to authenticate to the DAVDiscover service, but where do we get that authentication from? I'd like all clients to authenticate to the MailAutoConf service, but where do we get that authentication from?
We could have local accounts on the DAVDiscover server obviously, but I don't think this feels "fluent" enough. Maybe, using the primary IMAP server address, we could do an authentication request and if that succeeds the login is accepted and DAVDiscover information is sent. We could have local accounts on the MailAutoConf server obviously, but I don't think this feels "fluent" enough. Maybe, using the primary IMAP server address, we could do an authentication request and if that succeeds the login is accepted and MailAutoConf information is sent.
### More problems regarding authentication. ### More problems regarding authentication.
As we're intended to be used for self-hosted, distributed services, each service may have different usernames and passwords. As we're intended to be used for self-hosted, distributed services, each service may have different usernames and passwords.
This means there will have to be some sort of manual credential entry for each service. This means there will have to be some sort of manual credential entry for each service.
I am less concerned with this issue as it currently isn't really in the scope of DAVDiscover to handle this - the goal is to provide the core information (URL, Ports, etc.) not the credentials to log in. I of course want to make the experience as helpful as possible though, so I'll deal with any features surrounding this when I can. I am less concerned with this issue as it currently isn't really in the scope of MailAutoConf to handle this - the goal is to provide the core information (URL, Ports, etc.) not the credentials to log in. I of course want to make the experience as helpful as possible though, so I'll deal with any features surrounding this when I can.
## When will it be ready for production? ## When will it be ready for production?
Well, not yet. Well, not yet.

View file

@ -1,9 +1,9 @@
version: '3.3' version: '3.3'
services: services:
davdiscover: mailautoconf:
container_name: davdiscover container_name: mailautoconf
ports: ports:
- '8010:80' - '8010:80'
volumes: volumes:
- './config:/var/www/html/config' - './config:/var/www/html/config'
image: pswilde/davdiscover image: pswilde/mailautoconf

11
examples/dashboard.html Normal file
View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>DAVDiscover Dashboard Example</title>
</head>
<body>
<p>This is to show the basic output of DAVDiscover, displaying the first few URLs</p>
</body>
</html>

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
podman run --name davdiscover \ podman run --name mailautoconf \
--rm \ --rm \
-p "8010:80" \ -p "8010:80" \
-v ./config:/var/www/html/config \ -v ./config:/var/www/html/config \
pswilde/davdiscover pswilde/mailautoconf

View file

@ -19,9 +19,6 @@ class Loader {
break; break;
default: default:
$p = $this->get_page_name(); $p = $this->get_page_name();
if (substr($p,0,6) != "/admin") {
header('Content-Type: application/json'); // <-- header declaration
}
if(substr($p,0,1) == "/") { if(substr($p,0,1) == "/") {
Core::$CurrentPage = substr($p,1); Core::$CurrentPage = substr($p,1);
} else { } else {

View file

@ -1,5 +1,6 @@
<?php <?php
class Responder { class Responder {
private $Response;
public function show_response(){ public function show_response(){
// get the response detailed by the url requested // get the response detailed by the url requested
$response = $this->get_response(); $response = $this->get_response();
@ -13,8 +14,24 @@ class Responder {
} }
} }
private function send_response($response){ private function send_response($response){
// Send json encoded response switch ($response->content_type){
echo json_encode($response, true); case "json":
header('Content-Type: application/json');
// Send json encoded response
echo json_encode($response, true);
break;
case "ms-json":
header('Content-Type: application/json');
// Send json encoded response
echo json_encode($response->content, true);
break;
case "xml":
header('Content-Type: application/xml');
include ($response->content);
break;
}
} }
private function get_response(){ private function get_response(){
$resp = false; $resp = false;
@ -25,8 +42,16 @@ class Responder {
case "get/all": case "get/all":
$resp = $this->all_urls(); $resp = $this->all_urls();
break; break;
case "get/select": case "mail/config-v1.1.xml":
$resp = $this->selection(); $resp = $this->moz_auto_config();
break;
case "autodiscover/autodiscover.xml":
case "Autodiscover/Autodiscover.xml":
$resp = $this->ms_autodiscover();
break;
case "autodiscover/autodiscover.json": //?Email=psw%40wilde.cloud&Protocol=Autodiscoverv1&RedirectCount=1"
case "Autodiscover/Autodiscover.json":
$resp = $this->ms_autodiscover_json();
break; break;
case "none": case "none":
case "test": case "test":
@ -40,8 +65,6 @@ class Responder {
return $resp; return $resp;
} }
private function all_urls(){ private function all_urls(){
// This would be the default request from, say, an app.
$response = new Response(); $response = new Response();
// TODO:: Will work out a better message later // TODO:: Will work out a better message later
@ -49,23 +72,36 @@ class Responder {
// Cycle through each service and add to payload // Cycle through each service and add to payload
foreach (Core::$Config["Services"] as $key => $service){ foreach (Core::$Config["Services"] as $key => $service){
$response->payload[$key] = $service; $response->content[$key] = $service;
} }
return $response; return $response;
} }
private function selection(){ private function moz_auto_config(){
$response = new Response(); $response = new Response();
$response->message = "Not Implemented"; $response->content_type = "xml";
$uri = Core::full_url(); $response->content = "public/autoconfig.php";
$response->payload = parse_url($uri); return $response;
}
private function ms_autodiscover(){
$response = new Response();
$response->content_type = "xml";
$response->content = "public/autodiscover.php";
return $response;
}
private function ms_autodiscover_json(){
$response = new Response();
$response->content_type = "ms-json";
$response->content = new MSAutodiscoverJSONResponse();
$response->content->Protocol = "AutodiscoverV1";
$response->content->Url = Core::$Config["BaseURL"] . "/Autodiscover/Autodiscover.xml";
return $response; return $response;
} }
private function dummy_response(){ private function dummy_response(){
// Generate a dummy response for testing // Generate a dummy response for testing
$response = new Response(); $response = new Response();
$response->message = "OK, here's some scrumptious data! Enjoy!"; $response->message = "OK, here's some scrumptious data! Enjoy!";
$response->payload = array("data" => array("some_data" => "Ohhhhhmmmm nom nom nom nom nom nom", $response->content = array("data" => array("some_data" => "Ohhhhhmmmm nom nom nom nom nom nom",
"extra_data" => array("garnish" => "buuuuuuuuuuurp")), "extra_data" => array("garnish" => "buuuuuuuuuuurp")),
"more_data" => "yuuuuuum yum yum yum"); "more_data" => "yuuuuuum yum yum yum");
return $response; return $response;
@ -79,8 +115,9 @@ class Responder {
} }
class Response { class Response {
public $url; public $url;
public $content_type = "json";
public $message; public $message;
public $payload = array(); public $content = array();
public function __construct(){ public function __construct(){
// add requested page to response. I don't know why, but it could helpful for diagnostics at some point // add requested page to response. I don't know why, but it could helpful for diagnostics at some point
$this->url = Core::$CurrentPage; $this->url = Core::$CurrentPage;
@ -92,3 +129,7 @@ class Response {
} }
} }
class MSAutodiscoverJSONResponse {
public $Protocol;
public $Url;
}

84
src/public/autoconfig.php Normal file
View file

@ -0,0 +1,84 @@
<?php
$conf = Core::$Config["Services"];
$data = Core::get_get_data();
$email_provided = false;
$display_name = false;
$emailaddress = false;
if ($data["emailaddress"]) {
$email_address = $data["emailaddress"];
$display_name = $email_address;
$email_provided = true;
} else if ($data["path"]) {
$query = parse_url($data["path"]);
$email_address = explode("=",$query["query"]);
if ($email_address[0] == "emailaddress") {
$email_address = $email[1];
$email_provided = true;
$display_name = $email_address;
}
}
if ($email_provided) {
if(!Core::$Config["RequireAuthDomain"]) {
$email_address = str_ireplace("@".Core::$Config["Domain"],"",$email_address);
} else if (Core::$Config["LogonDomain"]) {
$email_address = str_ireplace(Core::$Config["Domain"],Core::$Config["LogonDomain"],$email_address);
}
}
// The below link has config-v1.1.xml information
// https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
?>
<clientConfig version="1.1">
<emailProvider id="<?php echo Core::$Config["Domain"]?>">
<domain><?php echo Core::$Config["Domain"]?></domain>
<displayName><?php echo $email_provided ? $display_name : "%EMAILADDRESS%" ;?></displayName>
<?php if($conf["InMail"]){
$in = $conf["InMail"]; ?>
<incomingServer type="<?php echo strtolower($in["Type"]);?>">
<hostname><?php echo $in["Server"];?></hostname>
<port><?php echo $in["Port"];?></port>
<socketType><?php echo $in["SocketType"];?></socketType>
<username><?php echo $email_provided ? $email_address : "%EMAILADDRESS%";?></username>
<authentication><?php echo $in["Authentication"];?></authentication>
</incomingServer>
<?php }
if($conf["OutMail"]){
$out = $conf["OutMail"]; ?>
<outgoingServer type="<?php echo strtolower($out["Type"]);?>">
<hostname><?php echo $out["Server"];?></hostname>
<port><?php echo $out["Port"];?></port>
<socketType><?php echo $out["SocketType"];?></socketType>
<username><?php echo $email_provided ? $email_address : "%EMAILADDRESS%";?></username>
<authentication><?php echo $out["Authentication"];?></authentication>
</outgoingServer>
<?php }
if ($conf["AddressBook"]) {
$card = $conf["AddressBook"]; ?>
<addressBook type="<?php echo strtolower($card["Type"]); ?>">
<username><?php echo $email_provided ? $email_address : "%EMAILADDRESS%";?></username>
<authentication><?php echo $card["Authentication"] ? $card["Authentication"] : "http-basic" ;?></authentication>
<serverURL><?php echo $card["Server"];?></serverURL>
</addressBook>
<?php }
if ($conf["Calendar"]){
$cal = $conf["Calendar"] ;?>
<calendar type="<?php echo strtolower($cal["Type"]);?>">
<username><?php echo $email_provided ? $email_address : "%EMAILADDRESS%";?></username>
<authentication><?php echo $card["Authentication"] ? $card["Authentication"] : "http-basic" ;?></authentication>
<serverURL><?php echo $card["Server"];?></serverURL>
</calendar>
<?php }
if ($conf["WebMail"]) {
$wm = $conf["WebMail"]; ?>
<webMail>
<loginPage url="<?php echo $wm["Server"];?>" />
<loginPageInfo url="<?php echo $wm["Server"];?>">
<username><?php echo $email_provided ? $email_address : "%EMAILADDRESS%";?></username>
<usernameField id="<?php echo $wm["UsernameDivID"];?>" name="<?php echo $wm["UsernameDivName"];?>" />
<passwordField name="<?php echo $wm["PasswordDivName"];?>" />
<loginButton id="<?php echo $wm["SubmitButtonID"];?>" name="<?php echo $wm["SubmitButtonName"];?>"/>
</loginPageInfo>
</webMail>
<?php } ?>
</emailProvider>
</clientConfig>

View file

@ -0,0 +1,55 @@
<?php
$conf = Core::$Config["Services"];
//get raw POST data so we can extract the email address
$data = file_get_contents("php://input");
// file_put_contents(Core::root_dir()."/xmltest", $data);
preg_match("/\<EMailAddress\>(.*?)\<\/EMailAddress\>/", $data, $matches);
//print_r($matches);
// Example POST Request (sent from client) :
// <?xml version="1.0" \?\>
// <Autodiscover xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
// <Request>
// <EMailAddress>psw@wilde.cloud</EMailAddress>
// <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
// </Request>
// </Autodiscover>
echo '<?xml version="1.0" encoding="utf-8" ?>';?>
<Autodiscover xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
<Account>
<AccountType>email</AccountType>
<Action>settings</Action>
<?php if ($conf["InMail"]){
$in = $conf["InMail"];?>
<Protocol>
<Type><?php echo $in["Type"];?></Type>
<Server><?php echo $in["Server"];?></Server>
<Port><?php echo $in["Port"];?></Port>
<DomainRequired><?php echo Core::$Config["RequireAuthDomain"] ? "on" : "off";?></DomainRequired>
<LoginName><?php echo $matches[1]; ?></LoginName>
<SPA><?php echo $in["SPA"] ? "on" : "off";?></SPA>
<SSL><?php echo $in["SocketType"] == "SSL" ? "on" : "off";?></SSL>
<AuthRequired><?php echo $in["NoAuthRequired"] ? "off" : "on";?></AuthRequired>
</Protocol>
<?php }
if ($conf["OutMail"]) {
$out = $conf["OutMail"];?>
<Protocol>
<Type><?php echo $out["Type"];?></Type>
<Server><?php echo $out["Server"];?></Server>
<Port><?php echo $out["Port"];?></Port>
<DomainRequired><?php echo Core::$Config["RequireAuthDomain"] ? "on" : "off";?></DomainRequired>
<LoginName><?php echo $matches[1]; ?></LoginName>
<SPA><?php echo $in["SPA"] ? "on" : "off";?></SPA>
<Encryption><?php echo $in["SocketType"];?></Encryption>
<AuthRequired><?php echo $in["NoAuthRequired"] ? "off" : "on";?></AuthRequired>
<UsePOPAuth><?php echo $in["POPAuth"] ? "on" : "off";?></UsePOPAuth>
<SMTPLast><?php echo $in["SMTPLast"] ? "on" : "off";?></SMTPLast>
</Protocol>
<?php } ?>
</Account>
</Response>
</Autodiscover>

View file

@ -1,6 +1,13 @@
; Sample config.ini file. ; Sample config.ini file.
; Copy this file to "config/config.ini" and adjust the settings to your requirements ; Copy this file to "config/config.ini" and adjust the settings to your requirements
; Set the base domain for use with this service
Domain = example.com
LogonDomain = example.local
RequireAuthDomain = false
BaseURL = "https://autoconfig.example.com"
; Admin User configuration ; Admin User configuration
; not in use yet ; not in use yet
;[AdminUser] ;[AdminUser]

View file

@ -1,14 +1,16 @@
[InMail] [InMail]
Type = "IMAP"
Server = "imap.example.com" Server = "imap.example.com"
Protocol = "IMAP"
Port = 993 Port = 993
TLS = true SocketType = SSL
Authentication = password-cleartext
[OutMail] [OutMail]
Type = "SMTP"
Server = "smtp.example.com" Server = "smtp.example.com"
Protocol = "SMTP"
Port = 465 Port = 465
TLS = true SocketType = SSL
Authentication = password-cleartext
[CalDav] [CalDav]
Server = "https://caldav.example.com/etc/etc/" Server = "https://caldav.example.com/etc/etc/"

1
src/xmltest Executable file
View file

@ -0,0 +1 @@
<EMailAddress>psw@wilde.cloud</EMailAddress>

4
test-entry.sh Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/env bash
a2enmod rewrite
service apache2 stop
exec apache2-foreground

10
test-server.sh Normal file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
podman run --name mailautoconf-test \
--rm \
-p "8010:80" \
-v ./src:/var/www/html/ \
-v ./config:/var/www/html/config \
-v ./test-entry.sh:/test-entry.sh \
--entrypoint "/bin/bash" \
php:7.4-apache \
/test-entry.sh