1 /* qmail-domains.c - qmail locals/virtualdomains lookup routines
2  * Copyright (C) 2010  Bruce Guenter <bruce@untroubled.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 #include <bglibs/sysdeps.h>
19 #include <errno.h>
20 #include <sys/stat.h>
21 
22 #include <bglibs/dict.h>
23 #include <bglibs/dict.h>
24 #include <bglibs/str.h>
25 
26 #include "qmail.h"
27 
28 static dict vdomains;
29 static struct stat vdomains_stat;
30 static str vdomains_path;
31 
32 static dict locals;
33 static struct stat locals_stat;
34 static str locals_path;
35 
36 static int assume_local = 0;
37 
map_lower(str * s)38 static int map_lower(str* s)
39 {
40   str_lower(s);
41   return 1;
42 }
43 
stat_changed(const char * path,const struct stat * orig,struct stat * curr)44 static int stat_changed(const char* path, const struct stat* orig,
45 			struct stat* curr)
46 {
47   if (stat(path, curr) != 0)
48     return -1;
49   if (orig->st_mtime != curr->st_mtime
50       || orig->st_ino != curr->st_ino
51       || orig->st_size != curr->st_size)
52     return 1;
53   return 0;
54 }
55 
load_dict(const char * path,struct stat * oldstat,dict * dictp,void (* free_fn)(void *),int (* load_fn)(void))56 static int load_dict(const char* path, struct stat* oldstat,
57 		     dict* dictp, void (*free_fn)(void*),
58 		     int (*load_fn)(void))
59 {
60   struct stat s;
61   switch (stat_changed(path, oldstat, &s)) {
62   case -1:
63     if (errno != ENOENT)
64       return 0;
65     oldstat->st_mtime = 0;
66     oldstat->st_ino = 0;
67     oldstat->st_size = 0;
68     dict_free(dictp, free_fn);
69     return 1;
70   case 0:
71     return 1;
72   }
73   // FIXME: obuf_putsflush(&errbuf, "Reloading *path*\n");
74   *oldstat = s;
75   dict_free(dictp, free_fn);
76   return load_fn();
77 }
78 
_load_vdomains(void)79 static int _load_vdomains(void)
80 {
81   return dict_load_map(&vdomains, vdomains_path.s, 0, ':', map_lower, 0);
82 }
83 
load_vdomains(void)84 static int load_vdomains(void)
85 {
86   return load_dict(vdomains_path.s, &vdomains_stat, &vdomains, dict_str_free, _load_vdomains);
87 }
88 
_load_locals(void)89 static int _load_locals(void)
90 {
91   return dict_load_list(&locals, locals_path.s, 0, map_lower);
92 }
93 
load_locals(void)94 static int load_locals(void)
95 {
96   return load_dict(locals_path.s, &locals_stat, &locals, 0, _load_locals);
97 }
98 
qmail_domains_reinit(void)99 int qmail_domains_reinit(void)
100 {
101   if (!load_locals()
102       || !load_vdomains())
103     return -1;
104 
105   return 0;
106 }
107 
qmail_domains_init(void)108 int qmail_domains_init(void)
109 {
110   assume_local = getenv("CVM_QMAIL_ASSUME_LOCAL") != 0;
111 
112   if (!str_copy2s(&vdomains_path, qmail_root, "/control/virtualdomains")
113       || !str_copy2s(&locals_path, qmail_root, "/control/locals"))
114     return -1;
115 
116   if (!load_locals()
117       || !load_vdomains())
118     return -1;
119 
120   return 0;
121 }
122 
qmail_domains_lookup(const str * d,str * domain,str * prefix)123 int qmail_domains_lookup(const str* d, str* domain, str* prefix)
124 {
125   dict_entry* e;
126 
127   if (!str_copy(domain, d))
128     return -1;
129   str_lower(domain);
130 
131   if ((e = dict_get(&locals, domain)) != 0)
132     return str_copys(prefix, "") ? 1 : -1;
133 
134   if ((e = dict_get(&vdomains, domain)) == 0) {
135     unsigned i;
136     while ((i = str_findnext(domain, '.', 1)) != (unsigned)-1) {
137       str_lcut(domain, i);
138       if ((e = dict_get(&vdomains, domain)) != 0)
139 	break;
140     }
141   }
142   if (e == 0) {
143     if (assume_local) {
144       if (!str_copys(prefix, "")) return -1;
145       if (!str_copy(domain, d)) return -1;
146       str_lower(domain);
147       return 1;
148     }
149     return 0;
150   }
151   if (!str_copy(prefix, (str*)e->data))
152     return -1;
153   return 1;
154 }
155