1 // Implements dm_plugin_load and base DMPlugin methods
2 
3 // For all support, instructions and copyright go to:
4 // http://e2guardian.org/
5 // Released under the GPL v2, with the OpenSSL exception described in the README file.
6 
7 // INCLUDES
8 
9 #ifdef HAVE_CONFIG_H
10 #include "dgconfig.h"
11 #endif
12 #include "DownloadManager.hpp"
13 #include "ConfigVar.hpp"
14 #include "OptionContainer.hpp"
15 #include "ConnectionHandler.hpp"
16 #include "RegExp.hpp"
17 
18 #include <iostream>
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <syslog.h>
24 
25 // GLOBALS
26 
27 extern bool is_daemonised;
28 extern OptionContainer o;
29 extern thread_local std::string thread_id;
30 
31 extern dmcreate_t defaultdmcreate;
32 
33 // find the class factory functions for any DM plugins we've been configured to build
34 
35 #ifdef ENABLE_FANCYDM
36 extern dmcreate_t fancydmcreate;
37 #endif
38 
39 #ifdef ENABLE_TRICKLEDM
40 extern dmcreate_t trickledmcreate;
41 #endif
42 
43 // IMPLEMENTATION
44 
45 //
46 // DMPlugin
47 //
48 
49 // constructor
DMPlugin(ConfigVar & definition)50 DMPlugin::DMPlugin(ConfigVar &definition)
51     : alwaysmatchua(false), cv(definition), mimelistenabled(false), extensionlistenabled(false)
52 {
53 }
54 
55 // default initialisation procedure
init(void * args)56 int DMPlugin::init(void *args)
57 {
58     bool lastplugin = *((bool *)args);
59     if (!lastplugin) {
60         // compile regex for matching supported user agents
61         String r(cv["useragentregexp"]);
62         if (r.length() > 0) {
63 #ifdef DGDEBUG
64             std::cerr << thread_id << "useragent regexp: " << r << std::endl;
65 #endif
66             ua_match.comp(r.toCharArray());
67         } else {
68 // no useragent regex? then default to .*
69 #ifdef DGDEBUG
70             std::cerr << thread_id << "no useragent regular expression; defaulting to .*" << std::endl;
71 #endif
72             alwaysmatchua = true;
73         }
74         if (!readStandardLists())
75             return -1;
76     }
77 #ifdef DGDEBUG
78     else
79         std::cerr << thread_id << "Fallback DM plugin; no matching options loaded" << std::endl;
80 #endif
81     return 0;
82 }
83 
84 // default method for sending the client a download link
sendLink(Socket & peersock,String & linkurl,String & prettyurl)85 bool DMPlugin::sendLink(Socket &peersock, String &linkurl, String &prettyurl)
86 {
87     // 1220 "<p>Scan complete.</p><p>Click here to download: "
88     String message(o.language_list.getTranslation(1220));
89     message += "<a href=\"" + linkurl + "\">" + prettyurl + "</a></p></body></html>\n";
90     return peersock.writeString(message.toCharArray());
91 }
92 
93 // default method for deciding whether we will handle a request
willHandle(HTTPHeader * requestheader,HTTPHeader * docheader)94 bool DMPlugin::willHandle(HTTPHeader *requestheader, HTTPHeader *docheader)
95 {
96     // match user agent first (quick)
97     RegResult Rre;
98     if (!(alwaysmatchua || ua_match.match(requestheader->userAgent().toCharArray(),Rre)))
99         return false;
100 
101     // then check standard lists (mimetypes & extensions)
102 
103     // mimetypes
104     String mimetype("");
105     bool matchedmime = false;
106     if (mimelistenabled) {
107         mimetype = docheader->getContentType();
108 #ifdef DGDEBUG
109         std::cerr << thread_id << "mimetype: " << mimetype << std::endl;
110 #endif
111         String lc;
112         if (mimetypelist.findInList(mimetype.toCharArray(), lc) == NULL) {
113             if (!extensionlistenabled)
114                 return false;
115         } else
116             matchedmime = true;
117     }
118 
119     if (extensionlistenabled && !matchedmime) {
120         // determine the extension
121         String path(requestheader->decode(requestheader->getUrl()));
122         path.removeWhiteSpace();
123         path.toLower();
124         path.removePTP();
125         path = path.after("/");
126         path.hexDecode();
127         path.realPath();
128         String disposition(docheader->disposition());
129         String extension;
130         if (disposition.length() > 2) {
131             extension = disposition;
132             while (extension.contains(".")) {
133                 extension = extension.after(".");
134             }
135             extension = "." + extension;
136         } else {
137             if (!path.contains("?")) {
138                 extension = path;
139             } else {
140                 if (mimetype.length() == 0)
141                     mimetype = docheader->getContentType();
142                 if (mimetype.contains("application/")) {
143                     extension = path;
144                     if (extension.contains("?")) {
145                         extension = extension.before("?");
146                     }
147                 }
148             }
149         }
150 #ifdef DGDEBUG
151         std::cerr << thread_id << "extension: " << extension << std::endl;
152 #endif
153         // check the extension list
154         String lc;
155         if (!extension.contains(".") || (extensionlist.findEndsWith(extension.toCharArray(), lc) == NULL))
156             return matchedmime;
157     }
158 
159     return true;
160 }
161 
162 // read in all the lists of various things we wish to handle
readStandardLists()163 bool DMPlugin::readStandardLists()
164 {
165     mimetypelist.reset(); // incase this is a reload
166     extensionlist.reset();
167 
168     String filename(cv["managedmimetypelist"]);
169     if (filename.length() > 0) {
170         if (!mimetypelist.readItemList(filename.toCharArray(), false, 0)) {
171             if (!is_daemonised) {
172                 std::cerr << thread_id << "Error opening managedmimetypelist" << std::endl;
173             }
174             syslog(LOG_ERR, "Error opening managedmimetypelist");
175             return false;
176         }
177         mimetypelist.doSort(false);
178         mimelistenabled = true;
179     } else {
180         mimelistenabled = false;
181     }
182 
183     filename = cv["managedextensionlist"];
184     if (filename.length() > 0) {
185         if (!extensionlist.readItemList(filename.toCharArray(), false, 0)) {
186             if (!is_daemonised) {
187                 std::cerr << thread_id << "Error opening managedextensionlist" << std::endl;
188             }
189             syslog(LOG_ERR, "Error opening managedextensionlist");
190             return false;
191         }
192         extensionlist.doSort(false);
193         extensionlistenabled = true;
194     } else {
195         extensionlistenabled = false;
196     }
197 
198     return true;
199 }
200 
201 // take in a DM plugin configuration file, find the DMPlugin descendent matching the value of plugname, and store its class factory funcs for later use
dm_plugin_load(const char * pluginConfigPath)202 DMPlugin *dm_plugin_load(const char *pluginConfigPath)
203 {
204     ConfigVar cv;
205 
206     if (cv.readVar(pluginConfigPath, "=") > 0) {
207         if (!is_daemonised) {
208             std::cerr << thread_id << "Unable to load plugin config: " << pluginConfigPath << std::endl;
209         }
210         syslog(LOG_ERR, "Unable to load plugin config %s", pluginConfigPath);
211         return NULL;
212     }
213 
214     String plugname(cv["plugname"]);
215 
216     if (plugname.length() < 1) {
217         if (!is_daemonised) {
218             std::cerr << thread_id << "Unable read plugin config plugname variable: " << pluginConfigPath << std::endl;
219         }
220         syslog(LOG_ERR, "Unable read plugin config plugname variable %s", pluginConfigPath);
221         return NULL;
222     }
223 
224     if (plugname == "default") {
225 #ifdef DGDEBUG
226         std::cerr << thread_id << "Enabling default DM plugin" << std::endl;
227 #endif
228         return defaultdmcreate(cv);
229     }
230 
231 #ifdef ENABLE_FANCYDM
232     if (plugname == "fancy") {
233 #ifdef DGDEBUG
234         std::cerr << thread_id << "Enabling fancy DM plugin" << std::endl;
235 #endif
236         return fancydmcreate(cv);
237     }
238 #endif
239 
240 #ifdef ENABLE_TRICKLEDM
241     if (plugname == "trickle") {
242 #ifdef DGDEBUG
243         std::cerr << thread_id << "Enabling trickle DM plugin" << std::endl;
244 #endif
245         return trickledmcreate(cv);
246     }
247 #endif
248 
249     if (!is_daemonised) {
250         std::cerr << thread_id << "Unable to load plugin: " << plugname << std::endl;
251     }
252     syslog(LOG_ERR, "Unable to load plugin %s", plugname.toCharArray());
253     return NULL;
254 }
255